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 unsafe fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
30 let file = File::open(path)
31 .map_err(|e| BinaryError::memory_map(format!("Failed to open file: {}", e)))?;
32
33 let mmap = unsafe {
35 MmapOptions::new().map(&file).map_err(|e| {
36 BinaryError::memory_map(format!("Failed to create memory map: {}", e))
37 })?
38 };
39
40 let size = mmap.len();
41
42 Ok(Self {
43 _file: file,
44 mmap,
45 size,
46 })
47 }
48
49 pub unsafe fn from_file(file: File) -> Result<Self> {
57 let mmap = unsafe {
59 MmapOptions::new().map(&file).map_err(|e| {
60 BinaryError::memory_map(format!("Failed to create memory map: {}", e))
61 })?
62 };
63
64 let size = mmap.len();
65
66 Ok(Self {
67 _file: file,
68 mmap,
69 size,
70 })
71 }
72
73 pub fn size(&self) -> usize {
75 self.size
76 }
77
78 pub fn slice(&self, range: Range<usize>) -> crate::types::ByteSliceResult<'_> {
80 if range.start > range.end {
81 return Err(BinaryError::memory_map("Invalid reversed range"));
82 }
83 self.mmap
84 .get(range)
85 .ok_or_else(|| BinaryError::memory_map("Range exceeds file size"))
86 }
87
88 pub fn read_at(&self, offset: usize, length: usize) -> crate::types::ByteSliceResult<'_> {
90 let end = offset
91 .checked_add(length)
92 .ok_or_else(|| BinaryError::memory_map("Read range overflows usize"))?;
93 self.mmap
94 .get(offset..end)
95 .ok_or_else(|| BinaryError::memory_map("Read exceeds file size"))
96 }
97
98 pub fn read_bytes(&self, offset: usize, count: usize) -> Result<Vec<u8>> {
100 let data = self.read_at(offset, count)?;
101 Ok(data.to_vec())
102 }
103
104 pub fn read_u8(&self, offset: usize) -> Result<u8> {
106 if offset >= self.size {
107 return Err(BinaryError::memory_map(
108 "Offset exceeds file size".to_string(),
109 ));
110 }
111 Ok(self.mmap[offset])
112 }
113
114 pub fn read_u16_le(&self, offset: usize) -> Result<u16> {
116 let bytes = self.read_at(offset, 2)?;
117 Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
118 }
119
120 pub fn read_u16_be(&self, offset: usize) -> Result<u16> {
122 let bytes = self.read_at(offset, 2)?;
123 Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
124 }
125
126 pub fn read_u32_le(&self, offset: usize) -> Result<u32> {
128 let bytes = self.read_at(offset, 4)?;
129 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
130 }
131
132 pub fn read_u32_be(&self, offset: usize) -> Result<u32> {
134 let bytes = self.read_at(offset, 4)?;
135 Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
136 }
137
138 pub fn read_u64_le(&self, offset: usize) -> Result<u64> {
140 let bytes = self.read_at(offset, 8)?;
141 Ok(u64::from_le_bytes([
142 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
143 ]))
144 }
145
146 pub fn read_u64_be(&self, offset: usize) -> Result<u64> {
148 let bytes = self.read_at(offset, 8)?;
149 Ok(u64::from_be_bytes([
150 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
151 ]))
152 }
153
154 pub fn read_cstring(&self, offset: usize, max_length: usize) -> Result<String> {
156 if offset > self.size {
157 return Err(BinaryError::memory_map("Offset exceeds file size"));
158 }
159
160 let mut end = offset;
161 let limit = offset.saturating_add(max_length).min(self.size);
162
163 while end < limit && self.mmap[end] != 0 {
164 end += 1;
165 }
166
167 let bytes = &self.mmap[offset..end];
168 String::from_utf8(bytes.to_vec())
169 .map_err(|e| BinaryError::memory_map(format!("Invalid UTF-8 string: {}", e)))
170 }
171
172 pub fn find_pattern(&self, pattern: &[u8]) -> Option<usize> {
174 if pattern.is_empty() {
175 return Some(0);
176 }
177
178 self.mmap
179 .windows(pattern.len())
180 .position(|window| window == pattern)
181 }
182
183 pub fn find_all_patterns(&self, pattern: &[u8]) -> Vec<usize> {
185 if pattern.is_empty() || pattern.len() > self.size {
188 return Vec::new();
189 }
190
191 let mut positions = Vec::new();
192 let mut start = 0;
193
194 while start <= self.size - pattern.len() {
195 if let Some(pos) = self.mmap[start..]
196 .windows(pattern.len())
197 .position(|window| window == pattern)
198 {
199 positions.push(start + pos);
200 start += pos + 1;
201 } else {
202 break;
203 }
204 }
205
206 positions
207 }
208
209 pub fn starts_with(&self, signature: &[u8]) -> bool {
211 if signature.len() > self.size {
212 return false;
213 }
214
215 &self.mmap[..signature.len()] == signature
216 }
217
218 pub fn hexdump(&self, offset: usize, length: usize) -> Result<String> {
220 let data = self.read_at(offset, length)?;
221 Ok(format_hexdump(data, offset))
222 }
223
224 pub fn view(&self, range: Range<usize>) -> Result<MappedView<'_>> {
226 if range.start > range.end {
227 return Err(BinaryError::memory_map("Invalid reversed range"));
228 }
229 let offset = range.start;
230 let data = self
231 .mmap
232 .get(range)
233 .ok_or_else(|| BinaryError::memory_map("Range exceeds file size"))?;
234
235 Ok(MappedView { data, offset })
236 }
237}
238
239impl Deref for MappedBinary {
240 type Target = [u8];
241
242 fn deref(&self) -> &Self::Target {
243 &self.mmap
244 }
245}
246
247#[derive(Debug)]
249pub struct MappedView<'a> {
250 data: &'a [u8],
251 offset: usize,
252}
253
254impl<'a> MappedView<'a> {
255 pub fn offset(&self) -> usize {
257 self.offset
258 }
259
260 pub fn size(&self) -> usize {
262 self.data.len()
263 }
264
265 pub fn to_vec(&self) -> Vec<u8> {
267 self.data.to_vec()
268 }
269}
270
271impl<'a> Deref for MappedView<'a> {
272 type Target = [u8];
273
274 fn deref(&self) -> &Self::Target {
275 self.data
276 }
277}
278
279fn format_hexdump(data: &[u8], base_offset: usize) -> String {
281 let mut result = String::new();
282
283 for (i, chunk) in data.chunks(16).enumerate() {
284 let offset = base_offset.saturating_add(i.saturating_mul(16));
285 result.push_str(&format!("{:08x}: ", offset));
286
287 for (j, byte) in chunk.iter().enumerate() {
289 if j == 8 {
290 result.push(' ');
291 }
292 result.push_str(&format!("{:02x} ", byte));
293 }
294
295 for j in chunk.len()..16 {
297 if j == 8 {
298 result.push(' ');
299 }
300 result.push_str(" ");
301 }
302
303 result.push_str(" |");
305 for byte in chunk {
306 if byte.is_ascii_graphic() || *byte == b' ' {
307 result.push(*byte as char);
308 } else {
309 result.push('.');
310 }
311 }
312 result.push_str("|\n");
313 }
314
315 result
316}
317
318#[derive(Debug, Clone, Default)]
320pub struct MmapConfig {
321 pub use_huge_pages: bool,
323 pub populate: bool,
326 pub lock_memory: bool,
328}
329
330#[derive(Debug)]
332pub struct AdvancedMmap {
333 _file: File,
334 mmap: Mmap,
335 config: MmapConfig,
336}
337
338impl AdvancedMmap {
339 pub unsafe fn new<P: AsRef<Path>>(path: P, config: MmapConfig) -> Result<Self> {
350 if config.use_huge_pages {
351 return Err(BinaryError::config(
352 "huge pages are not supported for file-backed memory maps",
353 ));
354 }
355
356 #[cfg(not(any(target_os = "linux", target_os = "android")))]
357 if config.populate {
358 return Err(BinaryError::config(
359 "populating mapped pages is unsupported on this platform",
360 ));
361 }
362
363 #[cfg(not(unix))]
364 if config.lock_memory {
365 return Err(BinaryError::config(
366 "locking mapped memory is unsupported on this platform",
367 ));
368 }
369
370 let file = File::open(path)
371 .map_err(|e| BinaryError::memory_map(format!("Failed to open file: {}", e)))?;
372
373 let mut options = MmapOptions::new();
374
375 if config.populate {
376 options.populate();
377 }
378
379 let mmap = unsafe {
381 options.map(&file).map_err(|e| {
382 BinaryError::memory_map(format!("Failed to create memory map: {}", e))
383 })?
384 };
385
386 #[cfg(unix)]
387 if config.lock_memory {
388 mmap.lock().map_err(|error| {
389 BinaryError::memory_map(format!("Failed to lock memory map: {error}"))
390 })?;
391 }
392
393 Ok(Self {
394 _file: file,
395 mmap,
396 config,
397 })
398 }
399
400 pub fn data(&self) -> &[u8] {
402 &self.mmap
403 }
404
405 pub fn config(&self) -> &MmapConfig {
407 &self.config
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414 use std::io::Write;
415 use tempfile::NamedTempFile;
416
417 fn mapped_binary(path: impl AsRef<Path>) -> Result<super::MappedBinary> {
418 unsafe { super::MappedBinary::new(path) }
421 }
422
423 fn mapped_binary_from_file(file: File) -> Result<super::MappedBinary> {
424 unsafe { super::MappedBinary::from_file(file) }
427 }
428
429 fn advanced_mmap(path: impl AsRef<Path>, config: MmapConfig) -> Result<super::AdvancedMmap> {
430 unsafe { super::AdvancedMmap::new(path, config) }
433 }
434
435 fn create_test_file() -> NamedTempFile {
436 let mut file = NamedTempFile::new().unwrap();
437 file.write_all(b"Hello, World! This is a test file.")
438 .unwrap();
439 file.flush().unwrap();
440 file
441 }
442
443 fn create_binary_test_file() -> NamedTempFile {
444 let mut file = NamedTempFile::new().unwrap();
445 let data = vec![
447 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, ];
456 file.write_all(&data).unwrap();
457 file.flush().unwrap();
458 file
459 }
460
461 fn create_empty_file() -> NamedTempFile {
462 NamedTempFile::new().unwrap()
463 }
464
465 #[test]
466 fn test_mapped_binary_creation() {
467 let file = create_test_file();
468 let mapped = mapped_binary(file.path());
469 assert!(mapped.is_ok());
470
471 let mapped = mapped.unwrap();
472 assert_eq!(mapped.size(), 34);
473 }
474
475 #[test]
476 fn test_mapped_binary_from_file() {
477 let temp_file = create_test_file();
478 let file = File::open(temp_file.path()).unwrap();
479 let mapped = mapped_binary_from_file(file);
480 assert!(mapped.is_ok());
481
482 let mapped = mapped.unwrap();
483 assert_eq!(mapped.size(), 34);
484 assert_eq!(&mapped[0..5], b"Hello");
485 }
486
487 #[test]
488 fn test_mapped_binary_deref() {
489 let file = create_test_file();
490 let mapped = mapped_binary(file.path()).unwrap();
491
492 assert_eq!(&mapped[0..5], b"Hello");
494 assert_eq!(mapped.len(), 34);
495 }
496
497 #[test]
498 fn test_slice_method() {
499 let file = create_test_file();
500 let mapped = mapped_binary(file.path()).unwrap();
501
502 let slice = mapped.slice(0..5).unwrap();
504 assert_eq!(slice, b"Hello");
505
506 let slice = mapped.slice(7..12).unwrap();
507 assert_eq!(slice, b"World");
508
509 let result = mapped.slice(0..100);
511 assert!(result.is_err());
512 assert!(
513 result
514 .unwrap_err()
515 .to_string()
516 .contains("Range exceeds file size")
517 );
518 }
519
520 #[test]
521 fn test_read_operations() {
522 let file = create_test_file();
523 let mapped = mapped_binary(file.path()).unwrap();
524
525 let data = mapped.read_at(0, 5).unwrap();
527 assert_eq!(data, b"Hello");
528
529 let bytes = mapped.read_bytes(7, 5).unwrap();
531 assert_eq!(bytes, b"World".to_vec());
532
533 let byte = mapped.read_u8(0).unwrap();
535 assert_eq!(byte, b'H');
536 }
537
538 #[test]
539 fn test_read_operations_errors() {
540 let file = create_test_file();
541 let mapped = mapped_binary(file.path()).unwrap();
542
543 let result = mapped.read_at(0, 100);
545 assert!(result.is_err());
546 assert!(
547 result
548 .unwrap_err()
549 .to_string()
550 .contains("Read exceeds file size")
551 );
552
553 let result = mapped.read_at(50, 5);
555 assert!(result.is_err());
556
557 let result = mapped.read_bytes(0, 100);
559 assert!(result.is_err());
560
561 let result = mapped.read_u8(100);
563 assert!(result.is_err());
564 assert!(
565 result
566 .unwrap_err()
567 .to_string()
568 .contains("Offset exceeds file size")
569 );
570
571 let result = mapped.read_at(usize::MAX, 2);
573 assert!(result.unwrap_err().to_string().contains("overflows usize"));
574 }
575
576 #[test]
577 fn test_integer_read_operations() {
578 let file = create_binary_test_file();
579 let mapped = mapped_binary(file.path()).unwrap();
580
581 let val_le = mapped.read_u16_le(0).unwrap();
583 assert_eq!(val_le, 0x3412); let val_be = mapped.read_u16_be(0).unwrap();
586 assert_eq!(val_be, 0x1234); let val_le = mapped.read_u32_le(0).unwrap();
590 assert_eq!(val_le, 0x78563412); let val_be = mapped.read_u32_be(0).unwrap();
593 assert_eq!(val_be, 0x12345678); let val_le = mapped.read_u64_le(24).unwrap();
597 assert_eq!(val_le, 0x0706050403020100); let val_be = mapped.read_u64_be(24).unwrap();
600 assert_eq!(val_be, 0x0001020304050607); }
602
603 #[test]
604 fn test_integer_read_operations_errors() {
605 let file = create_binary_test_file();
606 let mapped = mapped_binary(file.path()).unwrap();
607 let file_size = mapped.size();
608
609 assert!(mapped.read_u16_le(file_size).is_err());
611 assert!(mapped.read_u16_be(file_size - 1).is_err());
612
613 assert!(mapped.read_u32_le(file_size).is_err());
615 assert!(mapped.read_u32_be(file_size - 3).is_err());
616
617 assert!(mapped.read_u64_le(file_size).is_err());
619 assert!(mapped.read_u64_be(file_size - 7).is_err());
620 }
621
622 #[test]
623 fn test_read_cstring() {
624 let file = create_binary_test_file();
625 let mapped = mapped_binary(file.path()).unwrap();
626
627 let s = mapped.read_cstring(8, 10).unwrap();
629 assert_eq!(s, "Hello");
630
631 let s = mapped.read_cstring(14, 10).unwrap();
633 assert_eq!(s, "World");
634
635 let s = mapped.read_cstring(8, 3).unwrap();
637 assert_eq!(s, "Hel");
638
639 let result = mapped.read_cstring(8, 1000);
641 assert!(result.is_ok()); }
643
644 #[test]
645 fn test_read_cstring_errors() {
646 let mut file = NamedTempFile::new().unwrap();
647 file.write_all(&[0xFF, 0xFE, 0x00]).unwrap();
649 file.flush().unwrap();
650
651 let mapped = mapped_binary(file.path()).unwrap();
652
653 let result = mapped.read_cstring(0, 10);
655 assert!(result.is_err());
656 assert!(
657 result
658 .unwrap_err()
659 .to_string()
660 .contains("Invalid UTF-8 string")
661 );
662
663 assert!(mapped.read_cstring(usize::MAX, 1).is_err());
664 }
665
666 #[test]
667 fn test_pattern_finding() {
668 let file = create_test_file();
669 let mapped = mapped_binary(file.path()).unwrap();
670
671 let pos = mapped.find_pattern(b"World");
673 assert_eq!(pos, Some(7));
674
675 let pos = mapped.find_pattern(b"xyz");
676 assert_eq!(pos, None);
677
678 let pos = mapped.find_pattern(b"H");
680 assert_eq!(pos, Some(0));
681 }
682
683 #[test]
684 fn test_find_all_patterns() {
685 let mut file = NamedTempFile::new().unwrap();
686 file.write_all(b"ababcabab").unwrap();
687 file.flush().unwrap();
688
689 let mapped = mapped_binary(file.path()).unwrap();
690
691 let positions = mapped.find_all_patterns(b"ab");
693 assert_eq!(positions, vec![0, 2, 5, 7]);
694
695 let positions = mapped.find_all_patterns(b"abc");
697 assert_eq!(positions, vec![2]);
698
699 let positions = mapped.find_all_patterns(b"xyz");
701 assert_eq!(positions, Vec::<usize>::new());
702
703 let positions = mapped.find_all_patterns(b"a");
705 assert_eq!(positions, vec![0, 2, 5, 7]);
706 }
707
708 #[test]
709 fn test_pattern_edge_cases() {
710 let file = create_test_file();
711 let mapped = mapped_binary(file.path()).unwrap();
712
713 let long_pattern = vec![b'A'; 1000];
715 let result = mapped.find_pattern(&long_pattern);
716 assert_eq!(result, None);
717
718 let result = mapped.find_pattern(b"file.");
720 assert!(result.is_some());
721
722 assert_eq!(mapped.find_pattern(b""), Some(0));
723 assert!(mapped.find_all_patterns(b"").is_empty());
724 }
725
726 #[test]
727 fn test_starts_with() {
728 let file = create_test_file();
729 let mapped = mapped_binary(file.path()).unwrap();
730
731 assert!(mapped.starts_with(b"Hello"));
732 assert!(!mapped.starts_with(b"World"));
733 assert!(mapped.starts_with(b"")); let mut long_signature = vec![0; 100];
737 long_signature[0] = b'H';
738 assert!(!mapped.starts_with(&long_signature));
739 }
740
741 #[test]
742 fn test_hexdump_method() {
743 let file = create_binary_test_file();
744 let mapped = mapped_binary(file.path()).unwrap();
745
746 let hexdump = mapped.hexdump(0, 8).unwrap();
748 assert!(hexdump.contains("00000000:"));
749 assert!(hexdump.contains("12 34 56 78"));
750
751 let result = mapped.hexdump(0, 1000);
753 assert!(result.is_err());
754 }
755
756 #[test]
757 fn test_view_creation() {
758 let file = create_test_file();
759 let mapped = mapped_binary(file.path()).unwrap();
760
761 let view = mapped.view(0..5).unwrap();
762 assert_eq!(view.size(), 5);
763 assert_eq!(view.offset(), 0);
764 assert_eq!(&*view, b"Hello");
765
766 let result = mapped.view(0..100);
768 assert!(result.is_err());
769 assert!(
770 result
771 .unwrap_err()
772 .to_string()
773 .contains("Range exceeds file size")
774 );
775
776 let reversed_start = 5;
777 let reversed_end = 4;
778 assert!(mapped.view(reversed_start..reversed_end).is_err());
779 assert!(mapped.slice(reversed_start..reversed_end).is_err());
780 }
781
782 #[test]
783 fn test_mapped_view_methods() {
784 let file = create_test_file();
785 let mapped = mapped_binary(file.path()).unwrap();
786
787 let view = mapped.view(7..12).unwrap();
788 assert_eq!(view.offset(), 7);
789 assert_eq!(view.size(), 5);
790
791 let vec = view.to_vec();
793 assert_eq!(vec, b"World".to_vec());
794
795 assert_eq!(&*view, b"World");
797 assert_eq!(view.len(), 5);
798 }
799
800 #[test]
801 fn test_hexdump_function() {
802 let data = b"Hello, World!";
804 let hexdump = format_hexdump(data, 0);
805 assert!(hexdump.contains("48 65 6c 6c 6f 2c 20 57"));
806 assert!(hexdump.contains("Hello, W"));
807
808 let hexdump = format_hexdump(&[], 0);
810 assert_eq!(hexdump, "");
811
812 let data = &[
814 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
815 0x0E, 0x0F,
816 ];
817 let hexdump = format_hexdump(data, 0x1000);
818 assert!(hexdump.contains("00001000:"));
819 assert!(hexdump.contains("00 01 02 03 04 05 06 07"));
820 assert!(hexdump.contains("................"));
821
822 let data = b"This is a longer string that spans multiple lines";
824 let hexdump = format_hexdump(data, 0);
825 let lines: Vec<&str> = hexdump.lines().collect();
826 assert!(lines.len() > 1); let data = &[b'A', b'B', 0xFF, b'C', b'D'];
830 let hexdump = format_hexdump(data, 0);
831 assert!(hexdump.contains("AB.CD"));
832
833 let hexdump = format_hexdump(&[0; 32], usize::MAX - 4);
834 assert_eq!(hexdump.lines().count(), 2);
835 }
836
837 #[test]
838 fn test_empty_file_handling() {
839 let file = create_empty_file();
840 let mapped = mapped_binary(file.path()).unwrap();
841
842 assert_eq!(mapped.size(), 0);
843
844 assert!(mapped.read_u8(0).is_err());
846 assert!(mapped.read_at(0, 1).is_err());
847 assert_eq!(mapped.find_pattern(b"test"), None);
848 assert_eq!(mapped.find_all_patterns(b"test"), Vec::<usize>::new());
849 assert!(mapped.starts_with(b"")); assert!(!mapped.starts_with(b"test"));
851
852 let result = mapped.view(0..1);
853 assert!(result.is_err());
854 }
855
856 #[test]
857 fn test_mmap_config_default() {
858 let config = MmapConfig::default();
859 assert!(!config.use_huge_pages);
860 assert!(!config.populate);
861 assert!(!config.lock_memory);
862 }
863
864 #[test]
865 fn test_mmap_config_debug() {
866 let config = MmapConfig {
867 use_huge_pages: true,
868 populate: false,
869 lock_memory: true,
870 };
871 let debug_str = format!("{:?}", config);
872 assert!(debug_str.contains("use_huge_pages: true"));
873 assert!(debug_str.contains("populate: false"));
874 assert!(debug_str.contains("lock_memory: true"));
875 }
876
877 #[test]
878 fn test_mmap_config_clone() {
879 let config = MmapConfig {
880 use_huge_pages: true,
881 populate: true,
882 lock_memory: false,
883 };
884 let cloned = config.clone();
885 assert!(cloned.use_huge_pages);
886 assert!(cloned.populate);
887 assert!(!cloned.lock_memory);
888 }
889
890 #[test]
891 fn test_advanced_mmap() {
892 let file = create_test_file();
893 let config = MmapConfig::default();
894 let advanced = advanced_mmap(file.path(), config.clone()).unwrap();
895
896 assert_eq!(advanced.data().len(), 34);
897 assert_eq!(advanced.data()[0..5], *b"Hello");
898
899 let returned_config = advanced.config();
900 assert_eq!(returned_config.use_huge_pages, config.use_huge_pages);
901 assert_eq!(returned_config.populate, config.populate);
902 assert_eq!(returned_config.lock_memory, config.lock_memory);
903 }
904
905 #[cfg(any(target_os = "linux", target_os = "android"))]
906 #[test]
907 fn test_advanced_mmap_with_populate() {
908 let file = create_test_file();
909 let config = MmapConfig {
910 use_huge_pages: false,
911 populate: true,
912 lock_memory: false,
913 };
914 let advanced = advanced_mmap(file.path(), config).unwrap();
915
916 assert_eq!(advanced.data().len(), 34);
917 assert!(advanced.config().populate);
918 }
919
920 #[cfg(not(any(target_os = "linux", target_os = "android")))]
921 #[test]
922 fn test_advanced_mmap_rejects_populate_when_unsupported() {
923 let file = create_test_file();
924 let config = MmapConfig {
925 use_huge_pages: false,
926 populate: true,
927 lock_memory: false,
928 };
929
930 let error = advanced_mmap(file.path(), config).unwrap_err();
931 assert!(matches!(error, BinaryError::ConfigError(_)));
932 assert!(error.to_string().contains("unsupported on this platform"));
933 }
934
935 #[test]
936 fn test_advanced_mmap_rejects_ignored_huge_page_request() {
937 let file = create_test_file();
938 let config = MmapConfig {
939 use_huge_pages: true,
940 ..Default::default()
941 };
942
943 let error = advanced_mmap(file.path(), config).unwrap_err();
944 assert!(error.to_string().contains("not supported"));
945 }
946
947 #[test]
948 fn test_file_not_found_error() {
949 let result = mapped_binary("/nonexistent/file/path");
950 assert!(result.is_err());
951 let error = result.unwrap_err();
952 assert!(error.to_string().contains("Failed to open file"));
953 }
954
955 #[test]
956 fn test_advanced_mmap_file_not_found_error() {
957 let config = MmapConfig::default();
958 let result = advanced_mmap("/nonexistent/file/path", config);
959 assert!(result.is_err());
960 let error = result.unwrap_err();
961 assert!(error.to_string().contains("Failed to open file"));
962 }
963
964 #[test]
965 fn test_large_data_operations() {
966 let mut file = NamedTempFile::new().unwrap();
967 let large_data = vec![0xAB; 10000]; file.write_all(&large_data).unwrap();
969 file.flush().unwrap();
970
971 let mapped = mapped_binary(file.path()).unwrap();
972
973 assert_eq!(mapped.read_u8(5000).unwrap(), 0xAB);
975 assert_eq!(mapped.read_bytes(1000, 100).unwrap().len(), 100);
976
977 let positions = mapped.find_all_patterns(&[0xAB, 0xAB]);
979 assert!(positions.len() > 1000); let hexdump = mapped.hexdump(8000, 32).unwrap();
983 assert!(hexdump.contains("00001f40:")); }
985
986 #[test]
987 fn test_boundary_conditions() {
988 let file = create_test_file(); let mapped = mapped_binary(file.path()).unwrap();
990
991 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()); }
1007}