///|
/// A bounded little-endian reader for DICOM byte streams.
pub struct ByteReader {
bytes : Bytes
mut position : Int
}
///|
/// A parse failure with the byte offset where it was detected.
pub(all) suberror ParseError {
InvalidMagic(offset~ : Int)
Truncated(offset~ : Int, needed~ : Int, remaining~ : Int)
InvalidVr(offset~ : Int, code~ : String)
InvalidLength(offset~ : Int, length~ : Int)
Unsupported(offset~ : Int, message~ : String)
}
///|
/// Create a reader positioned at the first byte.
pub fn byte_reader(bytes : Bytes) -> ByteReader {
{ bytes, position: 0 }
}
///|
/// Return the current byte offset.
pub fn ByteReader::position(self : ByteReader) -> Int {
self.position
}
///|
/// Return the number of unread bytes.
pub fn ByteReader::remaining(self : ByteReader) -> Int {
self.bytes.length() - self.position
}
///|
fn ByteReader::require(
self : ByteReader,
length : Int,
) -> Unit raise ParseError {
if length < 0 {
raise ParseError::InvalidLength(offset=self.position, length~)
}
let remaining = self.remaining()
if remaining < length {
raise ParseError::Truncated(offset=self.position, needed=length, remaining~)
}
}
///|
fn ByteReader::peek_u16(self : ByteReader) -> Int raise ParseError {
self.require(2)
let low = self.bytes[self.position].to_int()
let high = self.bytes[self.position + 1].to_int()
low + high * 256
}
///|
fn ByteReader::read_u16(self : ByteReader) -> Int raise ParseError {
self.require(2)
let low = self.bytes[self.position].to_int()
let high = self.bytes[self.position + 1].to_int()
self.position += 2
low + high * 256
}
///|
fn ByteReader::read_u32(self : ByteReader) -> Int raise ParseError {
self.require(4)
let b0 = self.bytes[self.position].to_int()
let b1 = self.bytes[self.position + 1].to_int()
let b2 = self.bytes[self.position + 2].to_int()
let b3 = self.bytes[self.position + 3].to_int()
self.position += 4
b0 + b1 * 256 + b2 * 65536 + b3 * 16777216
}
///|
fn ByteReader::read_bytes(
self : ByteReader,
length : Int,
) -> Bytes raise ParseError {
self.require(length)
let start = self.position
self.position += length
self.bytes[start:self.position].to_owned()
}
///|
fn ByteReader::skip(self : ByteReader, length : Int) -> Unit raise ParseError {
self.require(length)
self.position += length
}
///|
fn bytes_to_ascii(bytes : Bytes) -> String {
let builder = StringBuilder()
for byte in bytes {
builder.write_char(byte.to_char())
}
builder.to_string()
}
///|
fn ByteReader::read_ascii(
self : ByteReader,
length : Int,
) -> String raise ParseError {
bytes_to_ascii(self.read_bytes(length))
}