///|
/// Deterministically encode an exact static string set as Unicode scalar words.
/// Strings remain in MPHF slot order, so decoding can verify both the original
/// text and its stable-hash routing.
///
/// Format: `[13, mphf_word_count, mphf_words..., scalar_count, scalars..., ...]`.
pub fn StaticStringSet::encode_words(self : StaticStringSet) -> Array[Int] {
  let mphf_words = self.mphf.encode_words()
  let words : Array[Int] = [13, mphf_words.length()]
  words.append(mphf_words)
  append_slot_strings(words, self.keys_by_slot)
  words
}

///|
/// Decode a static string set, rejecting malformed Unicode scalars, trailing
/// words, duplicate routing, and any key whose hash no longer agrees with its
/// recorded MPHF slot.
pub fn decode_static_string_set_words(
  words : Array[Int],
) -> Result[StaticStringSet, MphfError] {
  let (mphf, cursor) = match decode_string_codec_header(words, 13) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let (keys_by_slot, stop) = match
    decode_slot_strings(words, cursor, mphf.key_count) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if stop != words.length() {
    return Err(InvalidPayloadLength(stop, words.length()))
  }
  match validate_string_slot_keys(mphf, keys_by_slot) {
    Ok(_) => Ok({ mphf, keys_by_slot })
    Err(error) => Err(error)
  }
}

///|
/// Deterministically encode an exact string-to-integer map. Values follow the
/// scalar-encoded strings in the same MPHF slot order.
///
/// Format: `[14, mphf_word_count, mphf_words..., scalar_count, scalars..., values...]`.
pub fn StaticStringIntMap::encode_words(
  self : StaticStringIntMap,
) -> Array[Int] {
  let mphf_words = self.mphf.encode_words()
  let words : Array[Int] = [14, mphf_words.length()]
  words.append(mphf_words)
  append_slot_strings(words, self.keys_by_slot)
  words.append(self.values_by_slot)
  words
}

///|
/// Decode a static string map and validate scalar text, exact key routing, and
/// the final value-array length.
pub fn decode_static_string_int_map_words(
  words : Array[Int],
) -> Result[StaticStringIntMap, MphfError] {
  let (mphf, cursor) = match decode_string_codec_header(words, 14) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let (keys_by_slot, value_start) = match
    decode_slot_strings(words, cursor, mphf.key_count) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if mphf.key_count > words.length() - value_start {
    return Err(InvalidMetadata)
  }
  if mphf.key_count != words.length() - value_start {
    return Err(
      InvalidPayloadLength(value_start + mphf.key_count, words.length()),
    )
  }
  let values_by_slot : Array[Int] = []
  for index in value_start.. Ok({ mphf, keys_by_slot, values_by_slot })
    Err(error) => Err(error)
  }
}

///|
/// Append each source string as a scalar count followed by Unicode scalar
/// values. `String::to_array` gives scalar characters rather than UTF-8 bytes,
/// keeping the representation stable across MoonBit backends.
fn append_slot_strings(words : Array[Int], keys : Array[String]) -> Unit {
  for key in keys {
    let characters = key.to_array()
    words.push(characters.length())
    for character in characters {
      words.push(character.to_int())
    }
  }
}

///|
/// Parse the common versioned MPHF prefix after proving its declared length is
/// available, so an untrusted length cannot overflow cursor arithmetic.
fn decode_string_codec_header(
  words : Array[Int],
  version : Int,
) -> Result[(Mphf, Int), MphfError] {
  if words.length() == 0 {
    return Err(MissingHeader)
  }
  if words[0] != version {
    return Err(UnsupportedVersion(words[0]))
  }
  if words.length() < 2 {
    return Err(InvalidPayloadLength(2, words.length()))
  }
  let mphf_length = words[1]
  if mphf_length <= 0 || mphf_length > words.length() - 2 {
    return Err(InvalidMetadata)
  }
  let mphf_words : Array[Int] = []
  for index in 2..<(2 + mphf_length) {
    mphf_words.push(words[index])
  }
  let mphf = match decode_mphf_words(mphf_words) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok((mphf, 2 + mphf_length))
}

///|
/// Decode exactly one scalar-encoded string for each known MPHF slot.
fn decode_slot_strings(
  words : Array[Int],
  start : Int,
  key_count : Int,
) -> Result[(Array[String], Int), MphfError] {
  let keys : Array[String] = []
  let mut cursor = start
  for _ in 0..= words.length() {
      return Err(InvalidPayloadLength(cursor + 1, words.length()))
    }
    let scalar_count = words[cursor]
    cursor += 1
    if scalar_count < 0 || scalar_count > words.length() - cursor {
      return Err(InvalidMetadata)
    }
    let characters : Array[Char] = []
    for index in cursor..<(cursor + scalar_count) {
      let scalar = words[index]
      if !is_unicode_scalar(scalar) {
        return Err(InvalidMetadata)
      }
      characters.push(scalar.unsafe_to_char())
    }
    keys.push(String::from_iter(characters.iter()))
    cursor += scalar_count
  }
  Ok((keys, cursor))
}

///|
/// Reject negative values, values above Unicode's scalar limit, and surrogate
/// code points, which cannot occur in `String::to_array` output.
fn is_unicode_scalar(value : Int) -> Bool {
  value >= 0 && value <= 0x10ffff && !(value >= 0xd800 && value <= 0xdfff)
}

///|
/// Check that every decoded string is routed by its stable hash to its own
/// slot. This also rules out duplicate strings and stable-hash collisions.
fn validate_string_slot_keys(
  mphf : Mphf,
  keys_by_slot : Array[String],
) -> Result[Unit, MphfError] {
  if keys_by_slot.length() != mphf.key_count {
    return Err(InvalidPayloadLength(mphf.key_count, keys_by_slot.length()))
  }
  for slot in 0..