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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//! Binary serialization and deserialization for `libvctrl_core`.
//!
//! # Purpose
//!
//! This module provides a concrete implementation of the
//! [`Encoder`](libvctrl_handler::Encoder) and
//! [`Decoder`](libvctrl_handler::Decoder) traits. It translates in-memory
//! version control objects ([`Blob`](libvctrl_handler::Blob),
//! [`Tree`](libvctrl_handler::Tree), [`Commit`](libvctrl_handler::Commit),
//! [`Tag`](libvctrl_handler::Tag)) into a compact, deterministic binary
//! format and back.
//!
//! # Design Rationale
//!
//! The binary format is designed to be both space-efficient and fast to
//! parse. It uses little-endian integers and length-prefixed byte slices to
//! avoid expensive delimiter scanning. The encoder and decoder are separated
//! into distinct modules to isolate the reading and writing logic, making the
//! code easier to audit and maintain.
//!
//! ## Versioning
//!
//! Every serialized object begins with a single version byte. This allows
//! the format to evolve over time. A decoder that encounters an unsupported
//! version can reject the payload cleanly rather than attempting to parse
//! incompatible data.
//!
//! ## Endianness
//!
//! All integer fields are encoded in little-endian byte order. This matches
//! the native byte order of most modern CPU architectures (x86, x86_64,
//! ARM little-endian), which minimizes byte-swapping overhead during
//! encoding and decoding.
//!
//! ## Length-Prefixed Fields
//!
//! Variable-length fields such as names, email addresses, messages, and
//! blob data are prefixed with their length. This allows the decoder to
//! know exactly how many bytes to read and enables pre-allocation of buffers.
//! Length prefixes also provide a simple structural validation point: if the
//! declared length does not match the remaining data, the payload is corrupt.
//!
//! # Internal Mechanism
//!
//! The [`BinaryEncoder`] pre-allocates output buffers based on the estimated
//! size of the object to minimize heap reallocations. It then appends
//! fields sequentially using `extend_from_slice` for efficient bulk copies.
//!
//! The [`BinaryDecoder`] performs strict bounds checking on every slice
//! access. It maintains a cursor over the input and never reads past the
//! end of the buffer. If the data is truncated, malformed, or contains
//! invalid UTF-8, the decoder returns
//! [`VctrlError::CorruptedData`](libvctrl_handler::VctrlError::CorruptedData)
//! rather than panicking.
//!
//! # Security Considerations
//!
//! - **Panic-free decoding**: The decoder is designed to be completely
//! panic-free for arbitrary input. All accesses are bounds-checked.
//! - **DoS protection**: Before allocating memory for variable-length
//! fields, the decoder validates declared lengths against system limits
//! such as
//! [`MAX_BLOB_SIZE`](libvctrl_handler::MAX_BLOB_SIZE),
//! [`MAX_MESSAGE_LENGTH`](libvctrl_handler::MAX_MESSAGE_LENGTH), and
//! [`MAX_TREE_ENTRIES`](libvctrl_handler::MAX_TREE_ENTRIES). This
//! prevents a malicious payload from requesting a huge allocation.
//! - **Strict UTF-8 validation**: All string fields are validated using
//! [`std::str::from_utf8`]. Invalid sequences are rejected.
//!
//! # Round-Trip Guarantee
//!
//! For every object type supported by this codec, encoding an object with
//! [`BinaryEncoder`] and then decoding it with [`BinaryDecoder`] yields a
//! value equal to the original object. This property is tested extensively
//! with unit tests and property-based tests using `proptest`.
//!
//! # Examples
//!
//! Performing a full encode-decode round-trip on a `Blob`:
//!
//! ```
//! use libvctrl_handler::{Blob, Decoder, Encoder};
//! use libvctrl_core::codec::{BinaryDecoder, BinaryEncoder};
//!
//! let original_blob = Blob::new(b"hello world".to_vec());
//! let encoder = BinaryEncoder;
//! let decoder = BinaryDecoder;
//!
//! let encoded_bytes = encoder.encode_blob(&original_blob).unwrap();
//! let decoded_blob = decoder.decode_blob(&encoded_bytes).unwrap();
//!
//! assert_eq!(decoded_blob, original_blob);
//! ```
/// Module containing the [`BinaryDecoder`](crate::codec::BinaryDecoder)
/// implementation.
///
/// # Purpose
///
/// Handles the deserialization of version control objects from the binary
/// wire format.
///
/// # Design Rationale
///
/// The decoder is isolated in its own module to encapsulate the complex,
/// stateful parsing logic (cursor management and bounds checking) separate
/// from the encoding logic. This separation makes the code easier to audit
/// because all risky slice operations are confined to one file.
///
/// # Internal Mechanism
///
/// The decoder maintains a cursor index into the input byte slice. It reads
/// length prefixes, advances the cursor, and extracts sub-slices. Before
/// each access, it checks that the requested range is within bounds. This
/// guarantees that malformed input never causes an out-of-bounds panic.
///
/// # Examples
///
/// ```
/// use libvctrl_handler::{Blob, Decoder, Encoder};
/// use libvctrl_core::codec::binary_decoder::BinaryDecoder;
/// use libvctrl_core::codec::binary_encoder::BinaryEncoder;
///
/// let blob = Blob::new(vec![0u8]);
/// let bytes = BinaryEncoder.encode_blob(&blob).unwrap();
/// let decoded = BinaryDecoder.decode_blob(&bytes).unwrap();
/// assert_eq!(decoded, blob);
/// ```
/// Module containing the [`BinaryEncoder`](crate::codec::BinaryEncoder)
/// implementation.
///
/// # Purpose
///
/// Handles the serialization of version control objects into the binary wire
/// format.
///
/// # Design Rationale
///
/// The encoder is isolated in its own module to group all the serialization
/// and byte-allocation logic together, ensuring that changes to the wire
/// format only affect this specific part of the codebase. The encoder is
/// stateless and deterministic; encoding the same object always produces the
/// same byte sequence.
///
/// # Internal Mechanism
///
/// The encoder pre-allocates a `Vec<u8>` based on the estimated size of the
/// object, reducing the number of heap reallocations. It writes fields using
/// `extend_from_slice`, which compiles to efficient `memcpy` operations for
/// bulk data.
///
/// # Examples
///
/// ```
/// use libvctrl_handler::{Blob, Encoder};
/// use libvctrl_core::codec::binary_encoder::BinaryEncoder;
///
/// let blob = Blob::new(vec![0u8]);
/// let bytes = BinaryEncoder.encode_blob(&blob).unwrap();
/// assert!(!bytes.is_empty());
/// ```
/// Re-export of the [`BinaryDecoder`](crate::codec::binary_decoder::BinaryDecoder)
/// struct.
///
/// # Purpose
///
/// Provides convenient access to the decoder at the module root level without
/// requiring the caller to navigate the internal module hierarchy. This keeps
/// the public API surface clean and intuitive.
///
/// # Design Rationale
///
/// Re-exporting at the module root is a common Rust pattern. It allows users
/// to write `use libvctrl_core::codec::BinaryDecoder;` instead of the longer
/// `use libvctrl_core::codec::binary_decoder::BinaryDecoder;`.
///
/// # Examples
///
/// ```
/// use libvctrl_handler::{Blob, Decoder, Encoder};
/// use libvctrl_core::codec::{BinaryDecoder, BinaryEncoder};
///
/// let blob = Blob::new(vec![1, 2, 3]);
/// let bytes = BinaryEncoder.encode_blob(&blob).unwrap();
/// let decoder = BinaryDecoder;
/// assert!(decoder.decode_blob(&bytes).is_ok());
/// ```
pub use BinaryDecoder;
/// Re-export of the [`BinaryEncoder`](crate::codec::binary_encoder::BinaryEncoder)
/// struct.
///
/// # Purpose
///
/// Provides convenient access to the encoder at the module root level without
/// requiring the caller to navigate the internal module hierarchy. This keeps
/// the public API surface clean and intuitive.
///
/// # Design Rationale
///
/// Re-exporting at the module root simplifies imports and aligns with the
/// pattern used throughout the crate. It also ensures that if the internal
/// module layout changes, downstream code that uses the root re-export will
/// not break.
///
/// # Examples
///
/// ```
/// use libvctrl_handler::{Blob, Encoder};
/// use libvctrl_core::codec::BinaryEncoder;
///
/// let encoder = BinaryEncoder;
/// let blob = Blob::new(vec![1, 2, 3]);
/// assert!(encoder.encode_blob(&blob).is_ok());
/// ```
pub use BinaryEncoder;