8.4.633-stable Switch to dev

std > io

Source

Input/output module providing file operations, data serialization, network communication, and email functionality for GreyCat applications.

Writers & Readers

All reader types implement position tracking and support streaming operations for memory-efficient processing of large files. Writers support both append and overwrite modes. CSV operations include advanced analysis capabilities for automatic type inference and code generation.

GcbWriter / GcbReader

Binary format using GreyCat’s ABI encoding for efficient serialization of any GreyCat type.

// Write binary data
var writer = GcbWriter<MyType> { path: "/path/to/data.gcb" };
writer.write(my_object);
writer.flush();

// Read binary data back
var reader = GcbReader<MyType> { path: "/path/to/data.gcb" };
while (reader.can_read()) {
    var obj = reader.read();
    Assert::isNotNull(obj);
}

GcxWriter / GcxReader

GreyCat exchange format (.gcx): a binary stream of values that does not depend on the ABI, so a program whose types changed, or a tool outside GreyCat, can still read it. Values are tagged like GCB, objects carry their field names, and type, field and enum entry names are written once, in a dictionary at the end of the file, by close(). The file is readable only once closed.

var writer = GcxWriter<Person> { path: "people.gcx" };
writer.write(Person { name: "Ada", age: 36 });
writer.close();

var reader = GcxReader<Person> { path: "people.gcx" };
while (reader.can_read()) {
    var p = reader.read();
    Assert::equals(p.name, "Ada");
}

GcxWriter { path, append: true } keeps the values of an existing closed file and writes after them. close() is not optional: a file never closed has no dictionary, and the reader rejects it. A writer whose task was suspended (await) refuses further writes instead of starting the file over; GcxReader resumes at its pos.

Reading into a program whose types changed: a field the type no longer has is skipped; a field missing from the file reads as null, and is an error when the field is not nullable; an unknown type, enum entry, field or function value is an error naming it.

Values nest at most 512 objects or containers deep: the writer refuses a deeper value, as the reader does.

Byte layout

header      "GCX\0" (4 bytes), u16 format version = 1
values      encoded values, one after another
dictionary  vu name count, then per name: vu byte length, UTF-8 bytes
trailer     u64 byte length of the dictionary, "GCXE" (4 bytes)

Fixed-width integers are little-endian. Name ids are positions in the dictionary, from 0. vu is an unsigned varint: 7 bits per byte, low bits first, the high bit set when another byte follows; a ninth byte, if reached, carries the top 8 bits unmasked. vi is zig-zag mapped ((v << 1) ^ (v >> 63)), then vu.

Every value starts with a one-byte tag:

tag value body
0 null none
1 false none
2 true none
3 int vi
4 float 8 bytes, IEEE 754 binary64
5 char vu, the code point
6 time vi, microseconds since the epoch
7 duration vi, microseconds
8 geo 8 bytes, GreyCat’s 64-bit geo code
9 String vu byte length, UTF-8 bytes
10 Buffer vu byte length, bytes
11 node vu node reference
12 nodeTime vu node reference
13 nodeIndex vu node reference
14 nodeList vu node reference
15 nodeGeo vu node reference
16 enum vu type name, vu entry name
17 object vu type name, vu field count, then per field: vu field name, value
18 Array vu type name, vu element count, element block
19 Map vu type name, vu entry count, then per entry: key value, value
20 Table vu type name, vu rows, vu columns, then one element block of rows elements per column
21 Tensor vu TensorType entry name, vu dimension count, vi per dimension, raw little-endian data
22 type vu type name
23 field vu type name, vu field name
24 function vu module name, vu (type name + 1, or 0 for a module function), vu function name

A type name is the type’s full name, specialization included: project::Sensor, core::Array<core::int>. A function’s type name is the type’s own name (Sensor), its module given apart. An element block holds one tag followed by the bare bodies when every element is an int, float, char, time, duration, geo or one node kind; otherwise the byte 0xFF followed by every element as a tagged value. An object writes only its non-null fields. Node references are written raw: they mean something only in the store that wrote them.

TextWriter / TextReader

UTF-8 text file operations with line-based I/O.

// Write text lines
var writer = TextWriter<String> { path: "/path/to/output.txt" };
writer.writeln("First line");
writer.writeln("Second line");
writer.flush();

// Read text lines
var reader = TextReader { path: "/path/to/output.txt" };
Assert::equals(reader.read(), "First line");
Assert::equals(reader.read(), "Second line");
Assert::isFalse(reader.can_read());

