Programming Language

Nocter

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

/development/std/io/core.nct

core.nct

//! Portable byte-stream contracts and protocol-derived operations.
//!
//! Concrete files, descriptors, buffering policy, and target primitives live
//! outside this module. Every Reader and Writer receives the same common
//! operations through interface default methods.

use std/error.Error
use std/string.{String, bytes}
use std/vec.Vec

/// A source that initializes at most `buffer.len()` bytes per read.
///
/// A zero count means end of stream. Returning a larger count violates the
/// protocol and is rejected by the common collection operations.
pub interface Reader {
    pub method &+self.read(buffer: &+[u8]): usize!

    /// Reads until end of stream into independently owned byte storage.
    pub method &+self.read_to_end(): Vec<u8>! {
        return collect_bytes(self)?
    }

    /// Reads until end of stream and validates the complete input as UTF-8.
    pub method &+self.read_to_string(): String! {
        let collected = collect_bytes(self)?
        return String.from_utf8(&collected as &[u8])?
    }
}

/// A destination that accepts the complete byte view or returns an error.
pub interface Writer {
    pub method &+self.write(value: &[u8]): void!
    pub method &+self.flush(): void! { return }

    /// Writes the UTF-8 encoding bytes through the shared byte contract.
    pub method &+self.write_text(text: &str): void! {
        write_text_to(self, text)?
        return
    }
}

func collect_bytes<R: Reader>(reader: &+R): Vec<u8>! {
    var result: Vec<u8> = Vec.empty()
    var scratch: Vec<u8> = initialized_scratch(8192)
    while true {
        let received: usize = reader.read(&+scratch as &+[u8])?
        if received > scratch.len() {
            return invalid_read_count()
        }
        if received == 0 {
            break
        }
        result.reserve(received)
        let initialized: &[u8] = &scratch as &[u8]
        var offset: usize = 0
        while offset < received {
            result.push(initialized[offset])
            offset = offset + 1
        }
    }
    return move result
}

func initialized_scratch(size: usize): Vec<u8> {
    var scratch: Vec<u8> = Vec.with_capacity(size)
    while scratch.len() < size {
        scratch.push(0)
    }
    return move scratch
}

func write_text_to<W: Writer>(writer: &+W, text: &str): void! {
    let encoded: &[u8] = bytes(text)
    writer.write(encoded)?
    return
}

pub func invalid_read_count(): error {
    return Error.new(
        "std.io.invalid_read_count",
        "reader returned more bytes than the supplied buffer can hold",
    )
}