author | paulson <lp15@cam.ac.uk> |
Fri, 08 Aug 2025 16:46:03 +0100 | |
changeset 82969 | dedd9d13c79c |
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:
80353
diff
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:
80353
diff
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:
80353
diff
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:
80353
diff
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 |
} |