StreamWriters.Mos 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. (**
  2. AUTHOR: "Alexey Morozov, HighDim GmbH, 2011-2012";
  3. PURPOSE: "I/O buffering with formatted writing and reading";
  4. The implemented interface is used for outging messages from a TRM to another TRM which is responsible for translation of the messages sending via a comunication line to the user.
  5. *)
  6. MODULE StreamWriters;
  7. IMPORT
  8. SYSTEM;
  9. CONST
  10. Ok* = 0; (** zero result code means no error occurred *)
  11. EOF* = 4201; (** error returned when Receive reads past end of file or stream *)
  12. EOT* = 1AX; (** EOT character *)
  13. StringFull* = 4202;
  14. FormatError* = 4203; (** error returned when ReadInt fails *)
  15. DefaultWriterSize* = 4096;
  16. CR* = 0DX; LF* = 0AX;
  17. TYPE
  18. (** Any stream output procedure *)
  19. Sender* = PROCEDURE(VAR writer: Writer; CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT);
  20. Writer* = RECORD
  21. tail: LONGINT;
  22. buf: POINTER TO ARRAY OF CHAR;
  23. res*: LONGINT; (** result of last output operation. *)
  24. send: Sender;
  25. sent-: LONGINT; (** count of sent bytes *)
  26. (* buf[0..tail-1] contains data to write. *)
  27. END;
  28. Hugeint = ARRAY 2 OF LONGINT ;
  29. VAR
  30. months: ARRAY 12 * 4 + 1 OF CHAR;
  31. (** Initialize a writer given a sender and a buffer size *)
  32. PROCEDURE Init*(VAR wr: Writer; sender: Sender; size: LONGINT);
  33. BEGIN
  34. ASSERT(sender # NIL);
  35. NEW(wr.buf,size); wr.send := sender; Reset(wr);
  36. END Init;
  37. (** Reset a writer *)
  38. PROCEDURE Reset*(VAR wr: Writer);
  39. BEGIN
  40. wr.tail := 0; wr.res := Ok; wr.sent := 0
  41. END Reset;
  42. (** output all buffered data *)
  43. PROCEDURE Update*(VAR wr: Writer);
  44. BEGIN
  45. IF (wr.res = Ok) THEN
  46. wr.send( wr, wr.buf^, 0, wr.tail, TRUE , wr.res );
  47. IF wr.res = Ok THEN INC( wr.sent, wr.tail ); wr.tail := 0 END
  48. END
  49. END Update;
  50. (** Current write position. *)
  51. PROCEDURE Pos*(VAR wr: Writer ): LONGINT;
  52. BEGIN
  53. RETURN wr.sent + wr.tail
  54. END Pos;
  55. (* -- Write raw binary data -- *)
  56. (** Write one byte. *)
  57. PROCEDURE Char*(VAR wr: Writer; x: CHAR );
  58. BEGIN
  59. IF (wr.tail = LEN( wr.buf )) & (wr.res = Ok) THEN
  60. wr.send( wr, wr.buf^, 0, wr.tail, FALSE , wr.res );
  61. IF wr.res = Ok THEN INC( wr.sent, wr.tail ); wr.tail := 0 END
  62. END;
  63. IF wr.res = Ok THEN wr.buf[wr.tail] := x; INC( wr.tail ) END
  64. END Char;
  65. (** Write len bytes from x, starting at ofs. *)
  66. PROCEDURE Bytes*(VAR wr: Writer; CONST x: ARRAY OF CHAR; ofs, len: LONGINT );
  67. VAR n: LONGINT;
  68. BEGIN
  69. ASSERT ( len >= 0 );
  70. LOOP
  71. n := LEN( wr.buf ) - wr.tail; (* space available *)
  72. IF n = 0 THEN
  73. IF wr.res = Ok THEN (* send current buffer *)
  74. wr.send( wr, wr.buf^, 0, wr.tail, FALSE , wr.res );
  75. IF wr.res = Ok THEN INC( wr.sent, wr.tail ); wr.tail := 0 ELSE EXIT END
  76. ELSE
  77. EXIT (* should not be writing on an erroneous rider *)
  78. END;
  79. n := LEN( wr.buf )
  80. END;
  81. IF n > len THEN n := len END;
  82. ASSERT ( wr.tail + n <= LEN( wr.buf ) ); (* index check *)
  83. SYSTEM.MOVE( ADDRESSOF( x[ofs] ), ADDRESSOF( wr.buf[wr.tail] ), n ); INC( wr.tail, n );
  84. IF len = n THEN EXIT END; (* done *)
  85. INC( ofs, n ); DEC( len, n )
  86. END
  87. END Bytes;
  88. (** Write a SHORTINT. *)
  89. PROCEDURE RawSInt*(VAR wr: Writer; x: SHORTINT );
  90. BEGIN
  91. Char(wr, SYSTEM.VAL( CHAR, x ) )
  92. END RawSInt;
  93. (** Write an INTEGER. *)
  94. PROCEDURE RawInt*(VAR wr: Writer; x: INTEGER );
  95. BEGIN
  96. Char(wr,CHR(SYSTEM.MSK(x,0FFH)));
  97. x := ROT(x,-8);
  98. Char(wr,CHR(SYSTEM.MSK(x,0FFH)));
  99. END RawInt;
  100. (** Write a LONGINT. *)
  101. PROCEDURE RawLInt*(VAR wr: Writer; x: LONGINT );
  102. VAR i: LONGINT;
  103. BEGIN
  104. Char(wr,CHR(SYSTEM.MSK(x,0FFH)));
  105. FOR i := 0 TO 2 DO
  106. x := ROT(x,-8);
  107. Char(wr,CHR(SYSTEM.MSK(x,0FFH)));
  108. END;
  109. END RawLInt;
  110. (** Write a HUGEINT. *)
  111. PROCEDURE RawHInt*(VAR wr: Writer; x: HUGEINT );
  112. VAR xx: Hugeint;
  113. BEGIN
  114. xx := SYSTEM.VAL(Hugeint,x);
  115. RawLInt(wr,xx[0]); RawLInt(wr,xx[1]);
  116. END RawHInt;
  117. (** Write a 32 bit value in network byte order (most significant byte first) *)
  118. PROCEDURE Net32*(VAR wr: Writer; x: LONGINT );
  119. BEGIN
  120. Char(wr, CHR( x DIV 1000000H MOD 100H ) ); Char(wr, CHR( x DIV 10000H MOD 100H ) ); Char(wr, CHR( x DIV 100H MOD 100H ) );
  121. Char(wr, CHR( x MOD 100H ) )
  122. END Net32;
  123. (** Write a 16 bit value in network byte order (most significant byte first) *)
  124. PROCEDURE Net16*(VAR wr: Writer; x: LONGINT );
  125. BEGIN
  126. Char(wr, CHR( x DIV 100H MOD 100H ) ); Char(wr, CHR( x MOD 100H ) )
  127. END Net16;
  128. (** write unsigned byte *)
  129. PROCEDURE Net8*(VAR wr: Writer; x: LONGINT );
  130. BEGIN
  131. Char(wr, CHR( x MOD 100H ) )
  132. END Net8;
  133. (** Write a SET. *)
  134. PROCEDURE RawSet*(VAR wr: Writer; x: SET );
  135. BEGIN
  136. RawLInt(wr, SYSTEM.VAL( LONGINT, x ) )
  137. END RawSet;
  138. (** Write a BOOLEAN. *)
  139. PROCEDURE RawBool*(VAR wr: Writer; x: BOOLEAN );
  140. BEGIN
  141. IF x THEN Char(wr, 1X ) ELSE Char(wr, 0X ) END
  142. END RawBool;
  143. (** Write a REAL. *)
  144. PROCEDURE RawReal*(VAR wr: Writer; x: REAL );
  145. BEGIN
  146. RawLInt(wr, SYSTEM.VAL( LONGINT, x ) )
  147. END RawReal;
  148. (** Write a LONGREAL. *)
  149. PROCEDURE RawLReal*(VAR wr: Writer; x: LONGREAL );
  150. BEGIN
  151. RawHInt(wr,SYSTEM.VAL(HUGEINT,x));
  152. END RawLReal;
  153. (** Write a 0X-terminated string, including the 0X terminator *)
  154. PROCEDURE RawString*(VAR writer: Writer; CONST str: ARRAY OF CHAR );
  155. BEGIN
  156. String(writer,str); Char(writer,0X);
  157. END RawString;
  158. (** Write a number in a compressed format. *)
  159. PROCEDURE RawNum*(VAR wr: Writer; x: LONGINT );
  160. BEGIN
  161. WHILE (x < -64) OR (x > 63) DO Char(wr, CHR( x MOD 128 + 128 ) ); x := x DIV 128 END;
  162. Char(wr, CHR( x MOD 128 ) )
  163. END RawNum;
  164. (* -- Write formatted data -- *)
  165. (** Write an ASCII end-of-line (CR/LF). *)
  166. PROCEDURE Ln*(VAR wr: Writer);
  167. BEGIN
  168. Char(wr, CR ); Char(wr, LF )
  169. END Ln;
  170. (** Write a 0X-terminated string, excluding the 0X terminator *)
  171. PROCEDURE String*(VAR wr: Writer; CONST str: ARRAY OF CHAR);
  172. VAR i: LONGINT;
  173. BEGIN
  174. i := 0;
  175. WHILE (i < LEN(str)) & (str[i] # 0X) DO
  176. Char(wr,str[i]);
  177. INC(i);
  178. END;
  179. END String;
  180. (** Write a BOOLEAN as "TRUE" or "FALSE" *)
  181. PROCEDURE Bool*(VAR wr: Writer; x: BOOLEAN);
  182. BEGIN
  183. IF x THEN String(wr,"TRUE"); ELSE String(wr,"FALSE"); END;
  184. END Bool;
  185. (** Write an integer in decimal right-justified in a field of at least w characters. *)
  186. PROCEDURE Int*(VAR wr: Writer; x, w: LONGINT );
  187. VAR
  188. i, k, x0, y: LONGINT;
  189. str: ARRAY 12 OF CHAR;
  190. BEGIN
  191. IF x < 0 THEN
  192. IF x = MIN(LONGINT) THEN
  193. DEC(w,11);
  194. WHILE w > 0 DO Char(wr,' '); DEC(w); END;
  195. String(wr,"-2147483648"); RETURN;
  196. ELSE DEC(w); x0 := -x;
  197. END
  198. ELSIF x = 0 THEN
  199. WHILE w > 1 DO Char(wr,' '); DEC(w); END;
  200. Char(wr,'0'); RETURN;
  201. ELSE x0 := x;
  202. END;
  203. i := 0;
  204. WHILE x0 > 0 DO
  205. y := x0 DIV 10;
  206. k := y*10;
  207. k := x0-k;
  208. k := k + 48;
  209. (*str[i] := CHR(x - (y*10)+48);*) (*! compiler has a problem with this expression *)
  210. str[i] := CHR(k);
  211. x0 := y;
  212. INC(i);
  213. END;
  214. WHILE w > i DO Char(wr,' '); DEC(w); END;
  215. IF x < 0 THEN Char(wr,'-') END;
  216. REPEAT DEC(i); Char(wr,str[i]); UNTIL i = 0;
  217. (*VAR i, x0: LONGINT;
  218. a: ARRAY 12 OF CHAR;
  219. BEGIN
  220. IF x < 0 THEN
  221. IF x = MIN( LONGINT ) THEN
  222. DEC( w, 11 );
  223. WHILE w > 0 DO Char(wr, " " ); DEC( w ) END;
  224. String(wr, "-2147483648" ); RETURN
  225. ELSE DEC( w ); x0 := -x
  226. END
  227. ELSE x0 := x
  228. END;
  229. i := 0;
  230. REPEAT a[i] := CHR( x0 MOD 10 + 30H ); x0 := x0 DIV 10; INC( i ) UNTIL x0 = 0;
  231. WHILE w > i DO Char(wr, " " ); DEC( w ) END;
  232. IF x < 0 THEN Char(wr, "-" ) END;
  233. REPEAT DEC( i ); Char(wr, a[i] ) UNTIL i = 0*)
  234. END Int;
  235. (** Write a SET in Oberon notation. *)
  236. PROCEDURE Set*(VAR wr: Writer; s: SET ); (* from P. Saladin *)
  237. VAR i, last: LONGINT; dots: BOOLEAN;
  238. BEGIN
  239. Char(wr, "{" ); last := MIN( LONGINT ); dots := FALSE;
  240. FOR i := MIN( SET ) TO MAX( SET ) DO
  241. IF i IN s THEN
  242. IF last = (i - 1) THEN
  243. IF dots THEN String(wr, ".." ); dots := FALSE END;
  244. IF (i = MAX( SET )) OR ~((i + 1) IN s) THEN Int(wr, i, 1 ) END
  245. ELSE
  246. IF last >= MIN( SET ) THEN String(wr, ", " ) END;
  247. Int(wr, i, 1 ); dots := TRUE
  248. END;
  249. last := i
  250. END
  251. END;
  252. Char(wr, "}" )
  253. END Set;
  254. (**
  255. Write an integer in hexadecimal right-justified in a field of at least ABS(wr) characters.
  256. If w < 0 THEN w least significant hex digits of x are written (potentially including leading zeros)
  257. *)
  258. PROCEDURE Hex*(VAR wr: Writer; x: LONGINT; w: LONGINT);
  259. VAR filler: CHAR; i,maxw: LONGINT; a: ARRAY 10 OF CHAR; y: LONGINT;
  260. BEGIN
  261. IF w < 0 THEN filler := '0'; w := -w; maxw := w ELSE filler := ' '; maxw := 8 END;
  262. i := 0;
  263. REPEAT
  264. y := x MOD 10H;
  265. IF y < 10 THEN a[i] := CHR(y+ORD('0')) ELSE a[i] := CHR(y-10+ORD('A')) END;
  266. x := x DIV 10H;
  267. INC(i);
  268. UNTIL (x=0) OR (i=maxw);
  269. WHILE w > i DO Char(wr,filler); DEC(w); END;
  270. REPEAT DEC(i); Char(wr,a[i]); UNTIL i = 0;
  271. (*VAR filler: CHAR; i,maxw: LONGINT; a: ARRAY 20 OF CHAR; y: HUGEINT;
  272. BEGIN
  273. IF w < 0 THEN filler := '0'; w := -w; maxw := w ELSE filler := ' '; maxw := 16 END;
  274. i := 0;
  275. REPEAT
  276. y := x MOD 10H;
  277. IF y < 10 THEN a[i] := CHR(y+ORD('0')) ELSE a[i] := CHR(y-10+ORD('A')) END;
  278. x := x DIV 10H;
  279. INC(i);
  280. UNTIL (x=0) OR (i=maxw);
  281. WHILE w > i DO Char(wr,filler); DEC( w ) END;
  282. REPEAT DEC( i ); Char(wr, a[i] ) UNTIL i = 0*)
  283. END Hex;
  284. (** Write "x" as a hexadecimal address. Do not use Hex because of arithmetic shift of the sign !*)
  285. PROCEDURE Address* (VAR wr: Writer; x: ADDRESS);
  286. BEGIN
  287. Hex(wr,x,-2*SIZEOF(ADDRESS));
  288. END Address;
  289. PROCEDURE Pair(VAR wr: Writer; ch: CHAR; x: LONGINT );
  290. BEGIN
  291. IF ch # 0X THEN Char(wr, ch ) END;
  292. Char(wr, CHR( ORD( "0" ) + x DIV 10 MOD 10 ) ); Char(wr, CHR( ORD( "0" ) + x MOD 10 ) )
  293. END Pair;
  294. (** 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.
  295. If all parameters are within range, the output string is exactly 19 characters wide. The t or d parameter can be -1, in which
  296. case the time or date respectively are left out. *)
  297. PROCEDURE Date*(VAR wr: Writer; t, d: LONGINT );
  298. VAR ch: CHAR;
  299. BEGIN
  300. IF d # -1 THEN
  301. Int(wr, 1900 + d DIV 512, 4 ); (* year *)
  302. Pair(wr, "-", d DIV 32 MOD 16 ); (* month *)
  303. Pair(wr, "-", d MOD 32 ); (* day *)
  304. ch := " " (* space between date and time *)
  305. ELSE
  306. ch := 0X (* no space before time *)
  307. END;
  308. IF t # -1 THEN
  309. Pair(wr, ch, t DIV 4096 MOD 32 ); (* hour *)
  310. Pair(wr, ":", t DIV 64 MOD 64 ); (* min *)
  311. Pair(wr, ":", t MOD 64 ) (* sec *)
  312. END
  313. END Date;
  314. (** Write the date and time in RFC 822/1123 format without the optional day of the week (dd mmm yyyy hh:mm:ss SZZZZ) .
  315. The t and d parameters are in Oberon time and date format. The tz parameter specifies the time zone offset in minutes
  316. (from -720 to 720 in steps of 30). If all parameters are within range, the output string is exactly 26 characters wide.
  317. The t, d or tz parameter can be -1, in which case the time, date or timezone respectively are left out. *)
  318. PROCEDURE Date822*(VAR wr: Writer; t, d, tz: LONGINT );
  319. VAR i, m: LONGINT; ch: CHAR;
  320. BEGIN
  321. IF d # -1 THEN
  322. Int(wr, d MOD 32, 2 ); (* day *)
  323. m := (d DIV 32 MOD 16 - 1) * 4; (* month *)
  324. FOR i := m TO m + 3 DO Char(wr, months[i] ) END;
  325. Int(wr, 1900 + d DIV 512, 5 ); (* year *)
  326. ch := " " (* space *)
  327. ELSE
  328. ch := 0X (* no space *)
  329. END;
  330. IF t # -1 THEN
  331. Pair(wr, ch, t DIV 4096 MOD 32 ); (* hour *)
  332. Pair(wr, ":", t DIV 64 MOD 64 ); (* min *)
  333. Pair(wr, ":", t MOD 64 ); (* sec *)
  334. ch := " " (* space *)
  335. ELSE
  336. (* leave ch as before *)
  337. END;
  338. IF tz # -1 THEN
  339. IF ch # 0X THEN Char(wr, ch ) END;
  340. IF tz >= 0 THEN Pair(wr, "+", tz DIV 60 ) ELSE Pair(wr, "-", (-tz) DIV 60 ) END;
  341. Pair(wr, 0X, ABS( tz ) MOD 60 )
  342. END
  343. END Date822;
  344. (** Write a floating point number x using n character positions *)
  345. PROCEDURE Float*(VAR writer: Writer; x: REAL; n: LONGINT);
  346. BEGIN
  347. (*! current implementation does not support 'n' parameter *)
  348. FloatFix(writer,x,0,6,0);
  349. END Float;
  350. (** Write a floating point number 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). *)
  351. PROCEDURE FloatFix*(VAR writer: Writer; x: REAL; n, f, D: LONGINT);
  352. VAR
  353. d: LONGINT;
  354. BEGIN
  355. (*! current implementation does not support 'n' and 'D' parameters, to be implemented later *)
  356. IF x < 0 THEN x := -x; Char(writer,'-'); END;
  357. d := ENTIER(x);
  358. Int(writer,d,0);
  359. IF f > 0 THEN
  360. Char(writer,'.');
  361. x := x - d;
  362. WHILE f > 0 DO
  363. x := x * 10;
  364. d := ENTIER(x);
  365. Char(writer,CHR(48+d));
  366. x := x - d;
  367. DEC(f);
  368. END;
  369. END;
  370. END FloatFix;
  371. PROCEDURE NullSender*(VAR writer: Writer; CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT);
  372. BEGIN
  373. res := Ok
  374. END NullSender;
  375. PROCEDURE InitMod;
  376. BEGIN
  377. months := " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec";
  378. END InitMod;
  379. BEGIN
  380. InitMod;
  381. END StreamWriters.