CCSDS 121.0-B-3 Lossless Data Compression (Rice/Golomb coding)
0
fork

Configure Feed

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

Add queries/updates/introspection to codec packages + fmt fixes

+205 -47
+72 -47
lib/rice.ml
··· 416 416 end 417 417 end 418 418 419 - (** Decode a CCSDS block. Returns an array of residuals (including position 0 420 - for the reference if [is_ref]). Also returns the reconstructed reference 421 - sample via side effect on [ref_out]. *) 422 - let decode_ccsds_block br block_size bps id_len is_ref = 419 + (** Decode one or more CCSDS blocks. Writes residuals into [out] starting at 420 + [out_pos]. [is_ref] indicates whether the first decoded block starts an RSI. 421 + Returns the number of blocks decoded (>1 only for aggregated zero blocks). 422 + *) 423 + let decode_ccsds_blocks br block_size bps id_len is_ref out out_pos 424 + blocks_remaining = 423 425 let id = Bitreader.read_bits br id_len in 424 426 let max_id = (1 lsl id_len) - 1 in 425 427 if id = 0 then begin ··· 428 430 if sub = 0 then begin 429 431 (* Zero block *) 430 432 let ref_sample = if is_ref then Bitreader.read_bits br bps else 0 in 431 - (* Read FS for zero block count (we only use 1 with rsi=block_size) *) 432 - let _zero_count = Bitreader.read_unary br in 433 - let residuals = Array.make block_size 0 in 434 - (* All residuals are 0; reference goes in position 0 if is_ref *) 435 - if is_ref then residuals.(0) <- ref_sample; 436 - (residuals, ref_sample) 433 + (* Read FS for zero block count *) 434 + let fs = Bitreader.read_unary br in 435 + let zero_blocks = fs + 1 in 436 + (* If zero_blocks >= 5 (ROS encoding), adjust *) 437 + let zero_blocks = 438 + if zero_blocks = 5 then blocks_remaining (* rest of segment *) 439 + else if zero_blocks > 5 then zero_blocks - 1 440 + else zero_blocks 441 + in 442 + let zero_blocks = min zero_blocks blocks_remaining in 443 + (* Fill zero blocks: first block may have reference *) 444 + if is_ref then out.(out_pos) <- ref_sample; 445 + (* All other positions in all zero blocks are already 0 (from Array.make) 446 + but let's be explicit for blocks beyond the first *) 447 + let total_samples = zero_blocks * block_size in 448 + for i = 0 to total_samples - 1 do 449 + if not (is_ref && i = 0) then out.(out_pos + i) <- 0 450 + done; 451 + zero_blocks 437 452 end 438 453 else begin 439 454 (* Second extension *) 440 455 let ref_sample = if is_ref then Bitreader.read_bits br bps else 0 in 441 - let residuals = Array.make block_size 0 in 442 - if is_ref then residuals.(0) <- ref_sample; 456 + if is_ref then out.(out_pos) <- ref_sample; 443 457 let count = if is_ref then block_size - 1 else block_size in 444 - let res_ofs = if is_ref then 1 else 0 in 458 + let res_ofs = out_pos + if is_ref then 1 else 0 in 445 459 (* Decode pairs *) 446 460 let i = ref 0 in 447 461 while !i < count do ··· 453 467 done; 454 468 let b = fs_val - (!d * (!d + 1) / 2) in 455 469 let a = !d - b in 456 - residuals.(res_ofs + !i) <- a; 457 - if !i + 1 < count then residuals.(res_ofs + !i + 1) <- b; 470 + out.(res_ofs + !i) <- a; 471 + if !i + 1 < count then out.(res_ofs + !i + 1) <- b; 458 472 i := !i + 2 459 473 done; 460 - (residuals, ref_sample) 474 + 1 461 475 end 462 476 end 463 477 else if id = max_id then begin 464 478 (* Uncompressed *) 465 479 let ref_sample = if is_ref then Bitreader.read_bits br bps else 0 in 466 - let residuals = Array.make block_size 0 in 467 - if is_ref then residuals.(0) <- ref_sample; 480 + if is_ref then out.(out_pos) <- ref_sample; 468 481 let count = if is_ref then block_size - 1 else block_size in 469 - let res_ofs = if is_ref then 1 else 0 in 482 + let res_ofs = out_pos + if is_ref then 1 else 0 in 470 483 for i = 0 to count - 1 do 471 - residuals.(res_ofs + i) <- Bitreader.read_bits br bps 484 + out.(res_ofs + i) <- Bitreader.read_bits br bps 472 485 done; 473 - (residuals, ref_sample) 486 + 1 474 487 end 475 488 else begin 476 489 (* Split coding with k = id - 1 *) 477 490 let k = id - 1 in 478 491 let ref_sample = if is_ref then Bitreader.read_bits br bps else 0 in 479 - let residuals = Array.make block_size 0 in 480 - if is_ref then residuals.(0) <- ref_sample; 492 + if is_ref then out.(out_pos) <- ref_sample; 481 493 let count = if is_ref then block_size - 1 else block_size in 482 - let res_ofs = if is_ref then 1 else 0 in 494 + let res_ofs = out_pos + if is_ref then 1 else 0 in 483 495 for i = 0 to count - 1 do 484 496 let q = Bitreader.read_unary br in 485 497 let r = if k > 0 then Bitreader.read_bits br k else 0 in 486 - residuals.(res_ofs + i) <- (q lsl k) lor r 498 + out.(res_ofs + i) <- (q lsl k) lor r 487 499 done; 488 - (residuals, ref_sample) 500 + 1 489 501 end 490 502 491 503 (* -- Compress ------------------------------------------------------------- *) ··· 543 555 let id_len = id_len_of_bps bps in 544 556 let j = cfg.block_size in 545 557 let blocks_per_rsi = cfg.rsi in 546 - let n = sample_count in 547 - let all_residuals = Array.make n 0 in 548 - let pos = ref 0 in 549 - let done_ = ref false in 550 - while !pos < n && not !done_ do 558 + (* Pad to full blocks for decoding *) 559 + let n_padded = (sample_count + j - 1) / j * j in 560 + let all_residuals = Array.make n_padded 0 in 561 + let block_pos = ref 0 in 562 + (* current block index *) 563 + let total_blocks = n_padded / j in 564 + while !block_pos < total_blocks do 551 565 (* Decode one RSI *) 552 - for b = 0 to blocks_per_rsi - 1 do 553 - if !pos < n && not !done_ then begin 554 - let is_ref = b = 0 in 555 - if Bitreader.bits_remaining br < id_len + 1 then done_ := true 556 - else begin 557 - let block_len = min j (n - !pos) in 558 - let block, _ref_sample = 559 - decode_ccsds_block br j bps id_len is_ref 560 - in 561 - (* Copy only block_len samples (may be < j for last block) *) 562 - Array.blit block 0 all_residuals !pos block_len; 563 - pos := !pos + block_len 564 - end 566 + let rsi_start = !block_pos in 567 + let b = ref 0 in 568 + while !b < blocks_per_rsi && !block_pos < total_blocks do 569 + let is_ref = !b = 0 in 570 + if Bitreader.bits_remaining br < id_len + 1 then 571 + (* Not enough bits; fill remaining with zeros *) 572 + block_pos := total_blocks 573 + else begin 574 + let out_pos = !block_pos * j in 575 + let blocks_remaining = blocks_per_rsi - !b in 576 + let blocks_remaining = 577 + min blocks_remaining (total_blocks - !block_pos) 578 + in 579 + let consumed = 580 + decode_ccsds_blocks br j bps id_len is_ref all_residuals out_pos 581 + blocks_remaining 582 + in 583 + block_pos := !block_pos + consumed; 584 + b := !b + consumed 565 585 end 566 - done 586 + done; 587 + (* If RSI was not fully consumed, advance to next RSI boundary *) 588 + if !block_pos < rsi_start + blocks_per_rsi && !block_pos < total_blocks 589 + then block_pos := min total_blocks (rsi_start + blocks_per_rsi) 567 590 done; 591 + (* Truncate to requested sample count *) 592 + let residuals = Array.sub all_residuals 0 sample_count in 568 593 (* Reconstruct samples from residuals *) 569 - let samples = reconstruct_samples cfg all_residuals in 594 + let samples = reconstruct_samples cfg residuals in 570 595 (* Pack samples into output bytes *) 571 - let out = Bytes.make (n * bps_bytes) '\000' in 596 + let out = Bytes.make (sample_count * bps_bytes) '\000' in 572 597 Array.iteri (fun i s -> write_sample out (i * bps_bytes) bps s) samples; 573 598 Ok out 574 599 with
+133
test/debug/libaec_stubs.c
··· 1 + /* C stubs wrapping libaec's aec_encode / aec_decode for OCaml interop tests. 2 + 3 + libaec implements the CCSDS 121.0-B-3 Adaptive Entropy Coding standard. 4 + We expose two OCaml functions: 5 + - caml_libaec_encode : bytes -> block_size:int -> bits_per_sample:int -> bytes 6 + - caml_libaec_decode : bytes -> block_size:int -> bits_per_sample:int 7 + -> expected_size:int -> bytes 8 + */ 9 + 10 + #include <caml/mlvalues.h> 11 + #include <caml/memory.h> 12 + #include <caml/alloc.h> 13 + #include <caml/fail.h> 14 + #include <string.h> 15 + #include <libaec.h> 16 + 17 + /* Encode raw samples using libaec. 18 + Arguments: data (bytes), block_size (int), bits_per_sample (int) 19 + Returns: compressed bytes */ 20 + CAMLprim value caml_libaec_encode(value v_data, value v_block_size, 21 + value v_bits_per_sample) { 22 + CAMLparam3(v_data, v_block_size, v_bits_per_sample); 23 + CAMLlocal1(v_result); 24 + 25 + unsigned char *data = (unsigned char *)Bytes_val(v_data); 26 + int data_len = caml_string_length(v_data); 27 + int block_size = Int_val(v_block_size); 28 + int bits_per_sample = Int_val(v_bits_per_sample); 29 + 30 + /* Allocate output buffer -- worst case is slightly larger than input */ 31 + int out_size = data_len * 2 + 1024; 32 + unsigned char *out_buf = (unsigned char *)caml_stat_alloc(out_size); 33 + 34 + struct aec_stream strm; 35 + memset(&strm, 0, sizeof(strm)); 36 + 37 + strm.bits_per_sample = bits_per_sample; 38 + strm.block_size = block_size; 39 + strm.rsi = block_size; 40 + strm.flags = AEC_DATA_MSB | AEC_DATA_PREPROCESS; 41 + 42 + /* Set byte width flags based on bits_per_sample */ 43 + if (bits_per_sample > 16) { 44 + strm.flags |= AEC_DATA_3BYTE; 45 + } 46 + 47 + strm.next_in = data; 48 + strm.avail_in = data_len; 49 + strm.next_out = out_buf; 50 + strm.avail_out = out_size; 51 + 52 + int ret = aec_encode_init(&strm); 53 + if (ret != AEC_OK) { 54 + caml_stat_free(out_buf); 55 + caml_failwith("libaec: aec_encode_init failed"); 56 + } 57 + 58 + ret = aec_encode(&strm, AEC_FLUSH); 59 + if (ret != AEC_OK) { 60 + aec_encode_end(&strm); 61 + caml_stat_free(out_buf); 62 + caml_failwith("libaec: aec_encode failed"); 63 + } 64 + 65 + int compressed_len = out_size - strm.avail_out; 66 + 67 + aec_encode_end(&strm); 68 + 69 + v_result = caml_alloc_string(compressed_len); 70 + memcpy(Bytes_val(v_result), out_buf, compressed_len); 71 + caml_stat_free(out_buf); 72 + 73 + CAMLreturn(v_result); 74 + } 75 + 76 + /* Decode compressed data using libaec. 77 + Arguments: data (bytes), block_size (int), bits_per_sample (int), 78 + expected_size (int) 79 + Returns: decompressed bytes */ 80 + CAMLprim value caml_libaec_decode(value v_data, value v_block_size, 81 + value v_bits_per_sample, 82 + value v_expected_size) { 83 + CAMLparam4(v_data, v_block_size, v_bits_per_sample, v_expected_size); 84 + CAMLlocal1(v_result); 85 + 86 + unsigned char *data = (unsigned char *)Bytes_val(v_data); 87 + int data_len = caml_string_length(v_data); 88 + int block_size = Int_val(v_block_size); 89 + int bits_per_sample = Int_val(v_bits_per_sample); 90 + int expected_size = Int_val(v_expected_size); 91 + 92 + unsigned char *out_buf = (unsigned char *)caml_stat_alloc(expected_size); 93 + 94 + struct aec_stream strm; 95 + memset(&strm, 0, sizeof(strm)); 96 + 97 + strm.bits_per_sample = bits_per_sample; 98 + strm.block_size = block_size; 99 + strm.rsi = block_size; 100 + strm.flags = AEC_DATA_MSB | AEC_DATA_PREPROCESS; 101 + 102 + if (bits_per_sample > 16) { 103 + strm.flags |= AEC_DATA_3BYTE; 104 + } 105 + 106 + strm.next_in = data; 107 + strm.avail_in = data_len; 108 + strm.next_out = out_buf; 109 + strm.avail_out = expected_size; 110 + 111 + int ret = aec_decode_init(&strm); 112 + if (ret != AEC_OK) { 113 + caml_stat_free(out_buf); 114 + caml_failwith("libaec: aec_decode_init failed"); 115 + } 116 + 117 + ret = aec_decode(&strm, AEC_FLUSH); 118 + if (ret != AEC_OK) { 119 + aec_decode_end(&strm); 120 + caml_stat_free(out_buf); 121 + caml_failwith("libaec: aec_decode failed"); 122 + } 123 + 124 + int decompressed_len = expected_size - strm.avail_out; 125 + 126 + aec_decode_end(&strm); 127 + 128 + v_result = caml_alloc_string(decompressed_len); 129 + memcpy(Bytes_val(v_result), out_buf, decompressed_len); 130 + caml_stat_free(out_buf); 131 + 132 + CAMLreturn(v_result); 133 + }