Encoding #
The encoding modules provide encoding and decoding functionality for Base64, hexadecimal, URL encoding, JSON, and structured binary data.
Import #
use "std/encoding/b64"
Base64 Encoding #
b64.encode(data) #
Encode data to Base64 string
Parameters:
data- String to encode (Str)
Returns: Base64 encoded string (Str)
Example:
use "std/encoding/b64"
let encoded = b64.encode("Hello, World!")
puts(encoded) # SGVsbG8sIFdvcmxkIQ==
b64.decode(encoded) #
Decode Base64 string to original data
Parameters:
encoded- Base64 encoded string (Str)
Returns: Decoded string (Str)
Example:
use "std/encoding/b64"
let decoded = b64.decode("SGVsbG8sIFdvcmxkIQ==")
puts(decoded) # Hello, World!
URL-Safe Base64 Encoding #
URL-safe Base64 uses - and _ instead of + and /, and removes padding (= characters).
b64.encode_url(data) #
Encode data to URL-safe Base64 string (no padding)
Parameters:
data- String to encode (Str)
Returns: URL-safe Base64 encoded string (Str)
Example:
use "std/encoding/b64"
let encoded = b64.encode_url("test")
puts(encoded) # dGVzdA (note: no padding)
b64.decode_url(encoded) #
Decode URL-safe Base64 string
Parameters:
encoded- URL-safe Base64 encoded string (Str)
Returns: Decoded string (Str)
Example:
use "std/encoding/b64"
let decoded = b64.decode_url("dGVzdA")
puts(decoded) # test
String Methods #
Base64 and hex encoding can also be accessed via string methods:
.encode(format) #
Encode a string using the specified format
Parameters:
format- Encoding format:"b64","b64url", or"hex"(Str)
Returns: Encoded string (Str)
Examples:
# Base64 encoding
let encoded = "Hello World".encode("b64")
puts(encoded) # SGVsbG8gV29ybGQ=
# URL-safe Base64
let url_safe = "test".encode("b64url")
puts(url_safe) # dGVzdA
# Hex encoding
let hex = "test".encode("hex")
puts(hex) # 74657374
.decode(format) #
Decode a string using the specified format
Parameters:
format- Decoding format:"b64","b64url", or"hex"(Str)
Returns: Decoded string (Str)
Examples:
# Base64 decoding
let decoded = "SGVsbG8gV29ybGQ=".decode("b64")
puts(decoded) # Hello World
# URL-safe Base64
let url_decoded = "dGVzdA".decode("b64url")
puts(url_decoded) # test
# Hex decoding
let hex_decoded = "74657374".decode("hex")
puts(hex_decoded) # test
Common Use Cases #
Encoding Binary Data for Text Transmission #
use "std/encoding/b64"
use "std/io"
# Read file and encode for transmission
let file_data = io.read("data.bin")
let encoded = b64.encode(file_data)
puts("Encoded data: ", encoded)
Decoding API Responses #
use "std/encoding/b64"
# Decode Base64 data from API
let api_response = "SGVsbG8gZnJvbSBBUEk="
let decoded = b64.decode(api_response)
puts(decoded) # Hello from API
URL-Safe Tokens #
use "std/encoding/b64"
# Create URL-safe token
let token_data = "user:123:session:abc"
let token = b64.encode_url(token_data)
puts("Token: ", token)
# Later, decode the token
let decoded_token = b64.decode_url(token)
puts("User data: ", decoded_token)
Round-Trip Encoding #
use "std/encoding/b64"
# Using module functions
let original = "Important data"
let encoded = b64.encode(original)
let decoded = b64.decode(encoded)
puts(decoded) # Important data
# Using string methods
let data = "Test string"
let encoded2 = data.encode("b64")
let decoded2 = encoded2.decode("b64")
puts(decoded2) # Test string
Hex Encoding for Debugging #
# Convert string to hex for inspection
let data = "Hello"
let hex = data.encode("hex")
puts("Hex: ", hex) # 48656c6c6f
# Convert back
let original = hex.decode("hex")
puts("Original: ", original) # Hello
Encoding Configuration Data #
use "std/encoding/b64"
use "std/encoding/json"
use "std/io"
# Encode config for storage
let config = json.stringify({"host": "localhost", "port": 8080})
let encoded_config = b64.encode(config)
io.write("config.b64", encoded_config)
# Decode config when loading
let loaded = io.read("config.b64")
let config_str = b64.decode(loaded)
let config_obj = json.parse(config_str)
puts("Host: ", config_obj["host"])
Notes #
- Base64 encoding increases data size by approximately 33%
- URL-safe Base64 is ideal for use in URLs and file names
- Hex encoding doubles the data size but is human-readable
- All encoding functions work with UTF-8 strings
- Invalid encoded data will result in an error when decoding
hex - Hexadecimal Encoding #
The hex module provides hexadecimal encoding and decoding functionality for binary data.
Import #
use "std/encoding/hex"
Functions #
hex.encode(data) #
Encode bytes to lowercase hexadecimal string
Parameters:
data- Binary data to encode (Bytes)
Returns: Hexadecimal encoded string (Str)
Example:
use "std/encoding/hex"
let data = b"\xFF\x00\xAB"
let encoded = hex.encode(data)
puts(encoded) # ff00ab
hex.encode_upper(data) #
Encode bytes to uppercase hexadecimal string
Parameters:
data- Binary data to encode (Bytes)
Returns: Uppercase hexadecimal encoded string (Str)
Example:
use "std/encoding/hex"
let data = b"\xFF\x00\xAB"
let encoded = hex.encode_upper(data)
puts(encoded) # FF00AB
hex.encode_with_sep(data, separator) #
Encode bytes to hexadecimal string with custom separator
Parameters:
data- Binary data to encode (Bytes)separator- Separator string between hex pairs (Str)
Returns: Hexadecimal encoded string with separators (Str)
Example:
use "std/encoding/hex"
let data = b"\xFF\x00\xAB"
let encoded = hex.encode_with_sep(data, ":")
puts(encoded) # ff:00:ab
let encoded2 = hex.encode_with_sep(data, " ")
puts(encoded2) # ff 00 ab
hex.decode(encoded) #
Decode hexadecimal string to bytes
Parameters:
encoded- Hexadecimal encoded string (Str), supports separators like:,-, spaces
Returns: Decoded binary data (Bytes)
Example:
use "std/encoding/hex"
let decoded = hex.decode("ff00ab")
puts(decoded) # (bytes: ff00ab)
# Works with separators
let decoded2 = hex.decode("ff:00:ab")
let decoded3 = hex.decode("FF-00-AB")
hex.is_valid(encoded) #
Check if string is valid hexadecimal
Parameters:
encoded- String to validate (Str)
Returns: True if valid hex string (Bool)
Example:
use "std/encoding/hex"
puts(hex.is_valid("ff00ab")) # true
puts(hex.is_valid("FF:00:AB")) # true
puts(hex.is_valid("gg00ab")) # false
puts(hex.is_valid("ff0")) # false (odd length)
Common Use Cases #
Encoding Binary Data for Display #
use "std/encoding/hex"
let data = b"\x48\x65\x6c\x6c\x6f"
let hex_str = hex.encode(data)
puts("Hex: " .. hex_str) # Hex: 48656c6c6f
Decoding Hex Strings #
use "std/encoding/hex"
# Decode various hex formats
let data1 = hex.decode("48656c6c6f")
let data2 = hex.decode("48:65:6c:6c:6f")
let data3 = hex.decode("48-65-6c-6c-6f")
puts(data1.decode("utf-8")) # Hello
MAC Address Formatting #
use "std/encoding/hex"
let mac_bytes = b"\x00\x1A\x2B\x3C\x4D\x5E"
let mac_addr = hex.encode_with_sep(mac_bytes, ":")
puts(mac_addr) # 00:1a:2b:3c:4d:5e
Validating Hex Input #
use "std/encoding/hex"
fun parse_hex_color(color_str)
if not hex.is_valid(color_str)
raise "Invalid hex color: " .. color_str
end
hex.decode(color_str)
end
let color = parse_hex_color("FF5733")
puts("Color bytes: " .. hex.encode_upper(color))
url - URL Encoding #
The url module provides URL encoding/decoding and query string manipulation.
Import #
use "std/encoding/url"
Functions #
url.encode(text) #
Encode text for use in URL query values
Parameters:
text- Text to encode (Str)
Returns: URL encoded string (Str)
Example:
use "std/encoding/url"
let encoded = url.encode("Hello World!")
puts(encoded) # Hello%20World%21
url.encode_component(text) #
Encode text with stricter rules for URL components
Parameters:
text- Text to encode (Str)
Returns: URL encoded string (Str)
Example:
use "std/encoding/url"
let encoded = url.encode_component("user@example.com")
puts(encoded) # user%40example.com
url.encode_path(text) #
Encode text for use in URL paths (preserves /)
Parameters:
text- Text to encode (Str)
Returns: URL encoded string (Str)
Example:
use "std/encoding/url"
let encoded = url.encode_path("/path/to/file name.txt")
puts(encoded) # /path/to/file%20name.txt
url.encode_query(text) #
Encode text for query strings (preserves = and &)
Parameters:
text- Text to encode (Str)
Returns: URL encoded string (Str)
Example:
use "std/encoding/url"
let encoded = url.encode_query("a=1&b=2")
puts(encoded) # a=1&b=2
url.decode(encoded) #
Decode URL encoded string (treats + as space)
Parameters:
encoded- URL encoded string (Str)
Returns: Decoded string (Str)
Example:
use "std/encoding/url"
let decoded = url.decode("Hello%20World%21")
puts(decoded) # Hello World!
let decoded2 = url.decode("Hello+World")
puts(decoded2) # Hello World
url.decode_component(encoded) #
Decode URL encoded string (does not treat + as space)
Parameters:
encoded- URL encoded string (Str)
Returns: Decoded string (Str)
Example:
use "std/encoding/url"
let decoded = url.decode_component("user%40example.com")
puts(decoded) # user@example.com
url.build_query(params) #
Build URL query string from dictionary
Parameters:
params- Dictionary of query parameters (Dict)
Returns: URL encoded query string (Str)
Example:
use "std/encoding/url"
let params = {
"name": "John Doe",
"email": "john@example.com",
"age": "30"
}
let query = url.build_query(params)
puts(query) # name=John%20Doe&email=john%40example.com&age=30
url.parse_query(query) #
Parse URL query string into dictionary
Parameters:
query- URL query string (Str), with or without leading?
Returns: Dictionary of query parameters (Dict)
Example:
use "std/encoding/url"
let query = "name=John%20Doe&email=john%40example.com&age=30"
let params = url.parse_query(query)
puts(params["name"]) # John Doe
puts(params["email"]) # john@example.com
puts(params["age"]) # 30
# Also works with leading ?
let params2 = url.parse_query("?foo=bar&baz=qux")
puts(params2["foo"]) # bar
Common Use Cases #
Building API URLs #
use "std/encoding/url"
let base_url = "https://api.example.com/search"
let params = {
"q": "Quest language",
"limit": "10",
"offset": "0"
}
let query = url.build_query(params)
let full_url = base_url .. "?" .. query
puts(full_url)
# https://api.example.com/search?q=Quest%20language&limit=10&offset=0
Parsing Query Parameters #
use "std/encoding/url"
let url_string = "https://example.com/page?user=alice&action=edit&id=42"
# Extract query part (simplified)
let query = "user=alice&action=edit&id=42"
let params = url.parse_query(query)
if params["action"] == "edit"
puts("Editing item " .. params["id"])
end
Encoding Path Components #
use "std/encoding/url"
let username = "user@domain.com"
let encoded = url.encode_component(username)
let profile_url = "https://example.com/users/" .. encoded
puts(profile_url)
# https://example.com/users/user%40domain.com
Form Data Encoding #
use "std/encoding/url"
let form_data = {
"username": "alice",
"password": "secret123!",
"remember": "true"
}
let encoded_form = url.build_query(form_data)
puts(encoded_form)
# username=alice&password=secret123%21&remember=true
Notes #
url.encode()andurl.decode()are suitable for most query string use casesurl.encode_component()provides stricter encoding for individual URL componentsurl.encode_path()preserves forward slashes for encoding file paths in URLsurl.decode()treats+as space (standard for query strings)url.decode_component()does not treat+as spaceurl.build_query()andurl.parse_query()handle complete query strings- All functions follow RFC 3986 URL encoding standards
struct - Binary Data Packing #
The struct module provides Python-style binary data packing and unpacking for working with binary file formats, network protocols, and low-level data structures.
Import #
use "std/encoding/struct"
Format Strings #
Format strings control how data is packed/unpacked using format characters:
Byte Order Prefix #
First character specifies byte order (optional, defaults to native):
@- Native byte order, native size (default)=- Native byte order, standard size<- Little-endian>- Big-endian!- Network (big-endian)
Format Characters #
x- Pad byte (no value)c- Char (1 byte, single-character string)b- Signed byte (1 byte, -128 to 127)B- Unsigned byte (1 byte, 0 to 255)?- Bool (1 byte, true/false)h- Signed short (2 bytes, -32768 to 32767)H- Unsigned short (2 bytes, 0 to 65535)i/l- Signed int (4 bytes, -2147483648 to 2147483647)I/L- Unsigned int (4 bytes, 0 to 4294967295)q- Signed long long (8 bytes)Q- Unsigned long long (8 bytes)f- Float (4 bytes, 32-bit)d- Double (8 bytes, 64-bit)s- String (fixed length, null-padded)p- Pascal string (not yet fully implemented)
Repeat Counts #
Prefix format characters with numbers to repeat:
3i- Three signed ints10s- 10-byte string5B- Five unsigned bytes
Functions #
struct.calcsize(format) #
Calculate size in bytes for a format string
Parameters:
format- Format string (Str)
Returns: Size in bytes (Int)
Example:
use "std/encoding/struct"
let size = struct.calcsize("<3i")
puts(size) # 12 (3 ints × 4 bytes each)
let size2 = struct.calcsize(">hhl")
puts(size2) # 8 (2+2+4 bytes)
struct.pack(format, ...values) #
Pack values into bytes according to format string
Parameters:
format- Format string (Str)...values- Values to pack (must match format)
Returns: Packed binary data (Bytes)
Example:
use "std/encoding/struct"
# Pack three integers (little-endian)
let data = struct.pack("<3i", 1, 2, 3)
puts(data) # (bytes: 01000000 02000000 03000000)
# Pack mixed types
let data2 = struct.pack(">hhl", 100, 200, 300)
# Pack string (10 bytes, null-padded)
let data3 = struct.pack("10s", "Hello")
struct.unpack(format, data) #
Unpack bytes into values according to format string
Parameters:
format- Format string (Str)data- Binary data to unpack (Bytes)
Returns: Array of unpacked values (Array)
Example:
use "std/encoding/struct"
# Pack and unpack integers
let packed = struct.pack("<3i", 10, 20, 30)
let values = struct.unpack("<3i", packed)
puts(values[0]) # 10
puts(values[1]) # 20
puts(values[2]) # 30
# Unpack mixed types
let data = struct.pack(">hhl", 100, 200, 300)
let result = struct.unpack(">hhl", data)
puts(result[0]) # 100
puts(result[1]) # 200
puts(result[2]) # 300
struct.unpack_from(format, data, offset) #
Unpack bytes from specific offset
Parameters:
format- Format string (Str)data- Binary data to unpack (Bytes)offset- Byte offset to start unpacking (Int)
Returns: Array of unpacked values (Array)
Example:
use "std/encoding/struct"
# Pack multiple integers
let data = struct.pack("<5i", 1, 2, 3, 4, 5)
# Unpack from offset 4 (skip first int)
let values = struct.unpack_from("<3i", data, 4)
puts(values[0]) # 2
puts(values[1]) # 3
puts(values[2]) # 4
struct.pack_into(format, buffer, offset, ...values) #
Pack values into existing buffer at offset (not yet fully implemented)
Parameters:
format- Format string (Str)buffer- Mutable buffer (Bytes)offset- Byte offset (Int)...values- Values to pack
Note: Currently returns error as bytes are immutable in Quest
Common Use Cases #
Network Protocol Headers #
use "std/encoding/struct"
# Pack TCP-like header
fun create_tcp_header(src_port, dst_port, seq_num, ack_num)
struct.pack(
"!HHII",
src_port, # Source port (unsigned short)
dst_port, # Destination port (unsigned short)
seq_num, # Sequence number (unsigned int)
ack_num # Acknowledgment number (unsigned int)
)
end
let header = create_tcp_header(8080, 80, 1000, 2000)
puts("Header size: " .. header.len()) # 12 bytes
# Unpack header
let values = struct.unpack("!HHII", header)
puts("Source port: " .. values[0])
puts("Dest port: " .. values[1])
Binary File Format #
use "std/encoding/struct"
use "std/io"
# Write binary file with header
fun save_data_file(filename, version, count, data)
# File header: magic (4 bytes) + version (2 bytes) + count (2 bytes)
let magic = "DATA"
let header = struct.pack("4sHH", magic, version, count)
# Write header + data
io.write(filename, header .. data)
end
# Read binary file
fun load_data_file(filename)
let content = io.read(filename).bytes()
# Unpack header
let header = struct.unpack_from("4sHH", content, 0)
let magic = header[0]
let version = header[1]
let count = header[2]
# Get data (skip 8-byte header)
let data_start = struct.calcsize("4sHH")
let data = content.slice(data_start, content.len())
{"magic": magic, "version": version, "count": count, "data": data}
end
Little-Endian vs Big-Endian #
use "std/encoding/struct"
let num = 0x12345678
# Little-endian (Intel/AMD)
let le_data = struct.pack("<I", num)
puts(le_data) # 78 56 34 12
# Big-endian (network order)
let be_data = struct.pack(">I", num)
puts(be_data) # 12 34 56 78
# Native (depends on system)
let native_data = struct.pack("I", num)
Parsing Binary Data #
use "std/encoding/struct"
# Parse BMP file header (simplified)
fun parse_bmp_header(data)
# BMP header: signature (2 bytes) + size (4 bytes) + reserved (4 bytes) + offset (4 bytes)
let header = struct.unpack("<2sIIII", data)
{
"signature": header[0], # Should be "BM"
"file_size": header[1],
"reserved1": header[2],
"reserved2": header[3],
"data_offset": header[4]
}
end
Floating-Point Data #
use "std/encoding/struct"
# Pack floats and doubles
let data = struct.pack("<fd", 3.14, 2.71828)
# Unpack
let values = struct.unpack("<fd", data)
puts("Float: " .. values[0]) # 3.14
puts("Double: " .. values[1]) # 2.71828
Fixed-Length Strings #
use "std/encoding/struct"
# Pack 20-character string (null-padded)
let data = struct.pack("20s", "Hello")
# Unpack
let result = struct.unpack("20s", data)
puts(result[0]) # "Hello" (trailing nulls removed)
Working with Sensor Data #
use "std/encoding/struct"
use "std/serial"
# Read sensor data from serial port
let port = serial.open("/dev/ttyUSB0", 9600)
let raw_data = port.read(8)
# Unpack sensor readings (4 × 16-bit unsigned values)
let readings = struct.unpack("<4H", raw_data)
puts("Temperature: " .. readings[0])
puts("Humidity: " .. readings[1])
puts("Pressure: " .. readings[2])
puts("Light: " .. readings[3])
Format Examples #
use "std/encoding/struct"
# Single values
struct.pack("i", 42) # Signed int
struct.pack("f", 3.14) # Float
struct.pack("d", 2.71828) # Double
struct.pack("?", true) # Bool
# Multiple values
struct.pack("3i", 1, 2, 3) # Three ints
struct.pack("if", 42, 3.14) # Int + float
struct.pack("4B", 1, 2, 3, 4) # Four unsigned bytes
# Byte order
struct.pack("<i", 42) # Little-endian int
struct.pack(">i", 42) # Big-endian int
struct.pack("!H", 8080) # Network order short
# Strings
struct.pack("10s", "Hello") # 10-byte string (null-padded)
struct.pack("c", "A") # Single char
# Padding
struct.pack("ix", 42) # Int followed by padding byte
struct.pack("3x", ) # Three padding bytes (no values)
# Complex formats
struct.pack("<HHI", 1, 2, 3) # Two shorts + int (little-endian)
struct.pack("!BBHH", 1, 2, 3, 4) # Two bytes + two shorts (network)
Notes #
- Format strings are case-sensitive
- Byte order prefix applies to entire format string
- Repeat counts apply to single format character only
- String formats (
s) are fixed-length and null-padded - Pad bytes (
x) consume no values inpack()and produce no values inunpack() - Values must match format exactly (count and type)
- Out-of-range values raise errors
- Default byte order is native (system-dependent)
- Network byte order (
!) is always big-endian - Useful for: binary file formats, network protocols, hardware communication, data serialization
CSV - Comma-Separated Values #
The csv module provides CSV file parsing and generation with automatic type detection and header support.
Import #
use "std/encoding/csv"
Functions #
csv.parse(text) / csv.parse(text, options) #
Parse CSV text into array of dictionaries (with headers) or array of arrays (without headers).
Parameters:
text(Str) - CSV text to parseoptions(Dict, optional) - Parse options
Options:
has_headers(Bool) - First row contains headers, default: truedelimiter(Str) - Field delimiter, default: ","trim(Bool) - Trim whitespace, default: true
Returns:
- With headers: Array of Dict (column name → value)
- Without headers: Array of Array (raw rows)
Type Detection: Automatically converts "42" → Int, "3.14" → Float, "true" → Bool
Example:
use "std/encoding/csv"
let csv_text = "name,age,active\nAlice,30,true\nBob,25,false"
let rows = csv.parse(csv_text)
for row in rows
puts(row["name"] .. " is " .. row["age"]) # age is Int
end
csv.stringify(data) / csv.stringify(data, options) #
Convert array of dictionaries or arrays to CSV text.
Parameters:
data(Array) - Array of Dict or Array of Arrayoptions(Dict, optional) - Stringify options
Options:
delimiter(Str) - Field delimiter, default: ","headers(Array) - Custom headers for array of arrays
Returns: CSV text (Str)
Example:
use "std/encoding/csv"
let data = [
{"name": "Alice", "age": "30"},
{"name": "Bob", "age": "25"}
]
let csv_text = csv.stringify(data)
puts(csv_text)
# name,age
# Alice,30
# Bob,25
Common Use Cases #
Reading CSV Files #
use "std/encoding/csv"
use "std/io"
let csv_text = io.read("users.csv")
let users = csv.parse(csv_text)
for user in users
puts(user["name"] .. " - " .. user["email"])
end
Writing CSV Files #
use "std/encoding/csv"
use "std/io"
let data = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
let csv_text = csv.stringify(data)
io.write("output.csv", csv_text)
TSV (Tab-Separated Values) #
use "std/encoding/csv"
let tsv_text = "name\tage\nAlice\t30"
let rows = csv.parse(tsv_text, {"delimiter": "\t"})
let data = [{"name": "Alice", "age": "30"}]
let tsv_output = csv.stringify(data, {"delimiter": "\t"})
CSV without Headers #
use "std/encoding/csv"
let csv_text = "Alice,30\nBob,25"
let rows = csv.parse(csv_text, {"has_headers": false})
# Access by index
for row in rows
puts(row[0] .. " is " .. row[1])
end
Type Detection #
use "std/encoding/csv"
let csv_text = "name,age,score,active\nAlice,30,95.5,true"
let rows = csv.parse(csv_text)
let row = rows[0]
puts(row["age"].cls()) # "Int" (auto-detected)
puts(row["score"].cls()) # "Float" (auto-detected)
puts(row["active"].cls()) # "Bool" (auto-detected)
Notes #
- Automatic type detection converts numeric and boolean values
- Empty fields become empty strings
"" - Fields with delimiters or quotes are automatically quoted in output
- Follows RFC 4180 CSV standard
- Headers are inferred from first dictionary row
- All values are converted to strings when stringifying