gix_ref/store/packed/
buffer.rs1use std::path::PathBuf;
2
3use gix_error::{ErrorExt, ExnResult, Message, ResultExt, message};
4
5use crate::store_impl::packed;
6
7impl AsRef<[u8]> for packed::Buffer {
8 fn as_ref(&self) -> &[u8] {
9 &self.data.as_ref()[self.offset..]
10 }
11}
12
13impl AsRef<[u8]> for packed::Backing {
14 fn as_ref(&self) -> &[u8] {
15 match self {
16 packed::Backing::InMemory(data) => data,
17 packed::Backing::Mapped(map) => map,
18 }
19 }
20}
21
22impl packed::Buffer {
24 fn open_with_backing(backing: packed::Backing, path: PathBuf, object_hash: gix_hash::Kind) -> ExnResult<Self> {
25 let (backing, offset) = {
26 let (offset, sorted) = {
27 let mut input = backing.as_ref();
28 if *input.first().unwrap_or(&b' ') == b'#' {
29 let header = packed::decode::header(&mut input).map_err(|()| {
30 gix_error::corruption("The header could not be parsed, even though first line started with '#'")
31 .raise_erased()
32 })?;
33 let offset = backing.as_ref().len() - input.len();
34 (offset, header.sorted)
35 } else {
36 (0, false)
37 }
38 };
39
40 if !sorted {
41 let mut entries = packed::Iter::new(&backing.as_ref()[offset..], object_hash)
43 .or_raise_erased(|| message("Could not iterate unsorted packed refs"))?
44 .collect::<Result<Vec<_>, _>>()
45 .or_erased()?;
46 entries.sort_by_key(|e| e.name.as_bstr());
47 let mut serialized = Vec::<u8>::new();
48 for entry in entries {
49 serialized.extend_from_slice(entry.target);
50 serialized.push(b' ');
51 serialized.extend_from_slice(entry.name.as_bstr());
52 serialized.push(b'\n');
53 if let Some(object) = entry.object {
54 serialized.push(b'^');
55 serialized.extend_from_slice(object);
56 serialized.push(b'\n');
57 }
58 }
59 (packed::Backing::InMemory(serialized), 0)
60 } else {
61 (backing, offset)
62 }
63 };
64 Ok(packed::Buffer {
65 offset,
66 data: backing,
67 path,
68 object_hash,
69 })
70 }
71
72 pub fn open(
80 path: PathBuf,
81 use_memory_map_if_larger_than_bytes: u64,
82 object_hash: gix_hash::Kind,
83 ) -> ExnResult<Self> {
84 let backing = (|| -> std::io::Result<packed::Backing> {
85 Ok(
86 if std::fs::metadata(&path)?.len() <= use_memory_map_if_larger_than_bytes {
87 packed::Backing::InMemory(std::fs::read(&path)?)
88 } else {
89 packed::Backing::Mapped(
90 #[expect(unsafe_code)]
92 unsafe {
93 memmap2::MmapOptions::new().map_copy_read_only(&std::fs::File::open(&path)?)?
94 },
95 )
96 },
97 )
98 })()
99 .or_raise_erased(|| Message::new("Could not open packed refs").with("path", path.as_path()))?;
100 Self::open_with_backing(backing, path, object_hash)
101 }
102
103 pub fn from_bytes(bytes: &[u8], object_hash: gix_hash::Kind) -> ExnResult<Self> {
109 let backing = packed::Backing::InMemory(bytes.into());
110 Self::open_with_backing(backing, PathBuf::from("<memory>"), object_hash)
111 }
112}