///|
struct Wtf16(FixedArray[UInt16]) derive(Eq, Show, Hash, Compare, Default)
///|
pub suberror Malformed Int derive(Show, ToJson)
///|
fn is_valid_utf16(array : FixedArray[UInt16]) -> Int {
let len = array.length()
for pos = 0; pos < len; {
let b = array.unsafe_get(pos)
match b {
_..<0xD800 | 0xE000..<_ => continue pos + 1
0xDC00..=0xDFFF => return -pos - 1
0xD800..=0xDBFF => {
if pos + 1 >= len {
return -pos - 1
}
let b2 = array.unsafe_get(pos + 1)
if !(b2 is (0xDC00..=0xDFFF)) {
return -pos - 2
}
continue pos + 2
}
}
} else {
0
}
}
///|
fn FixedArray::unsafe_reinterpret_as_string(
self : FixedArray[UInt16],
) -> String = "%identity"
///|
fn String::unsafe_reinterpret_as_fixedarray_uint16(
self : String,
) -> FixedArray[UInt16] = "%identity"
///|
#as_free_fn
pub fn Wtf16::of_string(s : String) -> Wtf16 {
Wtf16(s.unsafe_reinterpret_as_fixedarray_uint16())
}
///|
#as_free_fn
pub fn Wtf16::of_codeunits(cu : FixedArray[UInt16]) -> Wtf16 {
let array : FixedArray[UInt16] = FixedArray::make(cu.length(), 0)
array.unsafe_blit(0, cu, 0, cu.length())
array
}
///|
#as_free_fn
pub fn Wtf16::of_codepoints(cps : FixedArray[@codepoint.CodePoint]) -> Wtf16 {
let len = cps.length()
let buf = buffer(capacity=len)
for i in 0.. String raise Malformed {
let err = is_valid_utf16(self.0)
if err < 0 {
raise Malformed(-err - 1)
}
self.0.unsafe_reinterpret_as_string()
}
///|
pub fn Wtf16::to_codeunits(self : Wtf16) -> FixedArray[UInt16] {
let array : FixedArray[UInt16] = FixedArray::make(self.0.length(), 0)
array.unsafe_blit(0, self.0, 0, self.0.length())
array
}
///|
pub fn Wtf16::to_codepoints(self : Wtf16) -> FixedArray[@codepoint.CodePoint] {
let len = self.0.length()
let buf : Array[@codepoint.CodePoint] = []
for pos = 0; pos < len; {
let p = self.0.unsafe_get(pos)
if p < 0xD800 || p > 0xDFFF {
buf.push(@codepoint.of_int_unchecked(p.to_int()))
continue pos + 1
}
if p is (0xD800..=0xDBFF) &&
pos + 1 < len &&
self.0.unsafe_get(pos + 1) is (0xDC00..=0xDFFF) {
let p2 = self.0.unsafe_get(pos + 1)
let v = ((p.to_int() - 0xD800) << 10) + (p2.to_int() - 0xDC00) + 0x10000
buf.push(@codepoint.of_int_unchecked(v))
continue pos + 2
}
buf.push(@codepoint.of_int_unchecked(p.to_int()))
continue pos + 1
}
FixedArray::from_array(buf)
}
///|
pub fn Wtf16::codepoints(self : Wtf16) -> Iter[@codepoint.CodePoint] {
Iter::new(each => {
let len = self.0.length()
for pos = 0; pos < len; {
let p = self.0.unsafe_get(pos)
if p < 0xD800 || p > 0xDFFF {
match each(@codepoint.of_int_unchecked(p.to_int())) {
IterEnd => break
IterContinue => continue pos + 1
}
continue pos + 1
}
if p is (0xD800..=0xDBFF) &&
pos + 1 < len &&
self.0.unsafe_get(pos + 1) is (0xDC00..=0xDFFF) {
let p2 = self.0.unsafe_get(pos + 1)
let v = ((p.to_int() - 0xD800) << 10) + (p2.to_int() - 0xDC00) + 0x10000
match each(@codepoint.of_int_unchecked(v)) {
IterEnd => break
IterContinue => continue pos + 2
}
}
match each(@codepoint.of_int_unchecked(p.to_int())) {
IterEnd => break
IterContinue => continue pos + 1
}
}
IterContinue
})
}
///|
pub impl ToJson for Wtf16 with to_json(self : Wtf16) -> Json {
self.0.to_json()
}