roxify 1.12.6 → 1.12.8

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.
@@ -0,0 +1,538 @@
1
+ use bytemuck::{Pod, Zeroable};
2
+ use image::ImageReader;
3
+ use std::io::{Cursor, Read, Seek, SeekFrom};
4
+
5
+ #[repr(C)]
6
+ #[derive(Clone, Copy, Pod, Zeroable)]
7
+ struct PngSignature([u8; 8]);
8
+
9
+ const PNG_SIG: PngSignature = PngSignature([137, 80, 78, 71, 13, 10, 26, 10]);
10
+
11
+ #[derive(Debug, Clone)]
12
+ pub struct PngChunk {
13
+ pub name: String,
14
+ pub data: Vec<u8>,
15
+ }
16
+
17
+ fn read_u32_be(data: &[u8]) -> u32 {
18
+ u32::from_be_bytes([data[0], data[1], data[2], data[3]])
19
+ }
20
+
21
+ fn write_u32_be(val: u32) -> [u8; 4] {
22
+ val.to_be_bytes()
23
+ }
24
+
25
+ pub fn extract_png_chunks_streaming<R: Read + Seek>(reader: &mut R) -> Result<Vec<PngChunk>, String> {
26
+ let mut sig = [0u8; 8];
27
+ reader.read_exact(&mut sig).map_err(|e| format!("read sig: {}", e))?;
28
+ if sig != PNG_SIG.0 {
29
+ return Err("Invalid PNG signature".to_string());
30
+ }
31
+
32
+ let mut chunks = Vec::new();
33
+ let mut header = [0u8; 8];
34
+
35
+ loop {
36
+ if reader.read_exact(&mut header).is_err() {
37
+ break;
38
+ }
39
+
40
+ let length = u32::from_be_bytes([header[0], header[1], header[2], header[3]]) as usize;
41
+ let chunk_type = [header[4], header[5], header[6], header[7]];
42
+ let name = String::from_utf8_lossy(&chunk_type).to_string();
43
+
44
+ if name == "IDAT" {
45
+ reader.seek(SeekFrom::Current(length as i64 + 4)).map_err(|e| format!("seek: {}", e))?;
46
+ } else {
47
+ let mut data = vec![0u8; length];
48
+ reader.read_exact(&mut data).map_err(|e| format!("read chunk {}: {}", name, e))?;
49
+ reader.seek(SeekFrom::Current(4)).map_err(|e| format!("seek crc: {}", e))?;
50
+ chunks.push(PngChunk { name: name.clone(), data });
51
+ }
52
+
53
+ if &chunk_type == b"IEND" {
54
+ break;
55
+ }
56
+ }
57
+
58
+ Ok(chunks)
59
+ }
60
+
61
+ pub fn extract_png_chunks(png_data: &[u8]) -> Result<Vec<PngChunk>, String> {
62
+ if png_data.len() < 8 || &png_data[..8] != &PNG_SIG.0 {
63
+ return Err("Invalid PNG signature".to_string());
64
+ }
65
+
66
+ let mut chunks = Vec::new();
67
+ let mut pos = 8;
68
+
69
+ while pos + 12 <= png_data.len() {
70
+ let length = read_u32_be(&png_data[pos..pos + 4]) as usize;
71
+ let chunk_type = &png_data[pos + 4..pos + 8];
72
+
73
+ if pos + 12 + length > png_data.len() {
74
+ break;
75
+ }
76
+
77
+ let chunk_data = &png_data[pos + 8..pos + 8 + length];
78
+
79
+ let name = String::from_utf8_lossy(chunk_type).to_string();
80
+ chunks.push(PngChunk {
81
+ name,
82
+ data: chunk_data.to_vec(),
83
+ });
84
+
85
+ pos += 12 + length;
86
+
87
+ if chunk_type == b"IEND" {
88
+ break;
89
+ }
90
+ }
91
+
92
+ Ok(chunks)
93
+ }
94
+
95
+ pub fn encode_png_chunks(chunks: &[PngChunk]) -> Result<Vec<u8>, String> {
96
+ let mut output = Vec::new();
97
+
98
+ output.extend_from_slice(&PNG_SIG.0);
99
+
100
+ for chunk in chunks {
101
+ let chunk_type = chunk.name.as_bytes();
102
+ if chunk_type.len() != 4 {
103
+ return Err(format!("Invalid chunk type length: {}", chunk.name));
104
+ }
105
+
106
+ let length = chunk.data.len() as u32;
107
+ output.extend_from_slice(&write_u32_be(length));
108
+ output.extend_from_slice(chunk_type);
109
+ output.extend_from_slice(&chunk.data);
110
+
111
+ let mut crc_data = Vec::new();
112
+ crc_data.extend_from_slice(chunk_type);
113
+ crc_data.extend_from_slice(&chunk.data);
114
+ let crc = crate::core::crc32_bytes(&crc_data);
115
+ output.extend_from_slice(&write_u32_be(crc));
116
+ }
117
+
118
+ Ok(output)
119
+ }
120
+
121
+ pub fn get_png_metadata(png_data: &[u8]) -> Result<(u32, u32, u8, u8), String> {
122
+ let chunks = extract_png_chunks(png_data)?;
123
+
124
+ let ihdr = chunks.iter()
125
+ .find(|c| c.name == "IHDR")
126
+ .ok_or("IHDR chunk not found")?;
127
+
128
+ if ihdr.data.len() < 13 {
129
+ return Err("Invalid IHDR chunk".to_string());
130
+ }
131
+
132
+ let width = read_u32_be(&ihdr.data[0..4]);
133
+ let height = read_u32_be(&ihdr.data[4..8]);
134
+ let bit_depth = ihdr.data[8];
135
+ let color_type = ihdr.data[9];
136
+
137
+ Ok((width, height, bit_depth, color_type))
138
+ }
139
+
140
+ pub fn extract_payload_from_png(png_data: &[u8]) -> Result<Vec<u8>, String> {
141
+ if let Ok(payload) = extract_payload_direct(png_data) {
142
+ if validate_payload_deep(&payload) {
143
+ return Ok(payload);
144
+ }
145
+ }
146
+ if let Ok(reconst) = crate::reconstitution::crop_and_reconstitute(png_data) {
147
+ if let Ok(payload) = extract_payload_direct(&reconst) {
148
+ if validate_payload_deep(&payload) {
149
+ return Ok(payload);
150
+ }
151
+ }
152
+ if let Ok(unstretched) = crate::reconstitution::unstretch_nn(&reconst) {
153
+ if let Ok(payload) = extract_payload_direct(&unstretched) {
154
+ if validate_payload_deep(&payload) {
155
+ return Ok(payload);
156
+ }
157
+ }
158
+ }
159
+ }
160
+ if let Ok(unstretched) = crate::reconstitution::unstretch_nn(png_data) {
161
+ if let Ok(payload) = extract_payload_direct(&unstretched) {
162
+ if validate_payload_deep(&payload) {
163
+ return Ok(payload);
164
+ }
165
+ }
166
+ }
167
+ if let Ok(payload) = extract_payload_from_embedded_nn(png_data) {
168
+ if validate_payload_deep(&payload) {
169
+ return Ok(payload);
170
+ }
171
+ }
172
+ Err("No valid payload found after all extraction attempts".to_string())
173
+ }
174
+
175
+ fn validate_payload_deep(payload: &[u8]) -> bool {
176
+ if payload.len() < 5 { return false; }
177
+ if payload[0] == 0x01 || payload[0] == 0x02 || payload[0] == 0x03 { return true; }
178
+ let compressed = if payload[0] == 0x00 { &payload[1..] } else { payload };
179
+ if compressed.starts_with(b"ROX1") { return true; }
180
+ crate::core::zstd_decompress_bytes(compressed, None).is_ok()
181
+ }
182
+
183
+ fn find_pixel_header(raw: &[u8]) -> Result<usize, String> {
184
+ let magic = b"PXL1";
185
+ for i in 0..(raw.len().saturating_sub(magic.len())) {
186
+ if &raw[i..i + magic.len()] == magic {
187
+ return Ok(i);
188
+ }
189
+ }
190
+ Err("PIXEL_MAGIC not found".to_string())
191
+ }
192
+
193
+ fn decode_to_rgb(png_data: &[u8]) -> Result<Vec<u8>, String> {
194
+ let mut reader = ImageReader::new(Cursor::new(png_data))
195
+ .with_guessed_format()
196
+ .map_err(|e| format!("format guess error: {}", e))?;
197
+ reader.no_limits();
198
+ let img = reader.decode().map_err(|e| format!("image decode error: {}", e))?;
199
+ Ok(img.to_rgb8().into_raw())
200
+ }
201
+
202
+ fn decode_to_rgba_grid(png_data: &[u8]) -> Result<(Vec<[u8; 4]>, u32, u32), String> {
203
+ let mut reader = ImageReader::new(Cursor::new(png_data))
204
+ .with_guessed_format()
205
+ .map_err(|e| format!("format guess error: {}", e))?;
206
+ reader.no_limits();
207
+ let img = reader.decode().map_err(|e| format!("image decode error: {}", e))?;
208
+ let rgba = img.to_rgba8();
209
+ let w = rgba.width();
210
+ let h = rgba.height();
211
+ let pixels: Vec<[u8; 4]> = rgba.pixels().map(|p| [p[0], p[1], p[2], p[3]]).collect();
212
+ Ok((pixels, w, h))
213
+ }
214
+
215
+ fn reconstruct_logical_pixels_from_nn(
216
+ pixels: &[[u8; 4]], width: u32, height: u32
217
+ ) -> Result<Vec<u8>, String> {
218
+ let w = width as usize;
219
+ let h = height as usize;
220
+ let get = |x: usize, y: usize| -> [u8; 4] { pixels[y * w + x] };
221
+
222
+ let magic = [b'P', b'X', b'L', b'1'];
223
+
224
+ let mut header_row = None;
225
+ let mut header_col = None;
226
+ 'outer: for y in 0..h {
227
+ for x in 0..w.saturating_sub(1) {
228
+ let p0 = get(x, y);
229
+ let p1 = get(x + 1, y);
230
+ let seq = [p0[0], p0[1], p0[2], p1[0], p1[1], p1[2]];
231
+ for start in 0..3 {
232
+ if start + 4 <= 6 && seq[start] == magic[0] && seq[start+1] == magic[1]
233
+ && seq[start+2] == magic[2] && seq[start+3] == magic[3]
234
+ {
235
+ header_row = Some(y);
236
+ header_col = Some(x);
237
+ break 'outer;
238
+ }
239
+ }
240
+ }
241
+ }
242
+ let header_row = header_row.ok_or("PXL1 not found in 2D pixel scan")?;
243
+ let header_col = header_col.ok_or("PXL1 column not found")?;
244
+
245
+ let mut scale_y = 1usize;
246
+ for dy in 1..h - header_row {
247
+ let y2 = header_row + dy;
248
+ let mut same = true;
249
+ for x in header_col..(header_col + 4).min(w) {
250
+ if get(x, y2) != get(x, header_row) { same = false; break; }
251
+ }
252
+ if same { scale_y += 1; } else { break; }
253
+ }
254
+
255
+ let cur = get(header_col, header_row);
256
+ let mut block_start = header_col;
257
+ while block_start > 0 && get(block_start - 1, header_row) == cur {
258
+ block_start -= 1;
259
+ }
260
+ let mut block_end = header_col + 1;
261
+ while block_end < w && get(block_end, header_row) == cur {
262
+ block_end += 1;
263
+ }
264
+ let scale_x = block_end - block_start;
265
+ if scale_x < 2 {
266
+ return Err("Could not determine NN scale_x".to_string());
267
+ }
268
+
269
+ let ref_y = header_row;
270
+ let mut embed_left = block_start;
271
+ loop {
272
+ if embed_left < scale_x { break; }
273
+ let candidate = embed_left - scale_x;
274
+ let c0 = get(candidate, ref_y);
275
+ let mut is_block = true;
276
+ for dx in 1..scale_x {
277
+ if candidate + dx >= w || get(candidate + dx, ref_y) != c0 {
278
+ is_block = false;
279
+ break;
280
+ }
281
+ }
282
+ if !is_block { break; }
283
+ if candidate + scale_x < w && get(candidate + scale_x, ref_y) == c0 {
284
+ break;
285
+ }
286
+ embed_left = candidate;
287
+ }
288
+
289
+ let mut embed_top = header_row;
290
+ loop {
291
+ if embed_top < scale_y { break; }
292
+ let candidate = embed_top - scale_y;
293
+ let mut is_block = true;
294
+ for dy in 0..scale_y {
295
+ if candidate + dy >= h { is_block = false; break; }
296
+ if dy > 0 && get(embed_left, candidate + dy) != get(embed_left, candidate) {
297
+ is_block = false;
298
+ break;
299
+ }
300
+ }
301
+ if !is_block { break; }
302
+ embed_top = candidate;
303
+ }
304
+
305
+ let mut logical_cols: Vec<usize> = Vec::new();
306
+ let mut x = embed_left;
307
+ while x < w {
308
+ logical_cols.push(x);
309
+ let c = get(x, ref_y);
310
+ let mut nx = x + 1;
311
+ while nx < w && get(nx, ref_y) == c {
312
+ nx += 1;
313
+ }
314
+ if nx >= w { break; }
315
+ let blk = nx - x;
316
+ if blk < scale_x.saturating_sub(2) || blk > scale_x + 2 {
317
+ break;
318
+ }
319
+ x = nx;
320
+ }
321
+
322
+ let mut logical_rows: Vec<usize> = Vec::new();
323
+ let mut y = embed_top;
324
+ while y < h {
325
+ logical_rows.push(y);
326
+ let c = get(embed_left, y);
327
+ let mut ny = y + 1;
328
+ while ny < h && get(embed_left, ny) == c {
329
+ ny += 1;
330
+ }
331
+ if ny >= h { break; }
332
+ let blk = ny - y;
333
+ if blk < scale_y.saturating_sub(2) || blk > scale_y + 2 {
334
+ break;
335
+ }
336
+ y = ny;
337
+ }
338
+
339
+ if logical_cols.len() < 3 || logical_rows.len() < 3 {
340
+ return Err("Embedded region too small".to_string());
341
+ }
342
+
343
+ let img_w = logical_cols.len();
344
+ let mut logical_rgb = Vec::with_capacity(img_w * logical_rows.len() * 3);
345
+ for &ry in &logical_rows {
346
+ for &cx in &logical_cols {
347
+ let p = get(cx, ry);
348
+ logical_rgb.push(p[0]);
349
+ logical_rgb.push(p[1]);
350
+ logical_rgb.push(p[2]);
351
+ }
352
+ }
353
+ Ok(logical_rgb)
354
+ }
355
+
356
+ fn extract_payload_from_embedded_nn(png_data: &[u8]) -> Result<Vec<u8>, String> {
357
+ let (pixels, width, height) = decode_to_rgba_grid(png_data)?;
358
+ let logical_rgb = reconstruct_logical_pixels_from_nn(&pixels, width, height)?;
359
+ let pos = {
360
+ let magic = b"PXL1";
361
+ let mut found = None;
362
+ for i in 0..logical_rgb.len().saturating_sub(4) {
363
+ if &logical_rgb[i..i+4] == magic {
364
+ found = Some(i);
365
+ break;
366
+ }
367
+ }
368
+ found.ok_or("PXL1 not found in reconstructed pixels")?
369
+ };
370
+ let mut idx = pos + 4;
371
+ if idx + 2 > logical_rgb.len() { return Err("Truncated header in embedded NN".to_string()); }
372
+ let _version = logical_rgb[idx]; idx += 1;
373
+ let name_len = logical_rgb[idx] as usize; idx += 1;
374
+ if idx + name_len > logical_rgb.len() { return Err("Truncated name in embedded NN".to_string()); }
375
+ idx += name_len;
376
+ if idx + 4 > logical_rgb.len() { return Err("Truncated payload length in embedded NN".to_string()); }
377
+ let payload_len = ((logical_rgb[idx] as u32) << 24)
378
+ | ((logical_rgb[idx+1] as u32) << 16)
379
+ | ((logical_rgb[idx+2] as u32) << 8)
380
+ | (logical_rgb[idx+3] as u32);
381
+ idx += 4;
382
+ let end = idx + (payload_len as usize);
383
+ if end > logical_rgb.len() { return Err("Truncated payload in embedded NN".to_string()); }
384
+ Ok(logical_rgb[idx..end].to_vec())
385
+ }
386
+
387
+ pub fn extract_name_from_png(png_data: &[u8]) -> Option<String> {
388
+ if let Some(name) = extract_name_direct(png_data) {
389
+ return Some(name);
390
+ }
391
+ if let Ok(reconst) = crate::reconstitution::crop_and_reconstitute(png_data) {
392
+ if let Some(name) = extract_name_direct(&reconst) {
393
+ return Some(name);
394
+ }
395
+ if let Ok(unstretched) = crate::reconstitution::unstretch_nn(&reconst) {
396
+ if let Some(name) = extract_name_direct(&unstretched) {
397
+ return Some(name);
398
+ }
399
+ }
400
+ }
401
+ if let Ok(unstretched) = crate::reconstitution::unstretch_nn(png_data) {
402
+ if let Some(name) = extract_name_direct(&unstretched) {
403
+ return Some(name);
404
+ }
405
+ }
406
+ if let Ok(name) = extract_name_from_embedded_nn(png_data) {
407
+ return Some(name);
408
+ }
409
+ None
410
+ }
411
+
412
+ fn extract_name_direct(png_data: &[u8]) -> Option<String> {
413
+ let raw = decode_to_rgb(png_data).ok()?;
414
+ let pos = find_pixel_header(&raw).ok()?;
415
+ let mut idx = pos + 4;
416
+ if idx + 2 > raw.len() { return None; }
417
+ idx += 1;
418
+ let name_len = raw[idx] as usize; idx += 1;
419
+ if name_len == 0 || idx + name_len > raw.len() { return None; }
420
+ String::from_utf8(raw[idx..idx + name_len].to_vec()).ok()
421
+ }
422
+
423
+ fn extract_name_from_embedded_nn(png_data: &[u8]) -> Result<String, String> {
424
+ let (pixels, width, height) = decode_to_rgba_grid(png_data)?;
425
+ let logical_rgb = reconstruct_logical_pixels_from_nn(&pixels, width, height)?;
426
+ let pos = find_pixel_header(&logical_rgb)?;
427
+ let mut idx = pos + 4;
428
+ if idx + 2 > logical_rgb.len() { return Err("Truncated".to_string()); }
429
+ idx += 1;
430
+ let name_len = logical_rgb[idx] as usize; idx += 1;
431
+ if name_len == 0 || idx + name_len > logical_rgb.len() { return Err("Truncated name".to_string()); }
432
+ String::from_utf8(logical_rgb[idx..idx + name_len].to_vec()).map_err(|e| e.to_string())
433
+ }
434
+
435
+ fn extract_payload_direct(png_data: &[u8]) -> Result<Vec<u8>, String> {
436
+ let raw = decode_to_rgb(png_data)?;
437
+ let pos = find_pixel_header(&raw)?;
438
+ let mut idx = pos + 4;
439
+ if idx + 2 > raw.len() { return Err("Truncated header".to_string()); }
440
+ let _version = raw[idx]; idx += 1;
441
+ let name_len = raw[idx] as usize; idx += 1;
442
+ if idx + name_len > raw.len() { return Err("Truncated name".to_string()); }
443
+ idx += name_len;
444
+ if idx + 4 > raw.len() { return Err("Truncated payload length".to_string()); }
445
+ let payload_len = ((raw[idx] as u32) << 24)
446
+ | ((raw[idx+1] as u32) << 16)
447
+ | ((raw[idx+2] as u32) << 8)
448
+ | (raw[idx+3] as u32);
449
+ idx += 4;
450
+ let end = idx + (payload_len as usize);
451
+ if end > raw.len() { return Err("Truncated payload".to_string()); }
452
+ let payload = raw[idx..end].to_vec();
453
+ Ok(payload)
454
+ }
455
+
456
+ pub fn extract_file_list_from_pixels(png_data: &[u8]) -> Result<String, String> {
457
+ if let Ok(result) = extract_file_list_direct(png_data) {
458
+ return Ok(result);
459
+ }
460
+ if let Ok(reconst) = crate::reconstitution::crop_and_reconstitute(png_data) {
461
+ if let Ok(result) = extract_file_list_direct(&reconst) {
462
+ return Ok(result);
463
+ }
464
+ if let Ok(unstretched) = crate::reconstitution::unstretch_nn(&reconst) {
465
+ if let Ok(result) = extract_file_list_direct(&unstretched) {
466
+ return Ok(result);
467
+ }
468
+ }
469
+ }
470
+ if let Ok(unstretched) = crate::reconstitution::unstretch_nn(png_data) {
471
+ if let Ok(result) = extract_file_list_direct(&unstretched) {
472
+ return Ok(result);
473
+ }
474
+ }
475
+ if let Ok(result) = extract_file_list_from_embedded_nn(png_data) {
476
+ return Ok(result);
477
+ }
478
+ Err("No file list found after all extraction attempts".to_string())
479
+ }
480
+
481
+ fn extract_file_list_from_embedded_nn(png_data: &[u8]) -> Result<String, String> {
482
+ let (pixels, width, height) = decode_to_rgba_grid(png_data)?;
483
+ let logical_rgb = reconstruct_logical_pixels_from_nn(&pixels, width, height)?;
484
+ let pos = find_pixel_header(&logical_rgb)?;
485
+ let mut idx = pos + 4;
486
+ if idx + 2 > logical_rgb.len() { return Err("Truncated".to_string()); }
487
+ idx += 1;
488
+ let name_len = logical_rgb[idx] as usize; idx += 1;
489
+ if idx + name_len > logical_rgb.len() { return Err("Truncated".to_string()); }
490
+ idx += name_len;
491
+ if idx + 4 > logical_rgb.len() { return Err("Truncated".to_string()); }
492
+ let payload_len = ((logical_rgb[idx] as u32) << 24)
493
+ | ((logical_rgb[idx+1] as u32) << 16)
494
+ | ((logical_rgb[idx+2] as u32) << 8)
495
+ | (logical_rgb[idx+3] as u32);
496
+ idx += 4;
497
+ idx += payload_len as usize;
498
+ if idx + 8 > logical_rgb.len() { return Err("No file list in embedded NN".to_string()); }
499
+ if &logical_rgb[idx..idx + 4] != b"rXFL" { return Err("No rXFL marker in embedded NN".to_string()); }
500
+ idx += 4;
501
+ let json_len = ((logical_rgb[idx] as u32) << 24)
502
+ | ((logical_rgb[idx+1] as u32) << 16)
503
+ | ((logical_rgb[idx+2] as u32) << 8)
504
+ | (logical_rgb[idx+3] as u32);
505
+ idx += 4;
506
+ let json_end = idx + json_len as usize;
507
+ if json_end > logical_rgb.len() { return Err("Truncated file list in embedded NN".to_string()); }
508
+ String::from_utf8(logical_rgb[idx..json_end].to_vec()).map_err(|e| format!("Invalid UTF-8: {}", e))
509
+ }
510
+
511
+ fn extract_file_list_direct(png_data: &[u8]) -> Result<String, String> {
512
+ let raw = decode_to_rgb(png_data)?;
513
+ let pos = find_pixel_header(&raw)?;
514
+ let mut idx = pos + 4;
515
+ if idx + 2 > raw.len() { return Err("Truncated header".to_string()); }
516
+ idx += 1;
517
+ let name_len = raw[idx] as usize; idx += 1;
518
+ if idx + name_len > raw.len() { return Err("Truncated name".to_string()); }
519
+ idx += name_len;
520
+ if idx + 4 > raw.len() { return Err("Truncated payload length".to_string()); }
521
+ let payload_len = ((raw[idx] as u32) << 24)
522
+ | ((raw[idx+1] as u32) << 16)
523
+ | ((raw[idx+2] as u32) << 8)
524
+ | (raw[idx+3] as u32);
525
+ idx += 4;
526
+ idx += payload_len as usize;
527
+ if idx + 8 > raw.len() { return Err("No file list in pixel data".to_string()); }
528
+ if &raw[idx..idx + 4] != b"rXFL" { return Err("No rXFL marker in pixel data".to_string()); }
529
+ idx += 4;
530
+ let json_len = ((raw[idx] as u32) << 24)
531
+ | ((raw[idx+1] as u32) << 16)
532
+ | ((raw[idx+2] as u32) << 8)
533
+ | (raw[idx+3] as u32);
534
+ idx += 4;
535
+ let json_end = idx + json_len as usize;
536
+ if json_end > raw.len() { return Err("Truncated file list JSON".to_string()); }
537
+ String::from_utf8(raw[idx..json_end].to_vec()).map_err(|e| format!("Invalid UTF-8 in file list: {}", e))
538
+ }
package/native/pool.rs ADDED
@@ -0,0 +1,101 @@
1
+ use anyhow::Result;
2
+ use parking_lot::RwLock;
3
+ use std::sync::Arc;
4
+
5
+ pub struct ReusableBuffer {
6
+ data: Vec<u8>,
7
+ capacity: usize,
8
+ }
9
+
10
+ impl ReusableBuffer {
11
+ pub fn new(capacity: usize) -> Self {
12
+ ReusableBuffer {
13
+ data: vec![0u8; capacity],
14
+ capacity,
15
+ }
16
+ }
17
+
18
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
19
+ &mut self.data
20
+ }
21
+
22
+ pub fn as_slice(&self) -> &[u8] {
23
+ &self.data
24
+ }
25
+
26
+ pub fn resize(&mut self, new_size: usize) -> Result<()> {
27
+ if new_size > self.capacity {
28
+ return Err(anyhow::anyhow!("Buffer overflow: {} > {}", new_size, self.capacity));
29
+ }
30
+ self.data.truncate(new_size);
31
+ Ok(())
32
+ }
33
+
34
+ pub fn clear(&mut self) {
35
+ self.data.clear();
36
+ }
37
+ }
38
+
39
+ pub struct BufferPool {
40
+ buffers: Arc<RwLock<Vec<Arc<RwLock<ReusableBuffer>>>>>,
41
+ default_capacity: usize,
42
+ }
43
+
44
+ impl BufferPool {
45
+ pub fn new(pool_size: usize, capacity: usize) -> Self {
46
+ let mut buffers = Vec::with_capacity(pool_size);
47
+ for _ in 0..pool_size {
48
+ buffers.push(Arc::new(RwLock::new(ReusableBuffer::new(capacity))));
49
+ }
50
+
51
+ BufferPool {
52
+ buffers: Arc::new(RwLock::new(buffers)),
53
+ default_capacity: capacity,
54
+ }
55
+ }
56
+
57
+ pub fn acquire(&self) -> Arc<RwLock<ReusableBuffer>> {
58
+ let mut pool = self.buffers.write();
59
+ if let Some(buf) = pool.pop() {
60
+ buf
61
+ } else {
62
+ Arc::new(RwLock::new(ReusableBuffer::new(self.default_capacity)))
63
+ }
64
+ }
65
+
66
+ pub fn release(&self, buf: Arc<RwLock<ReusableBuffer>>) {
67
+ buf.write().clear();
68
+ let mut pool = self.buffers.write();
69
+ if pool.len() < 16 {
70
+ pool.push(buf);
71
+ }
72
+ }
73
+ }
74
+
75
+ pub struct ZeroCopyBuffer {
76
+ ptr: *const u8,
77
+ len: usize,
78
+ owned: bool,
79
+ }
80
+
81
+ impl ZeroCopyBuffer {
82
+ pub fn from_slice(data: &[u8]) -> Self {
83
+ ZeroCopyBuffer {
84
+ ptr: data.as_ptr(),
85
+ len: data.len(),
86
+ owned: false,
87
+ }
88
+ }
89
+
90
+ pub fn as_slice(&self) -> &[u8] {
91
+ unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
92
+ }
93
+
94
+ pub fn len(&self) -> usize {
95
+ self.len
96
+ }
97
+
98
+ pub fn is_empty(&self) -> bool {
99
+ self.len == 0
100
+ }
101
+ }
@@ -0,0 +1,43 @@
1
+ use parking_lot::Mutex;
2
+ use std::sync::Arc;
3
+
4
+ pub struct ProgressBar {
5
+ total: u64,
6
+ current: Arc<Mutex<u64>>,
7
+ message: Arc<Mutex<String>>,
8
+ }
9
+
10
+ impl ProgressBar {
11
+ pub fn new(total: u64) -> Self {
12
+ Self {
13
+ total,
14
+ current: Arc::new(Mutex::new(0)),
15
+ message: Arc::new(Mutex::new(String::new())),
16
+ }
17
+ }
18
+
19
+ pub fn inc(&self, delta: u64) {
20
+ let mut current = self.current.lock();
21
+ *current += delta;
22
+ }
23
+
24
+ pub fn set(&self, value: u64) {
25
+ let mut current = self.current.lock();
26
+ *current = value;
27
+ }
28
+
29
+ pub fn set_message(&self, msg: String) {
30
+ let mut message = self.message.lock();
31
+ *message = msg;
32
+ }
33
+
34
+ pub fn get_progress(&self) -> (u64, u64) {
35
+ let current = *self.current.lock();
36
+ (current, self.total)
37
+ }
38
+
39
+ pub fn finish(&self) {
40
+ let mut current = self.current.lock();
41
+ *current = self.total;
42
+ }
43
+ }