///|
fn nibble_value(c : UInt16) -> Int? {
if c >= '0' && c <= '9' {
Some(c.to_int() - 48)
} else if c >= 'A' && c <= 'F' {
Some(c.to_int() - 55)
} else if c >= 'a' && c <= 'f' {
Some(c.to_int() - 87)
} else {
None
}
}
///|
fn nibble_char(value : Int, upper : Bool) -> Char {
if value < 10 {
(48 + value).unsafe_to_char()
} else if upper {
(55 + value).unsafe_to_char()
} else {
(87 + value).unsafe_to_char()
}
}
///|
pub fn hex_encode(data : Bytes, upper? : Bool = false) -> String {
let out = StringBuilder()
for byte in data {
let value = byte.to_int()
out.write_char(nibble_char((value >> 4) & 15, upper))
out.write_char(nibble_char(value & 15, upper))
}
out.to_string()
}
///|
pub fn hex_decode(text : String) -> Result[Bytes, IonError] {
if text.length() % 2 != 0 {
return Err(InvalidBlob(text))
}
let out : Array[Byte] = []
let mut i = 0
while i < text.length() {
match (nibble_value(text[i]), nibble_value(text[i + 1])) {
(Some(high), Some(low)) => out.push(((high << 4) | low).to_byte())
_ => return Err(InvalidBlob(text))
}
i += 2
}
Ok(Bytes::from_array(out))
}
///|
fn slice_text(text : String, start : Int, end : Int) -> String {
let out = StringBuilder()
let mut i = start
while i < end && i < text.length() {
out.write_char(text[i].to_int().unsafe_to_char())
i += 1
}
out.to_string()
}
///|
fn utf8_encode(text : String) -> Bytes {
let out : Array[Byte] = []
let mut i = 0
while i < text.length() {
let unit = text[i].to_int()
let mut code = unit
if unit >= 0xD800 && unit <= 0xDBFF && i + 1 < text.length() {
let next = text[i + 1].to_int()
if next >= 0xDC00 && next <= 0xDFFF {
code = 0x10000 + ((unit - 0xD800) << 10) + (next - 0xDC00)
i += 1
}
}
if code <= 0x7F {
out.push(code.to_byte())
} else if code <= 0x7FF {
out.push((0xC0 | (code >> 6)).to_byte())
out.push((0x80 | (code & 0x3F)).to_byte())
} else if code <= 0xFFFF {
out.push((0xE0 | (code >> 12)).to_byte())
out.push((0x80 | ((code >> 6) & 0x3F)).to_byte())
out.push((0x80 | (code & 0x3F)).to_byte())
} else {
out.push((0xF0 | (code >> 18)).to_byte())
out.push((0x80 | ((code >> 12) & 0x3F)).to_byte())
out.push((0x80 | ((code >> 6) & 0x3F)).to_byte())
out.push((0x80 | (code & 0x3F)).to_byte())
}
i += 1
}
Bytes::from_array(out)
}
///|
fn utf8_decode(data : Bytes) -> Result[String, IonError] {
let out = StringBuilder()
let arr = bytes_to_array(data)
let mut i = 0
while i < arr.length() {
let b0 = arr[i].to_int()
if b0 <= 0x7F {
out.write_char(b0.unsafe_to_char())
i += 1
} else if b0 >= 0xC2 && b0 <= 0xDF {
if i + 1 >= arr.length() {
return Err(InvalidUtf8)
}
let b1 = arr[i + 1].to_int()
if b1 < 0x80 || b1 > 0xBF {
return Err(InvalidUtf8)
}
let code = ((b0 & 0x1F) << 6) | (b1 & 0x3F)
out.write_char(code.unsafe_to_char())
i += 2
} else if b0 >= 0xE0 && b0 <= 0xEF {
if i + 2 >= arr.length() {
return Err(InvalidUtf8)
}
let b1 = arr[i + 1].to_int()
let b2 = arr[i + 2].to_int()
if b1 < 0x80 || b1 > 0xBF || b2 < 0x80 || b2 > 0xBF {
return Err(InvalidUtf8)
}
let code = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)
if code >= 0xD800 && code <= 0xDFFF {
return Err(InvalidUtf8)
}
out.write_char(code.unsafe_to_char())
i += 3
} else if b0 >= 0xF0 && b0 <= 0xF4 {
if i + 3 >= arr.length() {
return Err(InvalidUtf8)
}
let b1 = arr[i + 1].to_int()
let b2 = arr[i + 2].to_int()
let b3 = arr[i + 3].to_int()
if b1 < 0x80 ||
b1 > 0xBF ||
b2 < 0x80 ||
b2 > 0xBF ||
b3 < 0x80 ||
b3 > 0xBF {
return Err(InvalidUtf8)
}
let code = ((b0 & 0x07) << 18) |
((b1 & 0x3F) << 12) |
((b2 & 0x3F) << 6) |
(b3 & 0x3F)
if code < 0x10000 || code > 0x10FFFF {
return Err(InvalidUtf8)
}
let cp = code - 0x10000
out.write_char((0xD800 + (cp >> 10)).unsafe_to_char())
out.write_char((0xDC00 + (cp & 0x3FF)).unsafe_to_char())
i += 4
} else {
return Err(InvalidUtf8)
}
}
Ok(out.to_string())
}
///|
fn bytes_to_array(data : Bytes) -> Array[Byte] {
let out : Array[Byte] = []
for b in data {
out.push(b)
}
out
}
///|
fn array_to_bytes(data : Array[Byte]) -> Bytes {
Bytes::from_array(data)
}
///|
fn is_digit(c : Int) -> Bool {
c >= 48 && c <= 57
}
///|
fn is_alpha(c : Int) -> Bool {
(c >= 65 && c <= 90) || (c >= 97 && c <= 122)
}
///|
fn is_hex_char(c : Int) -> Bool {
is_digit(c) || (c >= 65 && c <= 70) || (c >= 97 && c <= 102)
}
///|
fn is_ident_start(c : Int) -> Bool {
is_alpha(c) || c == 36 || c == 95
}
///|
fn is_ident_part(c : Int) -> Bool {
is_ident_start(c) || is_digit(c)
}
///|
fn is_operator_char(c : Int) -> Bool {
c == 33 ||
c == 35 ||
c == 37 ||
c == 38 ||
c == 42 ||
c == 43 ||
c == 45 ||
c == 46 ||
c == 47 ||
c == 59 ||
c == 60 ||
c == 61 ||
c == 62 ||
c == 63 ||
c == 64 ||
c == 94 ||
c == 96 ||
c == 124 ||
c == 126
}
///|
fn push_u8(out : Array[Byte], value : Int) -> Unit {
out.push((value & 255).to_byte())
}
///|
fn slice_bytes(data : Array[Byte], start : Int, end : Int) -> Bytes {
let out : Array[Byte] = []
let mut i = start
while i < end && i < data.length() {
out.push(data[i])
i += 1
}
array_to_bytes(out)
}
///|
fn base64_encode(data : Bytes) -> String {
let table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
let arr = bytes_to_array(data)
let out = StringBuilder()
let mut i = 0
while i < arr.length() {
let b0 = arr[i].to_int()
let b1 = if i + 1 < arr.length() { arr[i + 1].to_int() } else { 0 }
let b2 = if i + 2 < arr.length() { arr[i + 2].to_int() } else { 0 }
let n = (b0 << 16) | (b1 << 8) | b2
out.write_char(table[(n >> 18) & 63].to_int().unsafe_to_char())
out.write_char(table[(n >> 12) & 63].to_int().unsafe_to_char())
if i + 1 < arr.length() {
out.write_char(table[(n >> 6) & 63].to_int().unsafe_to_char())
} else {
out.write_char('=')
}
if i + 2 < arr.length() {
out.write_char(table[n & 63].to_int().unsafe_to_char())
} else {
out.write_char('=')
}
i += 3
}
out.to_string()
}
///|
fn base64_decode(text : String) -> Result[Bytes, IonError] {
let cleaned = StringBuilder()
let mut i = 0
while i < text.length() {
let c = text[i].to_int()
if c != 32 && c != 10 && c != 13 && c != 9 {
cleaned.write_char(c.unsafe_to_char())
}
i += 1
}
let src = cleaned.to_string()
if src.length() % 4 != 0 {
return Err(InvalidBlob(text))
}
let out : Array[Byte] = []
let mut j = 0
while j < src.length() {
let mut n = 0
let mut pads = 0
let mut k = 0
while k < 4 {
let c = src[j + k].to_int()
let v = if c >= 65 && c <= 90 {
c - 65
} else if c >= 97 && c <= 122 {
c - 71
} else if c >= 48 && c <= 57 {
c + 4
} else if c == 43 {
62
} else if c == 47 {
63
} else if c == 61 {
pads += 1
0
} else {
return Err(InvalidBlob(text))
}
n = (n << 6) | v
k += 1
}
out.push(((n >> 16) & 255).to_byte())
if pads < 2 {
out.push(((n >> 8) & 255).to_byte())
}
if pads < 1 {
out.push((n & 255).to_byte())
}
j += 4
}
Ok(array_to_bytes(out))
}
///|
fn nibble_value_int(c : Int) -> Int? {
if c >= 48 && c <= 57 {
Some(c - 48)
} else if c >= 65 && c <= 70 {
Some(c - 55)
} else if c >= 97 && c <= 102 {
Some(c - 87)
} else {
None
}
}
///|
fn starts_with_at(text : String, start : Int, prefix : String) -> Bool {
if start + prefix.length() > text.length() {
false
} else {
slice_text(text, start, start + prefix.length()) == prefix
}
}