1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE Streams; (** AUTHOR "pjm/be"; PURPOSE "I/O buffering and formatted writing and reading"; *)
- IMPORT SYSTEM;
- 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;
- CONST
- CR = 0DX; LF = 0AX; TAB = 9X; SP = 20X;
- VAR
- H, L: INTEGER;
- TYPE
- (** Any stream output procedure or method. *)
- Sender* = PROCEDURE {DELEGATE} ( CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT );
- (** Any stream input procedure or method. *)
- Receiver* = PROCEDURE {DELEGATE} ( VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT );
- Connection* = OBJECT
- PROCEDURE Send*( CONST data: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT );
- END Send;
- PROCEDURE Receive*( VAR data: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT );
- END Receive;
- PROCEDURE Close*;
- END Close;
- END Connection;
- (** A writer buffers output before it is sent to a Sender. Must not be shared between processes. *)
- TYPE
- Writer* = OBJECT
- VAR
- tail: LONGINT;
- buf: POINTER TO ARRAY OF CHAR;
- res*: LONGINT; (** result of last output operation. *)
- send: Sender;
- sent*: LONGINT; (** count of sent bytes *)
- (* buf[0..tail-1] contains data to write. *)
- PROCEDURE & InitWriter*( send: Sender; size: LONGINT );
- BEGIN
- ASSERT ( send # NIL );
- NEW( buf, size ); 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: LONGINT );
- 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
- END
- END Update;
- (** Current write position. *)
- PROCEDURE Pos*( ): LONGINT;
- BEGIN
- RETURN sent + tail
- 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
- 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: LONGINT );
- VAR n: LONGINT;
- BEGIN
- ASSERT ( len >= 0 );
- LOOP
- n := LEN( buf ) - tail; (* space available *)
- 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
- 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 );
- 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: LONGINT;
- BEGIN
- i := 0;
- 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 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: LONGINT;
- BEGIN
- i := 0;
- 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: LONGINT );
- 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 := MIN( LONGINT ); dots := FALSE;
- FOR i := MIN( SET ) TO MAX( SET ) DO
- IF i IN s THEN
- IF last = (i - 1) THEN
- IF dots THEN String( ".." ); dots := FALSE END;
- IF (i = MAX( SET )) OR ~((i + 1) IN s) THEN Int( i, 1 ) END
- ELSE
- IF last >= MIN( SET ) THEN String( ", " ) END;
- Int( i, 1 ); dots := TRUE
- END;
- last := i
- END
- END;
- Char( "}" )
- END Set; *)
-
- 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: LONGINT);
- VAR filler: CHAR; i,maxw: LONGINT; a: ARRAY 20 OF CHAR; y: HUGEINT;
- 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: LONGINT );
- (* BM 1993.4.22. Do not simplify rounding! *)
- VAR e, h, l, i: LONGINT; z: LONGREAL;
- d: ARRAY 16 OF CHAR;
- BEGIN
- e := ExpoL( x );
- IF e = 2047 THEN
- WHILE n > 9 DO Char( " " ); DEC( n ) END;
- NaNCodeL( x, h, l );
- IF (h # 0) OR (l # 0) THEN String( " NaN" )
- ELSIF x < 0 THEN String( " -INF" )
- ELSE String( " INF" )
- END
- ELSE
- IF n <= 9 THEN n := 1 ELSE DEC( n, 8 ) END;
- REPEAT Char( " " ); DEC( n ) UNTIL n <= 15; (* 0 <= n <= 15 fraction digits *)
- IF (e # 0) & (x < 0) THEN Char( "-" ); x := -x ELSE Char( " " ) END;
- IF e = 0 THEN
- h := 0; l := 0 (* no denormals *)
- ELSE
- e := (e - 1023) * 301029 DIV 1000000; (* ln(2)/ln(10) = 0.301029996 *)
- z := Ten( e + 1 );
- IF x >= z THEN x := x / z; INC( e ) ELSE x := x * Ten( -e ) END;
- IF x >= 10 THEN x := x * Ten( -1 ) + 0.5D0 / Ten( n ); INC( e )
- ELSE
- x := x + 0.5D0 / Ten( n );
- IF x >= 10 THEN x := x * Ten( -1 ); INC( e ) END
- END;
- x := x * Ten( 7 ); h := ENTIER( x ); x := (x - h) * Ten( 8 ); l := ENTIER( x )
- END;
- i := 15;
- WHILE i > 7 DO d[i] := CHR( l MOD 10 + ORD( "0" ) ); l := l DIV 10; DEC( i ) END;
- WHILE i >= 0 DO d[i] := CHR( h MOD 10 + ORD( "0" ) ); h := h DIV 10; DEC( i ) END;
- Char( d[0] ); Char( "." ); i := 1;
- WHILE i <= n DO Char( d[i] ); INC( i ) END;
- IF e < 0 THEN String( "E-" ); e := -e ELSE String( "E+" ) END;
- Char( CHR( e DIV 100 + ORD( "0" ) ) ); e := e MOD 100; Char( CHR( e DIV 10 + ORD( "0" ) ) ); Char( CHR( e MOD 10 + ORD( "0" ) ) )
- END
- 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: LONGINT );
- (* BM 1993.4.22. Do not simplify rounding! / JG formatting adjusted *)
- VAR e, h, l, i: LONGINT; r, z: LONGREAL;
- d: ARRAY 16 OF CHAR;
- s: CHAR; dot: BOOLEAN;
- BEGIN
- e := ExpoL( x );
- IF (e = 2047) OR (ABS( D ) > 308) THEN
- WHILE n > 9 DO Char( " " ); DEC( n ) END;
- NaNCodeL( x, h, l );
- IF (h # 0) OR (l # 0) THEN String( " NaN" )
- ELSIF x < 0 THEN String( " -INF" )
- ELSE String( " INF" )
- END
- ELSE
- IF D = 0 THEN IF (f=0) THEN dot := FALSE; DEC( n, 1 ) ELSE dot := TRUE; DEC(n,2); END; ELSE dot := TRUE; DEC( n, 7 ) END;
- IF n < 2 THEN n := 2 END;
- IF f < 0 THEN f := 0 END;
- IF n < f + 2 THEN n := f + 2 END;
- DEC( n, f );
- IF (e # 0) & (x < 0) THEN s := "-"; x := -x ELSE s := " " END;
- IF e = 0 THEN
- h := 0; l := 0; DEC( e, D - 1 ) (* no denormals *)
- ELSE
- e := (e - 1023) * 301029 DIV 1000000; (* ln(2)/ln(10) = 0.301029996 *)
- z := Ten( e + 1 );
- IF x >= z THEN x := x / z; INC( e ) ELSE x := x * Ten( -e ) END;
- DEC( e, D - 1 ); i := -(e + f);
- IF i <= 0 THEN r := 5 * Ten( i ) ELSE r := 0 END;
- IF x >= 10 THEN x := x * Ten( -1 ) + r; INC( e )
- ELSE
- x := x + r;
- IF x >= 10 THEN x := x * Ten( -1 ); INC( e ) END
- END;
- x := x * Ten( 7 ); h := ENTIER( x ); x := (x - h) * Ten( 8 ); l := ENTIER( x )
- END;
- i := 15;
- WHILE i > 7 DO d[i] := CHR( l MOD 10 + ORD( "0" ) ); l := l DIV 10; DEC( i ) END;
- WHILE i >= 0 DO d[i] := CHR( h MOD 10 + ORD( "0" ) ); h := h DIV 10; DEC( i ) END;
- IF n <= e THEN n := e + 1 END;
- IF e > 0 THEN
- WHILE n > e DO Char( " " ); DEC( n ) END;
- Char( s ); e := 0;
- WHILE n > 0 DO
- DEC( n );
- IF e < 16 THEN Char( d[e] ); INC( e ) ELSE Char( "0" ) END
- END;
- IF dot THEN
- Char( "." )
- END;
- ELSE
- WHILE n > 1 DO Char( " " ); DEC( n ) END;
- Char( s ); Char( "0" ); IF dot THEN Char( "." ); END;
- WHILE (0 < f) & (e < 0) DO Char( "0" ); DEC( f ); INC( e ) END
- END;
- WHILE f > 0 DO
- DEC( f );
- IF e < 16 THEN Char( d[e] ); INC( e ) ELSE Char( "0" ) END
- END;
- IF D # 0 THEN
- IF D < 0 THEN String( "E-" ); D := -D ELSE String( "E+" ) END;
- Char( CHR( D DIV 100 + ORD( "0" ) ) ); D := D MOD 100; Char( CHR( D DIV 10 + ORD( "0" ) ) ); Char( CHR( D MOD 10 + ORD( "0" ) ) )
- END
- END
- END FloatFix;
- END Writer;
- (** A special writer that buffers output to be fetched by GetString or GetRawString. *)
- StringWriter* = OBJECT (Writer)
- PROCEDURE & InitStringWriter*( size: LONGINT );
- BEGIN
- InitWriter( Send, size )
- END InitStringWriter;
- PROCEDURE Send( CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT );
- BEGIN
- res := StringFull
- END Send;
- PROCEDURE CanSetPos*( ): BOOLEAN;
- BEGIN
- RETURN TRUE;
- END CanSetPos;
- (* Set the position for the writer *)
- PROCEDURE SetPos*( pos: LONGINT );
- BEGIN
- IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
- tail := 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: LONGINT;
- 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: LONGINT );
- VAR i, m: LONGINT;
- 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: LONGINT;
- buf: POINTER TO ARRAY OF CHAR;
- res*: LONGINT; (** result of last input operation. *)
- receive: Receiver;
- received*: LONGINT; (** count of received bytes *)
- (* buf[buf.head..buf.tail-1] contains data to read. *)
- PROCEDURE & InitReader*( receive: Receiver; size: LONGINT );
- 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: LONGINT );
- BEGIN
- HALT( 1234 )
- END SetPos;
- (** Return bytes currently available in input buffer. *)
- PROCEDURE Available*( ): LONGINT;
- VAR n: LONGINT;
- 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*( ): LONGINT;
- BEGIN
- RETURN 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: LONGINT; VAR len: LONGINT );
- VAR n: LONGINT;
- BEGIN
- ASSERT ( size >= 0 );
- 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: LONGINT );
- 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: LONGINT;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
- END RawLInt;
- (** Read a HUGEINT. *)
- PROCEDURE RawHInt*( VAR x: HUGEINT );
- VAR ignore: LONGINT;
- 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: LONGINT;
- 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: LONGINT;
- BEGIN
- Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
- END RawReal;
- (** Read a LONGREAL. *)
- PROCEDURE RawLReal*( VAR x: LONGREAL );
- VAR ignore: LONGINT;
- 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: LONGINT; 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( LONG( ORD( ch ) ) - 128, n ) ); INC( n, 7 ); ch := Get() END;
- x := ASH( LSH( LONG( ORD( ch ) ), 25 ), n - 25 ) + y
- END RawNum;
- (** -- 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: LONGINT; 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: LONGINT; 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: LONGINT; 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: LONGINT;
- 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 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: LONGINT );
- BEGIN
- InitReader( Receive, size )
- END InitStringReader;
- PROCEDURE CanSetPos*( ): BOOLEAN;
- BEGIN
- RETURN TRUE
- END CanSetPos;
- (** Set the reader position *)
- PROCEDURE SetPos*( pos: LONGINT );
- BEGIN
- IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
- head := pos; tail := LEN( buf ); received := LEN( buf ); res := Ok;
- END SetPos;
- PROCEDURE Receive( VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT );
- 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: LONGINT;
- 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: LONGINT );
- 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;
- (** from module Reals.Mod *)
- (*** the following procedures stem from Reals.Mod and are needed for Writer.Float and Writer.FloatFix *)
- (** Returns the NaN code (0 <= h < 1048576, MIN(LONGINT) <= l <= MAX(LONGINT)) or (-1,-1) if not NaN/Infinite. *)
- PROCEDURE NaNCodeL( x: LONGREAL; VAR h, l: LONGINT );
- BEGIN
- SYSTEM.GET( ADDRESSOF( x ) + H, h ); SYSTEM.GET( ADDRESSOF( x ) + L, l );
- IF ASH( h, -20 ) MOD 2048 = 2047 THEN (* Infinite or NaN *)
- h := h MOD 100000H (* lowest 20 bits *)
- ELSE h := -1; l := -1
- END
- END NaNCodeL;
- (** Returns the shifted binary exponent (0 <= e < 2048). *)
- PROCEDURE ExpoL( x: LONGREAL ): LONGINT;
- VAR i: LONGINT;
- BEGIN
- SYSTEM.GET( ADDRESSOF( x ) + H, i ); RETURN ASH( i, -20 ) MOD 2048
- END ExpoL;
- (** Convert hexadecimal to LONGREAL. h and l are the high and low parts.*)
- PROCEDURE RealL( h, l: LONGINT ): LONGREAL;
- VAR x: LONGREAL;
- BEGIN
- SYSTEM.PUT( ADDRESSOF( x ) + H, h ); SYSTEM.PUT( ADDRESSOF( x ) + L, l ); RETURN x
- END RealL;
- (** Returns 10^e (e <= 308, 308 < e delivers IEEE-code +INF). *)
- PROCEDURE Ten( e: LONGINT ): LONGREAL; (* naiive version *)
- VAR r: LONGREAL;
- BEGIN
- IF e < -307 THEN RETURN 0
- ELSIF 308 < e THEN RETURN RealL( 2146435072, 0 )
- END;
- r := 1;
- WHILE (e > 0) DO r := r * 10; DEC( e ); END;
- WHILE (e < 0) DO r := r / 10; INC( e ); END;
- RETURN r;
- END Ten;
- PROCEDURE InitHL;
- VAR i: ADDRESS; dmy: INTEGER; littleEndian: BOOLEAN;
- BEGIN
- dmy := 1; i := ADDRESSOF( dmy );
- SYSTEM.GET( i, littleEndian ); (* indirection via i avoids warning on SUN cc -O *)
- IF littleEndian THEN H := 4; L := 0 ELSE H := 0; L := 4 END
- END InitHL;
- BEGIN
- months := " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"; InitHL;
- 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)
- *)
|