1use crate::{BinaryError, Result};
7use memmap2::{Mmap, MmapOptions};
8use std::fs::File;
9use std::ops::{Deref, Range};
10use std::path::Path;
11
12#[derive(Debug)]
14pub struct MappedBinary {
15 _file: File,
16 mmap: Mmap,
17 size: usize,
18}
19
20impl MappedBinary {
21 pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
23 let file = File::open(path)
24 .map_err(|e| BinaryError::memory_map(format!("Failed to open file: {}", e)))?;
25
26 let mmap = unsafe {
27 MmapOptions::new().map(&file).map_err(|e| {
28 BinaryError::memory_map(format!("Failed to create memory map: {}", e))
29 })?
30 };
31
32 let size = mmap.len();
33
34 Ok(Self {
35 _file: file,
36 mmap,
37 size,
38 })
39 }
40
41 pub fn from_file(file: File) -> Result<Self> {
43 let mmap = unsafe {
44 MmapOptions::new().map(&file).map_err(|e| {
45 BinaryError::memory_map(format!("Failed to create memory map: {}", e))
46 })?
47 };
48
49 let size = mmap.len();
50
51 Ok(Self {
52 _file: file,
53 mmap,
54 size,
55 })
56 }
57
58 pub fn size(&self) -> usize {
60 self.size
61 }
62
63 pub fn slice(&self, range: Range<usize>) -> crate::types::ByteSliceResult<'_> {
65 if range.end > self.size {
66 return Err(BinaryError::memory_map(
67 "Range exceeds file size".to_string(),
68 ));
69 }
70
71 Ok(&self.mmap[range])
72 }
73
74 pub fn read_at(&self, offset: usize, length: usize) -> crate::types::ByteSliceResult<'_> {
76 if offset + length > self.size {
77 return Err(BinaryError::memory_map(
78 "Read exceeds file size".to_string(),
79 ));
80 }
81
82 Ok(&self.mmap[offset..offset + length])
83 }
84
85 pub fn read_bytes(&self, offset: usize, count: usize) -> Result<Vec<u8>> {
87 let data = self.read_at(offset, count)?;
88 Ok(data.to_vec())
89 }
90
91 pub fn read_u8(&self, offset: usize) -> Result<u8> {
93 if offset >= self.size {
94 return Err(BinaryError::memory_map(
95 "Offset exceeds file size".to_string(),
96 ));
97 }
98 Ok(self.mmap[offset])
99 }
100
101 pub fn read_u16_le(&self, offset: usize) -> Result<u16> {
103 let bytes = self.read_at(offset, 2)?;
104 Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
105 }
106
107 pub fn read_u16_be(&self, offset: usize) -> Result<u16> {
109 let bytes = self.read_at(offset, 2)?;
110 Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
111 }
112
113 pub fn read_u32_le(&self, offset: usize) -> Result<u32> {
115 let bytes = self.read_at(offset, 4)?;
116 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
117 }
118
119 pub fn read_u32_be(&self, offset: usize) -> Result<u32> {
121 let bytes = self.read_at(offset, 4)?;
122 Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
123 }
124
125 pub fn read_u64_le(&self, offset: usize) -> Result<u64> {
127 let bytes = self.read_at(offset, 8)?;
128 Ok(u64::from_le_bytes([
129 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
130 ]))
131 }
132
133 pub fn read_u64_be(&self, offset: usize) -> Result<u64> {
135 let bytes = self.read_at(offset, 8)?;
136 Ok(u64::from_be_bytes([
137 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
138 ]))
139 }
140
141 pub fn read_cstring(&self, offset: usize, max_length: usize) -> Result<String> {
143 let mut end = offset;
144 let limit = (offset + max_length).min(self.size);
145
146 while end < limit && self.mmap[end] != 0 {
147 end += 1;
148 }
149
150 let bytes = &self.mmap[offset..end];
151 String::from_utf8(bytes.to_vec())
152 .map_err(|e| BinaryError::memory_map(format!("Invalid UTF-8 string: {}", e)))
153 }
154
155 pub fn find_pattern(&self, pattern: &[u8]) -> Option<usize> {
157 self.mmap
158 .windows(pattern.len())
159 .position(|window| window == pattern)
160 }
161
162 pub fn find_all_patterns(&self, pattern: &[u8]) -> Vec<usize> {
164 let mut positions = Vec::new();
165 let mut start = 0;
166
167 while start + pattern.len() <= self.size {
168 if let Some(pos) = self.mmap[start..]
169 .windows(pattern.len())
170 .position(|window| window == pattern)
171 {
172 positions.push(start + pos);
173 start += pos + 1;
174 } else {
175 break;
176 }
177 }
178
179 positions
180 }
181
182 pub fn starts_with(&self, signature: &[u8]) -> bool {
184 if signature.len() > self.size {
185 return false;
186 }
187
188 &self.mmap[..signature.len()] == signature
189 }
190
191 pub fn hexdump(&self, offset: usize, length: usize) -> Result<String> {
193 let data = self.read_at(offset, length)?;
194 Ok(format_hexdump(data, offset))
195 }
196
197 pub fn view(&self, range: Range<usize>) -> Result<MappedView<'_>> {
199 if range.end > self.size {
200 return Err(BinaryError::memory_map(
201 "Range exceeds file size".to_string(),
202 ));
203 }
204
205 Ok(MappedView {
206 data: &self.mmap[range.clone()],
207 offset: range.start,
208 })
209 }
210}
211
212impl Deref for MappedBinary {
213 type Target = [u8];
214
215 fn deref(&self) -> &Self::Target {
216 &self.mmap
217 }
218}
219
220#[derive(Debug)]
222pub struct MappedView<'a> {
223 data: &'a [u8],
224 offset: usize,
225}
226
227impl<'a> MappedView<'a> {
228 pub fn offset(&self) -> usize {
230 self.offset
231 }
232
233 pub fn size(&self) -> usize {
235 self.data.len()
236 }
237
238 pub fn to_vec(&self) -> Vec<u8> {
240 self.data.to_vec()
241 }
242}
243
244impl<'a> Deref for MappedView<'a> {
245 type Target = [u8];
246
247 fn deref(&self) -> &Self::Target {
248 self.data
249 }
250}
251
252fn format_hexdump(data: &[u8], base_offset: usize) -> String {
254 let mut result = String::new();
255
256 for (i, chunk) in data.chunks(16).enumerate() {
257 let offset = base_offset + i * 16;
258 result.push_str(&format!("{:08x}: ", offset));
259
260 for (j, byte) in chunk.iter().enumerate() {
262 if j == 8 {
263 result.push(' ');
264 }
265 result.push_str(&format!("{:02x} ", byte));
266 }
267
268 for j in chunk.len()..16 {
270 if j == 8 {
271 result.push(' ');
272 }
273 result.push_str(" ");
274 }
275
276 result.push_str(" |");
278 for byte in chunk {
279 if byte.is_ascii_graphic() || *byte == b' ' {
280 result.push(*byte as char);
281 } else {
282 result.push('.');
283 }
284 }
285 result.push_str("|\n");
286 }
287
288 result
289}
290
291#[derive(Debug, Clone, Default)]
293pub struct MmapConfig {
294 pub use_huge_pages: bool,
296 pub populate: bool,
298 pub lock_memory: bool,
300}
301
302#[derive(Debug)]
304pub struct AdvancedMmap {
305 _file: File,
306 mmap: Mmap,
307 config: MmapConfig,
308}
309
310impl AdvancedMmap {
311 pub fn new<P: AsRef<Path>>(path: P, config: MmapConfig) -> Result<Self> {
313 let file = File::open(path)
314 .map_err(|e| BinaryError::memory_map(format!("Failed to open file: {}", e)))?;
315
316 let mut options = MmapOptions::new();
317
318 if config.populate {
319 options.populate();
320 }
321
322 let mmap = unsafe {
323 options.map(&file).map_err(|e| {
324 BinaryError::memory_map(format!("Failed to create memory map: {}", e))
325 })?
326 };
327
328 Ok(Self {
329 _file: file,
330 mmap,
331 config,
332 })
333 }
334
335 pub fn data(&self) -> &[u8] {
337 &self.mmap
338 }
339
340 pub fn config(&self) -> &MmapConfig {
342 &self.config
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349 use std::io::Write;
350 use tempfile::NamedTempFile;
351
352 fn create_test_file() -> NamedTempFile {
353 let mut file = NamedTempFile::new().unwrap();
354 file.write_all(b"Hello, World! This is a test file.")
355 .unwrap();
356 file.flush().unwrap();
357 file
358 }
359
360 fn create_binary_test_file() -> NamedTempFile {
361 let mut file = NamedTempFile::new().unwrap();
362 let data = vec![
364 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x00, 0xFF, 0xFE, 0xFD, 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x41, 0x42, 0x43, 0x80, 0x81, 0x82, 0x83, ];
373 file.write_all(&data).unwrap();
374 file.flush().unwrap();
375 file
376 }
377
378 fn create_empty_file() -> NamedTempFile {
379 NamedTempFile::new().unwrap()
380 }
381
382 #[test]
383 fn test_mapped_binary_creation() {
384 let file = create_test_file();
385 let mapped = MappedBinary::new(file.path());
386 assert!(mapped.is_ok());
387
388 let mapped = mapped.unwrap();
389 assert_eq!(mapped.size(), 34);
390 }
391
392 #[test]
393 fn test_mapped_binary_from_file() {
394 let temp_file = create_test_file();
395 let file = File::open(temp_file.path()).unwrap();
396 let mapped = MappedBinary::from_file(file);
397 assert!(mapped.is_ok());
398
399 let mapped = mapped.unwrap();
400 assert_eq!(mapped.size(), 34);
401 assert_eq!(&mapped[0..5], b"Hello");
402 }
403
404 #[test]
405 fn test_mapped_binary_deref() {
406 let file = create_test_file();
407 let mapped = MappedBinary::new(file.path()).unwrap();
408
409 assert_eq!(&mapped[0..5], b"Hello");
411 assert_eq!(mapped.len(), 34);
412 }
413
414 #[test]
415 fn test_slice_method() {
416 let file = create_test_file();
417 let mapped = MappedBinary::new(file.path()).unwrap();
418
419 let slice = mapped.slice(0..5).unwrap();
421 assert_eq!(slice, b"Hello");
422
423 let slice = mapped.slice(7..12).unwrap();
424 assert_eq!(slice, b"World");
425
426 let result = mapped.slice(0..100);
428 assert!(result.is_err());
429 assert!(result
430 .unwrap_err()
431 .to_string()
432 .contains("Range exceeds file size"));
433 }
434
435 #[test]
436 fn test_read_operations() {
437 let file = create_test_file();
438 let mapped = MappedBinary::new(file.path()).unwrap();
439
440 let data = mapped.read_at(0, 5).unwrap();
442 assert_eq!(data, b"Hello");
443
444 let bytes = mapped.read_bytes(7, 5).unwrap();
446 assert_eq!(bytes, b"World".to_vec());
447
448 let byte = mapped.read_u8(0).unwrap();
450 assert_eq!(byte, b'H');
451 }
452
453 #[test]
454 fn test_read_operations_errors() {
455 let file = create_test_file();
456 let mapped = MappedBinary::new(file.path()).unwrap();
457
458 let result = mapped.read_at(0, 100);
460 assert!(result.is_err());
461 assert!(result
462 .unwrap_err()
463 .to_string()
464 .contains("Read exceeds file size"));
465
466 let result = mapped.read_at(50, 5);
468 assert!(result.is_err());
469
470 let result = mapped.read_bytes(0, 100);
472 assert!(result.is_err());
473
474 let result = mapped.read_u8(100);
476 assert!(result.is_err());
477 assert!(result
478 .unwrap_err()
479 .to_string()
480 .contains("Offset exceeds file size"));
481 }
482
483 #[test]
484 fn test_integer_read_operations() {
485 let file = create_binary_test_file();
486 let mapped = MappedBinary::new(file.path()).unwrap();
487
488 let val_le = mapped.read_u16_le(0).unwrap();
490 assert_eq!(val_le, 0x3412); let val_be = mapped.read_u16_be(0).unwrap();
493 assert_eq!(val_be, 0x1234); let val_le = mapped.read_u32_le(0).unwrap();
497 assert_eq!(val_le, 0x78563412); let val_be = mapped.read_u32_be(0).unwrap();
500 assert_eq!(val_be, 0x12345678); let val_le = mapped.read_u64_le(24).unwrap();
504 assert_eq!(val_le, 0x0706050403020100); let val_be = mapped.read_u64_be(24).unwrap();
507 assert_eq!(val_be, 0x0001020304050607); }
509
510 #[test]
511 fn test_integer_read_operations_errors() {
512 let file = create_binary_test_file();
513 let mapped = MappedBinary::new(file.path()).unwrap();
514 let file_size = mapped.size();
515
516 assert!(mapped.read_u16_le(file_size).is_err());
518 assert!(mapped.read_u16_be(file_size - 1).is_err());
519
520 assert!(mapped.read_u32_le(file_size).is_err());
522 assert!(mapped.read_u32_be(file_size - 3).is_err());
523
524 assert!(mapped.read_u64_le(file_size).is_err());
526 assert!(mapped.read_u64_be(file_size - 7).is_err());
527 }
528
529 #[test]
530 fn test_read_cstring() {
531 let file = create_binary_test_file();
532 let mapped = MappedBinary::new(file.path()).unwrap();
533
534 let s = mapped.read_cstring(8, 10).unwrap();
536 assert_eq!(s, "Hello");
537
538 let s = mapped.read_cstring(14, 10).unwrap();
540 assert_eq!(s, "World");
541
542 let s = mapped.read_cstring(8, 3).unwrap();
544 assert_eq!(s, "Hel");
545
546 let result = mapped.read_cstring(8, 1000);
548 assert!(result.is_ok()); }
550
551 #[test]
552 fn test_read_cstring_errors() {
553 let mut file = NamedTempFile::new().unwrap();
554 file.write_all(&[0xFF, 0xFE, 0x00]).unwrap();
556 file.flush().unwrap();
557
558 let mapped = MappedBinary::new(file.path()).unwrap();
559
560 let result = mapped.read_cstring(0, 10);
562 assert!(result.is_err());
563 assert!(result
564 .unwrap_err()
565 .to_string()
566 .contains("Invalid UTF-8 string"));
567 }
568
569 #[test]
570 fn test_pattern_finding() {
571 let file = create_test_file();
572 let mapped = MappedBinary::new(file.path()).unwrap();
573
574 let pos = mapped.find_pattern(b"World");
576 assert_eq!(pos, Some(7));
577
578 let pos = mapped.find_pattern(b"xyz");
579 assert_eq!(pos, None);
580
581 let pos = mapped.find_pattern(b"H");
583 assert_eq!(pos, Some(0));
584 }
585
586 #[test]
587 fn test_find_all_patterns() {
588 let mut file = NamedTempFile::new().unwrap();
589 file.write_all(b"ababcabab").unwrap();
590 file.flush().unwrap();
591
592 let mapped = MappedBinary::new(file.path()).unwrap();
593
594 let positions = mapped.find_all_patterns(b"ab");
596 assert_eq!(positions, vec![0, 2, 5, 7]);
597
598 let positions = mapped.find_all_patterns(b"abc");
600 assert_eq!(positions, vec![2]);
601
602 let positions = mapped.find_all_patterns(b"xyz");
604 assert_eq!(positions, Vec::<usize>::new());
605
606 let positions = mapped.find_all_patterns(b"a");
608 assert_eq!(positions, vec![0, 2, 5, 7]);
609 }
610
611 #[test]
612 fn test_pattern_edge_cases() {
613 let file = create_test_file();
614 let mapped = MappedBinary::new(file.path()).unwrap();
615
616 let long_pattern = vec![b'A'; 1000];
618 let result = mapped.find_pattern(&long_pattern);
619 assert_eq!(result, None);
620
621 let result = mapped.find_pattern(b"file.");
623 assert!(result.is_some());
624 }
625
626 #[test]
627 fn test_starts_with() {
628 let file = create_test_file();
629 let mapped = MappedBinary::new(file.path()).unwrap();
630
631 assert!(mapped.starts_with(b"Hello"));
632 assert!(!mapped.starts_with(b"World"));
633 assert!(mapped.starts_with(b"")); let mut long_signature = vec![0; 100];
637 long_signature[0] = b'H';
638 assert!(!mapped.starts_with(&long_signature));
639 }
640
641 #[test]
642 fn test_hexdump_method() {
643 let file = create_binary_test_file();
644 let mapped = MappedBinary::new(file.path()).unwrap();
645
646 let hexdump = mapped.hexdump(0, 8).unwrap();
648 assert!(hexdump.contains("00000000:"));
649 assert!(hexdump.contains("12 34 56 78"));
650
651 let result = mapped.hexdump(0, 1000);
653 assert!(result.is_err());
654 }
655
656 #[test]
657 fn test_view_creation() {
658 let file = create_test_file();
659 let mapped = MappedBinary::new(file.path()).unwrap();
660
661 let view = mapped.view(0..5).unwrap();
662 assert_eq!(view.size(), 5);
663 assert_eq!(view.offset(), 0);
664 assert_eq!(&*view, b"Hello");
665
666 let result = mapped.view(0..100);
668 assert!(result.is_err());
669 assert!(result
670 .unwrap_err()
671 .to_string()
672 .contains("Range exceeds file size"));
673 }
674
675 #[test]
676 fn test_mapped_view_methods() {
677 let file = create_test_file();
678 let mapped = MappedBinary::new(file.path()).unwrap();
679
680 let view = mapped.view(7..12).unwrap();
681 assert_eq!(view.offset(), 7);
682 assert_eq!(view.size(), 5);
683
684 let vec = view.to_vec();
686 assert_eq!(vec, b"World".to_vec());
687
688 assert_eq!(&*view, b"World");
690 assert_eq!(view.len(), 5);
691 }
692
693 #[test]
694 fn test_hexdump_function() {
695 let data = b"Hello, World!";
697 let hexdump = format_hexdump(data, 0);
698 assert!(hexdump.contains("48 65 6c 6c 6f 2c 20 57"));
699 assert!(hexdump.contains("Hello, W"));
700
701 let hexdump = format_hexdump(&[], 0);
703 assert_eq!(hexdump, "");
704
705 let data = &[
707 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
708 0x0E, 0x0F,
709 ];
710 let hexdump = format_hexdump(data, 0x1000);
711 assert!(hexdump.contains("00001000:"));
712 assert!(hexdump.contains("00 01 02 03 04 05 06 07"));
713 assert!(hexdump.contains("................"));
714
715 let data = b"This is a longer string that spans multiple lines";
717 let hexdump = format_hexdump(data, 0);
718 let lines: Vec<&str> = hexdump.lines().collect();
719 assert!(lines.len() > 1); let data = &[b'A', b'B', 0xFF, b'C', b'D'];
723 let hexdump = format_hexdump(data, 0);
724 assert!(hexdump.contains("AB.CD"));
725 }
726
727 #[test]
728 fn test_empty_file_handling() {
729 let file = create_empty_file();
730 let mapped = MappedBinary::new(file.path()).unwrap();
731
732 assert_eq!(mapped.size(), 0);
733
734 assert!(mapped.read_u8(0).is_err());
736 assert!(mapped.read_at(0, 1).is_err());
737 assert_eq!(mapped.find_pattern(b"test"), None);
738 assert_eq!(mapped.find_all_patterns(b"test"), Vec::<usize>::new());
739 assert!(mapped.starts_with(b"")); assert!(!mapped.starts_with(b"test"));
741
742 let result = mapped.view(0..1);
743 assert!(result.is_err());
744 }
745
746 #[test]
747 fn test_mmap_config_default() {
748 let config = MmapConfig::default();
749 assert!(!config.use_huge_pages);
750 assert!(!config.populate);
751 assert!(!config.lock_memory);
752 }
753
754 #[test]
755 fn test_mmap_config_debug() {
756 let config = MmapConfig {
757 use_huge_pages: true,
758 populate: false,
759 lock_memory: true,
760 };
761 let debug_str = format!("{:?}", config);
762 assert!(debug_str.contains("use_huge_pages: true"));
763 assert!(debug_str.contains("populate: false"));
764 assert!(debug_str.contains("lock_memory: true"));
765 }
766
767 #[test]
768 fn test_mmap_config_clone() {
769 let config = MmapConfig {
770 use_huge_pages: true,
771 populate: true,
772 lock_memory: false,
773 };
774 let cloned = config.clone();
775 assert!(cloned.use_huge_pages);
776 assert!(cloned.populate);
777 assert!(!cloned.lock_memory);
778 }
779
780 #[test]
781 fn test_advanced_mmap() {
782 let file = create_test_file();
783 let config = MmapConfig::default();
784 let advanced = AdvancedMmap::new(file.path(), config.clone()).unwrap();
785
786 assert_eq!(advanced.data().len(), 34);
787 assert_eq!(advanced.data()[0..5], *b"Hello");
788
789 let returned_config = advanced.config();
790 assert_eq!(returned_config.use_huge_pages, config.use_huge_pages);
791 assert_eq!(returned_config.populate, config.populate);
792 assert_eq!(returned_config.lock_memory, config.lock_memory);
793 }
794
795 #[test]
796 fn test_advanced_mmap_with_populate() {
797 let file = create_test_file();
798 let config = MmapConfig {
799 use_huge_pages: false,
800 populate: true,
801 lock_memory: false,
802 };
803 let advanced = AdvancedMmap::new(file.path(), config).unwrap();
804
805 assert_eq!(advanced.data().len(), 34);
806 assert!(advanced.config().populate);
807 }
808
809 #[test]
810 fn test_file_not_found_error() {
811 let result = MappedBinary::new("/nonexistent/file/path");
812 assert!(result.is_err());
813 let error = result.unwrap_err();
814 assert!(error.to_string().contains("Failed to open file"));
815 }
816
817 #[test]
818 fn test_advanced_mmap_file_not_found_error() {
819 let config = MmapConfig::default();
820 let result = AdvancedMmap::new("/nonexistent/file/path", config);
821 assert!(result.is_err());
822 let error = result.unwrap_err();
823 assert!(error.to_string().contains("Failed to open file"));
824 }
825
826 #[test]
827 fn test_large_data_operations() {
828 let mut file = NamedTempFile::new().unwrap();
829 let large_data = vec![0xAB; 10000]; file.write_all(&large_data).unwrap();
831 file.flush().unwrap();
832
833 let mapped = MappedBinary::new(file.path()).unwrap();
834
835 assert_eq!(mapped.read_u8(5000).unwrap(), 0xAB);
837 assert_eq!(mapped.read_bytes(1000, 100).unwrap().len(), 100);
838
839 let positions = mapped.find_all_patterns(&[0xAB, 0xAB]);
841 assert!(positions.len() > 1000); let hexdump = mapped.hexdump(8000, 32).unwrap();
845 assert!(hexdump.contains("00001f40:")); }
847
848 #[test]
849 fn test_boundary_conditions() {
850 let file = create_test_file(); let mapped = MappedBinary::new(file.path()).unwrap();
852
853 assert!(mapped.read_u8(33).is_ok()); assert!(mapped.read_u8(34).is_err()); assert!(mapped.read_at(30, 4).is_ok()); assert!(mapped.read_at(30, 5).is_err()); assert!(mapped.slice(0..34).is_ok()); assert!(mapped.slice(0..35).is_err()); assert!(mapped.view(0..34).is_ok()); assert!(mapped.view(0..35).is_err()); }
869}