///|
struct Wtf8(Bytes) derive(Eq, Show, Hash, Compare, Default)
///|
pub suberror Malformed Int derive(Show, ToJson)
///|
fn validate(bytes : BytesView) -> Int {
loop bytes {
[] => break -1
[0x00..=0x7F, .. rest] => continue rest
[0xC2..=0xDF, 0x80..=0xBF, .. rest] => continue rest
[0xE0, 0xA0..=0xBF, 0x80..=0xBF, .. rest] => continue rest
[0xED, 0xA0..=0xBF, 0x80..=0xBF, 0xED, 0xB0..=0xBF, 0x80..=0xBF, ..] as view =>
return view.start_offset()
[0xE1..=0xEF, 0x80..=0xBF, 0x80..=0xBF, .. rest] => continue rest
[0xF0, 0x90..=0xBf, 0x80..=0xBF, 0x80..=0xBF, .. rest] => continue rest
[0xF1..=0xF3, 0x80..=0xBF, 0x80..=0xBF, 0x80..=0xBF, .. rest] =>
continue rest
[0xF4, 0x80..=0x8F, 0x80..=0xBF, 0x80..=0xBF, .. rest] => continue rest
[..] as view => return view.start_offset()
}
}
///|
#alias(of_codeunits)
#as_free_fn
pub fn Wtf8::of_bytes(bytes : Bytes) -> Wtf8 raise Malformed {
let error = validate(bytes)
if error >= 0 {
raise Malformed(error)
}
bytes
}
///|
pub fn Wtf8::of_codepoints(cps : FixedArray[@codepoint.CodePoint]) -> Wtf8 {
let len = cps.length()
let buf = buffer(capacity=len * 4)
for i in 0.. Bytes {
self.0
}
///|
pub fn Wtf8::codepoints(self : Wtf8) -> Iterator[@codepoint.CodePoint] {
let mut view = self.0[:]
Iterator::new(() => match view {
[] => None
[0x00..=0x7F as b0, .. rest] => {
view = rest
Some(@codepoint.of_int_unchecked(b0.to_int()))
}
[0xC2..=0xDF as b0, b1, .. rest] => {
view = rest
let b0 = b0.to_int()
let b1 = b1.to_int()
let c = ((b0 & 0x1F) << 6) | (b1 & 0x3F)
Some(@codepoint.of_int_unchecked(c))
}
[0xE0..=0xEF as b0, b1, b2, .. rest] => {
view = rest
let b0 = b0.to_int()
let b1 = b1.to_int()
let b2 = b2.to_int()
let c = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)
Some(@codepoint.of_int_unchecked(c))
}
[0xF0..=0xF4 as b0, b1, b2, b3, .. rest] => {
view = rest
let b0 = b0.to_int()
let b1 = b1.to_int()
let b2 = b2.to_int()
let b3 = b3.to_int()
let c = ((b0 & 0x07) << 18) |
((b1 & 0x3F) << 12) |
((b2 & 0x3F) << 6) |
(b3 & 0x3F)
Some(@codepoint.of_int_unchecked(c))
}
[..] => abort("Malformed WTF-8 sequence")
})
}
///|
pub fn Wtf8::to_codepoints(self : Wtf8) -> FixedArray[Int] {
let buf = []
loop self.0[:] {
[] => break
[0x00..=0x7F as b0, .. rest] => {
buf.push(b0.to_int())
continue rest
}
[0xC2..=0xDF as b0, b1, .. rest] => {
let b0 = b0.to_int()
let b1 = b1.to_int()
let c = ((b0 & 0x1F) << 6) | (b1 & 0x3F)
buf.push(c)
continue rest
}
[0xE0..=0xEF as b0, b1, b2, .. rest] => {
let b0 = b0.to_int()
let b1 = b1.to_int()
let b2 = b2.to_int()
let c = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)
buf.push(c)
continue rest
}
[0xF0..=0xF4 as b0, b1, b2, b3, .. rest] => {
let b0 = b0.to_int()
let b1 = b1.to_int()
let b2 = b2.to_int()
let b3 = b3.to_int()
let c = ((b0 & 0x07) << 18) |
((b1 & 0x3F) << 12) |
((b2 & 0x3F) << 6) |
(b3 & 0x3F)
buf.push(c)
continue rest
}
[..] => abort("Malformed WTF-8 sequence")
}
FixedArray::from_array(buf)
}
///|
pub impl ToJson for Wtf8 with to_json(self) {
self.to_bytes().to_array().to_json()
}
///|
pub fn Wtf8::to_utf8(self : Wtf8) -> @utf8.Utf8 raise @utf8.Malformed {
@utf8.of_bytes(self.to_bytes())
}