/examples/binary-record/record.nct
record.nct
use std/fixed.ByteBuffer
use std/fs
use std/io
use std/process
use std/scan.ByteCursor
const MAGIC: u32 = 1313035346
const RECORD_BYTES: usize = 18
copy struct Record {
version: u16
item_count: u32
measurement: f64
}
noalloc func sample_record(): Record {
return Record { version: 1, item_count: 42, measurement: 3.5 }
}
noalloc func encode(record: &Record): ByteBuffer<RECORD_BYTES>! {
var output: ByteBuffer<RECORD_BYTES> = ByteBuffer.empty()
if !output.try_push_u32_be(MAGIC)
|| !output.try_push_u16_be(record.version)
|| !output.try_push_u32_le(record.item_count)
|| !output.try_push_f64_be(record.measurement) {
return error.new("example.binary_record.capacity", "record exceeded its fixed wire size")
}
return move output
}
noalloc func decode(input: &[u8]): Record! {
var cursor = ByteCursor.new(input)
let magic = cursor.take_u32_be() otherwise {
return error.new("example.binary_record.truncated", "record header is incomplete")
}
if magic != MAGIC {
return error.new("example.binary_record.magic", "record magic is invalid")
}
let version = cursor.take_u16_be() otherwise {
return error.new("example.binary_record.truncated", "record version is incomplete")
}
let item_count = cursor.take_u32_le() otherwise {
return error.new("example.binary_record.truncated", "record count is incomplete")
}
let measurement = cursor.take_f64_be() otherwise {
return error.new("example.binary_record.truncated", "record measurement is incomplete")
}
if !cursor.is_finished() {
return error.new("example.binary_record.trailing", "record has trailing bytes")
}
return Record {
version: version,
item_count: item_count,
measurement: measurement,
}
}
blocking func run(): i32 {
let path = process.arg(1) catch failure {
report_failure(&failure)
return 2
} otherwise {
io.eprintln("usage: binary-record PATH") catch _ { return 2 }
return 2
}
let source = sample_record()
let encoded = encode(&source) catch failure {
report_failure(&failure)
return 2
}
fs.write_blocking(path, &encoded as &[u8]) catch failure {
report_failure(&failure)
return 2
}
let stored = fs.read_blocking(path) catch failure {
report_failure(&failure)
return 2
}
let decoded = decode(&stored as &[u8]) catch failure {
report_failure(&failure)
return 2
}
let summary = "version ${decoded.version}, items ${decoded.item_count}, measurement ${decoded.measurement}"
io.println(&summary) catch failure {
report_failure(&failure)
return 2
}
return 0
}
blocking func report_failure(failure: &error): void {
io.eprint("binary-record: ") catch _ { return }
io.eprint(failure.code()) catch _ { return }
io.eprint(": ") catch _ { return }
io.eprintln(failure.message()) catch _ { return }
return
}
test record_round_trips_every_field {
let source = sample_record()
let encoded = encode(&source)?
let decoded = decode(&encoded as &[u8])?
if decoded.version != source.version
|| decoded.item_count != source.item_count
|| decoded.measurement.to_bits() != source.measurement.to_bits() {
return error.new("example.binary_record.round_trip", "record field changed")
}
return
}
test record_rejects_truncated_input {
let truncated: Vec<u8> = Vec [u8.truncate(78), u8.truncate(67), u8.truncate(84)]
let _decoded = decode(&truncated as &[u8]) catch failure {
if failure.has_code("example.binary_record.truncated") { return }
return move failure
}
return error.new("example.binary_record.accepted_truncated", "truncated record was accepted")
}
test record_rejects_invalid_magic_and_trailing_bytes {
let source = sample_record()
let encoded = encode(&source)?
var invalid = Vec.from_slice(&encoded as &[u8])
invalid[0] = 0
let _invalid_record = decode(&invalid as &[u8]) catch failure {
if !failure.has_code("example.binary_record.magic") { return move failure }
invalid[0] = u8.truncate(78)
invalid.push(0)
let _trailing_record = decode(&invalid as &[u8]) catch trailing {
if trailing.has_code("example.binary_record.trailing") { return }
return move trailing
}
return error.new("example.binary_record.accepted_trailing", "trailing bytes were accepted")
}
return error.new("example.binary_record.accepted_magic", "invalid magic was accepted")
}