author | wenzelm |
Tue, 11 Jun 2024 21:32:26 +0200 | |
changeset 80357 | fe123d033e76 |
parent 80353 | 52154fef991d |
child 80362 | d395b7e14102 |
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 |
||
9 |
import java.io.{ByteArrayOutputStream, OutputStream, InputStream, IOException} |
|
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 |
||
69452 | 30 |
def read(stream: InputStream, n: Int): Bytes = |
31 |
Bytes.read_stream(stream, limit = n) |
|
69449 | 32 |
|
75393 | 33 |
def read_block(stream: InputStream, n: Int): (Option[Bytes], Int) = { |
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 |
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
wenzelm
parents:
80353
diff
changeset
|
36 |
(if (len == n) Some(msg) else None, len.toInt) |
69449 | 37 |
} |
38 |
||
75393 | 39 |
def read_line(stream: InputStream): Option[Bytes] = { |
69448 | 40 |
val line = new ByteArrayOutputStream(100) |
41 |
var c = 0 |
|
42 |
while ({ c = stream.read; c != -1 && c != 10 }) line.write(c) |
|
43 |
||
44 |
if (c == -1 && line.size == 0) None |
|
45 |
else { |
|
46 |
val a = line.toByteArray |
|
47 |
val n = a.length |
|
48 |
val len = if (n > 0 && a(n - 1) == 13) n - 1 else n |
|
49 |
Some(Bytes(a, 0, len)) |
|
50 |
} |
|
51 |
} |
|
52 |
||
53 |
||
69454 | 54 |
/* messages with multiple chunks (arbitrary content) */ |
69451 | 55 |
|
80357
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
wenzelm
parents:
80353
diff
changeset
|
56 |
private def make_header(ns: List[Long]): List[Bytes] = |
69454 | 57 |
List(Bytes(ns.mkString(",")), Bytes.newline) |
58 |
||
75393 | 59 |
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
|
60 |
write(stream, make_header(chunks.map(_.size)) ::: chunks) |
69454 | 61 |
flush(stream) |
69452 | 62 |
} |
63 |
||
69451 | 64 |
private def parse_header(line: String): List[Int] = |
69452 | 65 |
try { space_explode(',', line).map(Value.Nat.parse) } |
69454 | 66 |
catch { case ERROR(_) => error("Malformed message header: " + quote(line)) } |
69451 | 67 |
|
68 |
private def read_chunk(stream: InputStream, n: Int): Bytes = |
|
69452 | 69 |
read_block(stream, n) match { |
70 |
case (Some(chunk), _) => chunk |
|
71 |
case (None, len) => |
|
72 |
error("Malformed message chunk: unexpected EOF after " + len + " of " + n + " bytes") |
|
73 |
} |
|
69451 | 74 |
|
75 |
def read_message(stream: InputStream): Option[List[Bytes]] = |
|
69454 | 76 |
read_line(stream).map(line => parse_header(line.text).map(read_chunk(stream, _))) |
69451 | 77 |
|
78 |
||
79 |
/* hybrid messages: line or length+block (restricted content) */ |
|
69448 | 80 |
|
81 |
private def is_length(msg: Bytes): Boolean = |
|
82 |
!msg.is_empty && msg.iterator.forall(b => Symbol.is_ascii_digit(b.toChar)) |
|
83 |
||
80353
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
84 |
private def is_terminated(msg: Bytes): Boolean = |
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
85 |
msg.size match { |
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
86 |
case size if size > 0 => |
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
87 |
val c = msg(size - 1) |
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
88 |
c <= Char.MaxValue && Symbol.is_ascii_line_terminator(c.toChar) |
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
89 |
case _ => false |
52154fef991d
clarified signature: discontinue somewhat misleading Bytes <: CharSequence;
wenzelm
parents:
77570
diff
changeset
|
90 |
} |
69448 | 91 |
|
75393 | 92 |
def write_line_message(stream: OutputStream, msg: Bytes): Unit = { |
69452 | 93 |
if (is_length(msg) || is_terminated(msg)) |
69448 | 94 |
error ("Bad content for line message:\n" ++ msg.text.take(100)) |
95 |
||
80357
fe123d033e76
clarified signature: pro-forma support for Bytes with size: Long;
wenzelm
parents:
80353
diff
changeset
|
96 |
val n = msg.size |
69454 | 97 |
write(stream, |
98 |
(if (n > 100 || msg.iterator.contains(10)) make_header(List(n + 1)) else Nil) ::: |
|
99 |
List(msg, Bytes.newline)) |
|
69451 | 100 |
flush(stream) |
69448 | 101 |
} |
102 |
||
103 |
def read_line_message(stream: InputStream): Option[Bytes] = |
|
69451 | 104 |
read_line(stream) match { |
69452 | 105 |
case None => None |
69451 | 106 |
case Some(line) => |
69452 | 107 |
Value.Nat.unapply(line.text) match { |
108 |
case None => Some(line) |
|
109 |
case Some(n) => read_block(stream, n)._1.map(_.trim_line) |
|
110 |
} |
|
69448 | 111 |
} |
112 |
} |