JsonWriter / JsonReader

JSON serialization supporting both single objects and NDJSON (newline-delimited JSON) streams.

// Write JSON objects
var writer = JsonWriter<Person> { path: "/path/to/people.json" };
writer.writeln(person1); // Each call writes one JSON object per line
writer.writeln(person2);
writer.flush();

// Read JSON stream
var reader = JsonReader<Person> { path: "/path/to/people.json" };
var first_person = reader.read();
Assert::equals(first_person.name, "John");

// Parse JSON strings directly
var parsed = Json<Person>::parse("{\"name\":\"Alice\",\"age\":30}");
Assert::equals(parsed.name, "Alice");

CsvWriter / CsvReader

CSV file operations with automatic header generation and configurable formatting.

// Configure CSV format
var format = CsvFormat {
    header_lines: 1,
    separator: ',',
    string_delimiter: '"'
};

// Write structured data as CSV
var writer = CsvWriter<Employee> { path: "/path/to/employees.csv", format: format };
writer.write(employee1); // Headers written automatically on first write
writer.write(employee2);
writer.flush();

// Read CSV with type validation
var reader = CsvReader<Employee> { path: "/path/to/employees.csv", format: format };
while (reader.can_read()) {
    var emp = reader.read();
    Assert::isNotNull(emp.name);
}

// Re-use reader for multiple files
reader.set_path("/path/to/other_employees.csv");
Assert::isTrue(reader.can_read());

CSV Analysis & Code Generation

Csv

Static utility for analyzing CSV files and generating GreyCat types.

// Analyze CSV structure
var config = CsvAnalysisConfig {
    row_limit: 1000,
    enumerable_limit: 50
};

var files = Array<File> {File::open("/path/to/sales.csv")!!};
var stats = Csv::analyze(files, config);

// Generate GreyCat types based on analysis
var type_definitions = Csv::generate(stats);
Assert::isTrue(type_definitions.contains("type"));

// Sample data for preview
var reader = CsvReader<any> { path: "/path/to/sales.csv" };
var sample_table = Csv::sample(reader, 100);
Assert::equals(sample_table.rows(), 100);

File System Operations

File

Comprehensive file system utilities for file and directory management.

// File discovery and metadata
var csv_files = File::ls("/path/to/data", ".csv", true); // Recursive search
Assert::isTrue(csv_files.size() > 0);

var file = File::open("/path/to/important.txt")!!;
Assert::isFalse(file.isDir());
Assert::equals(file.extension()!!, "txt");
Assert::isNotNull(file.sha256()!!);

// File operations
Assert::isTrue(File::copy("/path/to/source.txt", "/path/to/dest.txt"));
Assert::isTrue(File::rename("/path/to/old.txt", "/path/to/new.txt"));
Assert::isTrue(File::delete("/path/to/unwanted.txt"));
Assert::isTrue(File::mkdir("/path/to/directory"));

// Working directories
var base_dir = File::baseDir();
var user_dir = File::userDir();
var work_dir = File::workingDir();
Assert::isNotNull(base_dir);

FileWalker

Iterator for traversing file system hierarchies.

// Walk through directory structure
var walker = FileWalker { path: "." };
var file_count = 0;

while (!walker.isEmpty()) {
    var file = walker.next();
    if (file != null && !file.isDir()) {
        file_count++;
    }
}

println("file count = ${file_count}");

Network & Web

Url

URL parsing and manipulation utility.

// Parse URL components
var url = Url::parse("https://api.example.com:8080/users?active=true#section1");

Assert::equals(url.protocol, "https");
Assert::equals(url.host, "api.example.com");
Assert::equals(url.port, 8080);
Assert::equals(url.path, "/users");
Assert::equals(url.params?.get("active"), "true");
Assert::equals(url.hash, "section1");

Email & Communication

Email & Smtp

Email composition and SMTP delivery.

// Configure SMTP server
var smtp = Smtp {
    host: "smtp.example.com",
    port: 587,
    mode: SmtpMode::starttls,
    authenticate: SmtpAuth::plain,
    user: "sender@example.com",
    pass: "password123"
};

// Compose and send email
var email = Email {
    from: "sender@example.com",
    to: ["recipient@example.com"],
    cc: ["cc@example.com"],
    subject: "Test Email",
    body: "<h1>Hello World</h1>",
    body_is_html: true
};

// Send email (would throw exception on failure)
smtp.send(email);