Quick Start
Five minutes with each tool. First download the archive for your system and put bxm-canon and bxm-memo on your PATH.
BXM Canon
Write the same data two ways and hash both:
echo '{"name":"bxm","tags":["a","b"],"n":3}' > a.json
echo '{ "n": 3, "tags": ["a", "b"], "name": "bxm" }' > b.json
bxm-canon hash --text a.json
bxm-canon hash --text b.json
The two hashes are the same, because both files hold the same value and Canon has one encoding for it.
Turn the text into canonical bytes and back:
bxm-canon encode a.json -o a.bxm
bxm-canon decode --pretty a.bxm
Check that a file is canonical. Here is a byte string that spells the number 0 the long way, which Canon refuses:
printf '\x03\x80\x00' > long.bxm
bxm-canon check long.bxm
# bad long.bxm: overlong-varint at byte 1: varint is not in its shortest form
See what changed between two versions of a value:
echo '{"name":"bxm","tags":["a","b","c"],"n":4}' > c.json
bxm-canon diff --text a.json c.json
# ~ $.n 3 -> 4
# + $.tags[2] "c"
Rewrite messy JSON in its one normal form, keys sorted and no spaces:
bxm-canon fmt b.json
# {"n":3,"name":"bxm","tags":["a","b"]}
BXM Canon in JavaScript
The same core runs in Node and browsers as WebAssembly. Install it from the latest release:
npm install https://github.com/softdevstudio/bxm/releases/latest/download/bxm-canon-js.tgz
import { load } from "bxm-canon"; // or require("bxm-canon")
const canon = await load();
canon.hashText('{"b":1,"a":2}');
// 'e70e7c7b89c3a8b38fe00713290413936c9f976266c102b35aa91556dcc5233a'
canon.hashText('{ "a": 2, "b": 1 }');
// the same hash
const bytes = canon.fromText('{"name":"bxm","raw":h"cafe"}'); // Uint8Array
canon.toText(bytes); // '{"name":"bxm","raw":h"cafe"}'
canon.isCanonical(bytes); // true
The hash is the one bxm-canon hash printed above for the same value, because it’s the same code.
BXM Canon in Python
Python 3.9 or later, on Linux, macOS or Windows. Install the wheel for your system from the release:
pip install bxm-canon --find-links https://github.com/softdevstudio/bxm/releases/expanded_assets/v0.3.0
import bxm_canon as canon
canon.hash_value({"b": 1, "a": 2})
# 'e70e7c7b89c3a8b38fe00713290413936c9f976266c102b35aa91556dcc5233a'
data = canon.encode({"n": 3, "x": 1.0, "raw": b"\xca\xfe"})
canon.decode(data) # {'n': 3, 'raw': b'\xca\xfe', 'x': 1.0}
canon.to_text(data) # '{"n":3,"raw":h"cafe","x":1.0}'
Same hash as the command line and the JavaScript package. Python’s int and float stay apart, so 3 and 3.0 encode differently, and bytes stay bytes.
BXM Memo
Make a small project with a slow step:
mkdir demo && cd demo
mkdir src && echo hello > src/a.txt && echo world > src/b.txt
cat > build.sh <<'SH'
#!/bin/sh
sleep 2
mkdir -p out
cat src/*.txt > out/all.txt
echo built
SH
chmod +x build.sh
Run it through Memo twice:
bxm-memo run -i src -i build.sh -o out -- ./build.sh
# bxm-memo: miss build.sh: no earlier run of this job in the cache
# built
bxm-memo run -i src -i build.sh -o out -- ./build.sh
# built
# bxm-memo: hit build.sh: restored 1 file(s) in 222µs (the run took 2.007s)
The second run printed the same built line, from the cache, and took no time. Now change an input:
echo there > src/b.txt
bxm-memo explain -i src -i build.sh -o out -- ./build.sh
# key b4633aea6db3e900663860a95beb651dbf12896fd298507f6a912f38a6e2c165
# inputs 3 file(s), outputs out
# miss:
# input src/b.txt changed
explain runs nothing. It tells you whether the next run would hit, and if not, why.
Next
- The Reference lists every command and flag.
- BXM Canon and BXM Memo explain what each tool promises.
- The Canon spec is two pages long.