Implementing a new algorithm¶
End-to-end recipe for adding a new pre-compression transform. The example
threads through naming a hypothetical myalgo algorithm that handles 32-bit
and 64-bit source data on the m/z stream.
1. Pick an ID¶
Algorithm IDs are small integer constants. The current range is
4700000..4700012 (see src/mscompress.h). Add the next unused value:
2. Implement the encoder and decoder¶
Create src/algos/myalgo.c with one pair of functions per source data type
you want to support. Use the existing algorithms as reference; the simplest
template is src/algos/lossless.c (pass-through), and src/algos/delta.c
shows the standard pattern of reading typed input and writing quantized
output.
// src/algos/myalgo.c
#include "../mscompress.h"
#include "algos.h"
void algo_encode_myalgo_64d(void* args) {
algo_args* a = (algo_args*)args;
const double* src = (const double*)a->src;
uint8_t* dest = (uint8_t*)a->dest;
size_t n = a->src_len / sizeof(double);
// ... your transform ...
*a->dest_len = bytes_written;
}
void algo_decode_myalgo_64d(void* args) {
algo_args* a = (algo_args*)args;
const uint8_t* src = (const uint8_t*)a->src;
double* dest = (double*)a->dest;
// ... inverse transform ...
*a->dest_len = bytes_written;
}
// Repeat for _32f if the algorithm supports float input.
Contract¶
args->srcandargs->destare caller-owned buffers. Don't store the pointers and don't free either one.- Read exactly
args->src_lenbytes; write the encoded result todestand set*args->dest_lento the number of bytes written. - Decoders must reverse exactly the byte sequence the encoder produced for
the same
algo_args.mz_scale_factor/int_scale_factor. Round-trip correctness is enforced by the test suite.
3. Declare the functions¶
// src/algos/algos.h
void algo_encode_myalgo_64d(void* args);
void algo_decode_myalgo_64d(void* args);
void algo_encode_myalgo_32f(void* args);
void algo_decode_myalgo_32f(void* args);
4. Register it¶
// src/algo.c, in algo_registry[]
{"myalgo", _myalgo_, TARGET_MZ, "My algorithm description", 1.0f, 0, 0},
target is a bitmask of TARGET_MZ and/or TARGET_INT. experimental=1
makes it visible but hides it from default help text.
5. Wire up dispatch¶
Add case clauses to both switches in src/algo.c:
// In set_compress_algo()
case _myalgo_: {
switch (accession) {
case _32f_: return algo_encode_myalgo_32f;
case _64d_: return algo_encode_myalgo_64d;
}
break;
}
// In set_decompress_algo()
case _myalgo_: {
switch (accession) {
case _32f_: return algo_decode_myalgo_32f;
case _64d_: return algo_decode_myalgo_64d;
}
break;
}
6. Add to the build¶
The source list lives in cli/CMakeLists.txt for the CLI and in setup.py
for the Python bindings. Both compile every .c file under src/algos/ —
adding a new file there is enough, but verify after editing.
7. Test it¶
Add a C test and a Python test that round-trip a known array:
// cli/test/test_myalgo.c
#include "../../src/mscompress.h"
// ... harness ...
void test_myalgo_roundtrip() {
double in[] = { /* known values */ };
// encode → decode → assert byte-exact (lossless) or assert within tolerance (lossy)
}
# python/test/test_myalgo.py
def test_myalgo_roundtrip(tmp_path, mzml_fixture):
out = tmp_path / "out.msz"
with mscompress.read(mzml_fixture) as f:
args = mscompress.RuntimeArguments()
args.mz_lossy = "myalgo"
f.compress(out, arguments=args)
with mscompress.read(out) as f:
# ... assert spectra survive round-trip ...
Also run mscompress --list-algorithms to confirm the new entry shows up.
8. Document it¶
Add a page under docs/algorithms/catalog/ describing:
- The math behind the transform
- Target stream (m/z, intensity, or both)
- The default scale factor and how to tune it
- Lossless vs lossy character and expected precision
- When this algorithm is the right choice
Then link it from docs/algorithms/catalog/index.md and the mkdocs nav.
Worked example¶
src/algos/delta.c is the cleanest worked example — it ships three variants
(delta16, delta24, delta32) of the same pattern (compute deltas,
quantize, pack), registered as three separate entries with three different
scale factors. Read it alongside set_compress_algo() / set_decompress_algo()
to see the full wiring.