IO64.Streams.Mod 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE Streams; (** AUTHOR "pjm/be"; PURPOSE "I/O buffering and formatted writing and reading"; *)
  3. IMPORT SYSTEM, RC := RealConversions;
  4. CONST
  5. Ok* = 0; (** zero result code means no error occurred *)
  6. EOF* = 4201; (** error returned when Receive reads past end of file or stream *)
  7. EOT* = 1AX; (** EOT character *)
  8. StringFull = 4202;
  9. FormatError* = 4203; (** error returned when ReadInt fails *)
  10. DefaultWriterSize* = 4096;
  11. DefaultReaderSize* = 4096;
  12. Invalid* = -1; (** invalid stream position *)
  13. CONST
  14. CR = 0DX; LF = 0AX; TAB = 9X; SP = 20X;
  15. TYPE
  16. Size* = SIZE; (* index type for array *)
  17. TYPE
  18. Position* = HUGEINT; (* offset in the stream *)
  19. (** Any stream output procedure or method. *)
  20. Sender* = PROCEDURE { DELEGATE } ( CONST buf: ARRAY OF CHAR; ofs, len: SIZE; propagate: BOOLEAN; VAR res: WORD );
  21. (** Any stream input procedure or method. *)
  22. Receiver* = PROCEDURE { DELEGATE } ( VAR buf: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: WORD );
  23. Connection* = OBJECT
  24. PROCEDURE Send* ( CONST data: ARRAY OF CHAR; ofs, len: SIZE; propagate: BOOLEAN; VAR res: WORD );
  25. END Send;
  26. PROCEDURE Receive* ( VAR data: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: WORD );
  27. END Receive;
  28. PROCEDURE Close*;
  29. END Close;
  30. END Connection;
  31. TYPE
  32. (** A writer buffers output before it is sent to a Sender. Must not be shared between processes. *)
  33. Writer* = OBJECT
  34. VAR
  35. (* buf[ 0..tail-1 ] contains data to write. *)
  36. tail: SIZE;
  37. buf: POINTER TO ARRAY OF CHAR;
  38. res*: WORD; (** result of last output operation. *)
  39. send: Sender;
  40. sent*: UNSIGNED64; (** count of sent bytes *)
  41. (** -- Initialization -- *)
  42. PROCEDURE & InitWriter* ( send: Sender; size: SIZE );
  43. BEGIN
  44. ASSERT( send # NIL );
  45. IF ( buf = NIL ) OR ( LEN( buf ) # size ) THEN
  46. NEW( buf, size );
  47. END;
  48. SELF.send := send;
  49. Reset;
  50. END InitWriter;
  51. PROCEDURE Reset* ( );
  52. BEGIN
  53. tail := 0; res := Ok; sent := 0;
  54. END Reset;
  55. (* -- -- *)
  56. PROCEDURE CanSetPos* ( ): BOOLEAN;
  57. BEGIN
  58. RETURN FALSE;
  59. END CanSetPos;
  60. PROCEDURE SetPos* ( pos: Position );
  61. BEGIN
  62. HALT( 1234 )
  63. END SetPos;
  64. PROCEDURE Update* ( );
  65. BEGIN
  66. IF ( res = Ok ) THEN
  67. send( buf^, 0, tail, TRUE, res );
  68. IF res = Ok THEN INC( sent, tail ); tail := 0 END; (*! UInt64 *)
  69. END;
  70. END Update;
  71. (** Current write position. *)
  72. PROCEDURE Pos* ( ): Position;
  73. BEGIN
  74. RETURN sent + tail; (*! UInt64 *)
  75. END Pos;
  76. (** -- Write raw binary data -- *)
  77. (** Write one byte. *)
  78. PROCEDURE Char* ( x: CHAR );
  79. BEGIN
  80. IF ( tail = LEN( buf )) & ( res = Ok ) THEN
  81. send( buf^, 0, tail, FALSE, res );
  82. IF res = Ok THEN INC( sent, tail ); tail := 0 END; (*! UInt64 *)
  83. END;
  84. IF res = Ok THEN buf[ tail ] := x; INC( tail ) END;
  85. END Char;
  86. (** Write len bytes from x, starting at ofs. *)
  87. PROCEDURE Bytes* ( CONST x: ARRAY OF CHAR; ofs, len: SIZE );
  88. VAR n: SIZE;
  89. BEGIN
  90. LOOP
  91. n := LEN( buf ) - tail; (* space available in the internal buffer *)
  92. IF n = 0 THEN
  93. IF res = Ok THEN (* send current buffer *)
  94. send( buf^, 0, tail, FALSE, res );
  95. IF res = Ok THEN INC( sent, tail ); tail := 0 ELSE EXIT END; (*! UInt64 *)
  96. ELSE
  97. EXIT (* should not be writing on an erroneous rider *)
  98. END;
  99. n := LEN( buf );
  100. END;
  101. IF n > len THEN n := len END;
  102. ASSERT ( tail + n <= LEN( buf )); (* index check *)
  103. SYSTEM.MOVE( ADDRESSOF( x[ ofs ] ), ADDRESSOF( buf[ tail ] ), n ); INC( tail, n );
  104. IF len = n THEN EXIT END; (* done *)
  105. INC( ofs, n ); DEC( len, n );
  106. END
  107. END Bytes;
  108. (** Write a SHORTINT. *)
  109. PROCEDURE RawSInt* ( x: SHORTINT );
  110. BEGIN
  111. Char( SYSTEM.VAL( CHAR, x ));
  112. END RawSInt;
  113. (** Write an INTEGER. *)
  114. PROCEDURE RawInt* ( x: INTEGER );
  115. BEGIN
  116. Bytes( SYSTEM.VAL( Bytes2, x ), 0, 2 );
  117. END RawInt;
  118. (** Write a LONGINT. *)
  119. PROCEDURE RawLInt* ( x: LONGINT );
  120. BEGIN
  121. Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 );
  122. END RawLInt;
  123. (** Write a HUGEINT. *)
  124. PROCEDURE RawHInt* ( x: HUGEINT );
  125. BEGIN
  126. Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 );
  127. END RawHInt;
  128. (** Write a 64 bit value in network byte order ( most significant byte first ) *)
  129. PROCEDURE Net64* ( x: HUGEINT );
  130. BEGIN
  131. Net32( LONGINT( x DIV 100000000H MOD 100000000H ));
  132. Net32( LONGINT( x MOD 100000000H ));
  133. END Net64;
  134. (** Write a 32 bit value in network byte order ( most significant byte first ) *)
  135. PROCEDURE Net32* ( x: LONGINT );
  136. BEGIN
  137. Char( CHR( x DIV 1000000H MOD 100H )); Char( CHR( x DIV 10000H MOD 100H )); Char( CHR( x DIV 100H MOD 100H ));
  138. Char( CHR( x MOD 100H ));
  139. END Net32;
  140. (** Write a 16 bit value in network byte order ( most significant byte first ) *)
  141. PROCEDURE Net16* ( x: LONGINT );
  142. BEGIN
  143. Char( CHR( x DIV 100H MOD 100H )); Char( CHR( x MOD 100H ));
  144. END Net16;
  145. (** write unsigned byte *)
  146. PROCEDURE Net8* ( x: LONGINT );
  147. BEGIN
  148. Char( CHR( x MOD 100H ));
  149. END Net8;
  150. (** Write a SET. *)
  151. PROCEDURE RawSet* ( x: SET ); (*! 64 bits *)
  152. BEGIN
  153. Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 );
  154. END RawSet;
  155. (** Write a BOOLEAN. *)
  156. PROCEDURE RawBool* ( x: BOOLEAN );
  157. BEGIN
  158. IF x THEN Char( 1X ) ELSE Char( 0X ) END;
  159. END RawBool;
  160. (** Write a REAL. *)
  161. PROCEDURE RawReal* ( x: REAL );
  162. BEGIN
  163. Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 );
  164. END RawReal;
  165. (** Write a LONGREAL. *)
  166. PROCEDURE RawLReal* ( x: LONGREAL );
  167. BEGIN
  168. Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 );
  169. END RawLReal;
  170. (** Write a 0X-terminated string, including the 0X terminator. *)
  171. PROCEDURE RawString* ( CONST x: ARRAY OF CHAR );
  172. VAR i := 0: SIZE;
  173. BEGIN
  174. WHILE x[ i ] # 0X DO Char( x[ i ] ); INC( i ) END;
  175. Char( 0X );
  176. END RawString;
  177. (** Write a number in a compressed format. *)
  178. PROCEDURE RawNum* ( x: LONGINT );
  179. BEGIN
  180. WHILE ( x < -64 ) OR ( x > 63 ) DO Char( CHR( x MOD 128 + 128 )); x := x DIV 128 END;
  181. Char( CHR( x MOD 128 ));
  182. END RawNum;
  183. (** Write a size in a compressed format. *)
  184. PROCEDURE RawSize* ( x: SIZE );
  185. BEGIN
  186. WHILE ( x < -64 ) OR ( x > 63 ) DO Char( CHR( x MOD 128 + 128 )); x := x DIV 128 END;
  187. Char( CHR( x MOD 128 ));
  188. END RawSize;
  189. (** -- Write formatted data -- *)
  190. (** Write an ASCII end-of-line ( CR/LF ). *)
  191. PROCEDURE Ln*;
  192. BEGIN
  193. Char( CR ); Char( LF );
  194. END Ln;
  195. (** Write a 0X-terminated string, excluding the 0X terminator. *)
  196. PROCEDURE String* ( CONST x: ARRAY OF CHAR );
  197. VAR i := 0: SIZE;
  198. BEGIN
  199. WHILE x[ i ] # 0X DO Char( x[ i ] ); INC( i ) END;
  200. END String;
  201. (** Write an integer in decimal right-justified in a field of at least w characters. *)
  202. PROCEDURE Int* ( x: HUGEINT; w: SIZE );
  203. VAR i: SIZE; x0: HUGEINT;
  204. a: ARRAY 21 OF CHAR;
  205. BEGIN
  206. IF x < 0 THEN
  207. IF x = MIN( HUGEINT ) THEN
  208. DEC( w, 20 );
  209. WHILE w > 0 DO Char( " " ); DEC( w ) END;
  210. String( "-9223372036854775808" );
  211. RETURN;
  212. ELSE
  213. DEC( w ); x0 := -x;
  214. END;
  215. ELSE
  216. x0 := x;
  217. END;
  218. i := 0;
  219. REPEAT a[ i ] := CHR( x0 MOD 10 + 30H ); x0 := x0 DIV 10; INC( i ) UNTIL x0 = 0;
  220. WHILE w > i DO Char( " " ); DEC( w ) END;
  221. IF x < 0 THEN Char( "-" ) END;
  222. REPEAT DEC( i ); Char( a[ i ] ) UNTIL i = 0;
  223. END Int;
  224. (** Write a SET in Oberon notation. *)
  225. PROCEDURE Set* ( s: SET ); (* from P. Saladin *)
  226. VAR i, last: LONGINT; dots: BOOLEAN;
  227. BEGIN
  228. Char( "{" ); last := MAX( LONGINT ); dots := FALSE;
  229. FOR i := MAX( SET ) TO 0 BY -1 DO
  230. IF i IN s THEN
  231. IF last = ( i + 1 ) THEN
  232. IF dots THEN String( ".." ); dots := FALSE END;
  233. IF ( i = 0 ) OR ~(( i - 1 ) IN s ) THEN Int( i, 1 ) END;
  234. ELSE
  235. IF last <= MAX( SET ) THEN String( ", " ) END;
  236. Int( i, 1 ); dots := TRUE;
  237. END;
  238. last := i;
  239. END;
  240. END;
  241. Char( "}" );
  242. END Set;
  243. (**
  244. Write an integer in hexadecimal right-justified in a field of at least ABS( w ) characters.
  245. If w < 0 THEN w least significant hex digits of x are written ( potentially including leading zeros )
  246. *)
  247. PROCEDURE Hex* ( x: HUGEINT; w: SIZE );
  248. VAR filler: CHAR; i, maxw: SIZE; y: HUGEINT; a: ARRAY 20 OF CHAR;
  249. BEGIN
  250. IF w < 0 THEN filler := '0'; w := -w; maxw := w ELSE filler := ' '; maxw := 16 END;
  251. i := 0;
  252. REPEAT
  253. y := x MOD 10H;
  254. IF y < 10 THEN a[ i ] := CHR( y + ORD( '0' )) ELSE a[ i ] := CHR( y-10+ORD( 'A' )) END;
  255. x := x DIV 10H;
  256. INC( i );
  257. UNTIL ( x=0 ) OR ( i=maxw );
  258. WHILE w > i DO Char( filler ); DEC( w ) END;
  259. REPEAT DEC( i ); Char( a[ i ] ) UNTIL i = 0;
  260. END Hex;
  261. (** Write "x" as a hexadecimal address. Do not use Hex because of arithmetic shift of the sign !*)
  262. PROCEDURE Address* ( x: ADDRESS );
  263. BEGIN
  264. Hex( x, -2*SIZEOF( ADDRESS ));
  265. END Address;
  266. PROCEDURE Pair( ch: CHAR; x: LONGINT );
  267. BEGIN
  268. IF ch # 0X THEN Char( ch ) END;
  269. Char( CHR( ORD( "0" ) + x DIV 10 MOD 10 )); Char( CHR( ORD( "0" ) + x MOD 10 ));
  270. END Pair;
  271. (** 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.
  272. If all parameters are within range, the output string is exactly 19 characters wide. The t or d parameter can be -1, in which
  273. case the time or date respectively are left out. *)
  274. PROCEDURE Date* ( t, d: LONGINT );
  275. VAR ch: CHAR;
  276. BEGIN
  277. IF d # -1 THEN
  278. Int( 1900 + d DIV 512, 4 ); (* year *)
  279. Pair( "-", d DIV 32 MOD 16 ); (* month *)
  280. Pair( "-", d MOD 32 ); (* day *)
  281. ch := " "; (* space between date and time *)
  282. ELSE
  283. ch := 0X; (* no space before time *)
  284. END;
  285. IF t # -1 THEN
  286. Pair( ch, t DIV 4096 MOD 32 ); (* hour *)
  287. Pair( ":", t DIV 64 MOD 64 ); (* min *)
  288. Pair( ":", t MOD 64 ); (* sec *)
  289. END;
  290. END Date;
  291. (** Write the date and time in RFC 822/1123 format without the optional day of the week ( dd mmm yyyy hh: mm: ss SZZZZ ) .
  292. The t and d parameters are in Oberon time and date format. The tz parameter specifies the time zone offset in minutes
  293. ( from -720 to 720 in steps of 30 ). If all parameters are within range, the output string is exactly 26 characters wide.
  294. The t, d or tz parameter can be -1, in which case the time, date or timezone respectively are left out. *)
  295. PROCEDURE Date822* ( t, d, tz: LONGINT );
  296. VAR i, m: LONGINT; ch: CHAR;
  297. BEGIN
  298. IF d # -1 THEN
  299. Int( d MOD 32, 2 ); (* day *)
  300. m := ( d DIV 32 MOD 16 - 1 ) * 4; (* month *)
  301. FOR i := m TO m + 3 DO Char( months[ i ] ) END;
  302. Int( 1900 + d DIV 512, 5 ); (* year *)
  303. ch := " "; (* space *)
  304. ELSE
  305. ch := 0X; (* no space *)
  306. END;
  307. IF t # -1 THEN
  308. Pair( ch, t DIV 4096 MOD 32 ); (* hour *)
  309. Pair( ":", t DIV 64 MOD 64 ); (* min *)
  310. Pair( ":", t MOD 64 ); (* sec *)
  311. ch := " "; (* space *)
  312. ELSE
  313. (* leave ch as before *)
  314. END;
  315. IF tz # -1 THEN
  316. IF ch # 0X THEN Char( ch ) END;
  317. IF tz >= 0 THEN Pair( "+", tz DIV 60 ) ELSE Pair( "-", ( -tz ) DIV 60 ) END;
  318. Pair( 0X, ABS( tz ) MOD 60 );
  319. END;
  320. END Date822;
  321. (** Write LONGREAL x using n character positions. *)
  322. PROCEDURE Float* ( x: LONGREAL; n: WORD );
  323. VAR buf: ARRAY 32 OF CHAR;
  324. BEGIN
  325. RC.RealToString( x, n, buf );
  326. String( buf );
  327. END Float;
  328. (** 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 ). *)
  329. PROCEDURE FloatFix* ( x: LONGREAL; n, f, D: WORD );
  330. VAR buf: ARRAY 64 OF CHAR;
  331. BEGIN
  332. RC.RealToStringFix( x, n, f, D, buf );
  333. String( buf );
  334. END FloatFix;
  335. END Writer;
  336. (** A special writer that buffers output to be fetched by GetString or GetRawString. *)
  337. StringWriter* = OBJECT ( Writer )
  338. PROCEDURE & InitStringWriter* ( size: SIZE );
  339. BEGIN
  340. InitWriter( Send, size )
  341. END InitStringWriter;
  342. PROCEDURE Send( CONST buf: ARRAY OF CHAR; ofs, len: SIZE; propagate: BOOLEAN; VAR res: WORD );
  343. BEGIN
  344. res := StringFull
  345. END Send;
  346. PROCEDURE CanSetPos* ( ): BOOLEAN;
  347. BEGIN
  348. RETURN TRUE;
  349. END CanSetPos;
  350. (* Set the position for the writer *)
  351. PROCEDURE SetPos* ( pos: Position );
  352. BEGIN
  353. IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
  354. tail := SIZE( pos ); sent := 0; res := Ok;
  355. END SetPos;
  356. PROCEDURE Update*;
  357. (* nothing to do *)
  358. END Update;
  359. (** Return the contents of the string writer ( 0X-terminated ). *)
  360. PROCEDURE Get* ( VAR s: ARRAY OF CHAR );
  361. VAR i, m: SIZE;
  362. BEGIN
  363. m := LEN( s ) - 1; i := 0;
  364. WHILE ( i # tail ) & ( i < m ) DO s[ i ] := buf[ i ]; INC( i ) END;
  365. s[ i ] := 0X; tail := 0; res := Ok
  366. END Get;
  367. (** Return the contents of the string writer ( not 0X-terminated ). The len parameters returns the string length. *)
  368. PROCEDURE GetRaw* ( VAR s: ARRAY OF CHAR; VAR len: SIZE );
  369. VAR i, m: SIZE;
  370. BEGIN
  371. m := LEN( s ); i := 0;
  372. WHILE ( i # tail ) & ( i < m ) DO s[ i ] := buf[ i ]; INC( i ) END;
  373. len := i; tail := 0; res := Ok
  374. END GetRaw;
  375. END StringWriter;
  376. TYPE
  377. (** A reader buffers input received from a Receiver. Must not be shared between processes. *)
  378. Reader* = OBJECT
  379. VAR
  380. head, tail: SIZE;
  381. buf: POINTER TO ARRAY OF CHAR;
  382. res*: WORD; (** result of last input operation. *)
  383. receive: Receiver;
  384. received*: UNSIGNED64; (** count of received bytes *)
  385. (* buf[ buf.head..buf.tail-1 ] contains data to read. *)
  386. PROCEDURE & InitReader* ( receive: Receiver; size: SIZE );
  387. BEGIN
  388. ASSERT ( receive # NIL );
  389. IF ( buf = NIL ) OR ( LEN( buf ) # size ) THEN
  390. NEW( buf, size );
  391. END;
  392. SELF.receive := receive; Reset;
  393. END InitReader;
  394. (** reset the reader by dropping the bytes in the buffer, resetting the result code and setting received to 0.
  395. This is used by seekable extensions of the reader *)
  396. PROCEDURE Reset*;
  397. BEGIN
  398. head := 0; tail := 0; res := Ok; received := 0
  399. END Reset;
  400. PROCEDURE CanSetPos* ( ): BOOLEAN;
  401. BEGIN
  402. RETURN FALSE
  403. END CanSetPos;
  404. PROCEDURE SetPos* ( pos: Position );
  405. BEGIN
  406. HALT( 1234 )
  407. END SetPos;
  408. (** Return bytes currently available in input buffer. *)
  409. PROCEDURE Available* ( ): SIZE;
  410. VAR n: SIZE;
  411. BEGIN
  412. IF ( res = Ok ) THEN
  413. IF ( head = tail ) THEN head := 0; receive( buf^, 0, LEN( buf ), 0, tail, res ); INC( received, tail );
  414. ELSIF ( tail # LEN( buf )) THEN
  415. receive( buf^, tail, LEN( buf ) - tail, 0, n, res ); (* poll *)
  416. INC( tail, n ); INC( received, n )
  417. END;
  418. IF res = EOF THEN res := Ok END (* ignore EOF here *)
  419. END;
  420. RETURN tail - head
  421. END Available;
  422. (** Current read position. *)
  423. PROCEDURE Pos* ( ): Position;
  424. BEGIN
  425. RETURN Position( received - ( tail - head ))
  426. END Pos;
  427. (** -- Read raw binary data -- *)
  428. (** Read one byte. x=0X if no success ( e.g. file ended ) *)
  429. PROCEDURE Char* ( VAR x: CHAR );
  430. BEGIN
  431. IF ( head = tail ) & ( res = Ok ) THEN head := 0; receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail ) END;
  432. IF res = Ok THEN x := buf[ head ]; INC( head ) ELSE x := 0X END
  433. END Char;
  434. (** Like Read, but return result. Return 0X if no success ( e.g. file ended ) *)
  435. PROCEDURE Get* ( ): CHAR;
  436. BEGIN
  437. IF ( head = tail ) & ( res = Ok ) THEN head := 0; receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail ) END;
  438. IF res = Ok THEN INC( head ); RETURN buf[ head - 1 ] ELSE RETURN 0X END
  439. END Get;
  440. (** Like Get, but leave the byte in the input buffer. *)
  441. PROCEDURE Peek* ( ): CHAR;
  442. BEGIN
  443. IF ( head = tail ) & ( res = Ok ) THEN
  444. head := 0; receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail );
  445. IF res = EOF THEN (* ignore EOF here *)
  446. res := Ok; tail := 0;
  447. RETURN 0X; (* Peek returns 0X at eof *)
  448. END
  449. END;
  450. IF res = Ok THEN RETURN buf[ head ] ELSE RETURN 0X END;
  451. END Peek;
  452. (** Read size bytes into x, starting at ofs. The len parameter returns the number of bytes that were actually read. *)
  453. PROCEDURE Bytes* ( VAR x: ARRAY OF CHAR; ofs, size: SIZE; VAR len: SIZE );
  454. VAR n: SIZE;
  455. BEGIN
  456. len := 0;
  457. LOOP
  458. n := tail - head; (* bytes available *)
  459. IF n = 0 THEN (* no data available *)
  460. head := 0;
  461. IF res = Ok THEN (* fill buffer *)
  462. receive( buf^, 0, LEN( buf ), 1, tail, res ); INC( received, tail );
  463. END;
  464. IF res # Ok THEN (* should not be reading from erroneous rider *)
  465. WHILE size # 0 DO x[ ofs ] := 0X; INC( ofs ); DEC( size ) END; (* clear rest of buffer *)
  466. IF ( res = EOF ) & ( len # 0 ) THEN res := Ok END; (* ignore EOF if some data being returned *)
  467. EXIT
  468. END;
  469. n := tail
  470. END;
  471. IF n > size THEN n := size END;
  472. ASSERT ( ofs + n <= LEN( x )); (* index check *)
  473. SYSTEM.MOVE( ADDRESSOF( buf[ head ] ), ADDRESSOF( x[ ofs ] ), n ); INC( head, n ); INC( len, n );
  474. IF size = n THEN EXIT END; (* done *)
  475. INC( ofs, n ); DEC( size, n )
  476. END
  477. END Bytes;
  478. (** Skip n bytes on the reader. *)
  479. PROCEDURE SkipBytes* ( n: Position );
  480. VAR ch: CHAR;
  481. BEGIN
  482. WHILE n > 0 DO ch := Get( ); DEC( n ) END
  483. END SkipBytes;
  484. (** Read a SHORTINT. *)
  485. PROCEDURE RawSInt* ( VAR x: SHORTINT );
  486. BEGIN
  487. x := SYSTEM.VAL( SHORTINT, Get( ))
  488. END RawSInt;
  489. (** Read an INTEGER. *)
  490. PROCEDURE RawInt* ( VAR x: INTEGER );
  491. VAR x0, x1: CHAR;
  492. BEGIN
  493. x0 := Get( ); x1 := Get( ); (* defined order *)
  494. x := ORD( x1 ) * 100H + ORD( x0 )
  495. END RawInt;
  496. (** Read a LONGINT. *)
  497. PROCEDURE RawLInt* ( VAR x: LONGINT );
  498. VAR ignore: SIZE;
  499. BEGIN
  500. Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
  501. END RawLInt;
  502. (** Read a HUGEINT. *)
  503. PROCEDURE RawHInt* ( VAR x: HUGEINT );
  504. VAR ignore: SIZE;
  505. BEGIN
  506. Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
  507. END RawHInt;
  508. (** Read a 64 bit value in network byte order ( most significant byte first ) *)
  509. PROCEDURE Net64* ( ): HUGEINT;
  510. BEGIN
  511. RETURN Net32( ) * 100000000H + Net32( )
  512. END Net64;
  513. (** Read a 32 bit value in network byte order ( most significant byte first ) *)
  514. PROCEDURE Net32* ( ): LONGINT;
  515. BEGIN
  516. RETURN LONG( ORD( Get( ))) * 1000000H + LONG( ORD( Get( ))) * 10000H + LONG( ORD( Get( ))) * 100H + LONG( ORD( Get( )))
  517. END Net32;
  518. (** Read an unsigned 16bit value in network byte order ( most significant byte first ) *)
  519. PROCEDURE Net16* ( ): LONGINT;
  520. BEGIN
  521. RETURN LONG( ORD( Get( ))) * 100H + LONG( ORD( Get( )))
  522. END Net16;
  523. (** Read an unsigned byte *)
  524. PROCEDURE Net8* ( ): LONGINT;
  525. BEGIN
  526. RETURN LONG( ORD( Get( )))
  527. END Net8;
  528. (** Read a SET. *)
  529. PROCEDURE RawSet* ( VAR x: SET );
  530. VAR ignore: SIZE;
  531. BEGIN
  532. Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
  533. END RawSet;
  534. (** Read a BOOLEAN. *)
  535. PROCEDURE RawBool* ( VAR x: BOOLEAN );
  536. BEGIN
  537. x := ( Get( ) # 0X )
  538. END RawBool;
  539. (** Read a REAL. *)
  540. PROCEDURE RawReal* ( VAR x: REAL );
  541. VAR ignore: SIZE;
  542. BEGIN
  543. Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
  544. END RawReal;
  545. (** Read a LONGREAL. *)
  546. PROCEDURE RawLReal* ( VAR x: LONGREAL );
  547. VAR ignore: SIZE;
  548. BEGIN
  549. Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
  550. END RawLReal;
  551. (** 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. *)
  552. PROCEDURE RawString* ( VAR x: ARRAY OF CHAR );
  553. VAR i, m: SIZE; ch: CHAR;
  554. BEGIN
  555. i := 0; m := LEN( x ) - 1;
  556. LOOP
  557. ch := Get( ); (* also returns 0X on error *)
  558. IF ch = 0X THEN EXIT END;
  559. IF i < m THEN x[ i ] := ch; INC( i ) END
  560. END;
  561. x[ i ] := 0X
  562. END RawString;
  563. (** Read a number in a compressed format. *)
  564. PROCEDURE RawNum* ( VAR x: LONGINT );
  565. VAR ch: CHAR; n, y: LONGINT;
  566. BEGIN
  567. n := 0; y := 0; ch := Get( );
  568. WHILE ch >= 80X DO INC( y, LSH( LONGINT( ORD( ch )) - 128, n )); INC( n, 7 ); ch := Get( ) END;
  569. x := ASH( LSH( LONGINT( ORD( ch )), 25 ), n - 25 ) + y
  570. END RawNum;
  571. (** Read a size in a compressed format. *)
  572. PROCEDURE RawSize* ( VAR x: SIZE );
  573. VAR ch: CHAR; n, y: SIZE;
  574. BEGIN
  575. n := 0; y := 0; ch := Get( );
  576. WHILE ch >= 80X DO INC( y, LSH( SIZE( ORD( ch )) - 128, n )); INC( n, 7 ); ch := Get( ) END;
  577. x := ASH( LSH( SIZE( ORD( ch )), SIZE OF SIZE * 8 - 7 ), n - SIZE OF SIZE * 8 - 7 ) + y
  578. END RawSize;
  579. (** -- Read formatted data ( uses Peek for one character lookahead ) -- *)
  580. (** Read an integer value in decimal or hexadecimal. If hex = TRUE, recognize the "H" postfix for hexadecimal numbers. *)
  581. PROCEDURE Int* ( VAR x: LONGINT; hex: BOOLEAN );
  582. VAR vd, vh, sgn, d: LONGINT; ch: CHAR; ok: BOOLEAN;
  583. BEGIN
  584. vd := 0; vh := 0; sgn := 1; ok := FALSE;
  585. IF Peek( ) = "-" THEN sgn := -1; ch := Get( ) END;
  586. LOOP
  587. ch := Peek( );
  588. IF ( ch >= "0" ) & ( ch <= "9" ) THEN d := ORD( ch ) - ORD( "0" )
  589. ELSIF hex & ( CAP( ch ) >= "A" ) & ( CAP( ch ) <= "F" ) THEN d := ORD( CAP( ch )) - ORD( "A" ) + 10
  590. ELSE EXIT
  591. END;
  592. vd := 10 * vd + d; vh := 16 * vh + d; (* ignore overflow *)
  593. ch := Get( ); ok := TRUE
  594. END;
  595. IF hex & ( CAP( ch ) = "H" ) THEN (* optional "H" present *)
  596. vd := vh; (* use the hex value *)
  597. ch := Get( )
  598. END;
  599. x := sgn * vd;
  600. IF ( res = 0 ) & ~ok THEN res := FormatError END
  601. END Int;
  602. (** Return TRUE iff at the end of a line ( or file ). *)
  603. PROCEDURE EOLN* ( ): BOOLEAN;
  604. VAR ch: CHAR;
  605. BEGIN
  606. ch := Peek( ); RETURN ( ch = CR ) OR ( ch = LF ) OR ( res # Ok )
  607. END EOLN;
  608. (** Read all characters until the end of the line ( inclusive ). If the input string is larger than x, read the full string and assign
  609. the truncated 0X-terminated value to x. *)
  610. PROCEDURE Ln* ( VAR x: ARRAY OF CHAR );
  611. VAR i, m: SIZE; ch: CHAR;
  612. BEGIN
  613. i := 0; m := LEN( x ) - 1;
  614. LOOP
  615. ch := Peek( );
  616. IF ( ch = CR ) OR ( ch = LF ) OR ( res # Ok ) THEN EXIT END;
  617. IF i < m THEN x[ i ] := ch; INC( i ) END;
  618. ch := Get( );
  619. END;
  620. x[ i ] := 0X;
  621. IF ch = CR THEN ch := Get( ) END;
  622. IF Peek( ) = LF THEN ch := Get( ) END;
  623. END Ln;
  624. (** Read all characters until the end of the line ( inclusive ) or an <EOT> character.
  625. If the input string is larger than x, read the full string and assign the truncated 0X-terminated
  626. value to x. *)
  627. PROCEDURE LnEOT* ( VAR x: ARRAY OF CHAR );
  628. VAR i, m: SIZE; ch: CHAR;
  629. BEGIN
  630. i := 0; m := LEN( x ) - 1;
  631. LOOP
  632. ch := Peek( );
  633. IF ( ch = CR ) OR ( ch = LF ) OR ( ch = EOT ) OR ( res # Ok ) THEN EXIT END;
  634. IF i < m THEN x[ i ] := ch; INC( i ) END;
  635. ch := Get( )
  636. END;
  637. x[ i ] := 0X;
  638. IF ch = CR THEN ch := Get( ) END;
  639. IF Peek( ) = LF THEN ch := Get( ) END;
  640. IF ch = EOT THEN ch := Get( ) END
  641. END LnEOT;
  642. (** Skip over all characters until the end of the line ( inclusive ). *)
  643. PROCEDURE SkipLn*;
  644. VAR ch: CHAR;
  645. BEGIN
  646. LOOP
  647. ch := Peek( );
  648. IF ( ch = CR ) OR ( ch = LF ) OR ( res # Ok ) THEN EXIT END;
  649. ch := Get( )
  650. END;
  651. IF ch = CR THEN ch := Get( ) END;
  652. IF Peek( ) = LF THEN ch := Get( ) END
  653. END SkipLn;
  654. (** Skip over space and TAB characters. *)
  655. PROCEDURE SkipSpaces*;
  656. VAR ch: CHAR;
  657. BEGIN
  658. LOOP
  659. ch := Peek( );
  660. IF ( ch # TAB ) & ( ch # SP ) THEN EXIT END;
  661. ch := Get( )
  662. END
  663. END SkipSpaces;
  664. (** Skip over space, TAB and EOLN characters. *)
  665. PROCEDURE SkipWhitespace*;
  666. VAR ch: CHAR;
  667. BEGIN
  668. LOOP
  669. ch := Peek( );
  670. IF ( ch # SP ) & ( ch # CR ) & ( ch # LF ) & ( ch # TAB ) THEN EXIT END;
  671. ch := Get( )
  672. END
  673. END SkipWhitespace;
  674. (** Read a token, consisting of any string of characters terminated by space, TAB or EOLN. *)
  675. PROCEDURE Token* ( VAR token: ARRAY OF CHAR );
  676. VAR j, max: SIZE; ch: CHAR;
  677. BEGIN
  678. j := 0; max := LEN( token ) - 1;
  679. LOOP
  680. ch := Peek( );
  681. IF ( ch = SP ) OR ( ch = CR ) OR ( ch = LF ) OR ( ch = TAB ) OR ( res # Ok ) THEN EXIT END;
  682. IF j < max THEN token[ j ] := ch; INC( j ) END;
  683. ch := Get( )
  684. END;
  685. token[ j ] := 0X
  686. END Token;
  687. (** Read an optionally "" or '' enquoted string. Will not read past the end of a line. *)
  688. PROCEDURE String* ( VAR string: ARRAY OF CHAR );
  689. VAR c, delimiter: CHAR; i, len: SIZE;
  690. BEGIN
  691. c := Peek( );
  692. IF ( c # "'" ) & ( c # '"' ) THEN Token( string )
  693. ELSE
  694. delimiter := Get( ); c := Peek( ); i := 0; len := LEN( string ) - 1;
  695. WHILE ( i < len ) & ( c # delimiter ) & ( c # CR ) & ( c # LF ) & ( res = Ok ) DO string[ i ] := Get( ); INC( i ); c := Peek( ) END;
  696. IF ( c = delimiter ) THEN c := Get( ) END;
  697. string[ i ] := 0X
  698. END
  699. END String;
  700. (** First skip whitespace, then read string *)
  701. PROCEDURE GetString* ( VAR string : ARRAY OF CHAR ): BOOLEAN;
  702. VAR c: CHAR;
  703. BEGIN
  704. SkipWhitespace;
  705. c := Peek( );
  706. String( string );
  707. RETURN ( string[ 0 ] # 0X ) OR ( c = "'" ) OR ( c = '"' );
  708. END GetString;
  709. (** First skip whitespace, then read integer *)
  710. PROCEDURE GetInteger* ( VAR integer : LONGINT; isHexadecimal : BOOLEAN ): BOOLEAN;
  711. BEGIN
  712. SkipWhitespace;
  713. Int( integer, isHexadecimal );
  714. RETURN res = Ok;
  715. END GetInteger;
  716. (** First skip whitespace, then read a real *)
  717. PROCEDURE GetReal* ( VAR real: LONGREAL ): BOOLEAN;
  718. BEGIN
  719. SkipWhitespace;
  720. real := RC.ScanReal( Get );
  721. RETURN res = Ok
  722. END GetReal;
  723. (** First skip whitespace, then read 1 byte character *)
  724. PROCEDURE GetChar* ( VAR ch : CHAR ): BOOLEAN;
  725. BEGIN
  726. SkipWhitespace;
  727. Char( ch );
  728. RETURN ch # 0X;
  729. END GetChar;
  730. END Reader;
  731. TYPE
  732. (** A special reader that buffers input set by SetString or SetRawString. *)
  733. StringReader* = OBJECT ( Reader )
  734. PROCEDURE & InitStringReader* ( size: SIZE );
  735. BEGIN
  736. InitReader( Receive, size )
  737. END InitStringReader;
  738. PROCEDURE CanSetPos* ( ): BOOLEAN;
  739. BEGIN
  740. RETURN TRUE
  741. END CanSetPos;
  742. (** Set the reader position *)
  743. PROCEDURE SetPos* ( pos: Position );
  744. BEGIN
  745. IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
  746. head := SIZE( pos ); tail := LEN( buf ); received := LEN( buf ); res := Ok;
  747. END SetPos;
  748. PROCEDURE Receive( VAR buf: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: WORD );
  749. BEGIN
  750. IF min = 0 THEN res := Ok ELSE res := EOF END;
  751. len := 0;
  752. END Receive;
  753. (** Set the contents of the string buffer. The s parameter is a 0X-terminated string. *)
  754. PROCEDURE Set* ( CONST s: ARRAY OF CHAR );
  755. VAR len: SIZE;
  756. BEGIN
  757. len := 0;
  758. WHILE s[ len ] # 0X DO INC( len ) END;
  759. IF len > LEN( buf ) THEN len := LEN( buf ) END;
  760. head := 0; tail := len; received := len; res := Ok;
  761. IF len > 0 THEN
  762. SYSTEM.MOVE( ADDRESSOF( s[ 0 ] ), ADDRESSOF( buf[ 0 ] ), len )
  763. END;
  764. END Set;
  765. (** Set the contents of the string buffer. The len parameter specifies the size of the buffer s. *)
  766. PROCEDURE SetRaw* ( CONST s: ARRAY OF CHAR; ofs, len: SIZE );
  767. BEGIN
  768. IF len > LEN( buf ) THEN len := LEN( buf ) END;
  769. head := 0; tail := len; received := len; res := Ok;
  770. ASSERT (( len >= 0 ) & ( ofs + len <= LEN( s ))); (* index check *)
  771. IF len > 0 THEN
  772. SYSTEM.MOVE( ADDRESSOF( s[ ofs ] ), ADDRESSOF( buf[ 0 ] ), len )
  773. END;
  774. END SetRaw;
  775. END StringReader;
  776. Bytes2 = ARRAY 2 OF CHAR;
  777. Bytes4 = ARRAY 4 OF CHAR;
  778. Bytes8 = ARRAY 8 OF CHAR;
  779. VAR
  780. months: ARRAY 12 * 4 + 1 OF CHAR;
  781. (** Open a writer to the specified stream sender. Update must be called after writing to ensure the buffer is written to the stream. *)
  782. PROCEDURE OpenWriter* ( VAR b: Writer; send: Sender );
  783. BEGIN
  784. NEW( b, send, DefaultWriterSize )
  785. END OpenWriter;
  786. (** Open a reader from the specified stream receiver. *)
  787. PROCEDURE OpenReader* ( VAR b: Reader; receive: Receiver );
  788. BEGIN
  789. NEW( b, receive, DefaultReaderSize )
  790. END OpenReader;
  791. (** Copy the contents of a reader to a writer *)
  792. PROCEDURE Copy* ( r: Reader; w: Writer );
  793. VAR char: CHAR;
  794. BEGIN
  795. WHILE r.res = Ok DO
  796. r.Char ( char );
  797. IF r.res = Ok THEN w.Char ( char ) END
  798. END;
  799. END Copy;
  800. BEGIN
  801. months := " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec";
  802. END Streams.
  803. (**
  804. Notes:
  805. o Any single buffer instance must not be accessed by more than one process concurrently.
  806. o The interface is blocking ( synchronous ). If an output buffer is full, it is written with a synchronous write, which returns
  807. only when all the data has been written. If an input buffer is empty, it is read with a synchronous read, which only returns
  808. once some data has been read. The only exception is the Available( ) procedure, which "peeks" at the input stream
  809. and returns 0 if no data is currently available.
  810. o All procedures set res to the error code reported by the lower-level I/O operation ( non-zero indicates error ).
  811. E.g. closing an underlying TCP connection will result in the Read* procedures returning a non-zero error code.
  812. o res is sticky. Once it becomes non-zero, it remains non-zero.
  813. 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.
  814. o All output written to an erroneous buffer is ignored.
  815. o The value returned when reading from an erroneous buffer is undefined, except for the Read procedure, which returns 0X.
  816. 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.
  817. o Raw format is little-endian 2's complement integers, IEEE reals and 0X-terminated strings.
  818. o Syntax for ReadInt with hex = FALSE: num = [ "-" ] digit { digit }. digit = "0".."9".
  819. o Syntax for ReadInt with hex = TRUE: [ "-" ] hexdigit { hexdigit } [ "H"|"h" ]. hexdigit = digit | "A".."F" | "a".."f".
  820. o ReadInt with hex = TRUE allows "A".."F" as digits, and looks for a "H" character after the number.
  821. If present, the number is interpreted as hexadecimal. If hexadecimal digits are present, but no "H" flag,
  822. the resulting decimal value is undefined.
  823. o ReadInt ignores overflow.
  824. 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,
  825. or an error occurs.
  826. o A Receiver receives up to size bytes from input into buf at ofs and returns the number of bytes read in len.
  827. It returns res non-zero on error. It waits until at least min bytes ( possibly zero ) are available, or an error occurs.
  828. o EOLN and ReadLn recognize the following end-of-line characters: CR, LF and CR/LF.
  829. o To read an unstructured file token-by-token: WHILE ( r.res = 0 ) DO SkipWhitespace; ReadToken END
  830. o To read a line structured file token-by-token: WHILE r.res = 0 DO SkipSpaces; WHILE ~EOLN DO ReadToken; SkipSpaces END END
  831. o A string writer is not flushed when it becomes full, but res is set to a non-zero value.
  832. o Update has no effect on a string writer.
  833. o GetString can be called on a string writer to return the buffer contents and reset it to empty.
  834. o GetString always appends a 0X character to the buffer, but returns the true length ( excluding the added 0X ) in the len parameter,
  835. so it can also be used for binary data that includes 0X characters.
  836. o Receive procedure should set res to EOF when attempting to read past the end of file.
  837. *)
  838. (*
  839. to do:
  840. o stream byte count
  841. o read formatted data
  842. o reads for all formatted writes
  843. o write reals
  844. o low-level version that can be used in kernel ( below KernelLog )
  845. *)