roxify 1.13.2 → 1.13.4
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 +3 -1
- package/README.md +17 -13
- package/dist/cli.js +14 -19
- 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/rust-cli-wrapper.d.ts +3 -2
- package/dist/utils/rust-cli-wrapper.js +26 -6
- package/native/core.rs +3 -48
- package/native/crypto.rs +5 -0
- package/native/encoder.rs +10 -228
- package/native/lib.rs +54 -39
- package/native/main.rs +53 -21
- package/native/packer.rs +57 -6
- package/native/streaming_decode.rs +141 -7
- package/native/streaming_encode.rs +284 -112
- package/package.json +1 -1
- 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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
use std::io::{Write, BufWriter, Read};
|
|
2
2
|
use std::fs::File;
|
|
3
|
-
use std::path::Path;
|
|
3
|
+
use std::path::{Path, PathBuf};
|
|
4
|
+
use rayon::prelude::*;
|
|
5
|
+
use serde::Serialize;
|
|
4
6
|
use walkdir::WalkDir;
|
|
5
|
-
use tar::{Builder, Header};
|
|
6
7
|
|
|
7
8
|
const PNG_HEADER: &[u8] = &[137, 80, 78, 71, 13, 10, 26, 10];
|
|
8
9
|
const PIXEL_MAGIC: &[u8] = b"PXL1";
|
|
@@ -10,9 +11,35 @@ const MARKER_START: [(u8, u8, u8); 3] = [(255, 0, 0), (0, 255, 0), (0, 0, 255)];
|
|
|
10
11
|
const MARKER_END: [(u8, u8, u8); 3] = [(0, 0, 255), (0, 255, 0), (255, 0, 0)];
|
|
11
12
|
const MARKER_ZSTD: (u8, u8, u8) = (0, 255, 0);
|
|
12
13
|
const MAGIC: &[u8] = b"ROX1";
|
|
14
|
+
const PACK_MAGIC: u32 = 0x524f5850;
|
|
15
|
+
|
|
16
|
+
const MIN_ZST_CAPACITY: usize = 16 * 1024 * 1024;
|
|
17
|
+
const MB: u64 = 1024 * 1024;
|
|
18
|
+
const MAX_FILE_BUFFER_CAPACITY: usize = 4 * 1024 * 1024;
|
|
19
|
+
const PARALLEL_IO_FILE_THRESHOLD: u64 = MB;
|
|
20
|
+
const PARALLEL_IO_BATCH_BYTES: u64 = 128 * MB;
|
|
21
|
+
const PARALLEL_IO_BATCH_FILES: usize = 512;
|
|
22
|
+
const PARALLEL_IO_MIN_FILES: usize = 8;
|
|
13
23
|
|
|
14
24
|
pub type ProgressCallback = Box<dyn Fn(u64, u64, &str) + Send>;
|
|
15
25
|
|
|
26
|
+
struct DirectoryFile {
|
|
27
|
+
path: PathBuf,
|
|
28
|
+
rel_path: String,
|
|
29
|
+
size: u64,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#[derive(Serialize)]
|
|
33
|
+
struct FileListEntry {
|
|
34
|
+
name: String,
|
|
35
|
+
size: u64,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
struct CollectedDirectory {
|
|
39
|
+
entries: Vec<DirectoryFile>,
|
|
40
|
+
total_bytes: u64,
|
|
41
|
+
}
|
|
42
|
+
|
|
16
43
|
pub fn encode_dir_to_png(
|
|
17
44
|
dir_path: &Path,
|
|
18
45
|
output_path: &Path,
|
|
@@ -42,21 +69,12 @@ pub fn encode_dir_to_png_encrypted_with_progress(
|
|
|
42
69
|
encrypt_type: Option<&str>,
|
|
43
70
|
progress: Option<ProgressCallback>,
|
|
44
71
|
) -> anyhow::Result<()> {
|
|
45
|
-
let
|
|
72
|
+
let (zst_buf, file_list_json) = compress_dir_to_zst_mem(dir_path, compression_level, &progress)?;
|
|
46
73
|
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
cb(90, 100, "writing_png");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let file_list_json = serde_json::to_string(&file_list)?;
|
|
54
|
-
|
|
55
|
-
let result = write_png_from_zst(
|
|
56
|
-
&tmp_zst, output_path, name, Some(&file_list_json),
|
|
57
|
-
passphrase, encrypt_type,
|
|
74
|
+
let result = write_png_from_zst_mem(
|
|
75
|
+
zst_buf, output_path, name, Some(&file_list_json),
|
|
76
|
+
passphrase, encrypt_type, &progress,
|
|
58
77
|
);
|
|
59
|
-
let _ = std::fs::remove_file(&tmp_zst);
|
|
60
78
|
|
|
61
79
|
if let Some(ref cb) = progress {
|
|
62
80
|
cb(100, 100, "done");
|
|
@@ -65,31 +83,23 @@ pub fn encode_dir_to_png_encrypted_with_progress(
|
|
|
65
83
|
result
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
fn
|
|
86
|
+
fn compress_dir_to_zst_mem(
|
|
69
87
|
dir_path: &Path,
|
|
70
|
-
zst_path: &Path,
|
|
71
88
|
compression_level: i32,
|
|
72
89
|
progress: &Option<ProgressCallback>,
|
|
73
|
-
) -> anyhow::Result<Vec<
|
|
74
|
-
let
|
|
75
|
-
|
|
76
|
-
let entries
|
|
77
|
-
.follow_links(false)
|
|
78
|
-
.into_iter()
|
|
79
|
-
.filter_map(|e| e.ok())
|
|
80
|
-
.filter(|e| e.file_type().is_file())
|
|
81
|
-
.collect();
|
|
82
|
-
|
|
83
|
-
let total_files = entries.len() as u64;
|
|
84
|
-
|
|
85
|
-
let zst_file = File::create(zst_path)?;
|
|
86
|
-
let buf_writer = BufWriter::with_capacity(16 * 1024 * 1024, zst_file);
|
|
90
|
+
) -> anyhow::Result<(Vec<u8>, String)> {
|
|
91
|
+
let collected = collect_directory_files(dir_path);
|
|
92
|
+
let total_bytes = collected.total_bytes;
|
|
93
|
+
let entries = collected.entries;
|
|
87
94
|
|
|
88
95
|
let actual_level = compression_level.min(3);
|
|
89
|
-
let mut encoder = zstd::stream::Encoder::new(
|
|
96
|
+
let mut encoder = zstd::stream::Encoder::new(
|
|
97
|
+
Vec::with_capacity(estimate_zst_capacity(total_bytes)),
|
|
98
|
+
actual_level,
|
|
99
|
+
)
|
|
90
100
|
.map_err(|e| anyhow::anyhow!("zstd init: {}", e))?;
|
|
91
101
|
|
|
92
|
-
let threads =
|
|
102
|
+
let threads = select_zstd_threads(total_bytes);
|
|
93
103
|
if threads > 1 {
|
|
94
104
|
let _ = encoder.multithread(threads);
|
|
95
105
|
}
|
|
@@ -97,62 +107,233 @@ fn compress_dir_to_zst(
|
|
|
97
107
|
let _ = encoder.window_log(30);
|
|
98
108
|
|
|
99
109
|
encoder.write_all(MAGIC)?;
|
|
110
|
+
encoder.write_all(&PACK_MAGIC.to_be_bytes())?;
|
|
111
|
+
encoder.write_all(&(entries.len() as u32).to_be_bytes())?;
|
|
112
|
+
|
|
113
|
+
let mut file_list = Vec::with_capacity(entries.len());
|
|
114
|
+
let mut bytes_processed: u64 = 0;
|
|
115
|
+
let mut last_pct: u64 = 0;
|
|
116
|
+
let mut entry_index = 0usize;
|
|
117
|
+
while entry_index < entries.len() {
|
|
118
|
+
let batch_end = select_parallel_batch_end(&entries, entry_index);
|
|
119
|
+
if batch_end > entry_index + 1 {
|
|
120
|
+
let loaded = load_small_file_batch(&entries[entry_index..batch_end])?;
|
|
121
|
+
for (entry, maybe_bytes) in entries[entry_index..batch_end].iter().zip(loaded.into_iter()) {
|
|
122
|
+
let Some(bytes) = maybe_bytes else {
|
|
123
|
+
continue;
|
|
124
|
+
};
|
|
100
125
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
};
|
|
113
|
-
let size = metadata.len();
|
|
114
|
-
|
|
115
|
-
let mut header = Header::new_gnu();
|
|
116
|
-
header.set_size(size);
|
|
117
|
-
header.set_mode(0o644);
|
|
118
|
-
header.set_cksum();
|
|
119
|
-
|
|
120
|
-
let file = match File::open(full) {
|
|
121
|
-
Ok(f) => f,
|
|
122
|
-
Err(_) => continue,
|
|
123
|
-
};
|
|
124
|
-
let buf_reader = std::io::BufReader::with_capacity(
|
|
125
|
-
(size as usize).min(4 * 1024 * 1024).max(8192),
|
|
126
|
-
file,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
tar_builder.append_data(&mut header, &rel_str, buf_reader)
|
|
130
|
-
.map_err(|e| anyhow::anyhow!("tar append {}: {}", rel_str, e))?;
|
|
131
|
-
|
|
132
|
-
file_list.push(serde_json::json!({"name": rel_str, "size": size}));
|
|
133
|
-
|
|
134
|
-
if let Some(ref cb) = progress {
|
|
135
|
-
let pct = ((idx as u64 + 1) * 85 / total_files.max(1)).min(85);
|
|
136
|
-
cb(pct, 100, "compressing");
|
|
126
|
+
write_pack_entry_header(&mut encoder, &entry.rel_path, entry.size)?;
|
|
127
|
+
encoder.write_all(&bytes)
|
|
128
|
+
.map_err(|e| anyhow::anyhow!("pack write {}: {}", entry.rel_path, e))?;
|
|
129
|
+
|
|
130
|
+
file_list.push(FileListEntry {
|
|
131
|
+
name: entry.rel_path.clone(),
|
|
132
|
+
size: entry.size,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
bytes_processed += entry.size;
|
|
136
|
+
report_compress_progress(progress, total_bytes, bytes_processed, &mut last_pct);
|
|
137
137
|
}
|
|
138
|
+
entry_index = batch_end;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let entry = &entries[entry_index];
|
|
143
|
+
if write_directory_entry(&mut encoder, entry)? {
|
|
144
|
+
file_list.push(FileListEntry {
|
|
145
|
+
name: entry.rel_path.clone(),
|
|
146
|
+
size: entry.size,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
bytes_processed += entry.size;
|
|
150
|
+
report_compress_progress(progress, total_bytes, bytes_processed, &mut last_pct);
|
|
138
151
|
}
|
|
139
|
-
|
|
152
|
+
entry_index += 1;
|
|
140
153
|
}
|
|
141
154
|
|
|
142
|
-
encoder.finish().map_err(|e| anyhow::anyhow!("zstd finish: {}", e))?;
|
|
155
|
+
let zst_buf = encoder.finish().map_err(|e| anyhow::anyhow!("zstd finish: {}", e))?;
|
|
156
|
+
let file_list_json = serde_json::to_string(&file_list)?;
|
|
143
157
|
|
|
144
|
-
Ok(
|
|
158
|
+
Ok((zst_buf, file_list_json))
|
|
145
159
|
}
|
|
146
160
|
|
|
147
|
-
fn
|
|
148
|
-
|
|
161
|
+
fn write_pack_entry_header<W: Write>(writer: &mut W, rel_path: &str, size: u64) -> anyhow::Result<()> {
|
|
162
|
+
let name_bytes = rel_path.as_bytes();
|
|
163
|
+
let name_len = u16::try_from(name_bytes.len())
|
|
164
|
+
.map_err(|_| anyhow::anyhow!("path too long for pack entry: {}", rel_path))?;
|
|
165
|
+
writer.write_all(&name_len.to_be_bytes())?;
|
|
166
|
+
writer.write_all(name_bytes)?;
|
|
167
|
+
writer.write_all(&size.to_be_bytes())?;
|
|
168
|
+
Ok(())
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fn write_directory_entry<W: Write>(writer: &mut W, entry: &DirectoryFile) -> anyhow::Result<bool> {
|
|
172
|
+
let file = match File::open(&entry.path) {
|
|
173
|
+
Ok(file) => file,
|
|
174
|
+
Err(_) => return Ok(false),
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
write_pack_entry_header(writer, &entry.rel_path, entry.size)?;
|
|
178
|
+
|
|
179
|
+
let mut buf_reader = std::io::BufReader::with_capacity(file_buffer_capacity(entry.size), file);
|
|
180
|
+
std::io::copy(&mut buf_reader, writer)
|
|
181
|
+
.map_err(|e| anyhow::anyhow!("pack write {}: {}", entry.rel_path, e))?;
|
|
182
|
+
|
|
183
|
+
Ok(true)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fn load_small_file_batch(entries: &[DirectoryFile]) -> anyhow::Result<Vec<Option<Vec<u8>>>> {
|
|
187
|
+
entries.par_iter().map(load_directory_entry_bytes).collect()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
fn load_directory_entry_bytes(entry: &DirectoryFile) -> anyhow::Result<Option<Vec<u8>>> {
|
|
191
|
+
let mut file = match File::open(&entry.path) {
|
|
192
|
+
Ok(file) => file,
|
|
193
|
+
Err(_) => return Ok(None),
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
let reserve = usize::try_from(entry.size.min(PARALLEL_IO_BATCH_BYTES)).unwrap_or(MAX_FILE_BUFFER_CAPACITY);
|
|
197
|
+
let mut bytes = Vec::with_capacity(reserve.max(8192));
|
|
198
|
+
file.read_to_end(&mut bytes)
|
|
199
|
+
.map_err(|e| anyhow::anyhow!("pack read {}: {}", entry.rel_path, e))?;
|
|
200
|
+
|
|
201
|
+
Ok(Some(bytes))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fn select_parallel_batch_end(entries: &[DirectoryFile], start: usize) -> usize {
|
|
205
|
+
let Some(first) = entries.get(start) else {
|
|
206
|
+
return start;
|
|
207
|
+
};
|
|
208
|
+
if !should_parallelize_entry(first) {
|
|
209
|
+
return start + 1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let mut end = start;
|
|
213
|
+
let mut batch_bytes = 0u64;
|
|
214
|
+
while end < entries.len() {
|
|
215
|
+
let entry = &entries[end];
|
|
216
|
+
if !should_parallelize_entry(entry) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
if end > start {
|
|
220
|
+
if end - start >= PARALLEL_IO_BATCH_FILES {
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
if batch_bytes.saturating_add(entry.size) > PARALLEL_IO_BATCH_BYTES {
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
batch_bytes = batch_bytes.saturating_add(entry.size);
|
|
228
|
+
end += 1;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if end - start >= PARALLEL_IO_MIN_FILES {
|
|
232
|
+
end
|
|
233
|
+
} else {
|
|
234
|
+
start + 1
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fn should_parallelize_entry(entry: &DirectoryFile) -> bool {
|
|
239
|
+
entry.size <= PARALLEL_IO_FILE_THRESHOLD
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn file_buffer_capacity(size: u64) -> usize {
|
|
243
|
+
usize::try_from(size)
|
|
244
|
+
.unwrap_or(MAX_FILE_BUFFER_CAPACITY)
|
|
245
|
+
.min(MAX_FILE_BUFFER_CAPACITY)
|
|
246
|
+
.max(8192)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fn report_compress_progress(
|
|
250
|
+
progress: &Option<ProgressCallback>,
|
|
251
|
+
total_bytes: u64,
|
|
252
|
+
bytes_processed: u64,
|
|
253
|
+
last_pct: &mut u64,
|
|
254
|
+
) {
|
|
255
|
+
if let Some(ref cb) = progress {
|
|
256
|
+
let pct = if total_bytes > 0 {
|
|
257
|
+
(bytes_processed * 89 / total_bytes).min(89)
|
|
258
|
+
} else {
|
|
259
|
+
89
|
|
260
|
+
};
|
|
261
|
+
if pct > *last_pct {
|
|
262
|
+
*last_pct = pct;
|
|
263
|
+
cb(pct, 100, "compressing");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fn collect_directory_files(dir_path: &Path) -> CollectedDirectory {
|
|
269
|
+
let mut entries = Vec::new();
|
|
270
|
+
let mut total_bytes = 0u64;
|
|
271
|
+
|
|
272
|
+
for entry in WalkDir::new(dir_path)
|
|
273
|
+
.follow_links(false)
|
|
274
|
+
.into_iter()
|
|
275
|
+
.filter_map(|entry| entry.ok())
|
|
276
|
+
.filter(|entry| entry.file_type().is_file())
|
|
277
|
+
{
|
|
278
|
+
let size = match entry.metadata() {
|
|
279
|
+
Ok(metadata) => metadata.len(),
|
|
280
|
+
Err(_) => continue,
|
|
281
|
+
};
|
|
282
|
+
let path = entry.into_path();
|
|
283
|
+
let rel = path.strip_prefix(dir_path).unwrap_or(path.as_path());
|
|
284
|
+
let rel_path = normalize_rel_path(rel);
|
|
285
|
+
|
|
286
|
+
total_bytes += size;
|
|
287
|
+
entries.push(DirectoryFile {
|
|
288
|
+
path,
|
|
289
|
+
rel_path,
|
|
290
|
+
size,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
CollectedDirectory {
|
|
295
|
+
entries,
|
|
296
|
+
total_bytes,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fn normalize_rel_path(path: &Path) -> String {
|
|
301
|
+
let rel_path = path.to_string_lossy();
|
|
302
|
+
if rel_path.contains('\\') {
|
|
303
|
+
rel_path.replace('\\', "/")
|
|
304
|
+
} else {
|
|
305
|
+
rel_path.into_owned()
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fn estimate_zst_capacity(total_bytes: u64) -> usize {
|
|
310
|
+
let capped = total_bytes.min(usize::MAX as u64) as usize;
|
|
311
|
+
(capped / 3).max(MIN_ZST_CAPACITY)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
fn select_zstd_threads(total_bytes: u64) -> u32 {
|
|
315
|
+
let max_threads = num_cpus::get().max(1) as u32;
|
|
316
|
+
if total_bytes <= 32 * MB {
|
|
317
|
+
1
|
|
318
|
+
} else if total_bytes <= 128 * MB {
|
|
319
|
+
max_threads.min(2)
|
|
320
|
+
} else if total_bytes <= 512 * MB {
|
|
321
|
+
max_threads.min(4)
|
|
322
|
+
} else {
|
|
323
|
+
max_threads.min(8)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
fn write_png_from_zst_mem(
|
|
328
|
+
zst_buf: Vec<u8>,
|
|
149
329
|
output_path: &Path,
|
|
150
330
|
name: Option<&str>,
|
|
151
331
|
file_list: Option<&str>,
|
|
152
332
|
passphrase: Option<&str>,
|
|
153
333
|
_encrypt_type: Option<&str>,
|
|
334
|
+
progress: &Option<ProgressCallback>,
|
|
154
335
|
) -> anyhow::Result<()> {
|
|
155
|
-
let zst_size =
|
|
336
|
+
let zst_size = zst_buf.len();
|
|
156
337
|
|
|
157
338
|
let mut encryptor = match passphrase {
|
|
158
339
|
Some(pass) if !pass.is_empty() => Some(crate::crypto::StreamingEncryptor::new(pass)?),
|
|
@@ -239,8 +420,7 @@ fn write_png_from_zst(
|
|
|
239
420
|
ihdr[9] = 2;
|
|
240
421
|
write_chunk_hdr(&mut w, b"IHDR", &ihdr)?;
|
|
241
422
|
|
|
242
|
-
let mut
|
|
243
|
-
let mut zst_reader = std::io::BufReader::with_capacity(16 * 1024 * 1024, &mut zst_file);
|
|
423
|
+
let mut zst_reader = std::io::Cursor::new(zst_buf);
|
|
244
424
|
|
|
245
425
|
write_idat_streaming(
|
|
246
426
|
&mut w,
|
|
@@ -255,6 +435,7 @@ fn write_png_from_zst(
|
|
|
255
435
|
marker_end_pos,
|
|
256
436
|
idat_len,
|
|
257
437
|
total_data_bytes,
|
|
438
|
+
progress,
|
|
258
439
|
)?;
|
|
259
440
|
|
|
260
441
|
if let Some(fl) = file_list {
|
|
@@ -291,6 +472,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
291
472
|
marker_end_pos: usize,
|
|
292
473
|
idat_len: usize,
|
|
293
474
|
total_data_bytes: usize,
|
|
475
|
+
progress: &Option<ProgressCallback>,
|
|
294
476
|
) -> anyhow::Result<()> {
|
|
295
477
|
w.write_all(&(idat_len as u32).to_be_bytes())?;
|
|
296
478
|
w.write_all(b"IDAT")?;
|
|
@@ -308,13 +490,18 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
308
490
|
let fl_chunk_data = file_list_chunk.unwrap_or(&[]);
|
|
309
491
|
let payload_total = header_bytes.len() + zst_size + hmac_trailer_len + fl_chunk_data.len();
|
|
310
492
|
let padding_after = total_data_bytes - payload_total.min(total_data_bytes);
|
|
311
|
-
|
|
312
493
|
let marker_end_bytes = build_marker_end_bytes();
|
|
313
494
|
|
|
314
495
|
let mut flat_pos: usize = 0;
|
|
315
496
|
let mut scanline_pos: usize = 0;
|
|
316
497
|
let mut deflate_block_remaining: usize = 0;
|
|
317
498
|
|
|
499
|
+
let mut adler = simd_adler32::Adler32::new();
|
|
500
|
+
|
|
501
|
+
let buf_size = 1024 * 1024;
|
|
502
|
+
let mut transfer_buf = vec![0u8; buf_size];
|
|
503
|
+
let zero_buf = vec![0u8; buf_size];
|
|
504
|
+
|
|
318
505
|
let mut header_pos: usize = 0;
|
|
319
506
|
let mut zst_remaining = zst_size;
|
|
320
507
|
let mut hmac_pos: usize = 0;
|
|
@@ -323,14 +510,9 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
323
510
|
let mut fl_pos: usize = 0;
|
|
324
511
|
let mut zero_remaining = padding_after;
|
|
325
512
|
|
|
326
|
-
let mut
|
|
327
|
-
let mut adler_b: u32 = 0;
|
|
513
|
+
let mut last_png_pct: u64 = 89;
|
|
328
514
|
|
|
329
|
-
|
|
330
|
-
let mut transfer_buf = vec![0u8; buf_size];
|
|
331
|
-
let zero_buf = vec![0u8; buf_size];
|
|
332
|
-
|
|
333
|
-
for _row in 0..height {
|
|
515
|
+
for row_idx in 0..height {
|
|
334
516
|
if deflate_block_remaining == 0 {
|
|
335
517
|
let remaining_scanlines = scanlines_total - scanline_pos;
|
|
336
518
|
let block_size = remaining_scanlines.min(65535);
|
|
@@ -350,8 +532,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
350
532
|
let filter_byte = [0u8];
|
|
351
533
|
w.write_all(&filter_byte)?;
|
|
352
534
|
crc.update(&filter_byte);
|
|
353
|
-
|
|
354
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
535
|
+
adler.write(&filter_byte);
|
|
355
536
|
scanline_pos += 1;
|
|
356
537
|
deflate_block_remaining -= 1;
|
|
357
538
|
|
|
@@ -388,10 +569,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
388
569
|
let slice = &marker_end_bytes[me_offset..me_offset + take];
|
|
389
570
|
w.write_all(slice)?;
|
|
390
571
|
crc.update(slice);
|
|
391
|
-
|
|
392
|
-
adler_a = (adler_a + b as u32) % 65521;
|
|
393
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
394
|
-
}
|
|
572
|
+
adler.write(slice);
|
|
395
573
|
flat_pos += take;
|
|
396
574
|
chunk_written += take;
|
|
397
575
|
scanline_pos += take;
|
|
@@ -406,10 +584,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
406
584
|
let slice = &header_bytes[header_pos..header_pos + take];
|
|
407
585
|
w.write_all(slice)?;
|
|
408
586
|
crc.update(slice);
|
|
409
|
-
|
|
410
|
-
adler_a = (adler_a + b as u32) % 65521;
|
|
411
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
412
|
-
}
|
|
587
|
+
adler.write(slice);
|
|
413
588
|
header_pos += take;
|
|
414
589
|
flat_pos += take;
|
|
415
590
|
chunk_written += take;
|
|
@@ -426,10 +601,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
426
601
|
}
|
|
427
602
|
w.write_all(&transfer_buf[..got])?;
|
|
428
603
|
crc.update(&transfer_buf[..got]);
|
|
429
|
-
|
|
430
|
-
adler_a = (adler_a + b as u32) % 65521;
|
|
431
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
432
|
-
}
|
|
604
|
+
adler.write(&transfer_buf[..got]);
|
|
433
605
|
zst_remaining -= got;
|
|
434
606
|
flat_pos += got;
|
|
435
607
|
chunk_written += got;
|
|
@@ -448,10 +620,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
448
620
|
let slice = &hmac_bytes[hmac_pos..hmac_pos + take];
|
|
449
621
|
w.write_all(slice)?;
|
|
450
622
|
crc.update(slice);
|
|
451
|
-
|
|
452
|
-
adler_a = (adler_a + b as u32) % 65521;
|
|
453
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
454
|
-
}
|
|
623
|
+
adler.write(slice);
|
|
455
624
|
hmac_pos += take;
|
|
456
625
|
flat_pos += take;
|
|
457
626
|
chunk_written += take;
|
|
@@ -470,10 +639,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
470
639
|
let slice = &fl_chunk_data[fl_pos..fl_pos + take];
|
|
471
640
|
w.write_all(slice)?;
|
|
472
641
|
crc.update(slice);
|
|
473
|
-
|
|
474
|
-
adler_a = (adler_a + b as u32) % 65521;
|
|
475
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
476
|
-
}
|
|
642
|
+
adler.write(slice);
|
|
477
643
|
fl_pos += take;
|
|
478
644
|
flat_pos += take;
|
|
479
645
|
chunk_written += take;
|
|
@@ -490,9 +656,7 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
490
656
|
if take == 0 { break; }
|
|
491
657
|
w.write_all(&zero_buf[..take])?;
|
|
492
658
|
crc.update(&zero_buf[..take]);
|
|
493
|
-
|
|
494
|
-
adler_b = (adler_b + adler_a) % 65521;
|
|
495
|
-
}
|
|
659
|
+
adler.write(&zero_buf[..take]);
|
|
496
660
|
zero_remaining -= take;
|
|
497
661
|
flat_pos += take;
|
|
498
662
|
chunk_written += take;
|
|
@@ -502,10 +666,18 @@ fn write_idat_streaming<W: Write, R: Read>(
|
|
|
502
666
|
}
|
|
503
667
|
}
|
|
504
668
|
}
|
|
669
|
+
|
|
670
|
+
if let Some(ref cb) = progress {
|
|
671
|
+
let pct = 90 + ((row_idx as u64 + 1) * 9 / height as u64).min(9);
|
|
672
|
+
if pct > last_png_pct {
|
|
673
|
+
last_png_pct = pct;
|
|
674
|
+
cb(pct, 100, "writing_png");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
505
677
|
}
|
|
506
678
|
|
|
507
|
-
let
|
|
508
|
-
let adler_bytes =
|
|
679
|
+
let adler_val = adler.finish();
|
|
680
|
+
let adler_bytes = adler_val.to_be_bytes();
|
|
509
681
|
w.write_all(&adler_bytes)?;
|
|
510
682
|
crc.update(&adler_bytes);
|
|
511
683
|
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|