src/Pure/PIDE/byte_message.scala
author wenzelm
Tue, 11 Jun 2024 21:32:26 +0200
changeset 80357 fe123d033e76
parent 80353 52154fef991d
child 80362 d395b7e14102
permissions -rw-r--r--
clarified signature: pro-forma support for Bytes with size: Long;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
77570
98b4a9902582 tuned headers;
wenzelm
parents: 75393
diff changeset
     1
/*  Title:      Pure/PIDE/byte_message.scala
69448
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     3
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     4
Byte-oriented messages.
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     5
*/
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     6
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     7
package isabelle
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     8
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
     9
import java.io.{ByteArrayOutputStream, OutputStream, InputStream, IOException}
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    10
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    11
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73340
diff changeset
    12
object Byte_Message {
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    13
  /* output operations */
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    14
69454
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    15
  def write(stream: OutputStream, bytes: List[Bytes]): Unit =
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    16
    bytes.foreach(_.write_stream(stream))
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    17
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    18
  def flush(stream: OutputStream): Unit =
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    19
    try { stream.flush() }
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    20
    catch { case _: IOException => }
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    21
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73340
diff changeset
    22
  def write_line(stream: OutputStream, bytes: Bytes): Unit = {
69467
e8893c893241 tuned signature;
wenzelm
parents: 69454
diff changeset
    23
    write(stream, List(bytes, Bytes.newline))
e8893c893241 tuned signature;
wenzelm
parents: 69454
diff changeset
    24
    flush(stream)
e8893c893241 tuned signature;
wenzelm
parents: 69454
diff changeset
    25
  }
e8893c893241 tuned signature;
wenzelm
parents: 69454
diff changeset
    26
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    27
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    28
  /* input operations */
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    29
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    30
  def read(stream: InputStream, n: Int): Bytes =
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    31
    Bytes.read_stream(stream, limit = n)
69449
b516fdf8005c more uniform multi-language operations;
wenzelm
parents: 69448
diff changeset
    32
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73340
diff changeset
    33
  def read_block(stream: InputStream, n: Int): (Option[Bytes], Int) = {
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    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
b516fdf8005c more uniform multi-language operations;
wenzelm
parents: 69448
diff changeset
    37
  }
b516fdf8005c more uniform multi-language operations;
wenzelm
parents: 69448
diff changeset
    38
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73340
diff changeset
    39
  def read_line(stream: InputStream): Option[Bytes] = {
69448
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    40
    val line = new ByteArrayOutputStream(100)
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    41
    var c = 0
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    42
    while ({ c = stream.read; c != -1 && c != 10 }) line.write(c)
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    43
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    44
    if (c == -1 && line.size == 0) None
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    45
    else {
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    46
      val a = line.toByteArray
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    47
      val n = a.length
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    48
      val len = if (n > 0 && a(n - 1) == 13) n - 1 else n
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    49
      Some(Bytes(a, 0, len))
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    50
    }
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    51
  }
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    52
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    53
69454
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    54
  /* messages with multiple chunks (arbitrary content) */
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    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
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    57
    List(Bytes(ns.mkString(",")), Bytes.newline)
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    58
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73340
diff changeset
    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
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    61
    flush(stream)
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    62
  }
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    63
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    64
  private def parse_header(line: String): List[Int] =
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    65
    try { space_explode(',', line).map(Value.Nat.parse) }
69454
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    66
    catch { case ERROR(_) => error("Malformed message header: " + quote(line)) }
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    67
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    68
  private def read_chunk(stream: InputStream, n: Int): Bytes =
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    69
    read_block(stream, n) match {
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    70
      case (Some(chunk), _) => chunk
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    71
      case (None, len) =>
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    72
        error("Malformed message chunk: unexpected EOF after " + len + " of " + n + " bytes")
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    73
    }
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    74
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    75
  def read_message(stream: InputStream): Option[List[Bytes]] =
69454
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    76
    read_line(stream).map(line => parse_header(line.text).map(read_chunk(stream, _)))
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    77
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    78
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
    79
  /* hybrid messages: line or length+block (restricted content) */
69448
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    80
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    81
  private def is_length(msg: Bytes): Boolean =
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    82
    !msg.is_empty && msg.iterator.forall(b => Symbol.is_ascii_digit(b.toChar))
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    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
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    91
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73340
diff changeset
    92
  def write_line_message(stream: OutputStream, msg: Bytes): Unit = {
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
    93
    if (is_length(msg) || is_terminated(msg))
69448
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    94
      error ("Bad content for line message:\n" ++ msg.text.take(100))
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
    95
80357
fe123d033e76 clarified signature: pro-forma support for Bytes with size: Long;
wenzelm
parents: 80353
diff changeset
    96
    val n = msg.size
69454
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    97
    write(stream,
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    98
      (if (n > 100 || msg.iterator.contains(10)) make_header(List(n + 1)) else Nil) :::
ef051edd4d10 more uniform multi-language operations;
wenzelm
parents: 69452
diff changeset
    99
        List(msg, Bytes.newline))
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
   100
    flush(stream)
69448
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
   101
  }
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
   102
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
   103
  def read_line_message(stream: InputStream): Option[Bytes] =
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
   104
    read_line(stream) match {
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
   105
      case None => None
69451
387894c2fb2c more uniform multi-language operations;
wenzelm
parents: 69449
diff changeset
   106
      case Some(line) =>
69452
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
   107
        Value.Nat.unapply(line.text) match {
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
   108
          case None => Some(line)
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
   109
          case Some(n) => read_block(stream, n)._1.map(_.trim_line)
704915cf59fa more uniform multi-language operations;
wenzelm
parents: 69451
diff changeset
   110
        }
69448
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
   111
    }
51e696887b81 more uniform multi-language operations;
wenzelm
parents:
diff changeset
   112
}