///|
/// UTF-8 encode a string to bytes (replaces the deprecated `String::to_bytes`).
pub fn string_to_bytes(s : String) -> Bytes {
@utf8.encode(s)
}
///|
/// Decode bytes back to a string, replacing any malformed sequences lossily.
/// Only use for content that is expected to be text.
pub fn bytes_to_string(b : Bytes) -> String {
@utf8.decode_lossy(b)
}
///|
/// The hex character for a nibble (lowercase).
fn hex_digit(v : Int) -> Char {
if v < 10 {
Int::unsafe_to_char(v + 0x30)
} else {
Int::unsafe_to_char(v - 10 + 0x61)
}
}
///|
/// Encode bytes to a lowercase hex string.
pub fn bytes_to_hex(b : Bytes) -> String {
let out = FixedArray::make(b.length() * 2, b'\x00')
for i = 0; i < b.length(); i = i + 1 {
let v = b[i].to_int()
out[i * 2] = hex_digit((v >> 4) & 0xf).to_int().to_byte()
out[i * 2 + 1] = hex_digit(v & 0xf).to_int().to_byte()
}
fixedarray_to_string(out, b.length() * 2)
}
///|
/// Find the byte index of `sub` in `s`, or `-1` when absent.
pub fn index_of(s : String, sub : String) -> Int {
match s.find(sub) {
Some(i) => i
None => -1
}
}
///|
/// Convert the first `len` bytes of a FixedArray into a string.
pub fn fixedarray_to_string(fa : FixedArray[Byte], len : Int) -> String {
bytes_to_string(Bytes::from_iter(fa.iter())[0:len].to_owned())
}
///|
/// Copy `arr[start:]` into a new array.
pub fn[T] slice_array(arr : Array[T], start : Int) -> Array[T] {
let out = []
for i = start; i < arr.length(); i = i + 1 {
out.push(arr[i])
}
out
}
///|
/// The uppercase hex character for a nibble.
pub fn to_hex_upper(v : Int) -> Byte {
if v < 10 {
(v + 0x30).to_byte()
} else {
(v - 10 + 0x41).to_byte()
}
}
///|
/// Decode a hex string to bytes. Raises on odd length or non-hex input.
pub fn hex_to_bytes(s : String) -> Bytes raise MailFailure {
if s.length() % 2 != 0 {
raise MailFailure::encode("hex string has odd length")
}
let src = string_to_bytes(s)
let out = FixedArray::make(src.length() / 2, b'\x00')
for i = 0; i < src.length(); i = i + 2 {
let hi = hex_val(src[i])
let lo = hex_val(src[i + 1])
out[i / 2] = ((hi << 4) | lo).to_byte()
}
Bytes::from_iter(out.iter())
}
///|
fn hex_val(b : Byte) -> Int raise MailFailure {
let c = b.to_int()
if c >= 0x30 && c <= 0x39 {
return c - 0x30
}
if c >= 0x61 && c <= 0x66 {
return c - 0x61 + 10
}
if c >= 0x41 && c <= 0x46 {
return c - 0x41 + 10
}
raise MailFailure::encode("invalid hex character")
}