///|
let byte_magic = 0x4d465331

///|
let byte_encoding_version = 1

///|
let byte_header_length = 32

///|
pub fn BinaryFuseFilter::encoded_byte_length(self : BinaryFuseFilter) -> Int {
  let width = if self.fingerprint_bits == 8 { 1 } else { 2 }
  byte_header_length + self.fingerprints.length() * width
}

///|
pub fn BinaryFuseFilter::encode_bytes(self : BinaryFuseFilter) -> Bytes {
  let width = if self.fingerprint_bits == 8 { 1 } else { 2 }
  let bytes = Array::make(self.encoded_byte_length(), (0).to_byte())
  write_u32(bytes, 0, byte_magic)
  bytes[4] = byte_encoding_version.to_byte()
  bytes[5] = self.fingerprint_bits.to_byte()
  bytes[6] = (0).to_byte()
  bytes[7] = (0).to_byte()
  write_u32(bytes, 8, self.key_count)
  write_u32(bytes, 12, self.segment_length)
  write_u32(bytes, 16, self.segment_count)
  write_u32(bytes, 20, self.seed)
  write_u32(bytes, 24, self.attempts)
  write_u32(bytes, 28, self.fingerprints.length())
  let mut cursor = byte_header_length
  for value in self.fingerprints {
    bytes[cursor] = (value & 0xff).to_byte()
    cursor = cursor + 1
    if width == 2 {
      bytes[cursor] = (value / 256).to_byte()
      cursor = cursor + 1
    }
  }
  Bytes::from_array(bytes)
}

///|
pub fn decode_bytes(bytes : Bytes) -> Result[BinaryFuseFilter, FuseError] {
  if bytes.length() < byte_header_length ||
    read_u32(bytes, 0) != byte_magic ||
    bytes[4].to_int() != byte_encoding_version ||
    (bytes[5].to_int() != 8 && bytes[5].to_int() != 16) ||
    bytes[6].to_int() != 0 ||
    bytes[7].to_int() != 0 {
    return Err(InvalidEncoding)
  }
  let key_count = read_u32(bytes, 8)
  let segment_length = read_u32(bytes, 12)
  let segment_count = read_u32(bytes, 16)
  let seed = read_u32(bytes, 20)
  let attempts = read_u32(bytes, 24)
  let fingerprint_count = read_u32(bytes, 28)
  let bits = bytes[5].to_int()
  let width = if bits == 8 { 1 } else { 2 }
  if key_count <= 0 ||
    segment_length <= 0 ||
    segment_count <= 0 ||
    seed < 0 ||
    attempts <= 0 ||
    fingerprint_count < 0 ||
    bytes.length() != byte_header_length + fingerprint_count * width {
    return Err(InvalidEncoding)
  }
  let words = Array::make(8 + fingerprint_count, 0)
  words[0] = encoding_version
  words[1] = key_count
  words[2] = segment_length
  words[3] = segment_count
  words[4] = seed
  words[5] = attempts
  words[6] = bits
  words[7] = fingerprint_count
  let mut cursor = byte_header_length
  for index in 0.. Ok(filter)
    Err(_) => Err(InvalidEncoding)
  }
}

///|
fn write_u32(bytes : Array[Byte], offset : Int, value : Int) -> Unit {
  bytes[offset] = (value & 0xff).to_byte()
  bytes[offset + 1] = ((value / 256) & 0xff).to_byte()
  bytes[offset + 2] = ((value / 65_536) & 0xff).to_byte()
  bytes[offset + 3] = ((value / 16_777_216) & 0xff).to_byte()
}

///|
fn read_u32(bytes : Bytes, offset : Int) -> Int {
  bytes[offset].to_int() +
  bytes[offset + 1].to_int() * 256 +
  bytes[offset + 2].to_int() * 65_536 +
  bytes[offset + 3].to_int() * 16_777_216
}