Step 03 — CLI + cross-language interop

Wrap the library in a uniform CLI and prove that all three implementations produce byte-identical dumps for the same scripted scenario.

The memtable binary

Every language exposes the same subcommands so the cross-test can drive them uniformly:

memtable new    PATH
memtable put    PATH KEY VALUE
memtable del    PATH KEY
memtable get    PATH KEY
memtable iter   PATH
memtable bulk   PATH N
memtable size   PATH
  • KEY and VALUE are passed as raw command-line strings. They may contain printable bytes; for testing we stick to ASCII to avoid shell quoting issues.
  • iter and get print hex (lowercase, no separator) so output is shell-safe.

Output formats

# iter
V <hex-key> <hex-value>
T <hex-key>

# get
value: <hex-value>
tombstone
absent

# size
entries=<N> size_bytes=<B>

The scripted scenario

scripts/cross_test.sh drives every language through this sequence:

new                                          # 8 bytes, empty
bulk 100                                     # 100 entries key0..key99 / val0..val99
put  key50  REPLACED                         # overwrite
del  key10                                   # tombstone
put  ""     empty-key-value                  # empty key as a valid key
del  key99                                   # tombstone at the tail

Then it dumps rust.bin, go.bin, cpp.bin and asserts:

shasum -a 256 rust.bin go.bin cpp.bin
# all three hashes must be identical

3×3 reader matrix

For every writer × reader combination, the script runs

$READER iter $WRITER.bin > out.${reader}.${writer}.txt

and diffs pairs of outputs. All nine outputs must agree byte-for-byte.

Why a bulk subcommand

Running 100 separate memtable put PATH key0 val0 … invocations would (a) thrash the disk and (b) test the CLI's argument parsing more than the data structure. bulk exists so the cross-test can build a non-trivial table in one process per language.

Spot-check get results

After the scenario the script also runs

get key50         # expect 'value: 5245504c41434544'   (REPLACED in hex)
get key10         # expect 'tombstone'
get key99         # expect 'tombstone'
get ""            # expect 'value: 656d7074792d6b65792d76616c7565'  (empty-key-value)
get nonexistent   # expect 'absent'

across all three readers.

Failure messages worth designing for

$ memtable get /tmp/m bogus
absent

$ memtable get /tmp/no-such-file foo
error: read /tmp/no-such-file: No such file or directory

$ memtable get /tmp/garbage.bin foo
error: bad magic

A consistent error vocabulary across languages makes the cross-test's grep patterns simpler.

Tying it together

scripts/verify.sh runs:

  1. Rust tests (cargo test --release).
  2. Go tests (go test ./...).
  3. C++ tests (cmake -S . -B build && cmake --build build && ctest).
  4. The cross-language script.

Final stdout must end with ALL GREEN.