1use crate::error::{GGUFError, Result};
4use crate::format::constants::GGUF_DEFAULT_ALIGNMENT;
5
6#[cfg(not(feature = "std"))]
7extern crate alloc;
8#[cfg(not(feature = "std"))]
9use alloc::{format, vec, vec::Vec};
10#[cfg(not(feature = "std"))]
11use core::{fmt, mem};
12
13pub fn calculate_padding(current_position: usize, alignment: usize) -> usize {
15 if alignment == 0 || alignment == 1 {
16 return 0;
17 }
18
19 let remainder = current_position % alignment;
20 if remainder == 0 {
21 0
22 } else {
23 alignment - remainder
24 }
25}
26
27pub fn align_to(position: usize, alignment: usize) -> usize {
32 checked_align_to(position, alignment).unwrap_or(usize::MAX)
33}
34
35pub fn checked_align_to(position: usize, alignment: usize) -> Option<usize> {
37 position.checked_add(calculate_padding(position, alignment))
38}
39
40pub fn align_to_default(position: usize) -> usize {
42 align_to(position, GGUF_DEFAULT_ALIGNMENT)
43}
44
45pub fn calculate_default_padding(position: usize) -> usize {
47 calculate_padding(position, GGUF_DEFAULT_ALIGNMENT)
48}
49
50pub fn is_aligned(position: usize, alignment: usize) -> bool {
52 if alignment == 0 || alignment == 1 {
53 return true;
54 }
55 position.is_multiple_of(alignment)
56}
57
58pub fn is_aligned_default(position: usize) -> bool {
60 is_aligned(position, GGUF_DEFAULT_ALIGNMENT)
61}
62
63pub fn create_padding(size: usize) -> Vec<u8> {
65 vec![0u8; size]
66}
67
68pub fn try_create_padding(size: usize) -> Result<Vec<u8>> {
70 let mut padding = Vec::new();
71 padding.try_reserve_exact(size).map_err(|_| {
72 GGUFError::InvalidTensorData(format!(
73 "Unable to allocate alignment padding of {} bytes",
74 size
75 ))
76 })?;
77 padding.resize(size, 0);
78 Ok(padding)
79}
80
81pub fn aligned_size(size: usize, alignment: usize) -> usize {
85 checked_aligned_size(size, alignment).unwrap_or(usize::MAX)
86}
87
88pub fn checked_aligned_size(size: usize, alignment: usize) -> Option<usize> {
90 checked_align_to(size, alignment)
91}
92
93pub fn aligned_size_default(size: usize) -> usize {
95 aligned_size(size, GGUF_DEFAULT_ALIGNMENT)
96}
97
98pub fn is_valid_alignment(alignment: usize) -> bool {
100 alignment > 0 && (alignment & (alignment - 1)) == 0
101}
102
103pub fn next_power_of_2(mut n: usize) -> usize {
107 if n == 0 {
108 return 1;
109 }
110
111 if n & (n - 1) == 0 {
112 return n; }
114
115 n -= 1;
117 n |= n >> 1;
118 n |= n >> 2;
119 n |= n >> 4;
120 n |= n >> 8;
121 n |= n >> 16;
122 #[cfg(feature = "std")]
123 {
124 if std::mem::size_of::<usize>() > 4 {
125 n |= n >> 32;
126 }
127 }
128 #[cfg(not(feature = "std"))]
129 {
130 if mem::size_of::<usize>() > 4 {
131 n |= n >> 32;
132 }
133 }
134 n.saturating_add(1)
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub struct AlignmentInfo {
140 pub position: usize,
142 pub alignment: usize,
144 pub padding: usize,
146 pub aligned_position: usize,
148}
149
150impl AlignmentInfo {
151 pub fn new(position: usize, alignment: usize) -> Self {
156 let padding = calculate_padding(position, alignment);
157 let aligned_position = position.saturating_add(padding);
158
159 Self { position, alignment, padding, aligned_position }
160 }
161
162 pub fn new_default(position: usize) -> Self {
164 Self::new(position, GGUF_DEFAULT_ALIGNMENT)
165 }
166
167 pub fn needs_padding(&self) -> bool {
169 self.padding > 0
170 }
171
172 pub fn padding_bytes(&self) -> Vec<u8> {
174 create_padding(self.padding)
175 }
176
177 pub fn try_padding_bytes(&self) -> Result<Vec<u8>> {
179 try_create_padding(self.padding)
180 }
181}
182
183#[cfg(feature = "std")]
184impl std::fmt::Display for AlignmentInfo {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(
187 f,
188 "AlignmentInfo {{ pos: {}, align: {}, padding: {}, aligned_pos: {} }}",
189 self.position, self.alignment, self.padding, self.aligned_position
190 )
191 }
192}
193
194#[cfg(not(feature = "std"))]
195impl fmt::Display for AlignmentInfo {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(
198 f,
199 "AlignmentInfo {{ pos: {}, align: {}, padding: {}, aligned_pos: {} }}",
200 self.position, self.alignment, self.padding, self.aligned_position
201 )
202 }
203}
204
205#[derive(Debug, Clone)]
207pub struct AlignmentTracker {
208 pub position: usize,
210 pub default_alignment: usize,
212}
213
214impl AlignmentTracker {
215 pub fn new(default_alignment: usize) -> Self {
217 Self { position: 0, default_alignment }
218 }
219
220 pub fn new_default() -> Self {
222 Self::new(GGUF_DEFAULT_ALIGNMENT)
223 }
224
225 pub fn advance(&mut self, bytes: usize) {
227 self.position = self.position.saturating_add(bytes);
228 }
229
230 pub fn checked_advance(&mut self, bytes: usize) -> Option<()> {
232 self.position = self.position.checked_add(bytes)?;
233 Some(())
234 }
235
236 pub fn calculate_padding(&self) -> usize {
238 calculate_padding(self.position, self.default_alignment)
239 }
240
241 pub fn calculate_padding_for(&self, alignment: usize) -> usize {
243 calculate_padding(self.position, alignment)
244 }
245
246 pub fn align_default(&mut self) -> AlignmentInfo {
248 let info = AlignmentInfo::new(self.position, self.default_alignment);
249 self.position = info.aligned_position;
250 info
251 }
252
253 pub fn align_to(&mut self, alignment: usize) -> AlignmentInfo {
255 let info = AlignmentInfo::new(self.position, alignment);
256 self.position = info.aligned_position;
257 info
258 }
259
260 pub fn current_alignment_info(&self) -> AlignmentInfo {
262 AlignmentInfo::new(self.position, self.default_alignment)
263 }
264
265 pub fn is_aligned(&self) -> bool {
267 is_aligned(self.position, self.default_alignment)
268 }
269
270 pub fn is_aligned_to(&self, alignment: usize) -> bool {
272 is_aligned(self.position, alignment)
273 }
274}
275
276#[cfg(all(test, feature = "std"))]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn test_calculate_padding() {
282 assert_eq!(calculate_padding(0, 32), 0);
283 assert_eq!(calculate_padding(1, 32), 31);
284 assert_eq!(calculate_padding(16, 32), 16);
285 assert_eq!(calculate_padding(32, 32), 0);
286 assert_eq!(calculate_padding(33, 32), 31);
287
288 assert_eq!(calculate_padding(10, 1), 0);
290 assert_eq!(calculate_padding(10, 0), 0);
291 }
292
293 #[test]
294 fn test_align_to() {
295 assert_eq!(align_to(0, 32), 0);
296 assert_eq!(align_to(1, 32), 32);
297 assert_eq!(align_to(16, 32), 32);
298 assert_eq!(align_to(32, 32), 32);
299 assert_eq!(align_to(33, 32), 64);
300 }
301
302 #[test]
303 fn test_is_aligned() {
304 assert!(is_aligned(0, 32));
305 assert!(is_aligned(32, 32));
306 assert!(is_aligned(64, 32));
307 assert!(!is_aligned(1, 32));
308 assert!(!is_aligned(33, 32));
309
310 assert!(is_aligned(42, 1));
312 assert!(is_aligned(42, 0));
313 }
314
315 #[test]
316 fn test_aligned_size() {
317 assert_eq!(aligned_size(0, 32), 0);
318 assert_eq!(aligned_size(1, 32), 32);
319 assert_eq!(aligned_size(16, 32), 32);
320 assert_eq!(aligned_size(32, 32), 32);
321 assert_eq!(aligned_size(33, 32), 64);
322 }
323
324 #[test]
325 fn test_is_valid_alignment() {
326 assert!(is_valid_alignment(1));
327 assert!(is_valid_alignment(2));
328 assert!(is_valid_alignment(4));
329 assert!(is_valid_alignment(8));
330 assert!(is_valid_alignment(16));
331 assert!(is_valid_alignment(32));
332
333 assert!(!is_valid_alignment(0));
334 assert!(!is_valid_alignment(3));
335 assert!(!is_valid_alignment(5));
336 assert!(!is_valid_alignment(6));
337 assert!(!is_valid_alignment(12));
338 }
339
340 #[test]
341 fn test_next_power_of_2() {
342 assert_eq!(next_power_of_2(0), 1);
343 assert_eq!(next_power_of_2(1), 1);
344 assert_eq!(next_power_of_2(2), 2);
345 assert_eq!(next_power_of_2(3), 4);
346 assert_eq!(next_power_of_2(4), 4);
347 assert_eq!(next_power_of_2(5), 8);
348 assert_eq!(next_power_of_2(15), 16);
349 assert_eq!(next_power_of_2(16), 16);
350 assert_eq!(next_power_of_2(17), 32);
351 }
352
353 #[test]
354 fn test_alignment_info() {
355 let info = AlignmentInfo::new(17, 32);
356 assert_eq!(info.position, 17);
357 assert_eq!(info.alignment, 32);
358 assert_eq!(info.padding, 15);
359 assert_eq!(info.aligned_position, 32);
360 assert!(info.needs_padding());
361
362 let info_aligned = AlignmentInfo::new(32, 32);
363 assert_eq!(info_aligned.padding, 0);
364 assert!(!info_aligned.needs_padding());
365 }
366
367 #[test]
368 fn test_alignment_tracker() {
369 let mut tracker = AlignmentTracker::new_default();
370 assert_eq!(tracker.position, 0);
371 assert!(tracker.is_aligned());
372
373 tracker.advance(17);
374 assert_eq!(tracker.position, 17);
375 assert!(!tracker.is_aligned());
376 assert_eq!(tracker.calculate_padding(), 15);
377
378 let info = tracker.align_default();
379 assert_eq!(info.padding, 15);
380 assert_eq!(tracker.position, 32);
381 assert!(tracker.is_aligned());
382 }
383
384 #[test]
385 fn test_default_alignment_functions() {
386 assert_eq!(align_to_default(17), 32);
387 assert_eq!(calculate_default_padding(17), 15);
388 assert!(is_aligned_default(32));
389 assert!(!is_aligned_default(17));
390 assert_eq!(aligned_size_default(17), 32);
391 }
392
393 #[test]
394 fn test_create_padding() {
395 let padding = create_padding(5);
396 assert_eq!(padding.len(), 5);
397 assert_eq!(padding, vec![0u8; 5]);
398
399 let empty_padding = create_padding(0);
400 assert!(empty_padding.is_empty());
401
402 assert_eq!(try_create_padding(5).unwrap(), vec![0u8; 5]);
403 assert!(try_create_padding(usize::MAX).is_err());
404 assert_eq!(AlignmentInfo::new(17, 32).try_padding_bytes().unwrap().len(), 15);
405 }
406
407 #[test]
408 fn test_alignment_info_display() {
409 let info = AlignmentInfo::new(17, 32);
410 let display_str = format!("{}", info);
411 assert!(display_str.contains("17"));
412 assert!(display_str.contains("32"));
413 assert!(display_str.contains("15"));
414 }
415
416 #[test]
417 fn test_alignment_overflow_is_explicit() {
418 assert_eq!(checked_align_to(usize::MAX, 2), None);
419 assert_eq!(align_to(usize::MAX, 2), usize::MAX);
420 assert_eq!(checked_aligned_size(usize::MAX, 8), None);
421 assert_eq!(next_power_of_2(usize::MAX), usize::MAX);
422
423 let mut tracker = AlignmentTracker::new(8);
424 tracker.position = usize::MAX;
425 assert_eq!(tracker.checked_advance(1), None);
426 assert_eq!(tracker.position, usize::MAX);
427 }
428}