The core is C++; you write your app in C# against thin, zero-copy wrappers. Every snippet below is lifted straight from the repo's samples — real API, not pseudo-code.
Open a shard, batch-ingest a column, then run a SIMD scan and an aggregate — answers come back in microseconds.
// from tests/Facts.Smoke using Dry.Facts; Facts.LoadNative(); // One shard: a single F64 "value" column, tags 0..999, no ref payload. using var db = new FactsDb(); db.Open(root, new[] { ShardSpec.WithColumns(tagMax: 999, hasRef: false, ColumnType.F64) }); // Ingest a batch: write the column, commit under a tag, flush. var w = db.CreateBatchBuffer(0, 4096, withTimestamps: false) .Writer(db.AppenderFor(0)); for (int i = 0; i < 1_000_000; i++) { w.ColumnSpan()[0] = BitConverter.DoubleToUInt64Bits(i * 0.5); w.Commit(tag: (uint)(i % 1000), refLen: 0); } w.Flush(); // Query: SIMD value-scan + a min/max/sum/mean aggregate. var rdr = db.ReaderFor(0); var recs = new ReadRangeRecord[16384]; var cols = new ulong[16384]; uint hits = rdr.ScanValueF64(col: 0, CmpOp.Gt, 32700.0, 0, ulong.MaxValue, recs, default, cols); ColStats st = rdr.AggValue(col: 0, 0, ulong.MaxValue); Console.WriteLine($"{hits} hits, mean={st.Mean:F2}, max={st.Max}");
A request/response server and client. The blocking round-trip is ~6µs on loopback; batched events go out with no per-message copies.
// from samples/Example NioUnsafe.Init(partitionCount: 8, cqSize: 1024); // Server: answer PING with PONG. var server = new Server(partitionId: 0); server.Listen("127.0.0.1", 19000); server.OnRequest = (Session s, uint seq, ushort type, ReadOnlySpan<byte> payload) => { if (type == TYPE_PING) s.Respond(seq, TYPE_PONG, payload); }; server.Start(); // Client: blocking request/response (~6 us round-trip on loopback). using var client = new Client(partitionId: 1); client.Connect("127.0.0.1", 19000); var reply = client.Query(TYPE_PING, ping); // reply.TypeId == TYPE_PONG // Fire millions of events with zero copies by batching into the TX slot. using (var batch = client.BeginBatch()) for (int i = 0; i < 32_000; i++) batch.SendEvent(TYPE_EVENT, payload);
What you build on top: a full file & directory transfer server and client — list, upload, download whole trees — is a thin layer over Nio. This is essentially the whole thing.
// from samples/FtServer + samples/FtClient using Dry.Nio; using Dry.FileTransfer; Nio.Init(partitionCount: 1, cqSize: 1024); // Server: serve a directory. var server = new FileTransferServer(partitionId: 0); server.OnTransferComplete = path => Console.WriteLine($"[sent] {path}"); server.Listen("127.0.0.1", 22100, rootPath: "ftroot"); server.Start(); // Client: connect, then pull a file — or a whole tree. using var client = new FileTransferClient(partitionId: 0); client.Connect("127.0.0.1", 22100); var r = client.Download("large.bin", "local.bin", overwrite: true); Console.WriteLine($"{r.BytesReceived} bytes success={r.Success}"); client.UploadDirectory("demo_docs", "uploads/docs", overwrite: true);
Tag a struct with [WireType]; the source generator
emits WriteTo / ReadFrom / MaxSize at build time. Blittable
types serialize at memcpy speed.
// from samples/Example2 using Dry.Zao; // Declare a wire type — codegen does the rest, no reflection at runtime. [WireType(0x01)] [StructLayout(LayoutKind.Sequential, Pack = 1)] public partial struct PlayerMoved { public int PlayerId; public float X, Y, Z; } var msg = new PlayerMoved { PlayerId = 42, X = 1.5f, Y = 2.5f, Z = -3f }; byte[] buf = new byte[PlayerMoved.MaxSize]; int n = msg.WriteTo(buf); // serialize (tier-0: pure memcpy) PlayerMoved back = PlayerMoved.ReadFrom(buf); // deserialize // Richer shapes just work too — strings, arrays, List<T>, Dictionary<K,V>.
The same engine underneath the C# wrapper. The C ABI is what every language binds to; this is the Linux bring-up smoke test.
// from src/factslib/linux_smoke.cpp #include "facts_db_capi.h" ColumnDesc cols[1] = {}; cols[0].type = ColumnType::F64; ShardConfig cfg{}; cfg.columns = cols; cfg.n_columns = 1; cfg.has_ref = 1; cfg.tag_min = 0; cfg.tag_max = 99; facts_db_t* db = facts_db_create(); facts_db_open(db, root, &cfg, 1, 0); facts_appender_t* ap = facts_db_appender(db); for (uint64_t i = 0; i < N; ++i) facts_appender_append(ap, /*tag*/ i % 100, 0, &cv, 0, ref8, 8); facts_db_flush(db); facts_reader_t* rd = facts_db_reader_for(db, 0); uint32_t got = facts_reader_scan_value(rd, /*col*/ 0, /*Gt*/ 1, f64_bits(0.0), 0, UINT64_MAX, recs, pay, paycap, colv, reccap);