roxify 1.12.10 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/Cargo.toml +98 -98
  2. package/dist/cli.js +48 -48
  3. package/dist/pack.js +1 -1
  4. package/dist/rox-macos-universal +0 -0
  5. package/dist/roxify_native +0 -0
  6. package/dist/roxify_native-macos-arm64 +0 -0
  7. package/dist/roxify_native-macos-x64 +0 -0
  8. package/dist/roxify_native.exe +0 -0
  9. package/dist/utils/decoder.js +59 -7
  10. package/dist/utils/encoder.js +12 -23
  11. package/dist/utils/optimization.js +2 -24
  12. package/dist/utils/zstd.js +1 -1
  13. package/native/bwt.rs +56 -56
  14. package/native/context_mixing.rs +117 -117
  15. package/native/core.rs +423 -382
  16. package/native/encoder.rs +635 -629
  17. package/native/gpu.rs +116 -116
  18. package/native/hybrid.rs +287 -287
  19. package/native/lib.rs +489 -489
  20. package/native/main.rs +527 -534
  21. package/native/mtf.rs +106 -106
  22. package/native/packer.rs +447 -447
  23. package/native/png_utils.rs +538 -538
  24. package/native/rans_byte.rs +286 -286
  25. package/native/streaming.rs +212 -212
  26. package/native/streaming_encode.rs +13 -5
  27. package/native/test_small_bwt.rs +31 -31
  28. package/package.json +114 -114
  29. package/roxify_native-aarch64-apple-darwin.node +0 -0
  30. package/roxify_native-aarch64-pc-windows-msvc.node +0 -0
  31. package/roxify_native-aarch64-unknown-linux-gnu.node +0 -0
  32. package/roxify_native-i686-pc-windows-msvc.node +0 -0
  33. package/roxify_native-i686-unknown-linux-gnu.node +0 -0
  34. package/roxify_native-x86_64-apple-darwin.node +0 -0
  35. package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
  36. package/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
  37. package/libroxify_native-x86_64-unknown-linux-gnu.node +0 -0
