Smart2Raw
Home Applications How it works Performance Get started

Commercial

Premium Licensing Investment

More

Technical scope Cite About
Get in touch

How it works

Smart2Raw is not a compressor. A compressor turns your data into something else and hands it back when you ask. Smart2Raw does the opposite: it decides, once, how wide the data actually needs to be — and then leaves it alone.

Step 1 · Measure the real range

An int64 column is 8 bytes per element whether it holds nanosecond timestamps or a sensor reading between 0 and 200. The first thing the library does is look:

int8_t cls = s2r_classify_array(values, n);   /* one pass, no allocation */

For 0..200 the answer is S2R_8. For −500..500 it is S2R_I16. The class is the width in bits, and its sign is the signedness — so abs(size) >> 3 is the number of bytes per element and size < 0 means signed. That one trick removes a whole table from the library.

Step 2 · Store in that class, natively

S2RPool p;
s2r_pool_init(&p, cls, n);
for (size_t i = 0; i < n; i++) s2r_push(&p, values[i]);

The elements are now uint8_t in memory. Not "8-bit codes" — actual uint8_t. A column of 0..200 with 4 million elements goes from 30.5 MB to 3.8 MB, and the bytes at p.data are an array a C compiler already knows how to read.

Step 3 · Operate without materialising

This is the part that separates the approach from dictionary encoding. Because the stored bytes are native integers, a predicate runs on them as they are:

size_t k = s2r_count_gt_fast(&p, 100);

There is no decode step, no dictionary lookup, no intermediate buffer. Reading one quarter of the bytes means one quarter of the memory traffic, and memory traffic is what a scan costs.

Three shapes, chosen by measurement

shapewhat it doeswhen it wins
flat poolevery element in the smallest class that fits the whole columnuniform columns; the default entry point
affinev = base + stride·i — the common step is divided out, exactlyfixed sampling intervals, fixed-point money, quantisation steps
block-wiseeach block is stored relative to its own minimum, with metadata that answers queries without touching the payloadtime-partitioned data, anything where local range is much narrower than global range

s2r_recommend() measures all three and tells you which one your column wants — because the obvious entry point is often the worst one. And s2r_blocked_plan() prices every candidate block size from a single pass over the data, so the block size is classified rather than guessed.

Why it can never expand your data

Every classical alternative has a regime where the output is larger than the input. Dictionary encoding of a high-cardinality column stores a dictionary the size of the data. RLE on unordered data stores one run per value. A bitmap only exists when there are two distinct values.

Smart2Raw classifies by range, and the widest class it has is the int64 input. So its worst case is "the range needs 64 bits", which is exactly the baseline. This is not luck or tuning — it is a structural consequence of the design, and benchmarks/format_matrix.c asserts the bound before printing each row.

The .s2r file

A serialised column is a small, fixed header followed by the payload, in canonical little-endian so the bytes are identical on any host, with a CRC32 at the end.

fmtwhat it is
1flat pool
2block-wise
3block-wise with a per-block stride

Format 3 is emitted only when some block actually has a stride above 1, so a column with no common step is byte-for-byte the file version 3.4.0 wrote — and a 3.4.0 build opens it. When a stride is present, the file is fmt = 3 and older builds correctly refuse it rather than misreading it.