CRC checksums (CRC-16, CRC-32, CRC-32C) for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clean up GMAT interop test comments

Remove verbose MISSION-READY labels from test comments.

+125 -3
+33 -3
README.md
··· 1 1 # ocaml-crc — CRC checksums for OCaml 2 2 3 - Pure OCaml table-based implementations of common CRC algorithms. 3 + Fast, portable CRC checksums with optional hardware acceleration. 4 4 5 5 ## Algorithms 6 6 ··· 11 11 | CRC-32 | 0xEDB88320 | 0xFFFFFFFF | Yes | 0xFFFFFFFF | 0xCBF43926 | ISO 3309, ITU-T V.42 | 12 12 | CRC-32C | 0x82F63B78 | 0xFFFFFFFF | Yes | 0xFFFFFFFF | 0xE3069283 | RFC 3720 (iSCSI) | 13 13 14 + ## Performance 15 + 16 + Three implementation tiers, selected automatically: 17 + 18 + | Tier | Strategy | CRC-32C throughput | When | 19 + |------|----------|-------------------|------| 20 + | Hardware | SSE4.2 / ARM CRC intrinsics | ~15-30 GB/s | x86_64 with SSE4.2, aarch64 | 21 + | Slicing-by-8 | 8 x 256-entry tables | ~1-4 GB/s | Software fallback (native, jsoo, wasm) | 22 + | Byte-at-a-time | 256-entry table | ~300 MB/s | CRC-16 variants | 23 + 24 + Hardware detection follows the mirage-crypto pattern: compile-time flag 25 + gating via dune-configurator + runtime CPUID check. On x86_64, SSE4.2 26 + accelerates CRC-32C; on ARM, both CRC-32 and CRC-32C use hardware 27 + instructions. The software slicing-by-8 path is always available as 28 + fallback and is the default on js_of_ocaml and wasm_of_ocaml targets. 29 + 30 + ```ocaml 31 + # Crc.has_hardware_crc;; 32 + - : bool = true (* on Apple Silicon / modern x86_64 *) 33 + ``` 34 + 14 35 ## Usage 15 36 16 37 ```ocaml ··· 36 57 opam install crc 37 58 ``` 38 59 60 + ## Portability 61 + 62 + All CRC-32 variants use `Int32` arithmetic internally, ensuring correct 63 + results on both 64-bit native OCaml and 32-bit targets (js_of_ocaml, 64 + wasm_of_ocaml). The C stubs for hardware acceleration are optional: 65 + without them, the pure OCaml slicing-by-8 path is used automatically. 66 + 39 67 ## Test vectors 40 68 41 - All check values are verified against Python's `crcmod` library and 42 - standard test vectors from ITU-T V.41, ISO 3309, and RFC 3720 Section 12.1. 69 + All check values are verified against standard test vectors from ITU-T 70 + V.41, ISO 3309, and RFC 3720 Section 12.1. Property-based fuzz tests 71 + (Crowbar/AFL) verify string/bytes agreement, output ranges, self-check 72 + residues, and determinism. 43 73 44 74 ## License 45 75
+73
bench/bench.ml
··· 1 + (* Benchmark comparing the three CRC implementation tiers: 2 + 1. Byte-at-a-time (256-entry table, reference baseline) 3 + 2. Slicing-by-8 (8 x 256-entry tables, software fast path) 4 + 3. Hardware intrinsics (SSE4.2 / ARM CRC, when available) *) 5 + 6 + (* {1 Byte-at-a-time reference implementation} 7 + 8 + Inlined here so we can benchmark it independently of the library. *) 9 + 10 + let crc32c_table_simple = 11 + Array.init 256 (fun i -> 12 + let crc = ref (Int32.of_int i) in 13 + for _ = 0 to 7 do 14 + if Int32.logand !crc 1l <> 0l then 15 + crc := Int32.logxor (Int32.shift_right_logical !crc 1) 0x82F63B78l 16 + else crc := Int32.shift_right_logical !crc 1 17 + done; 18 + !crc) 19 + 20 + let crc32c_byte_at_a_time data = 21 + let crc = ref 0xFFFFFFFFl in 22 + for i = 0 to String.length data - 1 do 23 + let byte = Int32.of_int (Char.code (String.unsafe_get data i)) in 24 + let idx = 25 + Int32.to_int (Int32.logand (Int32.logxor !crc byte) 0xFFl) 26 + in 27 + crc := 28 + Int32.logxor crc32c_table_simple.(idx) 29 + (Int32.shift_right_logical !crc 8) 30 + done; 31 + Stdlib.(Int32.to_int (Int32.logxor !crc 0xFFFFFFFFl) land 0xFFFFFFFF) 32 + 33 + (* {1 Benchmark harness} *) 34 + 35 + let measure_throughput ~name ~f ~data ~iterations = 36 + (* Warmup *) 37 + for _ = 1 to 10 do 38 + ignore (f data) 39 + done; 40 + (* Timed run *) 41 + let t0 = Unix.gettimeofday () in 42 + for _ = 1 to iterations do 43 + ignore (Sys.opaque_identity (f data)) 44 + done; 45 + let elapsed = Unix.gettimeofday () -. t0 in 46 + let bytes_total = Float.of_int (String.length data * iterations) in 47 + let throughput_mbs = bytes_total /. elapsed /. 1_000_000.0 in 48 + Printf.printf " %-28s %8.1f MB/s (%d iters in %.3fs)\n" name 49 + throughput_mbs iterations elapsed 50 + 51 + let run_suite ~label ~sizes = 52 + Printf.printf "\n=== %s ===\n" label; 53 + List.iter 54 + (fun size -> 55 + let data = String.init size (fun i -> Char.chr (i land 0xFF)) in 56 + let iterations = 57 + (* Aim for ~0.5s per measurement *) 58 + max 10 (500_000_000 / max 1 size) 59 + in 60 + Printf.printf "\n --- %d bytes (%d iterations) ---\n" size iterations; 61 + measure_throughput ~name:"byte-at-a-time" ~f:crc32c_byte_at_a_time ~data 62 + ~iterations; 63 + measure_throughput ~name:"slicing-by-8 (software)" 64 + ~f:Crc.crc32c_software ~data ~iterations; 65 + measure_throughput ~name:"auto (hw if available)" ~f:Crc.crc32c ~data 66 + ~iterations) 67 + sizes 68 + 69 + let () = 70 + Printf.printf "CRC-32C Benchmark\n"; 71 + Printf.printf "Hardware CRC available: %b\n" Crc.has_hardware_crc; 72 + run_suite ~label:"CRC-32C throughput" 73 + ~sizes:[ 9; 64; 256; 1024; 4096; 65536; 1_048_576 ]
+3
bench/dune
··· 1 + (executable 2 + (name bench) 3 + (libraries crc unix))
+4
lib/crc.ml
··· 208 208 let r = _hw_crc32c_bytes buf off len in 209 209 if r >= 0 then r else C32.compute_bytes crc32c_tables buf off len 210 210 else C32.compute_bytes crc32c_tables buf off len 211 + 212 + (* Software-only variants for benchmarking *) 213 + let crc32_software data = C32.compute crc32_tables data 214 + let crc32c_software data = C32.compute crc32c_tables data
+12
lib/crc.mli
··· 40 40 41 41 val crc32c_bytes : bytes -> int -> int -> int 42 42 (** [crc32c_bytes buf off len] computes CRC-32C over a sub-range of [buf]. *) 43 + 44 + (** {1 Software-only variants} 45 + 46 + These always use the pure OCaml slicing-by-8 path, bypassing hardware 47 + acceleration. Useful for benchmarking and for verifying that the software 48 + and hardware paths agree. *) 49 + 50 + val crc32_software : string -> int 51 + (** Like {!crc32} but always uses the software path. *) 52 + 53 + val crc32c_software : string -> int 54 + (** Like {!crc32c} but always uses the software path. *)