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.
- package/Cargo.toml +98 -98
- package/dist/cli.js +48 -48
- package/dist/pack.js +1 -1
- package/dist/rox-macos-universal +0 -0
- package/dist/roxify_native +0 -0
- package/dist/roxify_native-macos-arm64 +0 -0
- package/dist/roxify_native-macos-x64 +0 -0
- package/dist/roxify_native.exe +0 -0
- package/dist/utils/decoder.js +59 -7
- package/dist/utils/encoder.js +12 -23
- package/dist/utils/optimization.js +2 -24
- package/dist/utils/zstd.js +1 -1
- package/native/bwt.rs +56 -56
- package/native/context_mixing.rs +117 -117
- package/native/core.rs +423 -382
- package/native/encoder.rs +635 -629
- package/native/gpu.rs +116 -116
- package/native/hybrid.rs +287 -287
- package/native/lib.rs +489 -489
- package/native/main.rs +527 -534
- package/native/mtf.rs +106 -106
- package/native/packer.rs +447 -447
- package/native/png_utils.rs +538 -538
- package/native/rans_byte.rs +286 -286
- package/native/streaming.rs +212 -212
- package/native/streaming_encode.rs +13 -5
- package/native/test_small_bwt.rs +31 -31
- package/package.json +114 -114
- package/roxify_native-aarch64-apple-darwin.node +0 -0
- package/roxify_native-aarch64-pc-windows-msvc.node +0 -0
- package/roxify_native-aarch64-unknown-linux-gnu.node +0 -0
- package/roxify_native-i686-pc-windows-msvc.node +0 -0
- package/roxify_native-i686-unknown-linux-gnu.node +0 -0
- package/roxify_native-x86_64-apple-darwin.node +0 -0
- package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
- package/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
- 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.
|
|
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 =
|
|
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
|
package/dist/roxify_native
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/roxify_native.exe
CHANGED
|
Binary file
|
package/dist/utils/decoder.js
CHANGED
|
@@ -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
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
pixelBytes
|
|
870
|
-
|
|
871
|
-
|
|
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');
|
package/dist/utils/encoder.js
CHANGED
|
@@ -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 =
|
|
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);
|
package/dist/utils/zstd.js
CHANGED
|
@@ -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 =
|
|
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
|
+
}
|