///|
let utf8_replacement_char : Char = (65533).to_char().unwrap()
///|
fn push_utf8_byte(bytes : Array[Byte], value : Int) -> Unit {
bytes.push(value.to_byte())
}
///|
pub fn utf8_encode(text : String) -> Bytes {
let out : Array[Byte] = []
out.reserve_capacity(text.length())
text
.iter()
.each(fn(ch) {
let code = ch.to_int()
if code <= 127 {
push_utf8_byte(out, code)
} else if code <= 2047 {
push_utf8_byte(out, (192).lor(code >> 6))
push_utf8_byte(out, (128).lor(code.land(63)))
} else if code <= 65535 {
push_utf8_byte(out, (224).lor(code >> 12))
push_utf8_byte(out, (128).lor((code >> 6).land(63)))
push_utf8_byte(out, (128).lor(code.land(63)))
} else {
push_utf8_byte(out, (240).lor(code >> 18))
push_utf8_byte(out, (128).lor((code >> 12).land(63)))
push_utf8_byte(out, (128).lor((code >> 6).land(63)))
push_utf8_byte(out, (128).lor(code.land(63)))
}
})
Bytes::from_array(out)
}
///|
fn utf8_continuation_byte(value : Int) -> Bool {
value.land(192) == 128
}
///|
fn utf8_push_codepoint(builder : StringBuilder, code : Int) -> Unit {
match code.to_char() {
Some(ch) => builder.write_char(ch)
None => builder.write_char(utf8_replacement_char)
}
}
///|
pub fn utf8_decode_lossy(bytes : Bytes) -> String {
let builder = StringBuilder(size_hint=bytes.length())
let mut i = 0
while i < bytes.length() {
let first = bytes[i].to_int()
if first <= 127 {
utf8_push_codepoint(builder, first)
i = i + 1
continue
}
if first >= 194 && first <= 223 && i + 1 < bytes.length() {
let second = bytes[i + 1].to_int()
if utf8_continuation_byte(second) {
utf8_push_codepoint(builder, (first.land(31) << 6).lor(second.land(63)))
i = i + 2
continue
}
}
if first >= 194 && first <= 223 && i + 1 >= bytes.length() {
builder.write_char(utf8_replacement_char)
i = bytes.length()
continue
}
if first >= 224 && first <= 239 && i + 2 < bytes.length() {
let second = bytes[i + 1].to_int()
let third = bytes[i + 2].to_int()
let valid_second = if first == 224 {
second >= 160 && second <= 191
} else if first == 237 {
second >= 128 && second <= 159
} else {
utf8_continuation_byte(second)
}
if valid_second && utf8_continuation_byte(third) {
utf8_push_codepoint(
builder,
(first.land(15) << 12).lor(second.land(63) << 6).lor(third.land(63)),
)
i = i + 3
continue
}
}
if first >= 224 &&
first <= 239 &&
i + 2 >= bytes.length() &&
i + 1 < bytes.length() &&
utf8_continuation_byte(bytes[i + 1].to_int()) {
builder.write_char(utf8_replacement_char)
i = bytes.length()
continue
}
if first >= 240 && first <= 244 && i + 3 < bytes.length() {
let second = bytes[i + 1].to_int()
let third = bytes[i + 2].to_int()
let fourth = bytes[i + 3].to_int()
let valid_second = if first == 240 {
second >= 144 && second <= 191
} else if first == 244 {
second >= 128 && second <= 143
} else {
utf8_continuation_byte(second)
}
if valid_second &&
utf8_continuation_byte(third) &&
utf8_continuation_byte(fourth) {
utf8_push_codepoint(
builder,
(first.land(7) << 18)
.lor(second.land(63) << 12)
.lor(third.land(63) << 6)
.lor(fourth.land(63)),
)
i = i + 4
continue
}
}
if first >= 240 &&
first <= 244 &&
i + 3 >= bytes.length() &&
i + 1 < bytes.length() &&
utf8_continuation_byte(bytes[i + 1].to_int()) {
builder.write_char(utf8_replacement_char)
i = bytes.length()
continue
}
builder.write_char(utf8_replacement_char)
i = i + 1
}
builder.to_string()
}