NXS — Ruby
Zero-copy .nxb reader for Ruby 3.x. Pure-Ruby implementation with an optional C extension for hot-path columnar scans. No gems required.
Requirements
Ruby 3.0+. The C extension requires a C compiler and Ruby headers (ruby-dev / ruby-devel).
Read a file
require_relative "nxs"
bytes = File.binread("data.nxb")
reader = Nxs::Reader.new(bytes)
puts reader.record_count # instant — read from tail-index, no parse pass
obj = reader.record(42) # O(1) seek
puts obj.get_str("username")
puts obj.get_f64("score")
puts obj.get_bool("active")
puts obj.get_i64("id")
Columnar reducers
total = reader.sum_f64("score")
low = reader.min_f64("score")
high = reader.max_f64("score")
ages = reader.sum_i64("age")
C extension (hot path)
Build once:
bash ext/build.sh
require_relative "ext/nxs/nxs_ext" # loads Nxs::CReader and Nxs::CObject
reader = Nxs::CReader.new(bytes)
puts reader.record(42).get_str("username")
puts reader.sum_f64("score") # 6.78 ms at 1M records vs 942 ms pure Ruby
At 1M records the C extension is 139× faster than pure Ruby for sum_f64, and 5.6× faster than JSON.parse.
Write a file
require_relative "nxs_writer"
schema = Nxs::Schema.new(["id", "username", "score", "active"])
w = Nxs::Writer.new(schema)
w.begin_object
w.write_i64(0, 42)
w.write_str(1, "alice")
w.write_f64(2, 9.5)
w.write_bool(3, true)
w.end_object
data = w.finish # binary String (encoding ASCII-8BIT)
# Convenience: write from an array of hashes
data2 = Nxs::Writer.from_records(
["id", "username", "score"],
[{ "id" => 1, "username" => "bob", "score" => 8.2 }]
)
Tests
ruby test.rb ../js/fixtures # 22 tests
Benchmarks
ruby bench.rb ../js/fixtures # pure Ruby vs JSON
ruby bench_c.rb ../js/fixtures # C extension vs JSON
Files
| File | Purpose |
|---|---|
nxs.rb |
Pure-Ruby reader (Nxs::Reader, Nxs::Object) |
nxs_writer.rb |
Pure-Ruby writer (Nxs::Schema, Nxs::Writer) |
ext/nxs/nxs_ext.c |
C extension source (Nxs::CReader, Nxs::CObject) |
ext/nxs/extconf.rb |
Extension build configuration |
ext/build.sh |
Compiles the C extension |
Query engine
require_relative 'nxs'
data = File.binread("data.nxb")
reader = Nxs::Reader.new(data)
# Count matching records
n = reader.where(Nxs::Eq.new("active", true) & Nxs::Gt.new("score", 80.0)).count
# Iterate — yields Nxs::Object
reader.where(Nxs::Eq.new("active", true)).each do |obj|
puts obj.get_str("username")
end
# First match or nil
first = reader.where(Nxs::Gt.new("score", 99.0)).first
# All records
reader.all.each { |obj| ... }
Predicates
| Class | Matches |
|---|---|
Eq.new(key, value) |
equality — String, Integer, Float, boolean |
Gt.new(key, v) / Lt.new(key, v) |
numeric comparison |
p1 & p2 / `p1 \ |
p2/~p` |
Query includes Enumerable — all map, select, reject etc. are available.
For the format specification see SPEC.md. For cross-language examples see GETTING_STARTED.md.