///|
/// Distinguished value indicating "no more input".
pub let no_more : Int = -1
///|
/// Mutable byte buffer used throughout the port.
pub type MutableBytes = Array[Byte]
///|
/// Immutable core bytes type.
type CoreBytes = Bytes
///|
/// Input abstraction (seekable stream of bytes).
pub struct Input {
pos_in : () -> Int
seek_in : (Int) -> Unit
input_char : () -> Char?
input_byte : () -> Int
in_channel_length : Int
set_offset : (Int) -> Unit
source : String
}
///|
/// Output abstraction (seekable stream of bytes).
pub struct Output {
pos_out : () -> Int
seek_out : (Int) -> Unit
output_char : (Char) -> Unit
output_byte : (Int) -> Unit
output_string : (String) -> Unit
out_channel_length : () -> Int
flush : async () -> Unit
}
///|
/// Build bytes with the given size, filled with zero.
pub fn mkbytes(size : Int) -> MutableBytes {
Array::make(size, (0).to_byte())
}
///|
/// Size of bytes.
pub fn bytes_size(bytes : MutableBytes) -> Int {
bytes.length()
}
///|
/// Get the value at a position in bytes.
pub fn bget(bytes : MutableBytes, index : Int) -> Int {
bytes[index].to_int()
}
///|
/// Like bget, but without bounds checking.
pub fn bget_unsafe(bytes : MutableBytes, index : Int) -> Int {
bytes.unsafe_get(index).to_int()
}
///|
/// Set the value at a position in bytes.
pub fn bset(bytes : MutableBytes, index : Int, value : Int) -> Unit {
bytes[index] = value.to_byte()
}
///|
/// Like bset, but without bounds checking.
pub fn bset_unsafe(bytes : MutableBytes, index : Int, value : Int) -> Unit {
bytes.unsafe_set(index, value.to_byte())
}
///|
/// Make bytes from a string by taking the low 8 bits of each character.
pub fn bytes_of_string(s : String) -> MutableBytes {
let out = Array::new(capacity=s.length())
for ch in s {
out.push(ch.to_int().to_byte())
}
out
}
///|
/// Make bytes from core bytes.
pub fn bytes_of_caml_bytes(bytes : CoreBytes) -> MutableBytes {
bytes.to_array()
}
///|
/// Make bytes from an array of integers (each 0..255).
pub fn bytes_of_list(values : Array[Int]) -> MutableBytes {
let out = Array::new(capacity=values.length())
for v in values {
out.push(v.to_byte())
}
out
}
///|
/// Make bytes from a character array.
pub fn bytes_of_charlist(values : Array[Char]) -> MutableBytes {
let out = Array::new(capacity=values.length())
for v in values {
out.push(v.to_int().to_byte())
}
out
}
///|
/// Make bytes from a list of integer arrays.
pub fn bytes_of_arraylist(values : Array[Array[Int]]) -> MutableBytes {
let mut total = 0
for v in values {
total = total + v.length()
}
let out = Array::new(capacity=total)
for v in values {
for x in v {
out.push(x.to_byte())
}
}
out
}
///|
/// Make bytes from an integer array.
pub fn bytes_of_int_array(values : Array[Int]) -> MutableBytes {
bytes_of_list(values)
}
///|
/// Integer array from bytes.
pub fn int_array_of_bytes(bytes : MutableBytes) -> Array[Int] {
bytes.map(b => b.to_int())
}
///|
/// Integer array from a string (byte-wise).
pub fn int_array_of_string(s : String) -> Array[Int] {
bytes_of_string(s).map(b => b.to_int())
}
///|
/// String from a list of integer arrays.
pub fn string_of_int_arrays(values : Array[Array[Int]]) -> String {
string_of_int_array(values.flatten())
}
///|
/// String from a single int array.
pub fn string_of_int_array(values : Array[Int]) -> String {
string_of_bytes(bytes_of_list(values))
}
///|
/// Make a string by mapping each byte to a single character.
pub fn string_of_bytes(bytes : MutableBytes) -> String {
let chars = Array::new(capacity=bytes.length())
for b in bytes {
chars.push(b.to_char())
}
String::from_array(chars)
}
///|
/// Make a character array from bytes.
pub fn charlist_of_bytes(bytes : MutableBytes) -> Array[Char] {
bytes.map(b => b.to_char())
}
///|
/// Copy bytes.
pub fn copybytes(bytes : MutableBytes) -> MutableBytes {
bytes.copy()
}
///|
/// Build an input from bytes.
pub fn Input::of_bytes(
bytes : MutableBytes,
source? : String = "bytes",
) -> Input {
let mut pos = 0
let mut offset = 0
let len = bytes.length()
let input_int = () => {
let result = if pos > len - 1 { no_more } else { bget_unsafe(bytes, pos) }
pos = pos + 1
result
}
{
pos_in: () => pos - offset,
seek_in: p => pos = p + offset,
input_char: () => {
let v = input_int()
if v == no_more {
None
} else {
Some(v.to_byte().to_char())
}
},
input_byte: input_int,
in_channel_length: len,
set_offset: o => if offset == 0 { offset = o },
source,
}
}
///|
/// Build an input from a string.
pub fn Input::of_string(s : String, source? : String = "string") -> Input {
Input::of_bytes(bytes_of_string(s), source~)
}
///|
/// Build an output backed by an external `write_at` function.
///
/// The output uses an internal growable buffer. `flush` writes the full buffer
/// from the beginning (position 0).
pub fn Output::of_write_at(
write_at : async (BytesView, Int64) -> Unit,
) -> Output {
let mut pos = 0
let mut highest_written = -1
let single : MutableBytes = mkbytes(1)
let buffer : Ref[MutableBytes] = { val: mkbytes(4096) }
let output_bytes = (bytes : Bytes) => {
for b in bytes {
if pos > buffer.val.length() - 1 {
let new_len = if pos * 2 > 0 { pos * 2 } else { 1 }
let new_bytes = mkbytes(new_len)
buffer.val[:].blit_to(new_bytes)
buffer.val = new_bytes
}
buffer.val.unsafe_set(pos, b)
pos = pos + 1
}
let last = pos - 1
highest_written = if highest_written > last {
highest_written
} else {
last
}
}
async fn flush_output() -> Unit {
let out = mkbytes(pos)
buffer.val[:pos].blit_to(out)
let bytes = Bytes::from_array(out)
write_at(bytes[:], 0)
}
{
pos_out: () => pos,
seek_out: p => pos = p,
output_char: c => {
single[0] = c.to_int().to_byte()
output_bytes(Bytes::from_array(single))
},
output_byte: b => {
single[0] = b.to_byte()
output_bytes(Bytes::from_array(single))
},
output_string: s => output_bytes(Bytes::from_array(bytes_of_string(s))),
out_channel_length: () => highest_written + 1,
flush: flush_output,
}
}
///|
/// Build an input-output, with an initial buffer size.
pub fn Output::of_bytes(size : Int) -> (Output, Ref[MutableBytes]) {
let data : Ref[MutableBytes] = { val: mkbytes(size) }
(output_of_bytes(data), data)
}
///|
/// Extract the contents of an input-output in bytes.
pub fn Output::extract_bytes(
self : Output,
data : Ref[MutableBytes],
) -> MutableBytes {
let len = (self.pos_out)()
let out = mkbytes(len)
for i in 0.. Unit {
ignore((self.input_byte)())
}
///|
/// Move backward one byte.
pub fn Input::rewind(self : Input) -> Unit {
let pos = (self.pos_in)()
if pos <= 0 {
(self.seek_in)(0)
} else {
(self.seek_in)(pos - 1)
}
}
///|
/// Look at the next character without advancing the pointer.
pub fn Input::peek_char(self : Input) -> Char? {
let r = (self.input_char)()
self.rewind()
r
}
///|
/// Look at the next byte without advancing the pointer.
pub fn Input::peek_byte(self : Input) -> Int {
let r = (self.input_byte)()
self.rewind()
r
}
///|
/// Read the previous character, moving the pointer back one.
pub fn Input::read_char_back(self : Input) -> Char? {
let pos = (self.pos_in)()
if pos <= 0 {
return None
}
(self.seek_in)(pos - 1)
let chr = (self.input_char)()
(self.seek_in)(pos - 1)
chr
}
///|
/// Read a line from an input (newline not included).
pub fn Input::read_line(self : Input) -> String raise {
let sb = StringBuilder::new()
fn read_more(
sb : StringBuilder,
saw_any : Bool,
input : Input,
) -> String raise {
match (input.input_char)() {
None => if saw_any { sb.to_string() } else { fail("End_of_file") }
Some('\n') => sb.to_string()
Some(ch) => {
sb.write_char(ch)
read_more(sb, true, input)
}
}
}
read_more(sb, false, self)
}
///|
/// Read all lines from an input.
pub fn Input::read_lines(self : Input) -> Array[String] raise {
let lines = Array::new()
fn read_all(lines : Array[String], input : Input) -> Array[String] raise {
match input.peek_char() {
None => lines
Some(_) => {
lines.push(input.read_line())
read_all(lines, input)
}
}
}
read_all(lines, self)
}
///|
/// Set bytes o..o+l-1 from input.
pub fn Input::setinit(
self : Input,
bytes : MutableBytes,
offset : Int,
length : Int,
) -> Unit raise {
if length == 0 {
return
}
let max = bytes.length() - 1
let last = offset + length - 1
if offset > max || offset < 0 || last < 0 || last > max {
fail("setinit")
}
for i = offset; i < offset + length; i = i + 1 {
bytes[i] = (self.input_byte)().to_byte()
}
}
///|
/// Bytes with input o..o+l-1.
pub fn Input::bytes_of_input(
self : Input,
offset : Int,
length : Int,
) -> MutableBytes raise {
(self.seek_in)(offset)
let out = mkbytes(length)
self.setinit(out, 0, length)
out
}
///|
/// String of input contents.
pub fn Input::to_string(self : Input) -> String raise {
string_of_bytes(self.bytes_of_input(0, self.in_channel_length))
}
///|
/// Write bytes o..o+l-1 to output.
pub fn Output::getinit(
self : Output,
bytes : MutableBytes,
offset : Int,
length : Int,
) -> Unit raise {
if length == 0 {
return
}
let max = bytes.length() - 1
let last = offset + length - 1
if offset > max || offset < 0 || last < 0 || last > max {
fail("getinit")
}
for i = offset; i < offset + length; i = i + 1 {
(self.output_byte)(bget_unsafe(bytes, i))
}
}
///|
/// Most-significant-bit-first bitstream over an Input.
pub struct Bitstream {
input : Input
mut curr_byte : Int
mut bit : Int
mut bits_read : Int
}
///|
/// Position token for Bitstream; details will evolve during implementation.
pub struct BitstreamPosition {
pos : Int
curr_byte : Int
bit : Int
bits_read : Int
} derive(Debug)
///|
/// Most-significant-bit-first write bitstream (placeholder).
pub struct BitstreamWrite {
bytes : MutableBytes
mut bit : Int
} derive(Debug)
///|
/// Build a bitstream from an input.
pub fn Bitstream::of_input(input : Input) -> Bitstream {
{ input, curr_byte: 0, bit: 0, bits_read: 0 }
}
///|
/// Get the current position of a bitstream.
pub fn Bitstream::position(self : Bitstream) -> BitstreamPosition {
{
pos: (self.input.pos_in)(),
curr_byte: self.curr_byte,
bit: self.bit,
bits_read: self.bits_read,
}
}
///|
/// Seek to a previous bitstream position.
pub fn Bitstream::seek(self : Bitstream, pos : BitstreamPosition) -> Unit {
(self.input.seek_in)(pos.pos)
self.curr_byte = pos.curr_byte
self.bit = pos.bit
self.bits_read = pos.bits_read
}
///|
/// Read the next bit from a bitstream.
pub fn Bitstream::getbit(self : Bitstream) -> Bool raise {
if self.bit == 0 {
let value = (self.input.input_byte)()
if value == no_more {
fail("End_of_file")
}
self.curr_byte = value
self.bit = 128
}
let result = (self.curr_byte & self.bit) > 0
self.bits_read = self.bits_read + 1
self.bit = self.bit / 2
result
}
///|
/// Read the next bit as an integer, 0 or 1.
pub fn Bitstream::getbitint(self : Bitstream) -> Int raise {
if self.getbit() {
1
} else {
0
}
}
///|
/// Align a bitstream to the next byte boundary.
pub fn Bitstream::align(self : Bitstream) -> Unit {
if self.bit > 0 {
self.bits_read = (self.bits_read / 8 + 1) * 8
self.bit = 0
}
}
///|
/// Make a new write bitstream.
pub fn BitstreamWrite::new() -> BitstreamWrite {
{ bytes: Array::new(), bit: 0 }
}
///|
/// Put a single bit into a write bitstream.
pub fn BitstreamWrite::putbit(self : BitstreamWrite, bit : Int) -> Unit {
if self.bit == 0 {
self.bytes.push((0).to_byte())
}
let idx = self.bytes.length() - 1
if bit == 1 {
let curr = self.bytes[idx].to_int()
let mask = 1 << (7 - self.bit)
self.bytes[idx] = (curr | mask).to_byte()
}
self.bit = self.bit + 1
if self.bit == 8 {
self.bit = 0
}
}
///|
/// Put a multi-bit value into a write bitstream.
pub fn BitstreamWrite::putval(
self : BitstreamWrite,
bits : Int,
value : Int,
) -> Unit raise {
if bits < 0 || bits > 32 {
fail("putval")
}
if bits == 0 {
return
}
for i in (bits - 1)>=..0 {
self.putbit((value >> i) & 1)
}
}
///|
/// Align a write bitstream to the next byte boundary.
pub fn BitstreamWrite::align(self : BitstreamWrite) -> Unit {
if self.bit > 0 {
while self.bit != 0 {
self.putbit(0)
}
}
}
///|
/// Extract bytes from a write bitstream, padding with zeros.
pub fn BitstreamWrite::bytes(self : BitstreamWrite) -> MutableBytes {
self.align()
self.bytes.copy()
}
///|
/// Internal output builder over bytes.
fn output_of_bytes(data : Ref[MutableBytes]) -> Output {
let mut pos = 0
let mut highest_written = -1
let output_int = (value : Int) => if pos > data.val.length() - 1 {
let new_len = if pos * 2 > 0 { pos * 2 } else { 1 }
let new_bytes = mkbytes(new_len)
data.val[:].blit_to(new_bytes)
bset_unsafe(new_bytes, pos, value)
highest_written = if highest_written > pos { highest_written } else { pos }
pos = pos + 1
data.val = new_bytes
} else {
highest_written = if highest_written > pos { highest_written } else { pos }
bset_unsafe(data.val, pos, value)
pos = pos + 1
}
{
pos_out: () => pos,
seek_out: p => pos = p,
output_char: c => output_int(c.to_int()),
output_string: s => {
let bytes = bytes_of_string(s)
for b in bytes {
output_int(b.to_int())
}
},
output_byte: output_int,
out_channel_length: () => highest_written + 1,
flush: noop_flush,
}
}
///|
/// No-op flush for in-memory outputs.
#warnings("-unused_async")
async fn noop_flush() -> Unit {
()
}
///|
fn debug_next_char(input : Input) -> Unit {
match (input.input_char)() {
Some(chr) => @pdfe.log("\{chr} = \{chr.to_int()}\n")
None => ()
}
}
///|
/// Debug the next `count` characters to the log and rewind.
pub fn debug_next_n_chars(count : Int, input : Input) -> Unit {
let mut i = 0
while i < count {
debug_next_char(input)
i = i + 1
}
@pdfe.log("\n")
let mut rewind_count = 0
while rewind_count < count {
input.rewind()
rewind_count = rewind_count + 1
}
}