package/Cargo.toml CHANGED
@@ -1,98 +1,98 @@
1
- [package]
2
- name = "roxify_native"
3
- version = "1.12.10"
4
- edition = "2021"
5
- publish = false
6
-
7
- [lib]
8
- name = "roxify_native"
9
- crate-type = ["cdylib"]
10
- path = "native/lib.rs"
11
-
12
- [[bin]]
13
- name = "roxify_native"
14
- path = "native/main.rs"
15
-
16
- [[bin]]
17
- name = "bench_hybrid"
18
- path = "native/bench_hybrid.rs"
19
-
20
- [[bin]]
21
- name = "test_stages"
22
- path = "native/test_stages.rs"
23
-
24
- [[bin]]
25
- name = "test_small_bwt"
26
- path = "native/test_small_bwt.rs"
27
-
28
- [dev-dependencies]
29
-
30
- [dependencies]
31
- napi = "2"
32
- napi-derive = "2"
33
- rayon = "1.7"
34
- zstd = { version = "0.11", features = ["zstdmt"] }
35
- crc32fast = "1.3"
36
- num_cpus = "1.16"
37
- clap = { version = "4", features = ["derive"] }
38
- serde_json = "1.0"
39
- anyhow = "1.0"
40
- png = "0.18.0"
41
- image = { version = "0.25", default-features = false, features = ["png"] }
42
- # indicatif removed from default deps to reduce heavy build graph
43
- walkdir = "2.5.0"
44
- tar = "0.4"
45
- aes-gcm = "0.10"
46
- aes = "0.8"
47
- ctr = "0.9"
48
- cipher = { version = "0.4", features = ["std"] }
49
- hmac = "0.12"
50
- pbkdf2 = "0.12"
51
- rand = "0.8"
52
- sha2 = "0.10"
53
- mimalloc = "0.1"
54
-
55
- wgpu = { version = "0.19", optional = true }
56
- memmap2 = "0.9"
57
- bytemuck = { version = "1.14", features = ["derive"] }
58
- # tokio is optional now; enable via the 'async' feature
59
- tokio = { version = "1", features = ["sync", "rt"], optional = true }
60
- parking_lot = "0.12"
61
- pollster = { version = "0.3", optional = true }
62
- libsais = { version = "0.2.0", default-features = false }
63
-
64
- [features]
65
- # default is intentionally empty so the crate compiles fast for local checks.
66
- # Enable 'gpu' to pull in the WGPU and pollster dependencies (heavy).
67
- # Enable 'async' to include tokio runtime (optional).
68
- # Example: `cargo build -p roxify_native --features gpu`
69
- default = []
70
-
71
- gpu = ["wgpu", "pollster"]
72
- async = ["tokio"]
73
- full = ["gpu", "async"]
74
-
75
- [profile.release]
76
- opt-level = 3
77
- lto = "fat"
78
- codegen-units = 1
79
- strip = true
80
- panic = "abort"
81
-
82
- [profile.release-size]
83
- inherits = "release"
84
- opt-level = "z"
85
- lto = true
86
- strip = true
87
-
88
- [profile.fastdev]
89
- # Fast development profile for minimal user CPU and fast compilation.
90
- # Lower optimization and high codegen units to parallelize compilation work,
91
- # enable incremental to speed up subsequent incremental builds.
92
- inherits = "release"
93
- opt-level = 1
94
- lto = false
95
- codegen-units = 16
96
- debug = false
97
- incremental = true
98
- panic = "abort"
1
+ [package]
2
+ name = "roxify_native"
3
+ version = "1.13.0"
4
+ edition = "2021"
5
+ publish = false
6
+
7
+ [lib]
8
+ name = "roxify_native"
9
+ crate-type = ["cdylib"]
10
+ path = "native/lib.rs"
11
+
12
+ [[bin]]
13
+ name = "roxify_native"
14
+ path = "native/main.rs"
15
+
16
+ [[bin]]
17
+ name = "bench_hybrid"
18
+ path = "native/bench_hybrid.rs"
19
+
20
+ [[bin]]
21
+ name = "test_stages"
22
+ path = "native/test_stages.rs"
23
+
24
+ [[bin]]
25
+ name = "test_small_bwt"
26
+ path = "native/test_small_bwt.rs"
27
+
28
+ [dev-dependencies]
29
+
30
+ [dependencies]
31
+ napi = "2"
32
+ napi-derive = "2"
33
+ rayon = "1.7"
34
+ zstd = { version = "0.11", features = ["zstdmt"] }
35
+ crc32fast = "1.3"
36
+ num_cpus = "1.16"
37
+ clap = { version = "4", features = ["derive"] }
38
+ serde_json = "1.0"
39
+ anyhow = "1.0"
40
+ png = "0.18.0"
41
+ image = { version = "0.25", default-features = false, features = ["png"] }
42
+ # indicatif removed from default deps to reduce heavy build graph
43
+ walkdir = "2.5.0"
44
+ tar = "0.4"
45
+ aes-gcm = "0.10"
46
+ aes = "0.8"
47
+ ctr = "0.9"
48
+ cipher = { version = "0.4", features = ["std"] }
49
+ hmac = "0.12"
50
+ pbkdf2 = "0.12"
51
+ rand = "0.8"
52
+ sha2 = "0.10"
53
+ mimalloc = "0.1"
54
+
55
+ wgpu = { version = "0.19", optional = true }
56
+ memmap2 = "0.9"
57
+ bytemuck = { version = "1.14", features = ["derive"] }
58
+ # tokio is optional now; enable via the 'async' feature
59
+ tokio = { version = "1", features = ["sync", "rt"], optional = true }
60
+ parking_lot = "0.12"
61
+ pollster = { version = "0.3", optional = true }
62
+ libsais = { version = "0.2.0", default-features = false }
63
+
64
+ [features]
65
+ # default is intentionally empty so the crate compiles fast for local checks.
66
+ # Enable 'gpu' to pull in the WGPU and pollster dependencies (heavy).
67
+ # Enable 'async' to include tokio runtime (optional).
68
+ # Example: `cargo build -p roxify_native --features gpu`
69
+ default = []
70
+
71
+ gpu = ["wgpu", "pollster"]
72
+ async = ["tokio"]
73
+ full = ["gpu", "async"]
74
+
75
+ [profile.release]
76
+ opt-level = 3
77
+ lto = "fat"
78
+ codegen-units = 1
79
+ strip = true
80
+ panic = "abort"
81
+
82
+ [profile.release-size]
83
+ inherits = "release"
84
+ opt-level = "z"
85
+ lto = true
86
+ strip = true
87
+
88
+ [profile.fastdev]
89
+ # Fast development profile for minimal user CPU and fast compilation.
90
+ # Lower optimization and high codegen units to parallelize compilation work,
91
+ # enable incremental to speed up subsequent incremental builds.
92
+ inherits = "release"
93
+ opt-level = 1
94
+ lto = false
95
+ codegen-units = 16
96
+ debug = false
97
+ incremental = true
98
+ panic = "abort"
package/dist/cli.js CHANGED
@@ -51,54 +51,54 @@ async function readLargeFile(filePath) {
51
51
  return Buffer.concat(chunks);
52
52
  }
