velt/encoding
import { base64Encode, hexEncode, utf8Encode } from "velt/encoding". Converts between bytes and
text: Base64 (RFC 4648, standard and URL-safe alphabets), lowercase hex, and UTF-8. The functions
work on u8[], Velt's byte array; Uint8Array.fromBytes(b) and xs.toBytes() convert to and
from typed arrays. Data formats have modules of their own:
velt/encoding/json and velt/encoding/csv.
import { base64Encode, base64DecodeString, hexEncode, hexDecode, utf8Encode, utf8Decode } from "velt/encoding";
const bytes = utf8Encode("héllo?");
console.log(base64Encode(bytes), hexEncode(bytes)); // aMOpbGxvPw== 68c3a96c6c6f3f
console.log(base64DecodeString("aMOpbGxvPw=="), utf8Decode(hexDecode("c3a9"))); // héllo? é
try {
hexDecode("abc");
} catch (e) {
console.log(e.message); // Invalid hex: odd length
}Base64 and hex
base64Encode(data: u8[])(padded) andbase64Decode(s): u8[](padding optional).base64UrlEncode(data)(unpadded) andbase64UrlDecode(s): the URL-safe alphabet, for tokens and URLs.base64EncodeString(s)andbase64DecodeString(s): Base64 of the UTF-8 bytes of a string.hexEncode(data)andhexDecode(s);hexDecodeaccepts either case.
The decoders throw EncodingError for input that is not in their alphabet.
UTF-8
utf8Encode(s): u8[]. A lone surrogate becomes U+FFFD. The length isBuffer.byteLength(s), nots.length, which counts UTF-16 code units.utf8Decode(data)is strict: it throwsEncodingErrorfor invalid UTF-8.utf8DecodeLossy(data)writes U+FFFD for invalid sequences instead.utf8Valid(data): bool, andfirstInvalid(data): i64, the offset of the first byte that starts an invalid sequence, or -1.
A string's bytes without a copy
Strings keep their text as UTF-8, and parsers that scan bytes can read it in place:
utf8Length(s): usize: the bytes the string keeps its text in, O(1). That isBuffer.byteLength(s)for well-formed text, a lone surrogate counting its three-byte form.utf8ByteAt(s, i): u8: one byte (aRangeErrorpast the end).isAscii(s): bool, O(1).utf8Substring(s, start, end): the text between two byte positions. It copies nothing whenslicewould share the text, and throws aRangeErrorunless both positions are insidesat the start of a character.
With u8x16.loadString from velt/simd, a parser reads 16 bytes at a time.
import { utf8ByteAt, utf8Length, utf8Substring } from "velt/encoding";
const line = "naïve=café";
let eq: usize = 0;
while (utf8ByteAt(line, eq) != 61) {
eq++;
}
console.log(utf8Substring(line, 0, eq), utf8Substring(line, eq + 1, utf8Length(line))); // naïve caféTextEncoder and TextDecoder
The WHATWG classes are globals, as in Node: no import.
const bytes = new TextEncoder().encode("héllo");
console.log(bytes.length, new TextDecoder().decode(bytes)); // 6 héllonew TextEncoder().encode(s)gives the UTF-8 bytes in aUint8Array, andencodeInto(s, dest)writes as many whole characters as fit into aUint8Array, returning{ read, written }(code units read, bytes written).new TextDecoder(label = "utf-8", { fatal, ignoreBOM })takes UTF-8 only: another label throwsEncodingError(The "latin1" encoding is not supported), where Node also decodes legacy encodings.decode(bytes)takes a typed array, anArrayBuffer, aDataViewor au8[], drops a leading byte order mark unlessignoreBOM, and writes U+FFFD for invalid input, or throwsEncodingErrorwithfatal. There is nostreamoption.
btoa and atob
Node's Base64 of "binary strings" (one code unit per byte) are globals too, and velt/encoding
exports them as well. For bytes and UTF-8 text, use base64Encode and base64EncodeString.
console.log(btoa("hello"), atob(" aGVs bG8 "), atob("/w==").charCodeAt(0)); // aGVsbG8= hello 255
try {
atob("a");
} catch (e) {
console.log(e.name, e.message); // InvalidCharacterError The string to be decoded is not correctly encoded.
}btoa(s)encodes a string whose code units are all at most 0xFF (Latin-1) as padded standard Base64.atob(s)decodes as WHATWG's forgiving-base64 does: ASCII whitespace anywhere is ignored and padding is optional, but at most two=, only at the end and only where they complete a group of four. The result has one code unit (0 to 255) per byte.- Bad input throws a
DOMExceptionnamedInvalidCharacterErrorwith Node's message:Invalid character(a code unit above 0xFF forbtoa; a character outside the alphabet or a misplaced=foratob), orThe string to be decoded is not correctly encoded.(a length that cannot come from whole bytes). - Unlike JavaScript, the argument must be a string:
btoa(String(123)).