roxify 1.13.8 → 1.14.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/dist/cli.js +23 -21
- package/dist/stub-progress.d.ts +4 -4
- package/dist/stub-progress.js +4 -4
- package/dist/utils/decoder.d.ts +10 -1
- package/dist/utils/decoder.js +111 -7
- package/dist/utils/ecc.js +0 -1
- package/dist/utils/inspection.d.ts +1 -1
- package/dist/utils/inspection.js +2 -2
- package/dist/utils/robust-audio.js +0 -13
- package/dist/utils/robust-image.js +0 -26
- package/package.json +12 -29
- 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/{dist/rox-macos-universal → roxify_native-universal-apple-darwin.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/scripts/postinstall.cjs +23 -2
- package/Cargo.toml +0 -91
- 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/native/archive.rs +0 -220
- package/native/audio.rs +0 -151
- package/native/bench_hybrid.rs +0 -145
- package/native/bwt.rs +0 -56
- package/native/context_mixing.rs +0 -117
- package/native/core.rs +0 -378
- package/native/crypto.rs +0 -209
- package/native/encoder.rs +0 -405
- package/native/hybrid.rs +0 -297
- package/native/image_utils.rs +0 -82
- package/native/io_advice.rs +0 -43
- package/native/io_ntfs_optimized.rs +0 -99
- package/native/lib.rs +0 -480
- package/native/main.rs +0 -842
- package/native/mtf.rs +0 -106
- package/native/packer.rs +0 -604
- package/native/png_chunk_writer.rs +0 -146
- package/native/png_utils.rs +0 -554
- package/native/pool.rs +0 -101
- package/native/progress.rs +0 -142
- package/native/rans.rs +0 -149
- package/native/rans_byte.rs +0 -286
- package/native/reconstitution.rs +0 -623
- package/native/streaming.rs +0 -189
- package/native/streaming_decode.rs +0 -625
- package/native/streaming_encode.rs +0 -684
- package/native/test_small_bwt.rs +0 -31
- package/native/test_stages.rs +0 -70
package/native/image_utils.rs
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
use image::{ImageFormat, DynamicImage, ImageReader};
|
|
2
|
-
use std::io::Cursor;
|
|
3
|
-
|
|
4
|
-
fn load_no_limits(input: &[u8]) -> Result<DynamicImage, String> {
|
|
5
|
-
let mut reader = ImageReader::new(Cursor::new(input))
|
|
6
|
-
.with_guessed_format()
|
|
7
|
-
.map_err(|e| format!("Failed to guess format: {}", e))?;
|
|
8
|
-
reader.no_limits();
|
|
9
|
-
reader.decode().map_err(|e| format!("Failed to load image: {}", e))
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
pub fn sharp_resize(
|
|
13
|
-
input: &[u8],
|
|
14
|
-
width: u32,
|
|
15
|
-
height: u32,
|
|
16
|
-
kernel: &str,
|
|
17
|
-
) -> Result<Vec<u8>, String> {
|
|
18
|
-
let img = load_no_limits(input)?;
|
|
19
|
-
|
|
20
|
-
let filter = match kernel {
|
|
21
|
-
"nearest" => image::imageops::FilterType::Nearest,
|
|
22
|
-
"bilinear" | "linear" => image::imageops::FilterType::Triangle,
|
|
23
|
-
"cubic" | "bicubic" => image::imageops::FilterType::CatmullRom,
|
|
24
|
-
"lanczos" | "lanczos3" => image::imageops::FilterType::Lanczos3,
|
|
25
|
-
_ => image::imageops::FilterType::Nearest,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
let resized = img.resize_exact(width, height, filter);
|
|
29
|
-
|
|
30
|
-
let mut output = Vec::new();
|
|
31
|
-
resized.write_to(&mut Cursor::new(&mut output), ImageFormat::Png)
|
|
32
|
-
.map_err(|e| format!("Failed to encode PNG: {}", e))?;
|
|
33
|
-
|
|
34
|
-
Ok(output)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
pub fn sharp_raw_pixels(input: &[u8]) -> Result<(Vec<u8>, u32, u32), String> {
|
|
38
|
-
let img = load_no_limits(input)?;
|
|
39
|
-
|
|
40
|
-
let rgb = img.to_rgb8();
|
|
41
|
-
let width = rgb.width();
|
|
42
|
-
let height = rgb.height();
|
|
43
|
-
let raw = rgb.into_raw();
|
|
44
|
-
|
|
45
|
-
Ok((raw, width, height))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
pub fn sharp_metadata(input: &[u8]) -> Result<(u32, u32, String), String> {
|
|
49
|
-
let img = load_no_limits(input)?;
|
|
50
|
-
|
|
51
|
-
let width = img.width();
|
|
52
|
-
let height = img.height();
|
|
53
|
-
let format = match img {
|
|
54
|
-
DynamicImage::ImageLuma8(_) => "gray",
|
|
55
|
-
DynamicImage::ImageRgb8(_) => "rgb",
|
|
56
|
-
DynamicImage::ImageRgba8(_) => "rgba",
|
|
57
|
-
_ => "unknown",
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
Ok((width, height, format.to_string()))
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
pub fn rgb_to_png(rgb: &[u8], width: u32, height: u32) -> Result<Vec<u8>, String> {
|
|
64
|
-
use image::codecs::png::{PngEncoder, CompressionType, FilterType};
|
|
65
|
-
use image::ImageEncoder;
|
|
66
|
-
|
|
67
|
-
let mut output = Vec::new();
|
|
68
|
-
// Data is already zstd-compressed, so PNG deflate adds overhead.
|
|
69
|
-
// Use Uncompressed (stored blocks) to avoid wasting CPU on incompressible data.
|
|
70
|
-
let encoder = PngEncoder::new_with_quality(
|
|
71
|
-
&mut output,
|
|
72
|
-
CompressionType::Uncompressed,
|
|
73
|
-
FilterType::NoFilter,
|
|
74
|
-
);
|
|
75
|
-
encoder.write_image(rgb, width, height, image::ExtendedColorType::Rgb8)
|
|
76
|
-
.map_err(|e| format!("Failed to encode PNG: {}", e))?;
|
|
77
|
-
Ok(output)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
pub fn png_to_rgb(png: &[u8]) -> Result<(Vec<u8>, u32, u32), String> {
|
|
81
|
-
sharp_raw_pixels(png)
|
|
82
|
-
}
|
package/native/io_advice.rs
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
use std::fs::File;
|
|
2
|
-
#[cfg(target_os = "linux")]
|
|
3
|
-
use std::os::fd::AsRawFd;
|
|
4
|
-
|
|
5
|
-
pub const INPUT_DROP_GRANULARITY: u64 = 8 * 1024 * 1024;
|
|
6
|
-
|
|
7
|
-
pub fn advise_file_sequential(file: &File) {
|
|
8
|
-
#[cfg(target_os = "linux")]
|
|
9
|
-
unsafe {
|
|
10
|
-
let _ = libc::posix_fadvise(file.as_raw_fd(), 0, 0, libc::POSIX_FADV_SEQUENTIAL);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
#[cfg(not(target_os = "linux"))]
|
|
14
|
-
let _ = file;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
pub fn advise_drop(file: &File, offset: u64, len: u64) {
|
|
18
|
-
if len == 0 {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
#[cfg(target_os = "linux")]
|
|
23
|
-
unsafe {
|
|
24
|
-
let _ = libc::posix_fadvise(
|
|
25
|
-
file.as_raw_fd(),
|
|
26
|
-
offset as libc::off_t,
|
|
27
|
-
len as libc::off_t,
|
|
28
|
-
libc::POSIX_FADV_DONTNEED,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
#[cfg(not(target_os = "linux"))]
|
|
33
|
-
let _ = (file, offset, len);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
pub fn sync_and_drop(file: &File, len: u64) {
|
|
37
|
-
if len < INPUT_DROP_GRANULARITY {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let _ = file.sync_data();
|
|
42
|
-
advise_drop(file, 0, len);
|
|
43
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
use std::fs::{File, OpenOptions};
|
|
2
|
-
use std::io::{BufWriter, Write};
|
|
3
|
-
use std::path::Path;
|
|
4
|
-
|
|
5
|
-
/// Buffer size optimized for NTFS (larger = fewer syscalls)
|
|
6
|
-
#[cfg(windows)]
|
|
7
|
-
const NTFS_WRITE_BUFFER: usize = 4 * 1024 * 1024; // 4MB for Windows/NTFS
|
|
8
|
-
#[cfg(not(windows))]
|
|
9
|
-
const NTFS_WRITE_BUFFER: usize = 64 * 1024; // 64KB for Unix
|
|
10
|
-
|
|
11
|
-
/// Optimized file writer with large buffer for NTFS
|
|
12
|
-
pub struct OptimizedFileWriter {
|
|
13
|
-
writer: BufWriter<File>,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
impl OptimizedFileWriter {
|
|
17
|
-
pub fn create(path: &Path) -> std::io::Result<Self> {
|
|
18
|
-
let file = OpenOptions::new()
|
|
19
|
-
.write(true)
|
|
20
|
-
.create(true)
|
|
21
|
-
.truncate(true)
|
|
22
|
-
.open(path)?;
|
|
23
|
-
|
|
24
|
-
Ok(Self {
|
|
25
|
-
writer: BufWriter::with_capacity(NTFS_WRITE_BUFFER, file),
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
pub fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
|
30
|
-
self.writer.write_all(buf)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
pub fn flush(&mut self) -> std::io::Result<()> {
|
|
34
|
-
self.writer.flush()
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/// Write file with optimized buffering for target filesystem
|
|
39
|
-
pub fn write_file_optimized(path: &Path, content: &[u8]) -> std::io::Result<()> {
|
|
40
|
-
let mut writer = OptimizedFileWriter::create(path)?;
|
|
41
|
-
writer.write_all(content)?;
|
|
42
|
-
writer.flush()?;
|
|
43
|
-
Ok(())
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/// Batch write multiple files - keeps files open for better NTFS performance
|
|
47
|
-
pub fn write_files_batch(
|
|
48
|
-
base_dir: &Path,
|
|
49
|
-
files: &[(String, &[u8])],
|
|
50
|
-
) -> Result<Vec<String>, String> {
|
|
51
|
-
let mut written = Vec::with_capacity(files.len());
|
|
52
|
-
|
|
53
|
-
for (rel_path, content) in files {
|
|
54
|
-
let safe_path = sanitize_path(rel_path);
|
|
55
|
-
let dest = base_dir.join(&safe_path);
|
|
56
|
-
|
|
57
|
-
if let Some(parent) = dest.parent() {
|
|
58
|
-
std::fs::create_dir_all(parent)
|
|
59
|
-
.map_err(|e| format!("Cannot create parent dir {:?}: {}", parent, e))?;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
write_file_optimized(&dest, content)
|
|
63
|
-
.map_err(|e| format!("Cannot write {:?}: {}", dest, e))?;
|
|
64
|
-
|
|
65
|
-
written.push(safe_path.to_string_lossy().to_string());
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
Ok(written)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
fn sanitize_path(path: &str) -> std::path::PathBuf {
|
|
72
|
-
let mut safe = std::path::PathBuf::new();
|
|
73
|
-
for comp in std::path::Path::new(path).components() {
|
|
74
|
-
if let std::path::Component::Normal(osstr) = comp {
|
|
75
|
-
safe.push(osstr);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
safe
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/// Pre-allocate file space on NTFS to reduce fragmentation
|
|
82
|
-
#[cfg(windows)]
|
|
83
|
-
pub fn preallocate_file(path: &Path, size: u64) -> std::io::Result<()> {
|
|
84
|
-
use std::os::windows::fs::FileExt;
|
|
85
|
-
|
|
86
|
-
let file = OpenOptions::new()
|
|
87
|
-
.write(true)
|
|
88
|
-
.create(true)
|
|
89
|
-
.open(path)?;
|
|
90
|
-
|
|
91
|
-
// Pre-allocate space
|
|
92
|
-
file.set_len(size)?;
|
|
93
|
-
Ok(())
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
#[cfg(not(windows))]
|
|
97
|
-
pub fn preallocate_file(_path: &Path, _size: u64) -> std::io::Result<()> {
|
|
98
|
-
Ok(())
|
|
99
|
-
}
|
package/native/lib.rs
DELETED
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
#![allow(dead_code, unused_imports)]
|
|
2
|
-
use napi::bindgen_prelude::*;
|
|
3
|
-
use napi_derive::napi;
|
|
4
|
-
|
|
5
|
-
#[global_allocator]
|
|
6
|
-
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|
7
|
-
|
|
8
|
-
mod core;
|
|
9
|
-
mod rans;
|
|
10
|
-
mod rans_byte;
|
|
11
|
-
mod bwt;
|
|
12
|
-
mod mtf;
|
|
13
|
-
mod context_mixing;
|
|
14
|
-
mod pool;
|
|
15
|
-
mod hybrid;
|
|
16
|
-
mod encoder;
|
|
17
|
-
mod packer;
|
|
18
|
-
mod crypto;
|
|
19
|
-
mod png_utils;
|
|
20
|
-
mod png_chunk_writer;
|
|
21
|
-
mod io_advice;
|
|
22
|
-
mod image_utils;
|
|
23
|
-
mod audio;
|
|
24
|
-
mod progress;
|
|
25
|
-
mod reconstitution;
|
|
26
|
-
mod archive;
|
|
27
|
-
mod streaming;
|
|
28
|
-
|
|
29
|
-
pub use core::*;
|
|
30
|
-
pub use rans::*;
|
|
31
|
-
pub use bwt::*;
|
|
32
|
-
pub use context_mixing::*;
|
|
33
|
-
pub use pool::*;
|
|
34
|
-
pub use hybrid::*;
|
|
35
|
-
|
|
36
|
-
#[napi(object)]
|
|
37
|
-
pub struct ScanResult {
|
|
38
|
-
pub marker_positions: Vec<u32>,
|
|
39
|
-
pub magic_positions: Vec<u32>,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
#[napi(object)]
|
|
43
|
-
pub struct CompressionReport {
|
|
44
|
-
pub original_size: f64,
|
|
45
|
-
pub compressed_size: f64,
|
|
46
|
-
pub ratio: f64,
|
|
47
|
-
pub entropy_bits: f64,
|
|
48
|
-
pub blocks_count: u32,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
#[cfg(not(test))]
|
|
52
|
-
#[napi]
|
|
53
|
-
pub fn scan_pixels(buffer: Buffer, channels: u32, marker_bytes: Option<Buffer>) -> Result<ScanResult> {
|
|
54
|
-
let slice: &[u8] = &buffer;
|
|
55
|
-
let markers_slice: Option<&[u8]> = marker_bytes.as_ref().map(|b| &**b);
|
|
56
|
-
let res = core::scan_pixels_bytes(slice, channels as usize, markers_slice);
|
|
57
|
-
Ok(ScanResult { marker_positions: res.marker_positions, magic_positions: res.magic_positions })
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
#[cfg(not(test))]
|
|
61
|
-
#[napi]
|
|
62
|
-
pub fn native_crc32(buffer: Buffer) -> u32 {
|
|
63
|
-
core::crc32_bytes(&buffer)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
#[cfg(not(test))]
|
|
67
|
-
#[napi]
|
|
68
|
-
pub fn native_adler32(buffer: Buffer) -> u32 {
|
|
69
|
-
core::adler32_bytes(&buffer)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
#[cfg(not(test))]
|
|
73
|
-
#[napi]
|
|
74
|
-
pub fn native_delta_encode(buffer: Buffer) -> Buffer {
|
|
75
|
-
core::delta_encode_bytes(&buffer).into()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
#[cfg(not(test))]
|
|
79
|
-
#[napi]
|
|
80
|
-
pub fn native_delta_decode(buffer: Buffer) -> Buffer {
|
|
81
|
-
core::delta_decode_bytes(&buffer).into()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
#[cfg(not(test))]
|
|
85
|
-
#[napi]
|
|
86
|
-
pub fn native_zstd_compress(buffer: Buffer, level: i32) -> Result<Buffer> {
|
|
87
|
-
core::zstd_compress_bytes(&buffer, level, None).map(Buffer::from).map_err(|e| Error::from_reason(e))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
#[cfg(not(test))]
|
|
91
|
-
#[napi]
|
|
92
|
-
pub fn native_zstd_compress_with_dict(buffer: Buffer, level: i32, dict: Buffer) -> Result<Buffer> {
|
|
93
|
-
let dict_slice: &[u8] = &dict;
|
|
94
|
-
core::zstd_compress_bytes(&buffer, level, Some(dict_slice)).map(Buffer::from).map_err(|e| Error::from_reason(e))
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
#[cfg(not(test))]
|
|
98
|
-
#[napi]
|
|
99
|
-
pub fn native_zstd_decompress(buffer: Buffer) -> Result<Buffer> {
|
|
100
|
-
core::zstd_decompress_bytes(&buffer, None).map(Buffer::from).map_err(|e| Error::from_reason(e))
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
#[cfg(not(test))]
|
|
104
|
-
#[napi]
|
|
105
|
-
pub fn native_zstd_decompress_with_dict(buffer: Buffer, dict: Buffer) -> Result<Buffer> {
|
|
106
|
-
let dict_slice: &[u8] = &dict;
|
|
107
|
-
core::zstd_decompress_bytes(&buffer, Some(dict_slice)).map(Buffer::from).map_err(|e| Error::from_reason(e))
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
#[cfg(not(test))]
|
|
111
|
-
#[napi]
|
|
112
|
-
pub fn bwt_transform(buffer: Buffer) -> Result<Buffer> {
|
|
113
|
-
match bwt::bwt_encode(&buffer) {
|
|
114
|
-
Ok(result) => {
|
|
115
|
-
let mut output = Vec::with_capacity(4 + result.transformed.len());
|
|
116
|
-
output.extend_from_slice(&result.primary_index.to_le_bytes());
|
|
117
|
-
output.extend_from_slice(&result.transformed);
|
|
118
|
-
Ok(output.into())
|
|
119
|
-
}
|
|
120
|
-
Err(e) => Err(Error::from_reason(e.to_string())),
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
#[cfg(not(test))]
|
|
125
|
-
#[napi]
|
|
126
|
-
pub fn entropy_estimate(buffer: Buffer) -> f32 {
|
|
127
|
-
context_mixing::analyze_entropy(&buffer)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
#[cfg(not(test))]
|
|
131
|
-
#[napi]
|
|
132
|
-
pub fn hybrid_compress(buffer: Buffer) -> Result<Buffer> {
|
|
133
|
-
match hybrid::compress_high_performance(&buffer) {
|
|
134
|
-
Ok((compressed, _stats)) => Ok(Buffer::from(compressed)),
|
|
135
|
-
Err(e) => Err(Error::from_reason(e.to_string())),
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
#[cfg(not(test))]
|
|
140
|
-
#[napi]
|
|
141
|
-
pub fn hybrid_decompress(buffer: Buffer) -> Result<Buffer> {
|
|
142
|
-
hybrid::decompress_high_performance(&buffer)
|
|
143
|
-
.map(Buffer::from)
|
|
144
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
#[cfg(not(test))]
|
|
148
|
-
#[napi]
|
|
149
|
-
pub fn get_compression_stats(buffer: Buffer) -> CompressionReport {
|
|
150
|
-
match hybrid::compress_high_performance(&buffer) {
|
|
151
|
-
Ok((_compressed, stats)) => CompressionReport {
|
|
152
|
-
original_size: stats.original_size as f64,
|
|
153
|
-
compressed_size: stats.compressed_size as f64,
|
|
154
|
-
ratio: stats.ratio,
|
|
155
|
-
entropy_bits: stats.entropy_bits as f64,
|
|
156
|
-
blocks_count: stats.blocks_count as u32,
|
|
157
|
-
},
|
|
158
|
-
Err(_) => CompressionReport {
|
|
159
|
-
original_size: buffer.len() as f64,
|
|
160
|
-
compressed_size: 0.0,
|
|
161
|
-
ratio: 0.0,
|
|
162
|
-
entropy_bits: 0.0,
|
|
163
|
-
blocks_count: 0,
|
|
164
|
-
},
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
#[cfg(test)]
|
|
169
|
-
mod tests {
|
|
170
|
-
use super::*;
|
|
171
|
-
|
|
172
|
-
#[test]
|
|
173
|
-
fn test_scan_magic() {
|
|
174
|
-
let data = b"xxxxROX1yyyyROX1".to_vec();
|
|
175
|
-
let res = core::scan_pixels_bytes(&data, 3, None);
|
|
176
|
-
assert_eq!(res.magic_positions.len(), 2);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
#[test]
|
|
180
|
-
fn test_bwt() {
|
|
181
|
-
let data = b"banana".to_vec();
|
|
182
|
-
let enc = bwt::bwt_encode(&data).unwrap();
|
|
183
|
-
assert!(!enc.transformed.is_empty());
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
#[test]
|
|
187
|
-
fn test_entropy() {
|
|
188
|
-
let data = b"aaaaabbbcc";
|
|
189
|
-
let entropy = context_mixing::analyze_entropy(data);
|
|
190
|
-
assert!(entropy > 0.0 && entropy < 8.0);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
#[cfg(not(test))]
|
|
195
|
-
#[napi]
|
|
196
|
-
pub fn native_encode_png(buffer: Buffer, compression_level: i32) -> Result<Buffer> {
|
|
197
|
-
encoder::encode_to_png(&buffer, compression_level)
|
|
198
|
-
.map(Buffer::from)
|
|
199
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
#[cfg(not(test))]
|
|
203
|
-
#[napi]
|
|
204
|
-
pub fn native_encode_png_raw(buffer: Buffer, compression_level: i32) -> Result<Buffer> {
|
|
205
|
-
encoder::encode_to_png_raw(&buffer, compression_level)
|
|
206
|
-
.map(Buffer::from)
|
|
207
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
#[cfg(not(test))]
|
|
211
|
-
#[napi]
|
|
212
|
-
pub fn native_encode_png_with_name_and_filelist(
|
|
213
|
-
buffer: Buffer,
|
|
214
|
-
compression_level: i32,
|
|
215
|
-
name: Option<String>,
|
|
216
|
-
file_list_json: Option<String>,
|
|
217
|
-
) -> Result<Buffer> {
|
|
218
|
-
encoder::encode_to_png_with_name_and_filelist(
|
|
219
|
-
&buffer,
|
|
220
|
-
compression_level,
|
|
221
|
-
name.as_deref(),
|
|
222
|
-
file_list_json.as_deref(),
|
|
223
|
-
)
|
|
224
|
-
.map(Buffer::from)
|
|
225
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
#[cfg(not(test))]
|
|
229
|
-
#[napi]
|
|
230
|
-
pub fn native_encode_png_with_encryption_name_and_filelist(
|
|
231
|
-
buffer: Buffer,
|
|
232
|
-
compression_level: i32,
|
|
233
|
-
passphrase: Option<String>,
|
|
234
|
-
encrypt_type: Option<String>,
|
|
235
|
-
name: Option<String>,
|
|
236
|
-
file_list_json: Option<String>,
|
|
237
|
-
) -> Result<Buffer> {
|
|
238
|
-
encoder::encode_to_png_with_encryption_name_and_filelist(
|
|
239
|
-
&buffer,
|
|
240
|
-
compression_level,
|
|
241
|
-
passphrase.as_deref(),
|
|
242
|
-
encrypt_type.as_deref(),
|
|
243
|
-
name.as_deref(),
|
|
244
|
-
file_list_json.as_deref(),
|
|
245
|
-
)
|
|
246
|
-
.map(Buffer::from)
|
|
247
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
#[napi(object)]
|
|
251
|
-
pub struct PngChunkData {
|
|
252
|
-
pub name: String,
|
|
253
|
-
pub data: Buffer,
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
#[cfg(not(test))]
|
|
257
|
-
#[napi]
|
|
258
|
-
pub fn extract_png_chunks(png_buffer: Buffer) -> Result<Vec<PngChunkData>> {
|
|
259
|
-
let chunks = png_utils::extract_png_chunks(&png_buffer)
|
|
260
|
-
.map_err(|e| Error::from_reason(e))?;
|
|
261
|
-
|
|
262
|
-
Ok(chunks.into_iter().map(|c| PngChunkData {
|
|
263
|
-
name: c.name,
|
|
264
|
-
data: c.data.into(),
|
|
265
|
-
}).collect())
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
#[cfg(not(test))]
|
|
269
|
-
#[napi]
|
|
270
|
-
pub fn encode_png_chunks(chunks: Vec<PngChunkData>) -> Result<Buffer> {
|
|
271
|
-
let native_chunks: Vec<png_utils::PngChunk> = chunks.into_iter()
|
|
272
|
-
.map(|c| png_utils::PngChunk {
|
|
273
|
-
name: c.name,
|
|
274
|
-
data: c.data.to_vec(),
|
|
275
|
-
})
|
|
276
|
-
.collect();
|
|
277
|
-
|
|
278
|
-
png_utils::encode_png_chunks(&native_chunks)
|
|
279
|
-
.map(Buffer::from)
|
|
280
|
-
.map_err(|e| Error::from_reason(e))
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
#[napi(object)]
|
|
284
|
-
pub struct PngMetadata {
|
|
285
|
-
pub width: u32,
|
|
286
|
-
pub height: u32,
|
|
287
|
-
pub bit_depth: u32,
|
|
288
|
-
pub color_type: u32,
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
#[cfg(not(test))]
|
|
292
|
-
#[napi]
|
|
293
|
-
pub fn get_png_metadata(png_buffer: Buffer) -> Result<PngMetadata> {
|
|
294
|
-
let (width, height, bit_depth, color_type) = png_utils::get_png_metadata(&png_buffer)
|
|
295
|
-
.map_err(|e| Error::from_reason(e))?;
|
|
296
|
-
|
|
297
|
-
Ok(PngMetadata {
|
|
298
|
-
width,
|
|
299
|
-
height,
|
|
300
|
-
bit_depth: bit_depth as u32,
|
|
301
|
-
color_type: color_type as u32,
|
|
302
|
-
})
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
#[napi(object)]
|
|
306
|
-
pub struct SharpMetadata {
|
|
307
|
-
pub width: u32,
|
|
308
|
-
pub height: u32,
|
|
309
|
-
pub format: String,
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
#[cfg(not(test))]
|
|
313
|
-
#[napi]
|
|
314
|
-
pub fn sharp_resize_image(
|
|
315
|
-
input_buffer: Buffer,
|
|
316
|
-
width: u32,
|
|
317
|
-
height: u32,
|
|
318
|
-
kernel: String,
|
|
319
|
-
) -> Result<Buffer> {
|
|
320
|
-
image_utils::sharp_resize(&input_buffer, width, height, &kernel)
|
|
321
|
-
.map(Buffer::from)
|
|
322
|
-
.map_err(|e| Error::from_reason(e))
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
#[cfg(not(test))]
|
|
326
|
-
#[napi]
|
|
327
|
-
pub fn sharp_raw_pixels(input_buffer: Buffer) -> Result<Buffer> {
|
|
328
|
-
let (pixels, _w, _h) = image_utils::sharp_raw_pixels(&input_buffer)
|
|
329
|
-
.map_err(|e| Error::from_reason(e))?;
|
|
330
|
-
Ok(pixels.into())
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
#[napi(object)]
|
|
334
|
-
pub struct RawPixelsWithDimensions {
|
|
335
|
-
pub pixels: Buffer,
|
|
336
|
-
pub width: u32,
|
|
337
|
-
pub height: u32,
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
#[cfg(not(test))]
|
|
341
|
-
#[napi]
|
|
342
|
-
pub fn sharp_to_raw(input_buffer: Buffer) -> Result<RawPixelsWithDimensions> {
|
|
343
|
-
let (pixels, width, height) = image_utils::sharp_raw_pixels(&input_buffer)
|
|
344
|
-
.map_err(|e| Error::from_reason(e))?;
|
|
345
|
-
Ok(RawPixelsWithDimensions { pixels: pixels.into(), width, height })
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
#[cfg(not(test))]
|
|
349
|
-
#[napi]
|
|
350
|
-
pub fn sharp_metadata(input_buffer: Buffer) -> Result<SharpMetadata> {
|
|
351
|
-
let (width, height, format) = image_utils::sharp_metadata(&input_buffer)
|
|
352
|
-
.map_err(|e| Error::from_reason(e))?;
|
|
353
|
-
Ok(SharpMetadata { width, height, format })
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
#[cfg(not(test))]
|
|
357
|
-
#[napi]
|
|
358
|
-
pub fn rgb_to_png(rgb_buffer: Buffer, width: u32, height: u32) -> Result<Buffer> {
|
|
359
|
-
image_utils::rgb_to_png(&rgb_buffer, width, height)
|
|
360
|
-
.map(Buffer::from)
|
|
361
|
-
.map_err(|e| Error::from_reason(e))
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
#[cfg(not(test))]
|
|
365
|
-
#[napi]
|
|
366
|
-
pub fn png_to_rgb(png_buffer: Buffer) -> Result<RawPixelsWithDimensions> {
|
|
367
|
-
let (pixels, width, height) = image_utils::png_to_rgb(&png_buffer)
|
|
368
|
-
.map_err(|e| Error::from_reason(e))?;
|
|
369
|
-
Ok(RawPixelsWithDimensions { pixels: pixels.into(), width, height })
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
#[cfg(not(test))]
|
|
373
|
-
#[napi]
|
|
374
|
-
pub fn crop_and_reconstitute(png_buffer: Buffer) -> Result<Buffer> {
|
|
375
|
-
reconstitution::crop_and_reconstitute(&png_buffer)
|
|
376
|
-
.map(Buffer::from)
|
|
377
|
-
.map_err(|e| Error::from_reason(e))
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
#[cfg(not(test))]
|
|
381
|
-
#[napi]
|
|
382
|
-
pub fn unstretch_nn(png_buffer: Buffer) -> Result<Buffer> {
|
|
383
|
-
reconstitution::unstretch_nn(&png_buffer)
|
|
384
|
-
.map(Buffer::from)
|
|
385
|
-
.map_err(|e| Error::from_reason(e))
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
#[cfg(not(test))]
|
|
389
|
-
#[napi]
|
|
390
|
-
pub fn extract_payload_from_png(png_buffer: Buffer) -> Result<Buffer> {
|
|
391
|
-
png_utils::extract_payload_from_png(&png_buffer)
|
|
392
|
-
.map(Buffer::from)
|
|
393
|
-
.map_err(|e| Error::from_reason(e))
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
#[cfg(not(test))]
|
|
397
|
-
#[napi]
|
|
398
|
-
pub fn extract_file_list_from_pixels(png_buffer: Buffer) -> Result<String> {
|
|
399
|
-
png_utils::extract_file_list_from_pixels(&png_buffer)
|
|
400
|
-
.map_err(|e| Error::from_reason(e))
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// ─── WAV container NAPI exports ──────────────────────────────────────────────
|
|
404
|
-
|
|
405
|
-
#[cfg(not(test))]
|
|
406
|
-
#[napi]
|
|
407
|
-
pub fn native_encode_wav(buffer: Buffer, compression_level: i32) -> Result<Buffer> {
|
|
408
|
-
encoder::encode_to_wav(&buffer, compression_level)
|
|
409
|
-
.map(Buffer::from)
|
|
410
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
#[cfg(not(test))]
|
|
414
|
-
#[napi]
|
|
415
|
-
pub fn native_encode_wav_with_name_and_filelist(
|
|
416
|
-
buffer: Buffer,
|
|
417
|
-
compression_level: i32,
|
|
418
|
-
name: Option<String>,
|
|
419
|
-
file_list_json: Option<String>,
|
|
420
|
-
) -> Result<Buffer> {
|
|
421
|
-
encoder::encode_to_wav_with_name_and_filelist(
|
|
422
|
-
&buffer,
|
|
423
|
-
compression_level,
|
|
424
|
-
name.as_deref(),
|
|
425
|
-
file_list_json.as_deref(),
|
|
426
|
-
)
|
|
427
|
-
.map(Buffer::from)
|
|
428
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
#[cfg(not(test))]
|
|
432
|
-
#[napi]
|
|
433
|
-
pub fn native_encode_wav_with_encryption_name_and_filelist(
|
|
434
|
-
buffer: Buffer,
|
|
435
|
-
compression_level: i32,
|
|
436
|
-
passphrase: Option<String>,
|
|
437
|
-
encrypt_type: Option<String>,
|
|
438
|
-
name: Option<String>,
|
|
439
|
-
file_list_json: Option<String>,
|
|
440
|
-
) -> Result<Buffer> {
|
|
441
|
-
encoder::encode_to_wav_with_encryption_name_and_filelist(
|
|
442
|
-
&buffer,
|
|
443
|
-
compression_level,
|
|
444
|
-
passphrase.as_deref(),
|
|
445
|
-
encrypt_type.as_deref(),
|
|
446
|
-
name.as_deref(),
|
|
447
|
-
file_list_json.as_deref(),
|
|
448
|
-
)
|
|
449
|
-
.map(Buffer::from)
|
|
450
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
#[cfg(not(test))]
|
|
454
|
-
#[napi]
|
|
455
|
-
pub fn native_decode_wav_payload(wav_buffer: Buffer) -> Result<Buffer> {
|
|
456
|
-
encoder::decode_wav_payload(&wav_buffer)
|
|
457
|
-
.map(Buffer::from)
|
|
458
|
-
.map_err(|e| Error::from_reason(e.to_string()))
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
#[cfg(not(test))]
|
|
462
|
-
#[napi]
|
|
463
|
-
pub fn native_bytes_to_wav(buffer: Buffer) -> Buffer {
|
|
464
|
-
audio::bytes_to_wav(&buffer).into()
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
#[cfg(not(test))]
|
|
468
|
-
#[napi]
|
|
469
|
-
pub fn native_wav_to_bytes(wav_buffer: Buffer) -> Result<Buffer> {
|
|
470
|
-
audio::wav_to_bytes(&wav_buffer)
|
|
471
|
-
.map(Buffer::from)
|
|
472
|
-
.map_err(|e| Error::from_reason(e))
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
#[cfg(not(test))]
|
|
476
|
-
#[napi]
|
|
477
|
-
pub fn native_is_wav(buffer: Buffer) -> bool {
|
|
478
|
-
audio::is_wav(&buffer)
|
|
479
|
-
}
|
|
480
|
-
|