Parse a phar archive from raw bytes. Returns `None` if the format is invalid or unsupported.
(data: Vec<u8>)
| 29 | /// Parse a phar archive from raw bytes. |
| 30 | /// Returns `None` if the format is invalid or unsupported. |
| 31 | pub fn parse(data: Vec<u8>) -> Option<Self> { |
| 32 | // 1. Find the stub end marker. |
| 33 | let marker_pos = find_marker(&data)?; |
| 34 | |
| 35 | // The manifest starts after the marker + a line ending (\r\n or \n). |
| 36 | let after_marker = marker_pos + HALT_COMPILER_MARKER.len(); |
| 37 | let manifest_start = if data.get(after_marker..after_marker + 2) == Some(b"\r\n") { |
| 38 | after_marker + 2 |
| 39 | } else if data.get(after_marker..after_marker + 1) == Some(b"\n") { |
| 40 | after_marker + 1 |
| 41 | } else { |
| 42 | return None; |
| 43 | }; |
| 44 | |
| 45 | let mut cursor = manifest_start; |
| 46 | |
| 47 | // 2. Parse the manifest header. |
| 48 | let manifest_length = read_u32(&data, &mut cursor)? as usize; |
| 49 | let manifest_end = manifest_start + 4 + manifest_length; |
| 50 | if manifest_end > data.len() { |
| 51 | return None; |
| 52 | } |
| 53 | |
| 54 | let file_count = read_u32(&data, &mut cursor)? as usize; |
| 55 | let _api_version = read_u16(&data, &mut cursor)?; |
| 56 | let _global_flags = read_u32(&data, &mut cursor)?; |
| 57 | |
| 58 | // Alias: 4-byte length + alias bytes. |
| 59 | let alias_len = read_u32(&data, &mut cursor)? as usize; |
| 60 | if cursor + alias_len > data.len() { |
| 61 | return None; |
| 62 | } |
| 63 | cursor += alias_len; // skip alias bytes |
| 64 | |
| 65 | // Metadata: 4-byte length + metadata bytes. |
| 66 | let metadata_len = read_u32(&data, &mut cursor)? as usize; |
| 67 | if cursor + metadata_len > data.len() { |
| 68 | return None; |
| 69 | } |
| 70 | cursor += metadata_len; // skip metadata bytes |
| 71 | |
| 72 | // 3. Parse each file entry and build the index. |
| 73 | // We collect entries first, then compute offsets into the data area. |
| 74 | struct RawEntry { |
| 75 | filename: String, |
| 76 | uncompressed_size: usize, |
| 77 | compressed_size: usize, |
| 78 | flags: u32, |
| 79 | } |
| 80 | |
| 81 | let mut entries = Vec::with_capacity(file_count); |
| 82 | |
| 83 | for _ in 0..file_count { |
| 84 | let filename_len = read_u32(&data, &mut cursor)? as usize; |
| 85 | if cursor + filename_len > data.len() { |
| 86 | return None; |
| 87 | } |
| 88 | let filename = |