MODULE Streams;
IMPORT SYSTEM;
CONST
Ok* = 0;
EOF* = 4201;
EOT* = 1AX;
StringFull = 4202;
FormatError* = 4203;
DefaultWriterSize* = 4096;
DefaultReaderSize* = 4096;
CONST
CR = 0DX; LF = 0AX; TAB = 9X; SP = 20X;
VAR
H, L: INTEGER;
TYPE
Sender* = PROCEDURE {DELEGATE} ( CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT );
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;
TYPE
Writer* = OBJECT
VAR
tail: LONGINT;
buf: POINTER TO ARRAY OF CHAR;
res*: LONGINT;
send: Sender;
sent*: LONGINT;
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;
PROCEDURE Pos*( ): LONGINT;
BEGIN
RETURN sent + tail
END Pos;
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;
PROCEDURE Bytes*(CONST x: ARRAY OF CHAR; ofs, len: LONGINT );
VAR n: LONGINT;
BEGIN
ASSERT ( len >= 0 );
LOOP
n := LEN( buf ) - tail;
IF n = 0 THEN
IF res = Ok THEN
send( buf^, 0, tail, FALSE , res );
IF res = Ok THEN INC( sent, tail ); tail := 0 ELSE EXIT END
ELSE
EXIT
END;
n := LEN( buf )
END;
IF n > len THEN n := len END;
ASSERT ( tail + n <= LEN( buf ) );
SYSTEM.MOVE( SYSTEM.ADR( x[ofs] ), SYSTEM.ADR( buf[tail] ), n ); INC( tail, n );
IF len = n THEN EXIT END;
INC( ofs, n ); DEC( len, n )
END
END Bytes;
PROCEDURE RawSInt*( x: SHORTINT );
BEGIN
Char( SYSTEM.VAL( CHAR, x ) )
END RawSInt;
PROCEDURE RawInt*( x: INTEGER );
BEGIN
Bytes( SYSTEM.VAL( Bytes2, x ), 0, 2 )
END RawInt;
PROCEDURE RawLInt*( x: LONGINT );
BEGIN
Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
END RawLInt;
PROCEDURE RawHInt*( x: HUGEINT );
BEGIN
Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 )
END RawHInt;
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;
PROCEDURE Net16*( x: LONGINT );
BEGIN
Char( CHR( x DIV 100H MOD 100H ) ); Char( CHR( x MOD 100H ) )
END Net16;
PROCEDURE Net8*( x: LONGINT );
BEGIN
Char( CHR( x MOD 100H ) )
END Net8;
PROCEDURE RawSet*( x: SET );
BEGIN
Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
END RawSet;
PROCEDURE RawBool*( x: BOOLEAN );
BEGIN
IF x THEN Char( 1X ) ELSE Char( 0X ) END
END RawBool;
PROCEDURE RawReal*( x: REAL );
BEGIN
Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
END RawReal;
PROCEDURE RawLReal*( x: LONGREAL );
BEGIN
Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 )
END RawLReal;
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;
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;
PROCEDURE Ln*;
BEGIN
Char( CR ); Char( LF )
END Ln;
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;
PROCEDURE Int*( x, w: LONGINT );
VAR i, x0: LONGINT;
a: ARRAY 12 OF CHAR;
BEGIN
IF x < 0 THEN
IF x = MIN( LONGINT ) THEN
DEC( w, 11 );
WHILE w > 0 DO Char( " " ); DEC( w ) END;
String( "-2147483648" ); 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;
PROCEDURE Set*( s: SET );
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 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;
PROCEDURE Address* (x: SYSTEM.ADDRESS);
BEGIN
Hex(x,-2*SYSTEM.SIZEOF(SYSTEM.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;
PROCEDURE Date*( t, d: LONGINT );
VAR ch: CHAR;
BEGIN
IF d # -1 THEN
Int( 1900 + d DIV 512, 4 );
Pair( "-", d DIV 32 MOD 16 );
Pair( "-", d MOD 32 );
ch := " "
ELSE
ch := 0X
END;
IF t # -1 THEN
Pair( ch, t DIV 4096 MOD 32 );
Pair( ":", t DIV 64 MOD 64 );
Pair( ":", t MOD 64 )
END
END Date;
PROCEDURE Date822*( t, d, tz: LONGINT );
VAR i, m: LONGINT; ch: CHAR;
BEGIN
IF d # -1 THEN
Int( d MOD 32, 2 );
m := (d DIV 32 MOD 16 - 1) * 4;
FOR i := m TO m + 3 DO Char( months[i] ) END;
Int( 1900 + d DIV 512, 5 );
ch := " "
ELSE
ch := 0X
END;
IF t # -1 THEN
Pair( ch, t DIV 4096 MOD 32 );
Pair( ":", t DIV 64 MOD 64 );
Pair( ":", t MOD 64 );
ch := " "
ELSE
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;
PROCEDURE Float*( x: LONGREAL; n: LONGINT );
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;
IF (e # 0) & (x < 0) THEN Char( "-" ); x := -x ELSE Char( " " ) END;
IF e = 0 THEN
h := 0; l := 0
ELSE
e := (e - 1023) * 301029 DIV 1000000;
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;
PROCEDURE FloatFix*( x: LONGREAL; n, f, D: LONGINT );
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 )
ELSE
e := (e - 1023) * 301029 DIV 1000000;
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;
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;
PROCEDURE SetPos*( pos: LONGINT );
BEGIN
IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
tail := pos; sent := 0; res := Ok;
END SetPos;
PROCEDURE Update;
END Update;
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;
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
Reader* = OBJECT
VAR
head, tail: LONGINT;
buf: POINTER TO ARRAY OF CHAR;
res*: LONGINT;
receive: Receiver;
received*: LONGINT;
PROCEDURE & InitReader*( receive: Receiver; size: LONGINT );
BEGIN
ASSERT ( receive # NIL );
NEW( buf, size ); SELF.receive := receive; Reset
END InitReader;
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;
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 );
INC( tail, n ); INC( received, n )
END;
IF res = EOF THEN res := Ok END
END;
RETURN tail - head
END Available;
PROCEDURE Pos*( ): LONGINT;
BEGIN
RETURN received - (tail - head)
END Pos;
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;
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;
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
res := Ok; tail := 0; RETURN 0X
END
END;
IF res = Ok THEN RETURN buf[head] ELSE RETURN 0X END
END Peek;
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;
IF n = 0 THEN
head := 0;
IF res = Ok THEN
receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail )
END;
IF res # Ok THEN
WHILE size # 0 DO x[ofs] := 0X; INC( ofs ); DEC( size ) END;
IF (res = EOF) & (len # 0) THEN res := Ok END;
EXIT
END;
n := tail
END;
IF n > size THEN n := size END;
ASSERT ( ofs + n <= LEN( x ) );
SYSTEM.MOVE( SYSTEM.ADR( buf[head] ), SYSTEM.ADR( x[ofs] ), n ); INC( head, n ); INC( len, n );
IF size = n THEN EXIT END;
INC( ofs, n ); DEC( size, n )
END
END Bytes;
PROCEDURE SkipBytes*( n: LONGINT );
VAR ch: CHAR;
BEGIN
WHILE n > 0 DO ch := Get(); DEC( n ) END
END SkipBytes;
PROCEDURE RawSInt*( VAR x: SHORTINT );
BEGIN
x := SYSTEM.VAL( SHORTINT, Get() )
END RawSInt;
PROCEDURE RawInt*( VAR x: INTEGER );
VAR x0, x1: CHAR;
BEGIN
x0 := Get(); x1 := Get();
x := ORD( x1 ) * 100H + ORD( x0 )
END RawInt;
PROCEDURE RawLInt*( VAR x: LONGINT );
VAR ignore: LONGINT;
BEGIN
Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
END RawLInt;
PROCEDURE RawHInt*( VAR x: HUGEINT );
VAR ignore: LONGINT;
BEGIN
Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
END RawHInt;
PROCEDURE Net32*( ): LONGINT;
BEGIN
RETURN LONG( ORD( Get() ) ) * 1000000H + LONG( ORD( Get() ) ) * 10000H + LONG( ORD( Get() ) ) * 100H + LONG( ORD( Get() ) )
END Net32;
PROCEDURE Net16*( ): LONGINT;
BEGIN
RETURN LONG( ORD( Get() ) ) * 100H + LONG( ORD( Get() ) )
END Net16;
PROCEDURE Net8*( ): LONGINT;
BEGIN
RETURN LONG( ORD( Get() ) )
END Net8;
PROCEDURE RawSet*( VAR x: SET );
VAR ignore: LONGINT;
BEGIN
Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
END RawSet;
PROCEDURE RawBool*( VAR x: BOOLEAN );
BEGIN
x := (Get() # 0X)
END RawBool;
PROCEDURE RawReal*( VAR x: REAL );
VAR ignore: LONGINT;
BEGIN
Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
END RawReal;
PROCEDURE RawLReal*( VAR x: LONGREAL );
VAR ignore: LONGINT;
BEGIN
Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
END RawLReal;
PROCEDURE RawString*( VAR x: ARRAY OF CHAR );
VAR i, m: LONGINT; ch: CHAR;
BEGIN
i := 0; m := LEN( x ) - 1;
LOOP
ch := Get();
IF ch = 0X THEN EXIT END;
IF i < m THEN x[i] := ch; INC( i ) END
END;
x[i] := 0X
END RawString;
PROCEDURE RawNum*( VAR x: LONGINT );
VAR ch: CHAR; n, y: LONGINT;
BEGIN
n := 0; y := 0; ch := Get();
WHILE ch >= 80X DO INC( y, SYSTEM.LSH( LONG( ORD( ch ) ) - 128, n ) ); INC( n, 7 ); ch := Get() END;
x := ASH( SYSTEM.LSH( LONG( ORD( ch ) ), 25 ), n - 25 ) + y
END RawNum;
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;
ch := Get(); ok := TRUE
END;
IF hex & (CAP( ch ) = "H") THEN
vd := vh;
ch := Get()
END;
x := sgn * vd;
IF (res = 0) & ~ok THEN res := FormatError END
END Int;
PROCEDURE EOLN*( ): BOOLEAN;
VAR ch: CHAR;
BEGIN
ch := Peek(); RETURN (ch = CR) OR (ch = LF) OR (res # Ok)
END EOLN;
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;
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;
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;
PROCEDURE SkipSpaces*;
VAR ch: CHAR;
BEGIN
LOOP
ch := Peek();
IF (ch # TAB) & (ch # SP) THEN EXIT END;
ch := Get()
END
END SkipSpaces;
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;
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;
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;
PROCEDURE GetString*(VAR string : ARRAY OF CHAR): BOOLEAN;
BEGIN
SkipWhitespace;
String(string);
RETURN string[0] # 0X;
END GetString;
PROCEDURE GetInteger*(VAR integer : LONGINT; isHexadecimal : BOOLEAN): BOOLEAN;
BEGIN
SkipWhitespace;
Int(integer, isHexadecimal);
RETURN res = Ok;
END GetInteger;
PROCEDURE GetChar*(VAR ch : CHAR): BOOLEAN;
BEGIN
SkipWhitespace;
Char(ch);
RETURN ch # 0X;
END GetChar;
END Reader;
TYPE
StringReader* = OBJECT (Reader)
PROCEDURE & InitStringReader*( size: LONGINT );
BEGIN
InitReader( Receive, size )
END InitStringReader;
PROCEDURE CanSetPos*( ): BOOLEAN;
BEGIN
RETURN TRUE
END CanSetPos;
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;
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( SYSTEM.ADR( s[0] ), SYSTEM.ADR( buf[0] ), len )
END;
END Set;
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 )) );
IF len > 0 THEN
SYSTEM.MOVE( SYSTEM.ADR( s[ofs] ), SYSTEM.ADR( 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;
PROCEDURE OpenWriter*( VAR b: Writer; send: Sender );
BEGIN
NEW( b, send, DefaultWriterSize )
END OpenWriter;
PROCEDURE OpenReader*( VAR b: Reader; receive: Receiver );
BEGIN
NEW( b, receive, DefaultReaderSize )
END OpenReader;
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;
PROCEDURE NaNCodeL( x: LONGREAL; VAR h, l: LONGINT );
BEGIN
SYSTEM.GET( SYSTEM.ADR( x ) + H, h ); SYSTEM.GET( SYSTEM.ADR( x ) + L, l );
IF ASH( h, -20 ) MOD 2048 = 2047 THEN
h := h MOD 100000H
ELSE h := -1; l := -1
END
END NaNCodeL;
PROCEDURE ExpoL( x: LONGREAL ): LONGINT;
VAR i: LONGINT;
BEGIN
SYSTEM.GET( SYSTEM.ADR( x ) + H, i ); RETURN ASH( i, -20 ) MOD 2048
END ExpoL;
PROCEDURE RealL( h, l: LONGINT ): LONGREAL;
VAR x: LONGREAL;
BEGIN
SYSTEM.PUT( SYSTEM.ADR( x ) + H, h ); SYSTEM.PUT( SYSTEM.ADR( x ) + L, l ); RETURN x
END RealL;
PROCEDURE Ten( e: LONGINT ): LONGREAL;
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: SYSTEM.ADDRESS; dmy: INTEGER; littleEndian: BOOLEAN;
BEGIN
dmy := 1; i := SYSTEM.ADR( dmy );
SYSTEM.GET( i, littleEndian );
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)
*)