gix_pack/data/entry/
decode.rs1use std::io;
2
3use gix_features::decode::{leb64, leb64_from_read};
4
5use super::{BLOB, COMMIT, OFS_DELTA, REF_DELTA, TAG, TREE};
6use crate::data;
7
8#[derive(Debug, thiserror::Error)]
10#[allow(missing_docs)]
11#[error("Object type {type_id} is unsupported")]
12pub struct Error {
13 pub type_id: u8,
14}
15
16impl data::Entry {
18 pub fn from_bytes(d: &[u8], pack_offset: data::Offset, hash_len: usize) -> Result<data::Entry, Error> {
24 let (type_id, size, mut consumed) = parse_header_info(d);
25
26 use crate::data::entry::Header::*;
27 let object = match type_id {
28 OFS_DELTA => {
29 let (distance, leb_bytes) = leb64(&d[consumed..]);
30 let delta = OfsDelta {
31 base_distance: distance,
32 };
33 consumed += leb_bytes;
34 delta
35 }
36 REF_DELTA => {
37 let delta = RefDelta {
38 base_id: gix_hash::ObjectId::from_bytes_or_panic(&d[consumed..][..hash_len]),
39 };
40 consumed += hash_len;
41 delta
42 }
43 BLOB => Blob,
44 TREE => Tree,
45 COMMIT => Commit,
46 TAG => Tag,
47 other => return Err(Error { type_id: other }),
48 };
49 Ok(data::Entry {
50 header: object,
51 decompressed_size: size,
52 data_offset: pack_offset + consumed as u64,
53 })
54 }
55
56 pub fn from_read(r: &mut dyn io::Read, pack_offset: data::Offset, hash_len: usize) -> io::Result<data::Entry> {
58 let (type_id, size, mut consumed) = streaming_parse_header_info(r)?;
59
60 use crate::data::entry::Header::*;
61 let object = match type_id {
62 OFS_DELTA => {
63 let (distance, leb_bytes) = leb64_from_read(r)?;
64 let delta = OfsDelta {
65 base_distance: distance,
66 };
67 consumed += leb_bytes;
68 delta
69 }
70 REF_DELTA => {
71 let mut buf = gix_hash::Kind::buf();
72 let hash = &mut buf[..hash_len];
73 r.read_exact(hash)?;
74 #[allow(clippy::redundant_slicing)]
75 let delta = RefDelta {
76 base_id: gix_hash::ObjectId::from_bytes_or_panic(&hash[..]),
77 };
78 consumed += hash_len;
79 delta
80 }
81 BLOB => Blob,
82 TREE => Tree,
83 COMMIT => Commit,
84 TAG => Tag,
85 other => {
86 return Err(io::Error::new(
87 io::ErrorKind::Other,
88 format!("Object type {other} is unsupported"),
89 ))
90 }
91 };
92 Ok(data::Entry {
93 header: object,
94 decompressed_size: size,
95 data_offset: pack_offset + consumed as u64,
96 })
97 }
98}
99
100#[inline]
101fn streaming_parse_header_info(read: &mut dyn io::Read) -> Result<(u8, u64, usize), io::Error> {
102 let mut byte = [0u8; 1];
103 read.read_exact(&mut byte)?;
104 let mut c = byte[0];
105 let mut i = 1;
106 let type_id = (c >> 4) & 0b0000_0111;
107 let mut size = u64::from(c) & 0b0000_1111;
108 let mut s = 4;
109 while c & 0b1000_0000 != 0 {
110 read.read_exact(&mut byte)?;
111 c = byte[0];
112 i += 1;
113 size += u64::from(c & 0b0111_1111) << s;
114 s += 7;
115 }
116 Ok((type_id, size, i))
117}
118
119#[inline]
121fn parse_header_info(data: &[u8]) -> (u8, u64, usize) {
122 let mut c = data[0];
123 let mut i = 1;
124 let type_id = (c >> 4) & 0b0000_0111;
125 let mut size = u64::from(c) & 0b0000_1111;
126 let mut s = 4;
127 while c & 0b1000_0000 != 0 {
128 c = data[i];
129 i += 1;
130 size += u64::from(c & 0b0111_1111) << s;
131 s += 7;
132 }
133 (type_id, size, i)
134}