1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! Constants related to Git object formats and operational limits.
//!
//! # Architecture
//! This module centralizes all magic numbers and structural limits used across the crate.
//! By extracting these into named constants, we eliminate "magic numbers" from the business
//! logic, making the codebase easier to audit and maintain.
//!
//! # Design Rationale: Resource Exhaustion Prevention
//! Version control systems frequently handle untrusted or malformed data. Without strict
//! upper limits, a maliciously crafted repository could instruct the parser to allocate
//! gigabytes of memory (e.g., a blob claiming to be 10 Exabytes). The `MAX_*` constants
//! act as fail-fast circuit breakers during object construction, ensuring that memory
//! allocation remains bounded and predictable.
//!
//! # Git Protocol Compliance
//! Constants like [`HASH_LENGTH`] and the modes in [`entry_mode`] are dictated by the Git
//! core specification. Hardcoding them ensures strict compliance with standard Git clients
//! and servers, preventing protocol violations.
/// Git object entry modes.
///
/// # Architecture
/// In Git, filesystem objects are identified by a 32-bit mode. This module exposes
/// the specific constants recognized by the Git protocol. Using named constants
/// instead of raw integers prevents invalid mode combinations and makes tree
/// manipulation code self-documenting.
///
/// # How it works
/// The modes combine Unix permission bits with Git-specific object types.
/// For example, `0o100_644` indicates a regular file (`0o100`) with read/write
/// permissions for the owner and read-only for others (`0o644`).
/// The length of a hash in bytes (SHA-512 = 64).
///
/// # Why this exists
/// This crate mandates SHA-512 for cryptographic integrity. By hardcoding the length
/// to 64 bytes, we enable the use of fixed-size arrays (e.g., `[u8; HASH_LENGTH]`)
/// instead of dynamically allocated `Vec<u8>`. This shifts memory management to the
/// compile-time stack, eliminating heap allocation overhead and fragmentation for
/// every hash operation.
///
/// # How it works
/// The constant is evaluated at compile time. Any array sized with this constant
/// benefits from fixed stack layout, and the compiler can aggressively optimize
/// loops iterating exactly `HASH_LENGTH` times.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::constants::HASH_LENGTH;
/// assert_eq!(HASH_LENGTH, 64);
/// let hash_array = [0_u8; HASH_LENGTH];
/// assert_eq!(hash_array.len(), 64);
/// ```
pub const HASH_LENGTH: usize = 64;
/// The maximum allowed length for names (in bytes).
///
/// # Why this exists
/// Enforces a sane upper bound on file, directory, and reference names. This aligns
/// closely with typical filesystem limits (e.g., 255 bytes in most Unix filesystems).
/// It prevents malicious inputs from causing excessive memory consumption or
/// triggering filesystem errors during checkout operations.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::constants::MAX_NAME_LENGTH;
/// assert_eq!(MAX_NAME_LENGTH, 255);
/// ```
pub const MAX_NAME_LENGTH: u64 = 255;
/// The maximum allowed size for blob objects (in bytes).
///
/// # Why this exists
/// To prevent denial-of-service (`DoS`) via memory exhaustion. If unbounded, a parser
/// reading a malformed packfile could attempt to allocate gigabytes of memory for a
/// single blob. The 100 MiB limit provides ample room for legitimate source code and
/// small binary assets while acting as a circuit breaker against malicious payloads.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::constants::MAX_BLOB_SIZE;
/// assert_eq!(MAX_BLOB_SIZE, 100 * 1024 * 1024);
/// ```
pub const MAX_BLOB_SIZE: u64 = 100 * 1024 * 1024;
/// The maximum number of entries allowed in a tree.
///
/// # Why this exists
/// While Git allows a technically unlimited number of entries in a tree object,
/// performance degrades quadratically if entries are not handled correctly. Capping
/// this at 100,000 ensures that tree parsing, diffing, and serialization remain
/// performant and bounded in memory usage.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::constants::MAX_TREE_ENTRIES;
/// assert_eq!(MAX_TREE_ENTRIES, 100_000);
/// ```
pub const MAX_TREE_ENTRIES: u64 = 100_000;
/// The maximum allowed length for commit/tag messages (in bytes).
///
/// # Why this exists
/// Commit and tag messages are metadata. A 1 MiB limit is exceedingly generous for
/// textual descriptions but strictly prevents malicious actors from embedding massive
/// payloads (e.g., encoded binaries) into commit logs, which would bloat repository
/// history and memory usage during traversal.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::constants::MAX_MESSAGE_LENGTH;
/// assert_eq!(MAX_MESSAGE_LENGTH, 1024 * 1024);
/// ```
pub const MAX_MESSAGE_LENGTH: u64 = 1024 * 1024;
/// The maximum number of parent commits allowed (binary format uses u16).
///
/// # Why this exists
/// Restricts the complexity of octopus merges. While Git supports many parents,
/// allowing an unbounded number can lead to pathological graph structures that are
/// expensive to traverse. The limit of 65,535 corresponds to the maximum value of
/// an unsigned 16-bit integer, ensuring it can be packed efficiently if a binary
/// format is introduced.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::constants::MAX_PARENT_COUNT;
/// assert_eq!(MAX_PARENT_COUNT, 0xFFFF);
/// ```
pub const MAX_PARENT_COUNT: u64 = 0xFFFF;