| author | wenzelm | 
| Sun, 24 Aug 2025 20:26:02 +0200 | |
| changeset 83053 | c1ccd17fb70f | 
| parent 80549 | 769a52499f12 | 
| permissions | -rw-r--r-- | 
| 77570 | 1 | /* Title: Pure/PIDE/byte_message.scala | 
| 69448 | 2 | Author: Makarius | 
| 3 | ||
| 4 | Byte-oriented messages. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 80392 | 9 | import java.io.{OutputStream, InputStream, IOException}
 | 
| 69448 | 10 | |
| 11 | ||
| 75393 | 12 | object Byte_Message {
 | 
| 69451 | 13 | /* output operations */ | 
| 14 | ||
| 69454 | 15 | def write(stream: OutputStream, bytes: List[Bytes]): Unit = | 
| 16 | bytes.foreach(_.write_stream(stream)) | |
| 69451 | 17 | |
| 18 | def flush(stream: OutputStream): Unit = | |
| 19 |     try { stream.flush() }
 | |
| 20 |     catch { case _: IOException => }
 | |
| 21 | ||
| 75393 | 22 |   def write_line(stream: OutputStream, bytes: Bytes): Unit = {
 | 
| 69467 | 23 | write(stream, List(bytes, Bytes.newline)) | 
| 24 | flush(stream) | |
| 25 | } | |
| 26 | ||
| 69451 | 27 | |
| 28 | /* input operations */ | |
| 29 | ||
| 80390 | 30 | def read(stream: InputStream, n: Long): Bytes = | 
| 69452 | 31 | Bytes.read_stream(stream, limit = n) | 
| 69449 | 32 | |
| 80390 | 33 |   def read_block(stream: InputStream, n: Long): (Option[Bytes], Long) = {
 | 
| 69452 | 34 | val msg = read(stream, n) | 
| 80357 
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
 wenzelm parents: 
80353diff
changeset | 35 | val len = msg.size | 
| 80390 | 36 | (if (len == n) Some(msg) else None, len) | 
| 69449 | 37 | } | 
| 38 | ||
| 75393 | 39 |   def read_line(stream: InputStream): Option[Bytes] = {
 | 
| 69448 | 40 | var c = 0 | 
| 80392 | 41 | val line = | 
| 42 |       Bytes.Builder.use(hint = 100) { builder =>
 | |
| 43 |         while ({ c = stream.read; c != -1 && c != 10 }) {
 | |
| 44 | builder += c.toByte | |
| 45 | } | |
| 46 | } | |
| 80549 | 47 | if (c == -1 && line.is_empty) None else Some(line.trim_line) | 
| 69448 | 48 | } | 
| 49 | ||
| 50 | ||
| 69454 | 51 | /* messages with multiple chunks (arbitrary content) */ | 
| 69451 | 52 | |
| 80357 
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
 wenzelm parents: 
80353diff
changeset | 53 | private def make_header(ns: List[Long]): List[Bytes] = | 
| 69454 | 54 |     List(Bytes(ns.mkString(",")), Bytes.newline)
 | 
| 55 | ||
| 75393 | 56 |   def write_message(stream: OutputStream, chunks: List[Bytes]): Unit = {
 | 
| 80357 
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
 wenzelm parents: 
80353diff
changeset | 57 | write(stream, make_header(chunks.map(_.size)) ::: chunks) | 
| 69454 | 58 | flush(stream) | 
| 69452 | 59 | } | 
| 60 | ||
| 80390 | 61 |   private def parse_header(line: String): List[Long] = {
 | 
| 62 |     val args = space_explode(',', line)
 | |
| 63 | if (args.forall(is_length)) args.map(Value.Long.parse) | |
| 64 |     else error("Malformed message header: " + quote(line))
 | |
| 65 | } | |
| 69451 | 66 | |
| 80390 | 67 | private def read_chunk(stream: InputStream, n: Long): Bytes = | 
| 69452 | 68 |     read_block(stream, n) match {
 | 
| 69 | case (Some(chunk), _) => chunk | |
| 70 | case (None, len) => | |
| 71 |         error("Malformed message chunk: unexpected EOF after " + len + " of " + n + " bytes")
 | |
| 72 | } | |
| 69451 | 73 | |
| 74 | def read_message(stream: InputStream): Option[List[Bytes]] = | |
| 69454 | 75 | read_line(stream).map(line => parse_header(line.text).map(read_chunk(stream, _))) | 
| 69451 | 76 | |
| 77 | ||
| 78 | /* hybrid messages: line or length+block (restricted content) */ | |
| 69448 | 79 | |
| 80390 | 80 | private def is_length(str: String): Boolean = | 
| 81 | !str.isEmpty && str.iterator.forall(Symbol.is_ascii_digit) && | |
| 82 | Value.Long.unapply(str).isDefined | |
| 83 | ||
| 69448 | 84 | private def is_length(msg: Bytes): Boolean = | 
| 80548 | 85 | !msg.is_empty && msg.byte_iterator.forall(Symbol.is_ascii_digit) && | 
| 80390 | 86 | Value.Long.unapply(msg.text).isDefined | 
| 69448 | 87 | |
| 75393 | 88 |   def write_line_message(stream: OutputStream, msg: Bytes): Unit = {
 | 
| 80493 | 89 |     if (is_length(msg) || msg.terminated_line) {
 | 
| 69448 | 90 |       error ("Bad content for line message:\n" ++ msg.text.take(100))
 | 
| 80386 | 91 | } | 
| 69448 | 92 | |
| 80357 
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
 wenzelm parents: 
80353diff
changeset | 93 | val n = msg.size | 
| 69454 | 94 | write(stream, | 
| 80362 | 95 | (if (n > 100 || msg.byte_iterator.contains(10)) make_header(List(n + 1)) else Nil) ::: | 
| 69454 | 96 | List(msg, Bytes.newline)) | 
| 69451 | 97 | flush(stream) | 
| 69448 | 98 | } | 
| 99 | ||
| 100 | def read_line_message(stream: InputStream): Option[Bytes] = | |
| 69451 | 101 |     read_line(stream) match {
 | 
| 80390 | 102 | case Some(line) if is_length(line) => | 
| 103 | val n = Value.Long.parse(line.text) | |
| 104 | read_block(stream, n)._1.map(_.trim_line) | |
| 105 | case res => res | |
| 69448 | 106 | } | 
| 107 | } |