| 69448 |      1 | (*  Title:      Pure/General/byte_message.ML
 | 
|  |      2 |     Author:     Makarius
 | 
|  |      3 | 
 | 
|  |      4 | Byte-oriented messages.
 | 
|  |      5 | *)
 | 
|  |      6 | 
 | 
|  |      7 | signature BYTE_MESSAGE =
 | 
|  |      8 | sig
 | 
| 69454 |      9 |   val write: BinIO.outstream -> string list -> unit
 | 
| 69451 |     10 |   val flush: BinIO.outstream -> unit
 | 
| 69467 |     11 |   val write_line: BinIO.outstream -> string -> unit
 | 
| 69449 |     12 |   val read: BinIO.instream -> int -> string
 | 
| 69452 |     13 |   val read_block: BinIO.instream -> int -> string option * int
 | 
| 69451 |     14 |   val read_line: BinIO.instream -> string option
 | 
|  |     15 |   val write_message: BinIO.outstream -> string list -> unit
 | 
| 69449 |     16 |   val read_message: BinIO.instream -> string list option
 | 
| 69451 |     17 |   val write_line_message: BinIO.outstream -> string -> unit
 | 
|  |     18 |   val read_line_message: BinIO.instream -> string option
 | 
| 69448 |     19 | end;
 | 
|  |     20 | 
 | 
|  |     21 | structure Byte_Message: BYTE_MESSAGE =
 | 
|  |     22 | struct
 | 
|  |     23 | 
 | 
| 69451 |     24 | (* output operations *)
 | 
|  |     25 | 
 | 
| 69454 |     26 | fun write stream = List.app (fn s => BinIO.output (stream, Byte.stringToBytes s));
 | 
| 69451 |     27 | 
 | 
|  |     28 | fun flush stream = ignore (try BinIO.flushOut stream);
 | 
|  |     29 | 
 | 
| 69467 |     30 | fun write_line stream s = (write stream [s, "\n"]; flush stream);
 | 
|  |     31 | 
 | 
| 69451 |     32 | 
 | 
|  |     33 | (* input operations *)
 | 
|  |     34 | 
 | 
| 69449 |     35 | fun read stream n = Byte.bytesToString (BinIO.inputN (stream, n));
 | 
|  |     36 | 
 | 
|  |     37 | fun read_block stream n =
 | 
| 69452 |     38 |   let
 | 
|  |     39 |     val msg = read stream n;
 | 
|  |     40 |     val len = size msg;
 | 
|  |     41 |   in (if len = n then SOME msg else NONE, len) end;
 | 
| 69449 |     42 | 
 | 
| 69448 |     43 | fun read_line stream =
 | 
|  |     44 |   let
 | 
|  |     45 |     val result = trim_line o String.implode o rev;
 | 
| 69452 |     46 |     fun read_body cs =
 | 
| 69448 |     47 |       (case BinIO.input1 stream of
 | 
|  |     48 |         NONE => if null cs then NONE else SOME (result cs)
 | 
|  |     49 |       | SOME b =>
 | 
|  |     50 |           (case Byte.byteToChar b of
 | 
|  |     51 |             #"\n" => SOME (result cs)
 | 
| 69452 |     52 |           | c => read_body (c :: cs)));
 | 
|  |     53 |   in read_body [] end;
 | 
| 69448 |     54 | 
 | 
| 69449 |     55 | 
 | 
| 69454 |     56 | (* messages with multiple chunks (arbitrary content) *)
 | 
| 69451 |     57 | 
 | 
| 69848 |     58 | fun make_header ns =
 | 
| 69454 |     59 |   [space_implode "," (map Value.print_int ns), "\n"];
 | 
| 69451 |     60 | 
 | 
| 69454 |     61 | fun write_message stream chunks =
 | 
| 69848 |     62 |   (write stream (make_header (map size chunks) @ chunks); flush stream);
 | 
| 69451 |     63 | 
 | 
|  |     64 | fun parse_header line =
 | 
|  |     65 |   map Value.parse_nat (space_explode "," line)
 | 
| 69454 |     66 |     handle Fail _ => error ("Malformed message header: " ^ quote line);
 | 
| 69451 |     67 | 
 | 
| 69449 |     68 | fun read_chunk stream n =
 | 
| 69452 |     69 |   (case read_block stream n of
 | 
|  |     70 |     (SOME chunk, _) => chunk
 | 
|  |     71 |   | (NONE, len) =>
 | 
| 69449 |     72 |       error ("Malformed message chunk: unexpected EOF after " ^
 | 
| 69452 |     73 |         string_of_int len ^ " of " ^ string_of_int n ^ " bytes"));
 | 
| 69449 |     74 | 
 | 
|  |     75 | fun read_message stream =
 | 
| 69454 |     76 |   read_line stream |> Option.map (parse_header #> map (read_chunk stream));
 | 
| 69451 |     77 | 
 | 
|  |     78 | 
 | 
|  |     79 | (* hybrid messages: line or length+block (with content restriction) *)
 | 
|  |     80 | 
 | 
| 69452 |     81 | fun is_length msg =
 | 
|  |     82 |   msg <> "" andalso forall_string Symbol.is_ascii_digit msg;
 | 
| 69451 |     83 | 
 | 
| 69452 |     84 | fun is_terminated msg =
 | 
|  |     85 |   let val len = size msg
 | 
|  |     86 |   in len > 0 andalso Symbol.is_ascii_line_terminator (str (String.sub (msg, len - 1))) end;
 | 
| 69451 |     87 | 
 | 
|  |     88 | fun write_line_message stream msg =
 | 
| 69452 |     89 |   if is_length msg orelse is_terminated msg then
 | 
| 69451 |     90 |     error ("Bad content for line message:\n" ^ implode (take 100 (Symbol.explode msg)))
 | 
|  |     91 |   else
 | 
|  |     92 |     let val n = size msg in
 | 
| 69454 |     93 |       write stream
 | 
|  |     94 |         ((if n > 100 orelse exists_string (fn s => s = "\n") msg
 | 
| 69848 |     95 |           then make_header [n + 1] else []) @ [msg, "\n"]);
 | 
| 69451 |     96 |       flush stream
 | 
|  |     97 |     end;
 | 
|  |     98 | 
 | 
|  |     99 | fun read_line_message stream =
 | 
|  |    100 |   (case read_line stream of
 | 
| 69452 |    101 |     NONE => NONE
 | 
|  |    102 |   | SOME line =>
 | 
| 69451 |    103 |       (case try Value.parse_nat line of
 | 
|  |    104 |         NONE => SOME line
 | 
| 69452 |    105 |       | SOME n => Option.map trim_line (#1 (read_block stream n))));
 | 
| 69448 |    106 | 
 | 
|  |    107 | end;
 |