Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/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")
}