53
53
  function showHelp() {
54
- console.log(`
55
- ROX CLI — Encode/decode binary in PNG or WAV
56
-
57
- Usage:
58
- npx rox <command> [options]
59
-
60
- Commands:
61
- encode <input>... [output] Encode one or more files/directories
62
- decode <input> [output] Decode PNG/WAV to original file
63
- list <input> List files in a Rox archive
64
- havepassphrase <input> Check whether the archive requires a passphrase
65
-
66
- Options:
67
- --image Use PNG container (default)
68
- --sound Use WAV audio container (smaller overhead, faster)
69
- --bwt-ans Use BWT-ANS compression instead of Zstd
70
- -p, --passphrase <pass> Use passphrase (AES-256-GCM)
71
- -m, --mode <mode> Mode: screenshot (default)
72
- -e, --encrypt <type> auto|aes|xor|none
73
- --no-compress Disable compression
74
- --dict <file> Use zstd dictionary when compressing
75
- --force-ts Force TypeScript encoder (slower but supports encryption)
76
- -o, --output <path> Output file path
77
- -s, --sizes Show file sizes in 'list' output (default)
78
- --no-sizes Disable file size reporting in 'list'
79
- --files <list> Extract only specified files (comma-separated)
80
- --view-reconst Export the reconstituted PNG for debugging
81
- --debug Export debug images (doubled.png, reconstructed.png)
82
- -v, --verbose Show detailed errors
83
-
84
- Lossy-Resilient Encoding:
85
- --lossy-resilient Enable lossy-resilient mode (survives JPEG/MP3)
86
- --ecc-level <level> ECC redundancy: low|medium|quartile|high (default: medium)
87
- --block-size <n> Robust image block size: 2-8 pixels (default: 4)
88
-
89
- When --lossy-resilient is active, data is encoded with Reed-Solomon ECC
90
- and rendered as a QR-code-style grid (image) or MFSK tones (audio).
91
- Use --sound or --image to choose the container format.
92
-
93
- Examples:
94
- npx rox encode secret.pdf Encode to PNG
95
- npx rox encode secret.pdf --sound Encode to WAV
96
- npx rox encode secret.pdf --lossy-resilient Lossy-resilient PNG
97
- npx rox encode secret.pdf --lossy-resilient --sound --ecc-level high
98
- npx rox decode secret.pdf.png Decode back
99
- npx rox decode secret.pdf.wav Decode WAV back
100
-
101
- Run "npx rox help" for this message.
54
+ console.log(`
55
+ ROX CLI — Encode/decode binary in PNG or WAV
56
+
57
+ Usage:
58
+ npx rox <command> [options]
59
+
60
+ Commands:
61
+ encode <input>... [output] Encode one or more files/directories
62
+ decode <input> [output] Decode PNG/WAV to original file
63
+ list <input> List files in a Rox archive
64
+ havepassphrase <input> Check whether the archive requires a passphrase
65
+
66
+ Options:
67
+ --image Use PNG container (default)
68
+ --sound Use WAV audio container (smaller overhead, faster)
69
+ --bwt-ans Use BWT-ANS compression instead of Zstd
70
+ -p, --passphrase <pass> Use passphrase (AES-256-GCM)
71
+ -m, --mode <mode> Mode: screenshot (default)
72
+ -e, --encrypt <type> auto|aes|xor|none
73
+ --no-compress Disable compression
74
+ --dict <file> Use zstd dictionary when compressing
75
+ --force-ts Force TypeScript encoder (slower but supports encryption)
76
+ -o, --output <path> Output file path
77
+ -s, --sizes Show file sizes in 'list' output (default)
78
+ --no-sizes Disable file size reporting in 'list'
79
+ --files <list> Extract only specified files (comma-separated)
80
+ --view-reconst Export the reconstituted PNG for debugging
81
+ --debug Export debug images (doubled.png, reconstructed.png)
82
+ -v, --verbose Show detailed errors
83
+
84
+ Lossy-Resilient Encoding:
85
+ --lossy-resilient Enable lossy-resilient mode (survives JPEG/MP3)
86
+ --ecc-level <level> ECC redundancy: low|medium|quartile|high (default: medium)
87
+ --block-size <n> Robust image block size: 2-8 pixels (default: 4)
88
+
89
+ When --lossy-resilient is active, data is encoded with Reed-Solomon ECC
90
+ and rendered as a QR-code-style grid (image) or MFSK tones (audio).
91
+ Use --sound or --image to choose the container format.
92
+
93
+ Examples:
94
+ npx rox encode secret.pdf Encode to PNG
95
+ npx rox encode secret.pdf --sound Encode to WAV
96
+ npx rox encode secret.pdf --lossy-resilient Lossy-resilient PNG
97
+ npx rox encode secret.pdf --lossy-resilient --sound --ecc-level high
98
+ npx rox decode secret.pdf.png Decode back
99
+ npx rox decode secret.pdf.wav Decode WAV back
100
+
101
+ Run "npx rox help" for this message.
102
102
  `);
103
103
  }
104
104
  function parseArgs(args) {
package/dist/pack.js CHANGED
@@ -161,7 +161,7 @@ export async function packPathsGenerator(paths, baseDir, onProgress) {
161
161
  indexHeader.writeUInt32BE(indexBuf.length, 4);
162
162
  yield Buffer.concat([indexHeader, indexBuf]);
163
163
  let readSoFar = 0;
164
- const BATCH_SIZE = 1000;
164
+ const BATCH_SIZE = 100;
165
165
  const chunks = [];
166
166
  let chunkSize = 0;
167
167
  for (let batchStart = 0; batchStart < files.length; batchStart += BATCH_SIZE) {
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -849,6 +849,32 @@ export async function decodePngToBinary(input, opts = {}) {
849
849
  }
850
850
  }
851
851
  }
852
+ if (endStartPixel === -1) {
853
+ const scanLines = Math.min(logicalHeight, 5);
854
+ for (let row = logicalHeight - 1; row >= logicalHeight - scanLines && endStartPixel === -1; row--) {
855
+ const rowStart = row * logicalWidth;
856
+ for (let col = logicalWidth - MARKER_END.length; col >= 0 && endStartPixel === -1; col--) {
857
+ let match = true;
858
+ for (let mi = 0; mi < MARKER_END.length && match; mi++) {
859
+ const pixelIdx = rowStart + col + mi;
860
+ if (pixelIdx >= curTotalPixels) {
861
+ match = false;
862
+ break;
863
+ }
864
+ const offset = pixelIdx * 3;
865
+ if (!isColorMatch(logicalData[offset], logicalData[offset + 1], logicalData[offset + 2], MARKER_END[mi].r, MARKER_END[mi].g, MARKER_END[mi].b)) {
866
+ match = false;
867
+ }
868
+ }
869
+ if (match) {
870
+ endStartPixel = rowStart + col - startIdx;
871
+ if (process.env.ROX_DEBUG) {
872
+ console.log(`DEBUG: Found END marker via scan at row=${row}, col=${col}`);
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
852
878
  if (endStartPixel === -1) {
853
879
  if (process.env.ROX_DEBUG) {
854
880
  console.log('DEBUG: END marker not found at expected position');
@@ -862,13 +888,16 @@ export async function decodePngToBinary(input, opts = {}) {
862
888
  endStartPixel = curTotalPixels - startIdx;
863
889
  }
864
890
  const dataPixelCount = endStartPixel - (MARKER_START.length + 1);
865
- const pixelBytes = Buffer.allocUnsafe(dataPixelCount * 3);
866
- for (let i = 0; i < dataPixelCount; i++) {
867
- const srcOffset = (dataStartPixel + i) * 3;
868
- const dstOffset = i * 3;
869
- pixelBytes[dstOffset] = logicalData[srcOffset];
870
- pixelBytes[dstOffset + 1] = logicalData[srcOffset + 1];
871
- pixelBytes[dstOffset + 2] = logicalData[srcOffset + 2];
891
+ const srcByteOffset = dataStartPixel * 3;
892
+ const byteCount = dataPixelCount * 3;
893
+ const pixelBytes = Buffer.allocUnsafe(byteCount);
894
+ if (Buffer.isBuffer(logicalData)) {
895
+ logicalData.copy(pixelBytes, 0, srcByteOffset, srcByteOffset + byteCount);
896
+ }
897
+ else {
898
+ for (let i = 0; i < byteCount; i++) {
899
+ pixelBytes[i] = logicalData[srcByteOffset + i];
900
+ }
872
901
  }
873
902
  if (process.env.ROX_DEBUG) {
874
903
  console.log('DEBUG: extracted len', pixelBytes.length);
@@ -1079,6 +1108,29 @@ export async function decodePngToBinary(input, opts = {}) {
1079
1108
  }
1080
1109
  }
1081
1110
  }
1111
+ if (endStartPixel2 === -1) {
1112
+ const scanLines2 = Math.min(logicalHeight2, 5);
1113
+ for (let row2 = logicalHeight2 - 1; row2 >= logicalHeight2 - scanLines2 && endStartPixel2 === -1; row2--) {
1114
+ const rowStart2 = row2 * logicalWidth2;
1115
+ for (let col2 = logicalWidth2 - MARKER_END.length; col2 >= 0 && endStartPixel2 === -1; col2--) {
1116
+ let m2 = true;
1117
+ for (let mi = 0; mi < MARKER_END.length && m2; mi++) {
1118
+ const pixelIdx2 = rowStart2 + col2 + mi;
1119
+ if (pixelIdx2 >= curTotalPixels2) {
1120
+ m2 = false;
1121
+ break;
1122
+ }
1123
+ const off2 = pixelIdx2 * 3;
1124
+ if (!isColorMatch(logicalData2[off2], logicalData2[off2 + 1], logicalData2[off2 + 2], MARKER_END[mi].r, MARKER_END[mi].g, MARKER_END[mi].b)) {
1125
+ m2 = false;
1126
+ }
1127
+ }
1128
+ if (m2) {
1129
+ endStartPixel2 = rowStart2 + col2 - startIdx2;
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1082
1134
  if (endStartPixel2 === -1) {
1083
1135
  if (process.env.ROX_DEBUG) {
1084
1136
  console.log('DEBUG: END marker not found in fallback; using end of grid');
@@ -467,40 +467,29 @@ export async function encodeBinaryToPng(input, opts = {}) {
467
467
  console.log(`[DEBUG] Width=${width}, Height=${height}, Pixels=${width * height}`);
468
468
  console.log(`[DEBUG] outputFormat=${opts.outputFormat}, useManualPng=${useManualPng}`);
469
469
  }
470
+ const totalDataBytes = logicalWidth * logicalHeight * 3;
471
+ const markerEndPos = totalDataBytes - MARKER_END.length * 3;
472
+ const fullData = Buffer.alloc(totalDataBytes);
473
+ const flatData = Buffer.concat(dataWithMarkers);
474
+ flatData.copy(fullData, 0, 0, Math.min(flatData.length, markerEndPos));
475
+ let mOff = markerEndPos;
476
+ for (let i = 0; i < MARKER_END.length; i++) {
477
+ fullData[mOff++] = MARKER_END[i].r;
478
+ fullData[mOff++] = MARKER_END[i].g;
479
+ fullData[mOff++] = MARKER_END[i].b;
480
+ }
470
481
  let raw;
471
482
  let stride = 0;
472
483
  if (useManualPng) {
473
484
  stride = width * 3 + 1;
474
485
  raw = Buffer.alloc(height * stride);
475
- const flatData = Buffer.concat(dataWithMarkers);
476
- const markerEndBytes = Buffer.alloc(MARKER_END.length * 3);
477
- for (let i = 0; i < MARKER_END.length; i++) {
478
- markerEndBytes[i * 3] = MARKER_END[i].r;
479
- markerEndBytes[i * 3 + 1] = MARKER_END[i].g;
480
- markerEndBytes[i * 3 + 2] = MARKER_END[i].b;
481
- }
482
- const totalDataBytes = logicalWidth * logicalHeight * 3;
483
- const fullData = Buffer.alloc(totalDataBytes);
484
- const markerEndPos = totalDataBytes - MARKER_END.length * 3;
485
- flatData.copy(fullData, 0, 0, Math.min(flatData.length, markerEndPos));
486
- markerEndBytes.copy(fullData, markerEndPos);
487
486
  for (let row = 0; row < height; row++) {
488
487
  raw[row * stride] = 0;
489
488
  fullData.copy(raw, row * stride + 1, row * width * 3, (row + 1) * width * 3);
490
489
  }
491
490
  }
492
491
  else {
493
- raw = Buffer.alloc(width * height * 3);
494
- const flatData = Buffer.concat(dataWithMarkers);
495
- flatData.copy(raw, 0, 0, Math.min(flatData.length, raw.length));
496
- const markerEndBytes = Buffer.alloc(MARKER_END.length * 3);
497
- for (let i = 0; i < MARKER_END.length; i++) {
498
- markerEndBytes[i * 3] = MARKER_END[i].r;
499
- markerEndBytes[i * 3 + 1] = MARKER_END[i].g;
500
- markerEndBytes[i * 3 + 2] = MARKER_END[i].b;
501
- }
502
- const markerEndPos = raw.length - MARKER_END.length * 3;
503
- markerEndBytes.copy(raw, markerEndPos);
492
+ raw = fullData;
504
493
  }
505
494
  if (opts.onProgress)
506
495
  opts.onProgress({ phase: 'png_gen', loaded: 0, total: height });
@@ -112,6 +112,7 @@ export async function optimizePngBuffer(pngBuf, fast = false) {
112
112
  const row = raw.slice(rowStart, rowStart + rowBytes);
113
113
  let bestSum = Infinity;
114
114
  let bestFiltered = null;
115
+ let chosenFilter = 0;
115
116
  for (let f = 0; f <= 4; f++) {
116
117
  const filtered = Buffer.alloc(rowBytes);
117
118
  let sum = 0;
@@ -145,33 +146,10 @@ export async function optimizePngBuffer(pngBuf, fast = false) {
145
146
  if (sum < bestSum) {
146
147
  bestSum = sum;
147
148
  bestFiltered = filtered;
148
- }
149
- }
150
- const rowBuf = Buffer.alloc(1 + rowBytes);
151
- let chosenFilter = 0;
152
- for (let f = 0; f <= 4; f++) {
153
- const filtered = Buffer.alloc(rowBytes);
154
- for (let i = 0; i < rowBytes; i++) {
155
- const val = row[i];
156
- const left = i - bytesPerPixel >= 0 ? row[i - bytesPerPixel] : 0;
157
- const up = prevRow ? prevRow[i] : 0;
158
- const upLeft = prevRow && i - bytesPerPixel >= 0 ? prevRow[i - bytesPerPixel] : 0;
159
- if (f === 0)
160
- filtered[i] = val;
161
- else if (f === 1)
162
- filtered[i] = (val - left + 256) & 0xff;
163
- else if (f === 2)
164
- filtered[i] = (val - up + 256) & 0xff;
165
- else if (f === 3)
166
- filtered[i] = (val - Math.floor((left + up) / 2) + 256) & 0xff;
167
- else
168
- filtered[i] = (val - paethPredict(left, up, upLeft) + 256) & 0xff;
169
- }
170
- if (filtered.equals(bestFiltered)) {
171
149
  chosenFilter = f;
172
- break;
173
150
  }
174
151
  }
152
+ const rowBuf = Buffer.alloc(1 + rowBytes);
175
153
  rowBuf[0] = chosenFilter;
176
154
  bestFiltered.copy(rowBuf, 1);
177
155
  outRows.push(rowBuf);
@@ -45,7 +45,7 @@ export async function compressStream(stream, level = 19, onProgress, dict) {
45
45
  };
46
46
  }
47
47
  export async function parallelZstdCompress(payload, level = 19, onProgress, dict) {
48
- const chunkSize = 8 * 1024 * 1024;
48
+ const chunkSize = 32 * 1024 * 1024;
49
49
  // For small payloads (< chunkSize), concatenate and compress as single frame
50
50
  // to avoid multi-chunk overhead (16+ bytes header per chunk boundary).
51
51
  let flat = null;
package/native/bwt.rs CHANGED
@@ -1,56 +1,56 @@
1
- use anyhow::Result;
2
- use libsais::bwt::Bwt;
3
- use libsais::typestate::OwnedBuffer;
4
- use libsais::BwtConstruction;
5
- use rayon::prelude::*;
6
-
7
- pub struct BwtResult {
8
- pub transformed: Vec<u8>,
9
- pub primary_index: u32,
10
- }
11
-
12
- pub fn bwt_encode(data: &[u8]) -> Result<BwtResult> {
13
- let n = data.len();
14
- if n == 0 {
15
- return Ok(BwtResult { transformed: Vec::new(), primary_index: 0 });
16
- }
17
-
18
- let bwt_result = BwtConstruction::for_text(data)
19
- .with_owned_temporary_array_buffer32()
20
- .single_threaded()
21
- .run()
22
- .map_err(|e| anyhow::anyhow!("libsais BWT: {:?}", e))?;
23
-
24
- let primary_index = bwt_result.primary_index() as u32;
25
- let transformed = bwt_result.bwt().to_vec();
26
-
27
- Ok(BwtResult { transformed, primary_index })
28
- }
29
-
30
- pub fn bwt_decode(bwt_data: &[u8], primary_index: u32) -> Result<Vec<u8>> {
31
- if bwt_data.is_empty() {
32
- return Ok(Vec::new());
33
- }
34
-
35
- let bwt_obj: Bwt<'static, u8, OwnedBuffer> =
36
- unsafe { Bwt::from_parts(bwt_data.to_vec(), primary_index as usize) };
37
-
38
- let text = bwt_obj
39
- .unbwt()
40
- .with_owned_temporary_array_buffer32()
41
- .single_threaded()
42
- .run()
43
- .map_err(|e| anyhow::anyhow!("libsais UnBWT: {:?}", e))?;
44
-
45
- Ok(text.as_slice().to_vec())
46
- }
47
-
48
- pub fn bwt_encode_streaming(block_size: usize, data: &[u8]) -> Result<Vec<(BwtResult, usize)>> {
49
- data.par_chunks(block_size)
50
- .enumerate()
51
- .map(|(i, chunk)| {
52
- let result = bwt_encode(chunk)?;
53
- Ok((result, i * block_size))
54
- })
55
- .collect()
56
- }
1
+ use anyhow::Result;
2
+ use libsais::bwt::Bwt;
3
+ use libsais::typestate::OwnedBuffer;
4
+ use libsais::BwtConstruction;
5
+ use rayon::prelude::*;
6
+
7
+ pub struct BwtResult {
8
+ pub transformed: Vec<u8>,
9
+ pub primary_index: u32,
10
+ }
11
+
12
+ pub fn bwt_encode(data: &[u8]) -> Result<BwtResult> {
13
+ let n = data.len();
14
+ if n == 0 {
15
+ return Ok(BwtResult { transformed: Vec::new(), primary_index: 0 });
16
+ }
17
+
18
+ let bwt_result = BwtConstruction::for_text(data)
19
+ .with_owned_temporary_array_buffer32()
20
+ .single_threaded()
21
+ .run()
22
+ .map_err(|e| anyhow::anyhow!("libsais BWT: {:?}", e))?;
23
+
24
+ let primary_index = bwt_result.primary_index() as u32;
25
+ let transformed = bwt_result.bwt().to_vec();
26
+
27
+ Ok(BwtResult { transformed, primary_index })
28
+ }
29
+
30
+ pub fn bwt_decode(bwt_data: &[u8], primary_index: u32) -> Result<Vec<u8>> {
31
+ if bwt_data.is_empty() {
32
+ return Ok(Vec::new());
33
+ }
34
+
35
+ let bwt_obj: Bwt<'static, u8, OwnedBuffer> =
36
+ unsafe { Bwt::from_parts(bwt_data.to_vec(), primary_index as usize) };
37
+
38
+ let text = bwt_obj
39
+ .unbwt()
40
+ .with_owned_temporary_array_buffer32()
41
+ .single_threaded()
42
+ .run()
43
+ .map_err(|e| anyhow::anyhow!("libsais UnBWT: {:?}", e))?;
44
+
45
+ Ok(text.as_slice().to_vec())
46
+ }
47
+
48
+ pub fn bwt_encode_streaming(block_size: usize, data: &[u8]) -> Result<Vec<(BwtResult, usize)>> {
49
+ data.par_chunks(block_size)
50
+ .enumerate()
51
+ .map(|(i, chunk)| {
52
+ let result = bwt_encode(chunk)?;
53
+ Ok((result, i * block_size))
54
+ })
55
+ .collect()
56
+ }