///|
/// String codec: `var_i32(UTF-8 byte length)` followed by the UTF-8 bytes.
pub impl BinarySerializer for String with fn serialize(self, ctx) {
ctx.write_string_payload(self)
}
///|
pub impl BinaryDeserializer for String with fn deserialize(ctx) {
ctx.read_string_payload()
}
///|
/// `Char` codec.
///
/// Default mode encodes the Unicode scalar value as a raw (non-zigzag)
/// varint, matching desert-rust. With `chars_as_u16` set on the context the
/// encoding is one big-endian 16-bit UTF-16 unit, byte-compatible with the
/// Scala library; astral characters fail with `UnsupportedCharacter` in that
/// mode (they are 4 bytes in the default mode).
pub impl BinarySerializer for Char with fn serialize(self, ctx) {
if ctx.options.chars_as_u16 {
let cp = self.to_int()
if cp > 0xFFFF {
raise UnsupportedCharacter(self)
}
ctx.write_u16((cp & 0xFFFF).to_uint16())
} else {
ctx.write_var_u32(self.to_int().reinterpret_as_uint())
}
}
///|
pub impl BinaryDeserializer for Char with fn deserialize(ctx) {
if ctx.options.chars_as_u16 {
let unit = ctx.input.read_u16()
match unit.to_char() {
Some(ch) => ch
None => raise FailedToDecodeCharacter(unit)
}
} else {
let cp = ctx.input.read_var_u32().reinterpret_as_int()
// Validate like desert-rust: reject out-of-range and surrogate values
// instead of materializing an invalid Char.
if cp < 0 || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF) {
raise DeserializationFailure(
"Failed to decode character: invalid code point \{cp}",
)
}
cp.unsafe_to_char()
}
}
///|
/// Opt-in string deduplication wrapper (mirrors `DeduplicatedString` in both
/// existing implementations).
///
/// The first occurrence is written as a normal string and registered in the
/// session's dedup table; every later occurrence is written as the
/// zigzag-encoded negative id (`var_i32(-id)`). Ids start from 1. On the
/// reading side every inline string is registered, so a negative value not
/// present in the table fails with `InvalidStringId`.
///
/// Do not mix deduplicated and plain string fields inside evolvable records:
/// an older reader skipping a newly added deduplicated field would miss an id
/// assignment and every later back-reference would be corrupted. Plain
/// `String` fields are always evolution-safe.
#derive(Debug, Eq)
pub struct DeduplicatedString {
priv string : String
}
///|
pub fn DeduplicatedString::new(string : String) -> DeduplicatedString {
DeduplicatedString::{ string, }
}
///|
/// The wrapped string.
pub fn DeduplicatedString::get(self : DeduplicatedString) -> String {
self.string
}
///|
pub impl BinarySerializer for DeduplicatedString with fn serialize(self, ctx) {
match ctx.state.store_string(self.string) {
StringAlreadyStored(id) => ctx.write_var_i32(-id)
StringIsNew(_id) => ctx.write_string_payload(self.string)
}
}
///|
pub impl BinaryDeserializer for DeduplicatedString with fn deserialize(ctx) {
let count_or_id = ctx.read_var_i32()
if count_or_id < 0 {
let id = -count_or_id
match ctx.state.get_string(id) {
Some(s) => DeduplicatedString::{ string: s, }
None => raise InvalidStringId(id)
}
} else {
// count_or_id is the UTF-8 byte length of an inline string (registered
// under the next id).
let s = read_utf8(ctx.input.read_bytes_view(count_or_id))
ctx.state.store_read_string(s)
DeduplicatedString::{ string: s, }
}
}
///|
pub extend DeduplicatedString with BinaryDeserializer::{deserialize}
///|
pub extend DeduplicatedString with BinarySerializer::{serialize}
///|
pub extend DeduplicatedString with Eq::{not_equal, equal}
///|
pub extend DeduplicatedString with @moonbitlang/core/debug.Debug::{to_repr}