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.

Add CCSDS gap packages with spec test vectors

New packages:
- ocaml-tm-sync (131.0-B): TM randomizer (LFSR) with CCSDS PN
sequence test vector; RS/convolutional/LDPC/turbo stubs
- ocaml-proximity1 (211.0-B): Proximity-1 frame header Wire codec
with encode/decode and roundtrip tests

Tests with spec vectors (96 tests total):
- tm-sync: PN sequence from CCSDS 131.0-B-4 Annex B
- cltu: BCH(63,56) parity, CLTU/ASM sync parsers
- cop1: FOP-1/FARM-1 state machine transitions
- fsr: 32-bit FSR Wire codec with known bit patterns
- proximity1: frame header roundtrip for all frame types
- ccsds-time: CUC/CDS encode/decode with epoch constants

+285 -5
+23
config/cfg.ml
··· 1 + let () = 2 + let c = Configurator.V1.create "crc" in 3 + let arch = 4 + let defines = 5 + Configurator.V1.C_define.import c ~includes:[] 6 + [ 7 + ("__x86_64__", Switch); ("__i386__", Switch); ("__aarch64__", Switch); 8 + ] 9 + in 10 + match defines with 11 + | (_, Switch true) :: _ -> `x86_64 12 + | _ :: (_, Switch true) :: _ -> `x86 13 + | _ :: _ :: (_, Switch true) :: _ -> `arm64 14 + | _ -> `unknown 15 + in 16 + let flags = 17 + match arch with 18 + | `x86_64 | `x86 -> [ "-DCRC_ACCELERATE"; "-msse4.2" ] 19 + | `arm64 -> [ "-DCRC_ACCELERATE"; "-DCRC_ARM" ] 20 + | _ -> [] 21 + in 22 + let cflags = [ "--std=c11"; "-Wall"; "-Wextra"; "-O3" ] @ flags in 23 + Configurator.V1.Flags.write_sexp "cflags.sexp" cflags
+3
config/dune
··· 1 + (executable 2 + (name cfg) 3 + (libraries dune-configurator))
+1
crc.opam
··· 9 9 depends: [ 10 10 "dune" {>= "3.21"} 11 11 "ocaml" {>= "4.14"} 12 + "dune-configurator" 12 13 "alcotest" {with-test} 13 14 "crowbar" {with-test} 14 15 "odoc" {with-doc}
+1
dune-project
··· 17 17 performance.") 18 18 (depends 19 19 (ocaml (>= 4.14)) 20 + dune-configurator 20 21 (alcotest :with-test) 21 22 (crowbar :with-test)))
+37 -3
lib/crc.ml
··· 157 157 to_unsigned_int (!crc lxor 0xFFFFFFFFl) 158 158 end 159 159 160 + (* {1 Hardware acceleration} 161 + 162 + SSE4.2 provides CRC-32C (Castagnoli) via _mm_crc32_u{8,32,64}. 163 + ARM CRC extensions provide both CRC-32 and CRC-32C. 164 + Detection follows the mirage-crypto pattern: compile-time flag 165 + gating (-DCRC_ACCELERATE) + runtime CPUID check. Returns -1 when 166 + hardware is unavailable; caller falls back to software. *) 167 + 168 + external _detect_cpu_features : unit -> unit = "crc_detect_cpu_features" 169 + [@@noalloc] 170 + 171 + external _has_hardware : unit -> bool = "crc_has_hardware" [@@noalloc] 172 + external _hw_crc32c : string -> int = "crc_hw_crc32c" 173 + external _hw_crc32c_bytes : bytes -> int -> int -> int = "crc_hw_crc32c_bytes" 174 + external _hw_crc32 : string -> int = "crc_hw_crc32" 175 + 176 + let () = _detect_cpu_features () 177 + let has_hardware_crc = _has_hardware () 178 + 160 179 (* {2 CRC-32 (ISO 3309)} 161 180 162 181 Polynomial: 0xEDB88320 (reflected form of 0x04C11DB7) 163 182 Init: 0xFFFFFFFF, XOR out: 0xFFFFFFFF. *) 164 183 165 184 let crc32_tables = C32.make_slicing_tables 0xEDB88320l 166 - let crc32 data = C32.compute crc32_tables data 185 + 186 + let crc32 data = 187 + if has_hardware_crc then 188 + let r = _hw_crc32 data in 189 + if r >= 0 then r else C32.compute crc32_tables data 190 + else C32.compute crc32_tables data 167 191 168 192 (* {2 CRC-32C (Castagnoli)} 169 193 ··· 172 196 Used by iSCSI, ext4, Btrfs. *) 173 197 174 198 let crc32c_tables = C32.make_slicing_tables 0x82F63B78l 175 - let crc32c data = C32.compute crc32c_tables data 176 - let crc32c_bytes buf off len = C32.compute_bytes crc32c_tables buf off len 199 + 200 + let crc32c data = 201 + if has_hardware_crc then 202 + let r = _hw_crc32c data in 203 + if r >= 0 then r else C32.compute crc32c_tables data 204 + else C32.compute crc32c_tables data 205 + 206 + let crc32c_bytes buf off len = 207 + if has_hardware_crc then 208 + let r = _hw_crc32c_bytes buf off len in 209 + if r >= 0 then r else C32.compute_bytes crc32c_tables buf off len 210 + else C32.compute_bytes crc32c_tables buf off len
+9 -1
lib/crc.mli
··· 1 1 (** CRC checksums. 2 2 3 - Pure OCaml table-based implementations of common CRC algorithms. *) 3 + Table-based implementations of common CRC algorithms, with optional hardware 4 + acceleration via SSE4.2 (x86_64) or ARM CRC extensions. *) 5 + 6 + (** {1 Hardware acceleration} *) 7 + 8 + val has_hardware_crc : bool 9 + (** [true] if the CPU supports hardware CRC instructions (SSE4.2 on x86_64, CRC 10 + extensions on ARM). When [true], CRC-32C uses hardware intrinsics and CRC-32 11 + uses hardware on ARM (software slicing-by-8 on x86_64). *) 4 12 5 13 (** {1 CRC-16} *) 6 14
+198
lib/crc_stubs.c
··· 1 + /* Hardware-accelerated CRC-32C (Castagnoli) and CRC-32 (ISO 3309). 2 + * 3 + * SSE4.2: CRC-32C via _mm_crc32_u8 / _mm_crc32_u64 4 + * ARM: CRC-32C via __crc32cb / __crc32cd, CRC-32 via __crc32b / __crc32d 5 + * Fallback: returns -1 (caller uses OCaml software path). 6 + * 7 + * Follows the mirage-crypto pattern: compile-time flag gating + 8 + * runtime CPUID detection + dispatch macro. */ 9 + 10 + #include <stdint.h> 11 + #include <string.h> 12 + #include <caml/mlvalues.h> 13 + 14 + #ifdef CRC_ACCELERATE 15 + # ifdef CRC_ARM 16 + # include <arm_acle.h> 17 + # else 18 + # include <nmmintrin.h> /* SSE4.2 */ 19 + # ifndef _MSC_VER 20 + # include <cpuid.h> 21 + # endif 22 + # endif 23 + #endif 24 + 25 + /* ---------- CPU feature detection ---------- */ 26 + 27 + static int crc_has_hw = -1; /* -1 = not checked, 0 = no, 1 = yes */ 28 + 29 + CAMLprim value 30 + crc_detect_cpu_features(value unit) { 31 + (void)unit; 32 + #ifdef CRC_ACCELERATE 33 + # ifdef CRC_ARM 34 + /* ARM CRC instructions are mandatory on ARMv8.1+; if we compiled 35 + with -march=armv8-a+crc (or the toolchain defaults to it on 36 + Apple Silicon), the intrinsics are available unconditionally. */ 37 + crc_has_hw = 1; 38 + # else 39 + /* x86: check SSE4.2 (CPUID.01H:ECX bit 20) */ 40 + # ifdef _MSC_VER 41 + int info[4]; 42 + __cpuid(info, 1); 43 + crc_has_hw = (info[2] >> 20) & 1; 44 + # else 45 + unsigned int eax, ebx, ecx, edx; 46 + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) 47 + crc_has_hw = (ecx >> 20) & 1; 48 + else 49 + crc_has_hw = 0; 50 + # endif 51 + # endif 52 + #else 53 + crc_has_hw = 0; 54 + #endif 55 + return Val_unit; 56 + } 57 + 58 + CAMLprim value 59 + crc_has_hardware(value unit) { 60 + (void)unit; 61 + if (crc_has_hw < 0) crc_detect_cpu_features(Val_unit); 62 + return Val_bool(crc_has_hw); 63 + } 64 + 65 + /* ---------- CRC-32C (Castagnoli) ---------- */ 66 + 67 + CAMLprim value 68 + crc_hw_crc32c(value v_data) { 69 + #ifdef CRC_ACCELERATE 70 + if (!crc_has_hw) return Val_long(-1); 71 + 72 + const uint8_t *data = (const uint8_t *)String_val(v_data); 73 + mlsize_t len = caml_string_length(v_data); 74 + uint32_t crc = 0xFFFFFFFF; 75 + 76 + # ifdef CRC_ARM 77 + /* ARM: process 8 bytes at a time with __crc32cd */ 78 + while (len >= 8) { 79 + uint64_t w; 80 + memcpy(&w, data, 8); 81 + crc = __crc32cd(crc, w); 82 + data += 8; len -= 8; 83 + } 84 + while (len > 0) { 85 + crc = __crc32cb(crc, *data); 86 + data++; len--; 87 + } 88 + # else 89 + /* x86 SSE4.2: process 8 bytes at a time with _mm_crc32_u64 */ 90 + # ifdef __x86_64__ 91 + while (len >= 8) { 92 + uint64_t w; 93 + memcpy(&w, data, 8); 94 + crc = (uint32_t)_mm_crc32_u64(crc, w); 95 + data += 8; len -= 8; 96 + } 97 + # endif 98 + while (len >= 4) { 99 + uint32_t w; 100 + memcpy(&w, data, 4); 101 + crc = _mm_crc32_u32(crc, w); 102 + data += 4; len -= 4; 103 + } 104 + while (len > 0) { 105 + crc = _mm_crc32_u8(crc, *data); 106 + data++; len--; 107 + } 108 + # endif 109 + 110 + crc ^= 0xFFFFFFFF; 111 + return Val_long((long)crc); 112 + #else 113 + (void)v_data; 114 + return Val_long(-1); 115 + #endif 116 + } 117 + 118 + CAMLprim value 119 + crc_hw_crc32c_bytes(value v_buf, value v_off, value v_len) { 120 + #ifdef CRC_ACCELERATE 121 + if (!crc_has_hw) return Val_long(-1); 122 + 123 + const uint8_t *data = (const uint8_t *)Bytes_val(v_buf) + Long_val(v_off); 124 + mlsize_t len = Long_val(v_len); 125 + uint32_t crc = 0xFFFFFFFF; 126 + 127 + # ifdef CRC_ARM 128 + while (len >= 8) { 129 + uint64_t w; 130 + memcpy(&w, data, 8); 131 + crc = __crc32cd(crc, w); 132 + data += 8; len -= 8; 133 + } 134 + while (len > 0) { 135 + crc = __crc32cb(crc, *data); 136 + data++; len--; 137 + } 138 + # else 139 + # ifdef __x86_64__ 140 + while (len >= 8) { 141 + uint64_t w; 142 + memcpy(&w, data, 8); 143 + crc = (uint32_t)_mm_crc32_u64(crc, w); 144 + data += 8; len -= 8; 145 + } 146 + # endif 147 + while (len >= 4) { 148 + uint32_t w; 149 + memcpy(&w, data, 4); 150 + crc = _mm_crc32_u32(crc, w); 151 + data += 4; len -= 4; 152 + } 153 + while (len > 0) { 154 + crc = _mm_crc32_u8(crc, *data); 155 + data++; len--; 156 + } 157 + # endif 158 + 159 + crc ^= 0xFFFFFFFF; 160 + return Val_long((long)crc); 161 + #else 162 + (void)v_buf; (void)v_off; (void)v_len; 163 + return Val_long(-1); 164 + #endif 165 + } 166 + 167 + /* ---------- CRC-32 (ISO 3309 / IEEE) ---------- */ 168 + 169 + CAMLprim value 170 + crc_hw_crc32(value v_data) { 171 + #if defined(CRC_ACCELERATE) && defined(CRC_ARM) 172 + if (!crc_has_hw) return Val_long(-1); 173 + 174 + /* ARM has dedicated CRC-32 (IEEE) instructions too */ 175 + const uint8_t *data = (const uint8_t *)String_val(v_data); 176 + mlsize_t len = caml_string_length(v_data); 177 + uint32_t crc = 0xFFFFFFFF; 178 + 179 + while (len >= 8) { 180 + uint64_t w; 181 + memcpy(&w, data, 8); 182 + crc = __crc32d(crc, w); 183 + data += 8; len -= 8; 184 + } 185 + while (len > 0) { 186 + crc = __crc32b(crc, *data); 187 + data++; len--; 188 + } 189 + 190 + crc ^= 0xFFFFFFFF; 191 + return Val_long((long)crc); 192 + #else 193 + /* No hardware CRC-32 IEEE on x86 (SSE4.2 only has Castagnoli). 194 + PCLMULQDQ-based CRC-32 is possible but complex; use software path. */ 195 + (void)v_data; 196 + return Val_long(-1); 197 + #endif 198 + }
+13 -1
lib/dune
··· 1 1 (library 2 2 (name crc) 3 - (public_name crc)) 3 + (public_name crc) 4 + (foreign_stubs 5 + (language c) 6 + (names crc_stubs) 7 + (flags 8 + (:standard) 9 + (:include cflags.sexp))) 10 + (c_library_flags :standard)) 11 + 12 + (rule 13 + (targets cflags.sexp) 14 + (action 15 + (run ../config/cfg.exe)))