///|
fn u8(data : Bytes, at : Int32) -> Int32 raise FgbError {
  require_range(data, at, 1)
  data[at].to_int()
}

///|
fn u16(data : Bytes, at : Int32) -> Int32 raise FgbError {
  require_range(data, at, 2)
  data[at].to_int() | (data[at + 1].to_int() << 8)
}

///|
fn i16(data : Bytes, at : Int32) -> Int32 raise FgbError {
  let value = u16(data, at)
  if value >= 32768 {
    value - 65536
  } else {
    value
  }
}

///|
fn u32(data : Bytes, at : Int32) -> UInt32 raise FgbError {
  require_range(data, at, 4)
  let mut value = 0U
  for i in 0..<4 {
    value = value | (data[at + i].to_uint() << (i * 8))
  }
  value
}

///|
fn i32(data : Bytes, at : Int32) -> Int32 raise FgbError {
  u32(data, at).reinterpret_as_int()
}

///|
fn size32(data : Bytes, at : Int32) -> Int32 raise FgbError {
  let value = u32(data, at)
  if value > 2147483647U {
    raise InvalidData(at, "32-bit size exceeds addressable range")
  }
  value.reinterpret_as_int()
}

///|
fn u64(data : Bytes, at : Int32) -> UInt64 raise FgbError {
  require_range(data, at, 8)
  let mut value = 0UL
  for i in 0..<8 {
    value = value | (data[at + i].to_int().to_uint64() << (i * 8))
  }
  value
}

///|
fn size64(data : Bytes, at : Int32) -> Int32 raise FgbError {
  let value = u64(data, at)
  if value > 2147483647UL {
    raise InvalidData(at, "64-bit size exceeds supported 2 GiB address space")
  }
  value.to_int()
}

///|
fn f64(data : Bytes, at : Int32) -> Double raise FgbError {
  let result = u64(data, at).reinterpret_as_double()
  if !finite(result) {
    raise InvalidData(at, "non-finite number")
  }
  result
}

///|
fn f32(data : Bytes, at : Int32) -> Double raise FgbError {
  let result = Float::reinterpret_from_uint(u32(data, at)).to_double()
  if !finite(result) {
    raise InvalidData(at, "non-finite number")
  }
  result
}

///|
fn slice_bytes(data : Bytes, at : Int32, size : Int32) -> Bytes raise FgbError {
  require_range(data, at, size)
  Bytes::makei(size, i => data[at + i])
}

///|
fn decode_text(
  data : Bytes,
  at : Int32,
  size : Int32,
  limits : Limits,
) -> String raise FgbError {
  enforce(size, limits.max_string_bytes, "string bytes")
  let value = slice_bytes(data, at, size)
  @utf8.decode(value) catch {
    _ => raise InvalidData(at, "invalid UTF-8")
  }
}

///|
struct ByteWriter {
  data : Array[Byte]
}

///|
fn ByteWriter::new() -> ByteWriter {
  { data: [], }
}

///|
fn ByteWriter::position(self : ByteWriter) -> Int32 {
  self.data.length()
}

///|
fn ByteWriter::zeros(self : ByteWriter, count : Int32) -> Unit {
  for _ in 0.. Unit {
  while (self.position() + bias) % alignment != 0 {
    self.data.push(0)
  }
}

///|
fn ByteWriter::byte(self : ByteWriter, value : Int32) -> Unit {
  self.data.push((value & 255).to_byte())
}

///|
fn ByteWriter::short(self : ByteWriter, value : Int32) -> Unit {
  self.byte(value)
  self.byte(value >> 8)
}

///|
fn ByteWriter::word(self : ByteWriter, value : UInt32) -> Unit {
  for i in 0..<4 {
    self.byte((value >> (i * 8)).reinterpret_as_int())
  }
}

///|
fn ByteWriter::long(self : ByteWriter, value : UInt64) -> Unit {
  for i in 0..<8 {
    self.byte((value >> (i * 8)).to_int())
  }
}

///|
fn ByteWriter::double(self : ByteWriter, value : Double) -> Unit {
  self.long(value.reinterpret_as_uint64())
}

///|
fn ByteWriter::float(self : ByteWriter, value : Double) -> Unit {
  self.word(Float::from_double(value).reinterpret_as_uint())
}

///|
fn ByteWriter::bytes(self : ByteWriter, bytes : Bytes) -> Unit {
  for byte in bytes {
    self.data.push(byte)
  }
}

///|
fn ByteWriter::patch_word(self : ByteWriter, at : Int32, value : Int32) -> Unit {
  for i in 0..<4 {
    self.data[at + i] = ((value >> (i * 8)) & 255).to_byte()
  }
}

///|
fn ByteWriter::patch_short(
  self : ByteWriter,
  at : Int32,
  value : Int32,
) -> Unit {
  self.data[at] = (value & 255).to_byte()
  self.data[at + 1] = ((value >> 8) & 255).to_byte()
}

///|
fn ByteWriter::patch_long(
  self : ByteWriter,
  at : Int32,
  value : UInt64,
) -> Unit {
  for i in 0..<8 {
    self.data[at + i] = ((value >> (i * 8)).to_int() & 255).to_byte()
  }
}

///|
fn ByteWriter::finish(self : ByteWriter) -> Bytes {
  Bytes::makei(self.data.length(), i => self.data[i])
}