123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE Streams; (** AUTHOR "pjm/be"; PURPOSE "I/O buffering and formatted writing and reading"; *)
- IMPORT SYSTEM, RC := RealConversions;
- CONST
- Ok* = 0; (** zero result code means no error occurred *)
- EOF* = 4201; (** error returned when Receive reads past end of file or stream *)
- EOT* = 1AX; (** EOT character *)
- StringFull = 4202;
- FormatError* = 4203; (** error returned when ReadInt fails *)
- DefaultWriterSize* = 4096;
- DefaultReaderSize* = 4096;
- Invalid* = -1; (** invalid stream position *)
- CONST
- CR = 0DX; LF = 0AX; TAB = 9X; SP = 20X;
- TYPE
- Size* = SIZE; (* index type for array *)
- TYPE
- Position* = HUGEINT; (* offset in the stream *)
- (** Any stream output procedure or method. *)
- Sender* = PROCEDURE { DELEGATE } ( CONST buf: ARRAY OF CHAR; ofs, len: SIZE; propagate: BOOLEAN; VAR res: WORD );
- (** Any stream input procedure or method. *)
- Receiver* = PROCEDURE { DELEGATE } ( VAR buf: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: WORD );
- Connection* = OBJECT
- PROCEDURE Send* ( CONST data: ARRAY OF CHAR; ofs, len: SIZE; propagate: BOOLEAN; VAR res: WORD );
- END Send;
- PROCEDURE Receive* ( VAR data: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: WORD );
- END Receive;
- PROCEDURE Close*;
- END Close;
- END Connection;
- TYPE
- (** A writer buffers output before it is sent to a Sender. Must not be shared between processes. *)
- Writer* = OBJECT
- VAR
- (* buf[ 0..tail-1 ] contains data to write. *)
- tail: SIZE;
- buf: POINTER TO ARRAY OF CHAR;
- res*: WORD; (** result of last output operation. *)
- send: Sender;
- sent*: UNSIGNED64; (** count of sent bytes *)
- (** -- Initialization -- *)
-
- PROCEDURE & InitWriter* ( send: Sender; size: SIZE );
- BEGIN
- ASSERT( send # NIL );
- IF ( buf = NIL ) OR ( LEN( buf ) # size ) THEN
- NEW( buf, size );
- END;
- SELF.send := send;
- Reset;
- END InitWriter;
- PROCEDURE Reset* ( );
- BEGIN
- tail := 0; res := Ok; sent := 0;
- END Reset;
-
- (* -- -- *)
- PROCEDURE CanSetPos* ( ): BOOLEAN;
- BEGIN
- RETURN FALSE;
- END CanSetPos;
- PROCEDURE SetPos* ( pos: Position );
- BEGIN
- HALT( 1234 )
- END SetPos;
- PROCEDURE Update* ( );
- BEGIN
- IF ( res = Ok ) THEN
- send( buf^, 0, tail, TRUE, res );
- IF res = Ok THEN INC( sent, tail ); tail := 0 END; (*! UInt64 *)
- END;
- END Update;
- (** Current write position. *)
- PROCEDURE Pos* ( ): Position;
- BEGIN
- RETURN sent + tail; (*! UInt64 *)
- END Pos;
- (** -- Write raw binary data -- *)
- (** Write one byte. *)
- PROCEDURE Char* ( x: CHAR );
- BEGIN
- IF ( tail = LEN( buf )) & ( res = Ok ) THEN
- send( buf^, 0, tail, FALSE, res );
- IF res = Ok THEN INC( sent, tail ); tail := 0 END; (*! UInt64 *)
- END;
- IF res = Ok THEN buf[ tail ] := x; INC( tail ) END;
- END Char;
- (** Write len bytes from x, starting at ofs. *)
- PROCEDURE Bytes* ( CONST x: ARRAY OF CHAR; ofs, len: SIZE );
- VAR n: SIZE;
- BEGIN
- LOOP
- n := LEN( buf ) - tail; (* space available in the internal buffer *)
- IF n = 0 THEN
- IF res = Ok THEN (* send current buffer *)
- send( buf^, 0, tail, FALSE, res );
- IF res = Ok THEN INC( sent, tail ); tail := 0 ELSE EXIT END; (*! UInt64 *)
- ELSE
- EXIT (* should not be writing on an erroneous rider *)
- END;
- n := LEN( buf );
- END;
- IF n > len THEN n := len END;
- ASSERT ( tail + n <= LEN( buf )); (* index check *)
- SYSTEM.MOVE( ADDRESSOF( x[ ofs ] ), ADDRESSOF( buf[ tail ] ), n ); INC( tail, n );
- IF len = n THEN EXIT END; (* done *)
- INC( ofs, n ); DEC( len, n );
- END
- END Bytes;
- (** Write a SHORTINT. *)
- PROCEDURE RawSInt* ( x: SHORTINT );
- BEGIN
- Char( SYSTEM.VAL( CHAR, x ));
- END RawSInt;
- (** Write an INTEGER. *)
- PROCEDURE RawInt* ( x: INTEGER );
- BEGIN
- Bytes( SYSTEM.VAL( Bytes2, x ), 0, 2 );
- END RawInt;
- (** Write a LONGINT. *)
- PROCEDURE RawLInt* ( x: LONGINT );
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 );
- END RawLInt;
- (** Write a HUGEINT. *)
- PROCEDURE RawHInt* ( x: HUGEINT );
- BEGIN
- Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 );
- END RawHInt;
- (** Write a 64 bit value in network byte order ( most significant byte first ) *)
- PROCEDURE Net64* ( x: HUGEINT );
- BEGIN
- Net32( LONGINT( x DIV 100000000H MOD 100000000H ));
- Net32( LONGINT( x MOD 100000000H ));
- END Net64;
- (** Write a 32 bit value in network byte order ( most significant byte first ) *)
- PROCEDURE Net32* ( x: LONGINT );
- BEGIN
- Char( CHR( x DIV 1000000H MOD 100H )); Char( CHR( x DIV 10000H MOD 100H )); Char( CHR( x DIV 100H MOD 100H ));
- Char( CHR( x MOD 100H ));
- END Net32;
- (** Write a 16 bit value in network byte order ( most significant byte first ) *)
- PROCEDURE Net16* ( x: LONGINT );
- BEGIN
- Char( CHR( x DIV 100H MOD 100H )); Char( CHR( x MOD 100H ));
- END Net16;
- (** write unsigned byte *)
- PROCEDURE Net8* ( x: LONGINT );
- BEGIN
- Char( CHR( x MOD 100H ));
- END Net8;
- (** Write a SET. *)
- PROCEDURE RawSet* ( x: SET ); (*! 64 bits *)
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 );
- END RawSet;
- (** Write a BOOLEAN. *)
- PROCEDURE RawBool* ( x: BOOLEAN );
- BEGIN
- IF x THEN Char( 1X ) ELSE Char( 0X ) END;
- END RawBool;
- (** Write a REAL. *)
- PROCEDURE RawReal* ( x: REAL );
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 );
- END RawReal;
- (** Write a LONGREAL. *)
- PROCEDURE RawLReal* ( x: LONGREAL );
- BEGIN
- Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 );
- END RawLReal;
- (** Write a 0X-terminated string, including the 0X terminator. *)
- PROCEDURE RawString* ( CONST x: ARRAY OF CHAR );
- VAR i := 0: SIZE;
- BEGIN
- WHILE x[ i ] # 0X DO Char( x[ i ] ); INC( i ) END;
- Char( 0X );
- END RawString;
- (** Write a number in a compressed format. *)
- PROCEDURE RawNum* ( x: LONGINT );
- BEGIN
- WHILE ( x < -64 ) OR ( x > 63 ) DO Char( CHR( x MOD 128 + 128 )); x := x DIV 128 END;
- Char( CHR( x MOD 128 ));
- END RawNum;
- (** Write a size in a compressed format. *)
- PROCEDURE RawSize* ( x: SIZE );
- BEGIN
- WHILE ( x < -64 ) OR ( x > 63 ) DO Char( CHR( x MOD 128 + 128 )); x := x DIV 128 END;
- Char( CHR( x MOD 128 ));
- END RawSize;
- (** -- Write formatted data -- *)
- (** Write an ASCII end-of-line ( CR/LF ). *)
- PROCEDURE Ln*;
- BEGIN
- Char( CR ); Char( LF );
- END Ln;
- (** Write a 0X-terminated string, excluding the 0X terminator. *)
- PROCEDURE String* ( CONST x: ARRAY OF CHAR );
- VAR i := 0: SIZE;
- BEGIN
- WHILE x[ i ] # 0X DO Char( x[ i ] ); INC( i ) END;
- END String;
- (** Write an integer in decimal right-justified in a field of at least w characters. *)
- PROCEDURE Int* ( x: HUGEINT; w: SIZE );
- VAR i: SIZE; x0: HUGEINT;
- a: ARRAY 21 OF CHAR;
- BEGIN
- IF x < 0 THEN
- IF x = MIN( HUGEINT ) THEN
- DEC( w, 20 );
- WHILE w > 0 DO Char( " " ); DEC( w ) END;
- String( "-9223372036854775808" );
- RETURN;
- ELSE
- DEC( w ); x0 := -x;
- END;
- ELSE
- x0 := x;
- END;
- i := 0;
- REPEAT a[ i ] := CHR( x0 MOD 10 + 30H ); x0 := x0 DIV 10; INC( i ) UNTIL x0 = 0;
- WHILE w > i DO Char( " " ); DEC( w ) END;
- IF x < 0 THEN Char( "-" ) END;
- REPEAT DEC( i ); Char( a[ i ] ) UNTIL i = 0;
- END Int;
- (** Write a SET in Oberon notation. *)
- PROCEDURE Set* ( s: SET ); (* from P. Saladin *)
- VAR i, last: LONGINT; dots: BOOLEAN;
- BEGIN
- Char( "{" ); last := MAX( LONGINT ); dots := FALSE;
- FOR i := MAX( SET ) TO 0 BY -1 DO
- IF i IN s THEN
- IF last = ( i + 1 ) THEN
- IF dots THEN String( ".." ); dots := FALSE END;
- IF ( i = 0 ) OR ~(( i - 1 ) IN s ) THEN Int( i, 1 ) END;
- ELSE
- IF last <= MAX( SET ) THEN String( ", " ) END;
- Int( i, 1 ); dots := TRUE;
- END;
- last := i;
- END;
- END;
- Char( "}" );
- END Set;
- (**
- Write an integer in hexadecimal right-justified in a field of at least ABS( w ) characters.
- If w < 0 THEN w least significant hex digits of x are written ( potentially including leading zeros )
- *)
- PROCEDURE Hex* ( x: HUGEINT; w: SIZE );
- VAR filler: CHAR; i, maxw: SIZE; y: HUGEINT; a: ARRAY 20 OF CHAR;
- BEGIN
- IF w < 0 THEN filler := '0'; w := -w; maxw := w ELSE filler := ' '; maxw := 16 END;
- i := 0;
- REPEAT
- y := x MOD 10H;
- IF y < 10 THEN a[ i ] := CHR( y + ORD( '0' )) ELSE a[ i ] := CHR( y-10+ORD( 'A' )) END;
- x := x DIV 10H;
- INC( i );
- UNTIL ( x=0 ) OR ( i=maxw );
- WHILE w > i DO Char( filler ); DEC( w ) END;
- REPEAT DEC( i ); Char( a[ i ] ) UNTIL i = 0;
- END Hex;
- (** Write "x" as a hexadecimal address. Do not use Hex because of arithmetic shift of the sign !*)
- PROCEDURE Address* ( x: ADDRESS );
- BEGIN
- Hex( x, -2*SIZEOF( ADDRESS ));
- END Address;
- PROCEDURE Pair( ch: CHAR; x: LONGINT );
- BEGIN
- IF ch # 0X THEN Char( ch ) END;
- Char( CHR( ORD( "0" ) + x DIV 10 MOD 10 )); Char( CHR( ORD( "0" ) + x MOD 10 ));
- END Pair;
- (** Write the date and time in ISO format ( yyyy-mm-dd hh: mm: ss ). The t and d parameters are in Oberon time and date format.
- If all parameters are within range, the output string is exactly 19 characters wide. The t or d parameter can be -1, in which
- case the time or date respectively are left out. *)
- PROCEDURE Date* ( t, d: LONGINT );
- VAR ch: CHAR;
- BEGIN
- IF d # -1 THEN
- Int( 1900 + d DIV 512, 4 ); (* year *)
- Pair( "-", d DIV 32 MOD 16 ); (* month *)
- Pair( "-", d MOD 32 ); (* day *)
- ch := " "; (* space between date and time *)
- ELSE
- ch := 0X; (* no space before time *)
- END;
- IF t # -1 THEN
- Pair( ch, t DIV 4096 MOD 32 ); (* hour *)
- Pair( ":", t DIV 64 MOD 64 ); (* min *)
- Pair( ":", t MOD 64 ); (* sec *)
- END;
- END Date;
- (** Write the date and time in RFC 822/1123 format without the optional day of the week ( dd mmm yyyy hh: mm: ss SZZZZ ) .
- The t and d parameters are in Oberon time and date format. The tz parameter specifies the time zone offset in minutes
- ( from -720 to 720 in steps of 30 ). If all parameters are within range, the output string is exactly 26 characters wide.
- The t, d or tz parameter can be -1, in which case the time, date or timezone respectively are left out. *)
- PROCEDURE Date822* ( t, d, tz: LONGINT );
- VAR i, m: LONGINT; ch: CHAR;
- BEGIN
- IF d # -1 THEN
- Int( d MOD 32, 2 ); (* day *)
- m := ( d DIV 32 MOD 16 - 1 ) * 4; (* month *)
- FOR i := m TO m + 3 DO Char( months[ i ] ) END;
- Int( 1900 + d DIV 512, 5 ); (* year *)
- ch := " "; (* space *)
- ELSE
- ch := 0X; (* no space *)
- END;
- IF t # -1 THEN
- Pair( ch, t DIV 4096 MOD 32 ); (* hour *)
- Pair( ":", t DIV 64 MOD 64 ); (* min *)
- Pair( ":", t MOD 64 ); (* sec *)
- ch := " "; (* space *)
- ELSE
- (* leave ch as before *)
- END;
- IF tz # -1 THEN
- IF ch # 0X THEN Char( ch ) END;
- IF tz >= 0 THEN Pair( "+", tz DIV 60 ) ELSE Pair( "-", ( -tz ) DIV 60 ) END;
- Pair( 0X, ABS( tz ) MOD 60 );
- END;
- END Date822;
- (** Write LONGREAL x using n character positions. *)
- PROCEDURE Float* ( x: LONGREAL; n: WORD );
- VAR buf: ARRAY 32 OF CHAR;
- BEGIN
- RC.RealToString( x, n, buf );
- String( buf );
- END Float;
- (** Write LONGREAL x in a fixed point notation. n is the overall minimal length for the output field, f the number of fraction digits following the decimal point, D the fixed exponent ( printed only when D # 0 ). *)
- PROCEDURE FloatFix* ( x: LONGREAL; n, f, D: WORD );
- VAR buf: ARRAY 64 OF CHAR;
- BEGIN
- RC.RealToStringFix( x, n, f, D, buf );
- String( buf );
- END FloatFix;
- END Writer;
- (** A special writer that buffers output to be fetched by GetString or GetRawString. *)
- StringWriter* = OBJECT ( Writer )
- PROCEDURE & InitStringWriter* ( size: SIZE );
- BEGIN
- InitWriter( Send, size )
- END InitStringWriter;
- PROCEDURE Send( CONST buf: ARRAY OF CHAR; ofs, len: SIZE; propagate: BOOLEAN; VAR res: WORD );
- BEGIN
- res := StringFull
- END Send;
- PROCEDURE CanSetPos* ( ): BOOLEAN;
- BEGIN
- RETURN TRUE;
- END CanSetPos;
- (* Set the position for the writer *)
- PROCEDURE SetPos* ( pos: Position );
- BEGIN
- IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
- tail := SIZE( pos ); sent := 0; res := Ok;
- END SetPos;
- PROCEDURE Update*;
- (* nothing to do *)
- END Update;
- (** Return the contents of the string writer ( 0X-terminated ). *)
- PROCEDURE Get* ( VAR s: ARRAY OF CHAR );
- VAR i, m: SIZE;
- BEGIN
- m := LEN( s ) - 1; i := 0;
- WHILE ( i # tail ) & ( i < m ) DO s[ i ] := buf[ i ]; INC( i ) END;
- s[ i ] := 0X; tail := 0; res := Ok
- END Get;
- (** Return the contents of the string writer ( not 0X-terminated ). The len parameters returns the string length. *)
- PROCEDURE GetRaw* ( VAR s: ARRAY OF CHAR; VAR len: SIZE );
- VAR i, m: SIZE;
- BEGIN
- m := LEN( s ); i := 0;
- WHILE ( i # tail ) & ( i < m ) DO s[ i ] := buf[ i ]; INC( i ) END;
- len := i; tail := 0; res := Ok
- END GetRaw;
- END StringWriter;
- TYPE
- (** A reader buffers input received from a Receiver. Must not be shared between processes. *)
- Reader* = OBJECT
- VAR
- head, tail: SIZE;
- buf: POINTER TO ARRAY OF CHAR;
- res*: WORD; (** result of last input operation. *)
- receive: Receiver;
- received*: UNSIGNED64; (** count of received bytes *)
- (* buf[ buf.head..buf.tail-1 ] contains data to read. *)
- PROCEDURE & InitReader* ( receive: Receiver; size: SIZE );
- BEGIN
- ASSERT ( receive # NIL );
- IF ( buf = NIL ) OR ( LEN( buf ) # size ) THEN
- NEW( buf, size );
- END;
- SELF.receive := receive; Reset;
- END InitReader;
- (** reset the reader by dropping the bytes in the buffer, resetting the result code and setting received to 0.
- This is used by seekable extensions of the reader *)
- PROCEDURE Reset*;
- BEGIN
- head := 0; tail := 0; res := Ok; received := 0
- END Reset;
- PROCEDURE CanSetPos* ( ): BOOLEAN;
- BEGIN
- RETURN FALSE
- END CanSetPos;
- PROCEDURE SetPos* ( pos: Position );
- BEGIN
- HALT( 1234 )
- END SetPos;
- (** Return bytes currently available in input buffer. *)
- PROCEDURE Available* ( ): SIZE;
- VAR n: SIZE;
- BEGIN
- IF ( res = Ok ) THEN
- IF ( head = tail ) THEN head := 0; receive( buf^, 0, LEN( buf ), 0, tail, res ); INC( received, tail );
- ELSIF ( tail # LEN( buf )) THEN
- receive( buf^, tail, LEN( buf ) - tail, 0, n, res ); (* poll *)
- INC( tail, n ); INC( received, n )
- END;
- IF res = EOF THEN res := Ok END (* ignore EOF here *)
- END;
- RETURN tail - head
- END Available;
- (** Current read position. *)
- PROCEDURE Pos* ( ): Position;
- BEGIN
- RETURN Position( received - ( tail - head ))
- END Pos;
- (** -- Read raw binary data -- *)
- (** Read one byte. x=0X if no success ( e.g. file ended ) *)
- PROCEDURE Char* ( VAR x: CHAR );
- BEGIN
- IF ( head = tail ) & ( res = Ok ) THEN head := 0; receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail ) END;
- IF res = Ok THEN x := buf[ head ]; INC( head ) ELSE x := 0X END
- END Char;
- (** Like Read, but return result. Return 0X if no success ( e.g. file ended ) *)
- PROCEDURE Get* ( ): CHAR;
- BEGIN
- IF ( head = tail ) & ( res = Ok ) THEN head := 0; receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail ) END;
- IF res = Ok THEN INC( head ); RETURN buf[ head - 1 ] ELSE RETURN 0X END
- END Get;
- (** Like Get, but leave the byte in the input buffer. *)
- PROCEDURE Peek* ( ): CHAR;
- BEGIN
- IF ( head = tail ) & ( res = Ok ) THEN
- head := 0; receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail );
- IF res = EOF THEN (* ignore EOF here *)
- res := Ok; tail := 0;
- RETURN 0X; (* Peek returns 0X at eof *)
- END
- END;
- IF res = Ok THEN RETURN buf[ head ] ELSE RETURN 0X END;
- END Peek;
- (** Read size bytes into x, starting at ofs. The len parameter returns the number of bytes that were actually read. *)
- PROCEDURE Bytes* ( VAR x: ARRAY OF CHAR; ofs, size: SIZE; VAR len: SIZE );
- VAR n: SIZE;
- BEGIN
- len := 0;
- LOOP
- n := tail - head; (* bytes available *)
- IF n = 0 THEN (* no data available *)
- head := 0;
- IF res = Ok THEN (* fill buffer *)
- receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail );
- END;
- IF res # Ok THEN (* should not be reading from erroneous rider *)
- WHILE size # 0 DO x[ ofs ] := 0X; INC( ofs ); DEC( size ) END; (* clear rest of buffer *)
- IF ( res = EOF ) & ( len # 0 ) THEN res := Ok END; (* ignore EOF if some data being returned *)
- EXIT
- END;
- n := tail
- END;
- IF n > size THEN n := size END;
- ASSERT ( ofs + n <= LEN( x )); (* index check *)
- SYSTEM.MOVE( ADDRESSOF( buf[ head ] ), ADDRESSOF( x[ ofs ] ), n ); INC( head, n ); INC( len, n );
- IF size = n THEN EXIT END; (* done *)
- INC( ofs, n ); DEC( size, n )
- END
- END Bytes;
- (** Skip n bytes on the reader. *)
- PROCEDURE SkipBytes* ( n: Position );
- VAR ch: CHAR;
- BEGIN
- WHILE n > 0 DO ch := Get( ); DEC( n ) END
- END SkipBytes;
- (** Read a SHORTINT. *)
- PROCEDURE RawSInt* ( VAR x: SHORTINT );
- BEGIN
- x := SYSTEM.VAL( SHORTINT, Get( ))
- END RawSInt;
- (** Read an INTEGER. *)
- PROCEDURE RawInt* ( VAR x: INTEGER );
- VAR x0, x1: CHAR;
- BEGIN
- x0 := Get( ); x1 := Get( ); (* defined order *)
- x := ORD( x1 ) * 100H + ORD( x0 )
- END RawInt;
- (** Read a LONGINT. *)
- PROCEDURE RawLInt* ( VAR x: LONGINT );
- VAR ignore: SIZE;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
- END RawLInt;
- (** Read a HUGEINT. *)
- PROCEDURE RawHInt* ( VAR x: HUGEINT );
- VAR ignore: SIZE;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
- END RawHInt;
- (** Read a 64 bit value in network byte order ( most significant byte first ) *)
- PROCEDURE Net64* ( ): HUGEINT;
- BEGIN
- RETURN Net32( ) * 100000000H + Net32( )
- END Net64;
- (** Read a 32 bit value in network byte order ( most significant byte first ) *)
- PROCEDURE Net32* ( ): LONGINT;
- BEGIN
- RETURN LONG( ORD( Get( ))) * 1000000H + LONG( ORD( Get( ))) * 10000H + LONG( ORD( Get( ))) * 100H + LONG( ORD( Get( )))
- END Net32;
- (** Read an unsigned 16bit value in network byte order ( most significant byte first ) *)
- PROCEDURE Net16* ( ): LONGINT;
- BEGIN
- RETURN LONG( ORD( Get( ))) * 100H + LONG( ORD( Get( )))
- END Net16;
- (** Read an unsigned byte *)
- PROCEDURE Net8* ( ): LONGINT;
- BEGIN
- RETURN LONG( ORD( Get( )))
- END Net8;
- (** Read a SET. *)
- PROCEDURE RawSet* ( VAR x: SET );
- VAR ignore: SIZE;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
- END RawSet;
- (** Read a BOOLEAN. *)
- PROCEDURE RawBool* ( VAR x: BOOLEAN );
- BEGIN
- x := ( Get( ) # 0X )
- END RawBool;
- (** Read a REAL. *)
- PROCEDURE RawReal* ( VAR x: REAL );
- VAR ignore: SIZE;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
- END RawReal;
- (** Read a LONGREAL. *)
- PROCEDURE RawLReal* ( VAR x: LONGREAL );
- VAR ignore: SIZE;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
- END RawLReal;
- (** Read a 0X-terminated string. If the input string is larger than x, read the full string and assign the truncated 0X-terminated value to x. *)
- PROCEDURE RawString* ( VAR x: ARRAY OF CHAR );
- VAR i, m: SIZE; ch: CHAR;
- BEGIN
- i := 0; m := LEN( x ) - 1;
- LOOP
- ch := Get( ); (* also returns 0X on error *)
- IF ch = 0X THEN EXIT END;
- IF i < m THEN x[ i ] := ch; INC( i ) END
- END;
- x[ i ] := 0X
- END RawString;
- (** Read a number in a compressed format. *)
- PROCEDURE RawNum* ( VAR x: LONGINT );
- VAR ch: CHAR; n, y: LONGINT;
- BEGIN
- n := 0; y := 0; ch := Get( );
- WHILE ch >= 80X DO INC( y, LSH( LONGINT( ORD( ch )) - 128, n )); INC( n, 7 ); ch := Get( ) END;
- x := ASH( LSH( LONGINT( ORD( ch )), 25 ), n - 25 ) + y
- END RawNum;
- (** Read a size in a compressed format. *)
- PROCEDURE RawSize* ( VAR x: SIZE );
- VAR ch: CHAR; n, y: SIZE;
- BEGIN
- n := 0; y := 0; ch := Get( );
- WHILE ch >= 80X DO INC( y, LSH( SIZE( ORD( ch )) - 128, n )); INC( n, 7 ); ch := Get( ) END;
- x := ASH( LSH( SIZE( ORD( ch )), SIZE OF SIZE * 8 - 7 ), n - SIZE OF SIZE * 8 - 7 ) + y
- END RawSize;
- (** -- Read formatted data ( uses Peek for one character lookahead ) -- *)
- (** Read an integer value in decimal or hexadecimal. If hex = TRUE, recognize the "H" postfix for hexadecimal numbers. *)
- PROCEDURE Int* ( VAR x: LONGINT; hex: BOOLEAN );
- VAR vd, vh, sgn, d: LONGINT; ch: CHAR; ok: BOOLEAN;
- BEGIN
- vd := 0; vh := 0; sgn := 1; ok := FALSE;
- IF Peek( ) = "-" THEN sgn := -1; ch := Get( ) END;
- LOOP
- ch := Peek( );
- IF ( ch >= "0" ) & ( ch <= "9" ) THEN d := ORD( ch ) - ORD( "0" )
- ELSIF hex & ( CAP( ch ) >= "A" ) & ( CAP( ch ) <= "F" ) THEN d := ORD( CAP( ch )) - ORD( "A" ) + 10
- ELSE EXIT
- END;
- vd := 10 * vd + d; vh := 16 * vh + d; (* ignore overflow *)
- ch := Get( ); ok := TRUE
- END;
- IF hex & ( CAP( ch ) = "H" ) THEN (* optional "H" present *)
- vd := vh; (* use the hex value *)
- ch := Get( )
- END;
- x := sgn * vd;
- IF ( res = 0 ) & ~ok THEN res := FormatError END
- END Int;
- (** Return TRUE iff at the end of a line ( or file ). *)
- PROCEDURE EOLN* ( ): BOOLEAN;
- VAR ch: CHAR;
- BEGIN
- ch := Peek( ); RETURN ( ch = CR ) OR ( ch = LF ) OR ( res # Ok )
- END EOLN;
- (** Read all characters until the end of the line ( inclusive ). If the input string is larger than x, read the full string and assign
- the truncated 0X-terminated value to x. *)
- PROCEDURE Ln* ( VAR x: ARRAY OF CHAR );
- VAR i, m: SIZE; ch: CHAR;
- BEGIN
- i := 0; m := LEN( x ) - 1;
- LOOP
- ch := Peek( );
- IF ( ch = CR ) OR ( ch = LF ) OR ( res # Ok ) THEN EXIT END;
- IF i < m THEN x[ i ] := ch; INC( i ) END;
- ch := Get( );
- END;
- x[ i ] := 0X;
- IF ch = CR THEN ch := Get( ) END;
- IF Peek( ) = LF THEN ch := Get( ) END;
- END Ln;
- (** Read all characters until the end of the line ( inclusive ) or an <EOT> character.
- If the input string is larger than x, read the full string and assign the truncated 0X-terminated
- value to x. *)
- PROCEDURE LnEOT* ( VAR x: ARRAY OF CHAR );
- VAR i, m: SIZE; ch: CHAR;
- BEGIN
- i := 0; m := LEN( x ) - 1;
- LOOP
- ch := Peek( );
- IF ( ch = CR ) OR ( ch = LF ) OR ( ch = EOT ) OR ( res # Ok ) THEN EXIT END;
- IF i < m THEN x[ i ] := ch; INC( i ) END;
- ch := Get( )
- END;
- x[ i ] := 0X;
- IF ch = CR THEN ch := Get( ) END;
- IF Peek( ) = LF THEN ch := Get( ) END;
- IF ch = EOT THEN ch := Get( ) END
- END LnEOT;
- (** Skip over all characters until the end of the line ( inclusive ). *)
- PROCEDURE SkipLn*;
- VAR ch: CHAR;
- BEGIN
- LOOP
- ch := Peek( );
- IF ( ch = CR ) OR ( ch = LF ) OR ( res # Ok ) THEN EXIT END;
- ch := Get( )
- END;
- IF ch = CR THEN ch := Get( ) END;
- IF Peek( ) = LF THEN ch := Get( ) END
- END SkipLn;
- (** Skip over space and TAB characters. *)
- PROCEDURE SkipSpaces*;
- VAR ch: CHAR;
- BEGIN
- LOOP
- ch := Peek( );
- IF ( ch # TAB ) & ( ch # SP ) THEN EXIT END;
- ch := Get( )
- END
- END SkipSpaces;
- (** Skip over space, TAB and EOLN characters. *)
- PROCEDURE SkipWhitespace*;
- VAR ch: CHAR;
- BEGIN
- LOOP
- ch := Peek( );
- IF ( ch # SP ) & ( ch # CR ) & ( ch # LF ) & ( ch # TAB ) THEN EXIT END;
- ch := Get( )
- END
- END SkipWhitespace;
- (** Read a token, consisting of any string of characters terminated by space, TAB or EOLN. *)
- PROCEDURE Token* ( VAR token: ARRAY OF CHAR );
- VAR j, max: SIZE; ch: CHAR;
- BEGIN
- j := 0; max := LEN( token ) - 1;
- LOOP
- ch := Peek( );
- IF ( ch = SP ) OR ( ch = CR ) OR ( ch = LF ) OR ( ch = TAB ) OR ( res # Ok ) THEN EXIT END;
- IF j < max THEN token[ j ] := ch; INC( j ) END;
- ch := Get( )
- END;
- token[ j ] := 0X
- END Token;
- (** Read an optionally "" or '' enquoted string. Will not read past the end of a line. *)
- PROCEDURE String* ( VAR string: ARRAY OF CHAR );
- VAR c, delimiter: CHAR; i, len: SIZE;
- BEGIN
- c := Peek( );
- IF ( c # "'" ) & ( c # '"' ) THEN Token( string )
- ELSE
- delimiter := Get( ); c := Peek( ); i := 0; len := LEN( string ) - 1;
- WHILE ( i < len ) & ( c # delimiter ) & ( c # CR ) & ( c # LF ) & ( res = Ok ) DO string[ i ] := Get( ); INC( i ); c := Peek( ) END;
- IF ( c = delimiter ) THEN c := Get( ) END;
- string[ i ] := 0X
- END
- END String;
- (** First skip whitespace, then read string *)
- PROCEDURE GetString* ( VAR string : ARRAY OF CHAR ): BOOLEAN;
- VAR c: CHAR;
- BEGIN
- SkipWhitespace;
- c := Peek( );
- String( string );
- RETURN ( string[ 0 ] # 0X ) OR ( c = "'" ) OR ( c = '"' );
- END GetString;
- (** First skip whitespace, then read integer *)
- PROCEDURE GetInteger* ( VAR integer : LONGINT; isHexadecimal : BOOLEAN ): BOOLEAN;
- BEGIN
- SkipWhitespace;
- Int( integer, isHexadecimal );
- RETURN res = Ok;
- END GetInteger;
- (** First skip whitespace, then read a real *)
- PROCEDURE GetReal* ( VAR real: LONGREAL ): BOOLEAN;
- BEGIN
- SkipWhitespace;
- real := RC.ScanReal( Get );
- RETURN res = Ok
- END GetReal;
- (** First skip whitespace, then read 1 byte character *)
- PROCEDURE GetChar* ( VAR ch : CHAR ): BOOLEAN;
- BEGIN
- SkipWhitespace;
- Char( ch );
- RETURN ch # 0X;
- END GetChar;
- END Reader;
- TYPE
- (** A special reader that buffers input set by SetString or SetRawString. *)
- StringReader* = OBJECT ( Reader )
- PROCEDURE & InitStringReader* ( size: SIZE );
- BEGIN
- InitReader( Receive, size )
- END InitStringReader;
- PROCEDURE CanSetPos* ( ): BOOLEAN;
- BEGIN
- RETURN TRUE
- END CanSetPos;
- (** Set the reader position *)
- PROCEDURE SetPos* ( pos: Position );
- BEGIN
- IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
- head := SIZE( pos ); tail := LEN( buf ); received := LEN( buf ); res := Ok;
- END SetPos;
- PROCEDURE Receive( VAR buf: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: WORD );
- BEGIN
- IF min = 0 THEN res := Ok ELSE res := EOF END;
- len := 0;
- END Receive;
- (** Set the contents of the string buffer. The s parameter is a 0X-terminated string. *)
- PROCEDURE Set* ( CONST s: ARRAY OF CHAR );
- VAR len: SIZE;
- BEGIN
- len := 0;
- WHILE s[ len ] # 0X DO INC( len ) END;
- IF len > LEN( buf ) THEN len := LEN( buf ) END;
- head := 0; tail := len; received := len; res := Ok;
- IF len > 0 THEN
- SYSTEM.MOVE( ADDRESSOF( s[ 0 ] ), ADDRESSOF( buf[ 0 ] ), len )
- END;
- END Set;
- (** Set the contents of the string buffer. The len parameter specifies the size of the buffer s. *)
- PROCEDURE SetRaw* ( CONST s: ARRAY OF CHAR; ofs, len: SIZE );
- BEGIN
- IF len > LEN( buf ) THEN len := LEN( buf ) END;
- head := 0; tail := len; received := len; res := Ok;
- ASSERT (( len >= 0 ) & ( ofs + len <= LEN( s ))); (* index check *)
- IF len > 0 THEN
- SYSTEM.MOVE( ADDRESSOF( s[ ofs ] ), ADDRESSOF( buf[ 0 ] ), len )
- END;
- END SetRaw;
- END StringReader;
- Bytes2 = ARRAY 2 OF CHAR;
- Bytes4 = ARRAY 4 OF CHAR;
- Bytes8 = ARRAY 8 OF CHAR;
- VAR
- months: ARRAY 12 * 4 + 1 OF CHAR;
- (** Open a writer to the specified stream sender. Update must be called after writing to ensure the buffer is written to the stream. *)
- PROCEDURE OpenWriter* ( VAR b: Writer; send: Sender );
- BEGIN
- NEW( b, send, DefaultWriterSize )
- END OpenWriter;
- (** Open a reader from the specified stream receiver. *)
- PROCEDURE OpenReader* ( VAR b: Reader; receive: Receiver );
- BEGIN
- NEW( b, receive, DefaultReaderSize )
- END OpenReader;
- (** Copy the contents of a reader to a writer *)
- PROCEDURE Copy* ( r: Reader; w: Writer );
- VAR char: CHAR;
- BEGIN
- WHILE r.res = Ok DO
- r.Char ( char );
- IF r.res = Ok THEN w.Char ( char ) END
- END;
- END Copy;
- BEGIN
- months := " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec";
- END Streams.
- (**
- Notes:
- o Any single buffer instance must not be accessed by more than one process concurrently.
- o The interface is blocking ( synchronous ). If an output buffer is full, it is written with a synchronous write, which returns
- only when all the data has been written. If an input buffer is empty, it is read with a synchronous read, which only returns
- once some data has been read. The only exception is the Available( ) procedure, which "peeks" at the input stream
- and returns 0 if no data is currently available.
- o All procedures set res to the error code reported by the lower-level I/O operation ( non-zero indicates error ).
- E.g. closing an underlying TCP connection will result in the Read* procedures returning a non-zero error code.
- o res is sticky. Once it becomes non-zero, it remains non-zero.
- o The only way to detect end of file is to attempt to read past the end of file, which returns a non-zero error code.
- o All output written to an erroneous buffer is ignored.
- o The value returned when reading from an erroneous buffer is undefined, except for the Read procedure, which returns 0X.
- o ReadBytes sets the len parameter to the number of bytes that were actually read, e.g. if size = 10, and only 8 bytes are read, len is 8.
- o Raw format is little-endian 2's complement integers, IEEE reals and 0X-terminated strings.
- o Syntax for ReadInt with hex = FALSE: num = [ "-" ] digit { digit }. digit = "0".."9".
- o Syntax for ReadInt with hex = TRUE: [ "-" ] hexdigit { hexdigit } [ "H"|"h" ]. hexdigit = digit | "A".."F" | "a".."f".
- o ReadInt with hex = TRUE allows "A".."F" as digits, and looks for a "H" character after the number.
- If present, the number is interpreted as hexadecimal. If hexadecimal digits are present, but no "H" flag,
- the resulting decimal value is undefined.
- o ReadInt ignores overflow.
- o A Sender sends len bytes from buf at ofs to output and returns res non-zero on error. It waits until all the data is written,
- or an error occurs.
- o A Receiver receives up to size bytes from input into buf at ofs and returns the number of bytes read in len.
- It returns res non-zero on error. It waits until at least min bytes ( possibly zero ) are available, or an error occurs.
- o EOLN and ReadLn recognize the following end-of-line characters: CR, LF and CR/LF.
- o To read an unstructured file token-by-token: WHILE ( r.res = 0 ) DO SkipWhitespace; ReadToken END
- o To read a line structured file token-by-token: WHILE r.res = 0 DO SkipSpaces; WHILE ~EOLN DO ReadToken; SkipSpaces END END
- o A string writer is not flushed when it becomes full, but res is set to a non-zero value.
- o Update has no effect on a string writer.
- o GetString can be called on a string writer to return the buffer contents and reset it to empty.
- o GetString always appends a 0X character to the buffer, but returns the true length ( excluding the added 0X ) in the len parameter,
- so it can also be used for binary data that includes 0X characters.
- o Receive procedure should set res to EOF when attempting to read past the end of file.
- *)
- (*
- to do:
- o stream byte count
- o read formatted data
- o reads for all formatted writes
- o write reals
- o low-level version that can be used in kernel ( below KernelLog )
- *)
|