Skip to main content

git_zero_server/upload_pack/
storage.rs

1//! Caller-owned fetch storage, with a borrowed view for each request.
2use git_zero_core::{ObjectId, RevisionLimits, ValidationMetadata};
3
4/// Explicit opt-in for time/exclusion history. One additional state byte per
5/// repository object, with limits shared by ref resolution and graph selection.
6/// Existing selection entrypoints retain their old capabilities and behavior.
7pub struct RevisionStorage<'a> {
8    pub selection: SelectionStorage<'a>,
9    pub state: &'a mut [u8],
10    pub limits: RevisionLimits,
11}
12
13impl RevisionStorage<'_> {
14    pub fn reborrow(&mut self) -> RevisionStorage<'_> {
15        RevisionStorage {
16            selection: self.selection.reborrow(),
17            state: self.state,
18            limits: self.limits,
19        }
20    }
21}
22
23/// Fixed-capacity backing for explicitly enabled revision selection. The added
24/// boundary state is exactly OBJECTS bytes; no request-sized allocation is hidden.
25pub struct RevisionBuffers<
26    const WANTS: usize,
27    const OBJECTS: usize,
28    const READ: usize,
29    const PARSE: usize,
30> {
31    selection: SelectionBuffers<WANTS, OBJECTS, READ, PARSE>,
32    state: [u8; OBJECTS],
33}
34
35impl<const WANTS: usize, const OBJECTS: usize, const READ: usize, const PARSE: usize>
36    RevisionBuffers<WANTS, OBJECTS, READ, PARSE>
37{
38    pub const fn new() -> Self {
39        Self {
40            selection: SelectionBuffers::new(),
41            state: [0; OBJECTS],
42        }
43    }
44    pub fn storage(
45        &mut self,
46        graph_read_limit: u64,
47        limits: RevisionLimits,
48    ) -> RevisionStorage<'_> {
49        RevisionStorage {
50            selection: self.selection.storage(graph_read_limit),
51            state: &mut self.state,
52            limits,
53        }
54    }
55    pub const fn storage_bytes(&self) -> usize {
56        core::mem::size_of::<Self>()
57    }
58}
59
60impl<const WANTS: usize, const OBJECTS: usize, const READ: usize, const PARSE: usize> Default
61    for RevisionBuffers<WANTS, OBJECTS, READ, PARSE>
62{
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68/// Independent per-request storage for bounded reachable-object selection.
69/// `wants` bounds distinct wanted IDs. `objects` and `indices` each need the
70/// repository's complete object count; their extra slots remain untouched.
71/// Read/parse scratch budgets match `ValidatedRepository::select_objects`.
72pub struct SelectionStorage<'a> {
73    pub wants: &'a mut [ObjectId],
74    pub objects: &'a mut [Option<ValidationMetadata>],
75    pub indices: &'a mut [usize],
76    pub read: &'a mut [u8],
77    pub parse: &'a mut [u8],
78    /// Maximum structural bytes read while traversing a request's graph.
79    pub graph_read_limit: u64,
80}
81
82impl SelectionStorage<'_> {
83    /// Borrow the same arrays for one request without moving the host descriptor.
84    pub fn reborrow(&mut self) -> SelectionStorage<'_> {
85        SelectionStorage {
86            wants: self.wants,
87            objects: self.objects,
88            indices: self.indices,
89            read: self.read,
90            parse: self.parse,
91            graph_read_limit: self.graph_read_limit,
92        }
93    }
94}
95
96/// Fixed-capacity backing storage for one independent selected-fetch session.
97/// All capacities are explicit const parameters; construction allocates nothing.
98/// Keep separate values for overlapping responses. Only the borrowed descriptor
99/// reaches the protocol encoder, so applications may also keep their own arrays
100/// and construct `SelectionStorage` directly.
101pub struct SelectionBuffers<
102    const WANTS: usize,
103    const OBJECTS: usize,
104    const READ: usize,
105    const PARSE: usize,
106> {
107    wants: [ObjectId; WANTS],
108    objects: [Option<ValidationMetadata>; OBJECTS],
109    indices: [usize; OBJECTS],
110    read: [u8; READ],
111    parse: [u8; PARSE],
112}
113
114impl<const WANTS: usize, const OBJECTS: usize, const READ: usize, const PARSE: usize>
115    SelectionBuffers<WANTS, OBJECTS, READ, PARSE>
116{
117    pub const fn new() -> Self {
118        Self {
119            wants: [ObjectId::Sha1([0; 20]); WANTS],
120            objects: [None; OBJECTS],
121            indices: [0; OBJECTS],
122            read: [0; READ],
123            parse: [0; PARSE],
124        }
125    }
126
127    /// Borrow a fresh request descriptor. Traversal resets the work arrays;
128    /// no clearing or reallocation is needed between successful/failed requests.
129    pub fn storage(&mut self, graph_read_limit: u64) -> SelectionStorage<'_> {
130        SelectionStorage {
131            wants: &mut self.wants,
132            objects: &mut self.objects,
133            indices: &mut self.indices,
134            read: &mut self.read,
135            parse: &mut self.parse,
136            graph_read_limit,
137        }
138    }
139
140    pub const fn want_capacity(&self) -> usize {
141        WANTS
142    }
143    pub const fn object_capacity(&self) -> usize {
144        OBJECTS
145    }
146    pub const fn read_capacity(&self) -> usize {
147        READ
148    }
149    pub const fn parse_capacity(&self) -> usize {
150        PARSE
151    }
152    /// Bytes owned by this value, including metadata and alignment padding.
153    pub const fn storage_bytes(&self) -> usize {
154        core::mem::size_of::<Self>()
155    }
156}
157
158impl<const WANTS: usize, const OBJECTS: usize, const READ: usize, const PARSE: usize> Default
159    for SelectionBuffers<WANTS, OBJECTS, READ, PARSE>
160{
161    fn default() -> Self {
162        Self::new()
163    }
164}