///|
/// Wire-level scalar decoding helpers for PostgreSQL protocol values.
///
/// Primary references:
/// - https://www.postgresql.org/docs/current/protocol-overview.html#PROTOCOL-OVERVIEW
/// - https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS
/// Convert a count to the signed 16-bit representation used by many protocol
/// fields, such as parameter counts and format-code counts.
pub fn i16_from_usize(len : Int) -> Int16 raise ProtocolError {
if len < 0 || len > 0x7fff {
raise Overflow("value too large to transmit")
}
Int16::from_int(len)
}
///|
/// Convert a length to the signed 32-bit representation used by PostgreSQL
/// length fields.
///
/// Whether the resulting value includes the length word itself depends on the
/// surrounding message format.
pub fn i32_from_usize(len : Int) -> Int raise ProtocolError {
if len < 0 || len > 0x7fff_ffff {
raise Overflow("value too large to transmit")
}
len
}
///|
/// Ensure a value fits in the signed 32-bit range accepted by wire `Int32`
/// fields.
pub fn checked_i32(value : Int) -> Int raise ProtocolError {
if value < -0x8000_0000 || value > 0x7fff_ffff {
raise Overflow("value out of range for i32")
}
value
}
///|
/// Ensure a value fits in the signed 16-bit range accepted by wire `Int16`
/// fields.
pub fn checked_i16(value : Int) -> Int16 raise ProtocolError {
if value < -0x8000 || value > 0x7fff {
raise Overflow("value out of range for i16")
}
Int16::from_int(value)
}
///|
/// Convert signed i32 (stored in Int) to u32 bits in UInt.
pub fn u32_from_i32(value : Int) -> UInt raise ProtocolError {
let _ = checked_i32(value)
value.reinterpret_as_uint()
}
///|
/// Convert u32 bits to signed i32 (stored in Int).
pub fn i32_from_u32(value : UInt) -> Int {
value.reinterpret_as_int()
}
///|
/// Cursor-based reader for the scalar types from protocol Section 54.6.
///
/// All integer reads use network byte order, and `read_cstr` implements the
/// protocol `String` type (a NUL-terminated byte sequence).
pub struct ByteReader {
buf : BytesView
mut idx : Int
}
///|
/// Create a byte reader over `buf`.
pub fn ByteReader::new(buf : BytesView) -> ByteReader {
{ buf, idx: 0, }
}
///|
/// Return the number of unread bytes.
pub fn ByteReader::remaining(self : ByteReader) -> Int {
self.buf.length() - self.idx
}
///|
/// Return whether all bytes have been consumed.
pub fn ByteReader::is_empty(self : ByteReader) -> Bool {
self.remaining() <= 0
}
///|
/// Ensure that at least `n` unread bytes remain before reading a fixed-width
/// field.
fn ByteReader::ensure(self : ByteReader, n : Int) -> Unit raise ProtocolError {
if self.remaining() < n {
raise UnexpectedEof("unexpected EOF")
}
}
///|
/// Read the next byte.
pub fn ByteReader::read_u8(self : ByteReader) -> Byte raise ProtocolError {
self.ensure(1)
let b = self.buf[self.idx]
self.idx = self.idx + 1
b
}
///|
/// Read the next big-endian unsigned 16-bit integer.
pub fn ByteReader::read_u16_be(self : ByteReader) -> UInt16 raise ProtocolError {
self.ensure(2)
let b0 = self.buf[self.idx].to_uint16()
let b1 = self.buf[self.idx + 1].to_uint16()
self.idx = self.idx + 2
(b0 << 8) | b1
}
///|
/// Read the next big-endian signed 16-bit integer.
pub fn ByteReader::read_i16_be(self : ByteReader) -> Int raise ProtocolError {
let u = self.read_u16_be()
let v = Int16::reinterpret_from_uint16(u)
v.to_int()
}
///|
/// Read the next big-endian unsigned 32-bit integer.
pub fn ByteReader::read_u32_be(self : ByteReader) -> UInt raise ProtocolError {
self.ensure(4)
let b0 = self.buf[self.idx].to_uint()
let b1 = self.buf[self.idx + 1].to_uint()
let b2 = self.buf[self.idx + 2].to_uint()
let b3 = self.buf[self.idx + 3].to_uint()
self.idx = self.idx + 4
(b0 << 24) | (b1 << 16) | (b2 << 8) | b3
}
///|
/// Read the next big-endian signed 32-bit integer.
pub fn ByteReader::read_i32_be(self : ByteReader) -> Int raise ProtocolError {
let u = self.read_u32_be()
i32_from_u32(u)
}
///|
/// Read the next big-endian unsigned 64-bit integer.
pub fn ByteReader::read_u64_be(self : ByteReader) -> UInt64 raise ProtocolError {
self.ensure(8)
let b0 = self.buf[self.idx].to_uint64()
let b1 = self.buf[self.idx + 1].to_uint64()
let b2 = self.buf[self.idx + 2].to_uint64()
let b3 = self.buf[self.idx + 3].to_uint64()
let b4 = self.buf[self.idx + 4].to_uint64()
let b5 = self.buf[self.idx + 5].to_uint64()
let b6 = self.buf[self.idx + 6].to_uint64()
let b7 = self.buf[self.idx + 7].to_uint64()
self.idx = self.idx + 8
(b0 << 56) |
(b1 << 48) |
(b2 << 40) |
(b3 << 32) |
(b4 << 24) |
(b5 << 16) |
(b6 << 8) |
b7
}
///|
/// Read the next big-endian signed 64-bit integer.
pub fn ByteReader::read_i64_be(self : ByteReader) -> Int64 raise ProtocolError {
let u = self.read_u64_be()
u.reinterpret_as_int64()
}
///|
/// Read the next big-endian IEEE-754 `Float`.
pub fn ByteReader::read_f32_be(self : ByteReader) -> Float raise ProtocolError {
let u = self.read_u32_be()
Float::reinterpret_from_uint(u)
}
///|
/// Read the next big-endian IEEE-754 `Double`.
pub fn ByteReader::read_f64_be(self : ByteReader) -> Double raise ProtocolError {
let u = self.read_u64_be()
u.reinterpret_as_double()
}
///|
/// Read the next `len` bytes from a preceding `Byte n` or length-prefixed
/// field.
pub fn ByteReader::read_bytes(
self : ByteReader,
len : Int,
) -> BytesView raise ProtocolError {
if len < 0 {
raise InvalidInput("invalid length")
}
self.ensure(len)
let start = self.idx
let end = self.idx + len
self.idx = end
self.buf[start:end]
}
///|
/// Consume and return all remaining bytes.
///
/// Many message formats end with `Byte n`, where `n` is implied by the overall
/// message length. This helper returns that trailing payload.
pub fn ByteReader::read_all(self : ByteReader) -> BytesView {
let start = self.idx
self.idx = self.buf.length()
self.buf[start:]
}
///|
/// Find the next NUL terminator used by a protocol `String`.
fn find_null(buf : BytesView, start : Int) -> Int raise ProtocolError {
let mut i = start
while i < buf.length() {
if buf[i] == 0 {
return i
}
i = i + 1
}
raise UnexpectedEof("unexpected EOF")
}
///|
/// Read a protocol `String`.
///
/// PostgreSQL protocol strings are C-style NUL-terminated byte sequences with
/// no separate length prefix.
pub fn ByteReader::read_cstr(self : ByteReader) -> Bytes raise ProtocolError {
let end = find_null(self.buf, self.idx)
let slice = self.buf[self.idx:end]
self.idx = end + 1
Bytes::from_iter(slice.iter())
}