///|
/// Encode a validated set patch in sorted key order.
///
/// Format: `[10, addition_count, additions..., removal_count, removals...]`.
pub fn StaticSetPatch::encode_words(self : StaticSetPatch) -> Array[Int] {
let words : Array[Int] = [10, self.additions.length()]
words.append(self.additions)
words.push(self.removals.length())
words.append(self.removals)
words
}
///|
/// Decode a set patch using the same duplicate and overlap checks as direct
/// construction. This makes patches safe to store beside a static snapshot.
pub fn decode_static_set_patch_words(
words : Array[Int],
) -> Result[StaticSetPatch, MphfError] {
if words.length() == 0 {
return Err(MissingHeader)
}
if words[0] != 10 {
return Err(UnsupportedVersion(words[0]))
}
if words.length() < 3 || words[1] < 0 {
return Err(InvalidMetadata)
}
let addition_count = words[1]
if addition_count > words.length() - 3 {
return Err(InvalidMetadata)
}
let removal_count_position = 2 + addition_count
if removal_count_position >= words.length() {
return Err(InvalidPayloadLength(removal_count_position + 1, words.length()))
}
let removal_count = words[removal_count_position]
if removal_count < 0 {
return Err(InvalidMetadata)
}
if removal_count > words.length() - removal_count_position - 1 {
return Err(InvalidMetadata)
}
let expected = removal_count_position + 1 + removal_count
if words.length() != expected {
return Err(InvalidPayloadLength(expected, words.length()))
}
let additions = patch_word_slice(words, 2, removal_count_position)
let removals = patch_word_slice(words, removal_count_position + 1, expected)
StaticSetPatch::new(additions, removals)
}
///|
/// Encode a validated map patch. Upserts are stored in ascending key order so
/// the byte-for-byte word representation is independent of caller order.
///
/// Format: `[11, upsert_count, key, value..., removal_count, removals...]`.
pub fn StaticIntMapPatch::encode_words(self : StaticIntMapPatch) -> Array[Int] {
let words : Array[Int] = [11, self.upserts.length()]
for entry in self.upserts {
words.push(entry.key)
words.push(entry.value)
}
words.push(self.removals.length())
words.append(self.removals)
words
}
///|
/// Decode a map patch, rejecting inconsistent declared lengths before any
/// source map is rebuilt.
pub fn decode_static_int_map_patch_words(
words : Array[Int],
) -> Result[StaticIntMapPatch, MphfError] {
if words.length() == 0 {
return Err(MissingHeader)
}
if words[0] != 11 {
return Err(UnsupportedVersion(words[0]))
}
if words.length() < 3 || words[1] < 0 {
return Err(InvalidMetadata)
}
let upsert_count = words[1]
if upsert_count > (words.length() - 3) / 2 {
return Err(InvalidMetadata)
}
let removal_count_position = 2 + upsert_count * 2
if removal_count_position >= words.length() {
return Err(InvalidPayloadLength(removal_count_position + 1, words.length()))
}
let removal_count = words[removal_count_position]
if removal_count < 0 {
return Err(InvalidMetadata)
}
if removal_count > words.length() - removal_count_position - 1 {
return Err(InvalidMetadata)
}
let expected = removal_count_position + 1 + removal_count
if words.length() != expected {
return Err(InvalidPayloadLength(expected, words.length()))
}
let upserts : Array[IntEntry] = []
let mut cursor = 2
for _ in 0.. Array[Int] {
let result : Array[Int] = []
for index in start..