Skip to main content

bufkit/chunk_mut/
putter.rs

1use core::ops::{Bound, RangeBounds};
2
3use crate::error::TryAdvanceError;
4
5use super::{
6  super::{must_non_zero, panic_advance},
7  ChunkMut, ChunkWriter,
8};
9
10/// A putter for writing to a buffer without modifying the original buffer's cursor.
11///
12/// `Putter` provides a non-destructive way to write buffer contents by maintaining
13/// its own independent cursor position. This is particularly useful when you need to:
14/// - Preview write operations before committing them
15/// - Write data that might need to be rolled back
16/// - Implement transactional write operations
17/// - Share write access to the same buffer data from different positions
18/// - Test serialization without modifying the original buffer
19///
20/// The putter can be constrained to a specific range within the buffer, making it
21/// safe for write operations that should not exceed certain boundaries.
22///
23/// # Examples
24///
25/// ```rust
26/// use bufkit::{ChunkMut, Putter};
27///
28/// let mut data = [0u8; 10];
29/// let mut putter = Putter::new(&mut data[..]);  // No need for &mut &mut
30///
31/// // Write without affecting the original buffer's cursor
32/// putter.put_u8(0x42);
33/// putter.put_u16_le(0x1234);
34///
35/// // Original buffer's state is unchanged until we access it
36/// // The writes are staged in the putter's view
37/// ```
38#[derive(Debug)]
39pub struct Putter<B: ?Sized> {
40  /// Current cursor position relative to the buffer's current position
41  cursor: usize,
42  /// The original start bound of the putter, used for resetting.
43  start: Bound<usize>,
44  /// The original end bound of the putter, used for resetting.
45  end: Bound<usize>,
46  /// Current limit bound of the putter
47  limit: Bound<usize>,
48  /// The underlying buffer wrapped in ChunkWriter for ergonomic access
49  buf: ChunkWriter<B>,
50}
51
52impl<B: ChunkMut> From<B> for Putter<B> {
53  #[inline]
54  fn from(buf: B) -> Self {
55    Self::new(buf)
56  }
57}
58
59impl<B> Putter<B> {
60  /// Creates a new `Putter` instance with the given buffer.
61  ///
62  /// The putter starts at the beginning of the buffer's current position
63  /// and can write to all remaining space in the buffer.
64  ///
65  /// # Examples
66  ///
67  /// ```rust
68  /// use bufkit::{ChunkMut, Putter};
69  ///
70  /// let mut data = [0u8; 10];
71  /// let putter = Putter::new(&mut data[..]);
72  /// assert_eq!(putter.remaining_mut(), 10);
73  /// ```
74  #[inline]
75  pub fn new(buf: impl Into<ChunkWriter<B>>) -> Self
76  where
77    B: ChunkMut,
78  {
79    Self::with_cursor_and_bounds_inner(buf.into(), 0, Bound::Included(0), Bound::Unbounded)
80  }
81
82  /// Creates a new `Putter` instance with the given buffer.
83  ///
84  /// The putter starts at the beginning of the buffer's current position
85  /// and can write to all remaining space in the buffer.
86  ///
87  /// # Examples
88  ///
89  /// ```rust
90  /// use bufkit::{ChunkMut, Putter};
91  ///
92  /// let mut data = [0u8; 10];
93  /// let mut slice: &mut [u8] = &mut data[..];
94  /// let putter = Putter::const_new(&mut slice);
95  /// assert_eq!(putter.remaining_mut(), 10);
96  /// ```
97  #[inline]
98  pub const fn const_new(buf: B) -> Self {
99    let buf = ChunkWriter::new(buf);
100    Self::with_cursor_and_bounds_inner(buf, 0, Bound::Included(0), Bound::Unbounded)
101  }
102
103  /// Creates a new `Putter` constrained to a specific length.
104  ///
105  /// This is useful when you want to ensure the putter cannot write beyond
106  /// a certain number of bytes, providing additional safety for serialization operations.
107  ///
108  /// # Examples
109  ///
110  /// ```rust
111  /// use bufkit::{ChunkMut, Putter};
112  ///
113  /// let mut data = [0u8; 10];
114  /// let putter = Putter::with_limit(&mut data[..], 5); // Only write first 5 bytes
115  /// assert_eq!(putter.remaining_mut(), 5);
116  /// ```
117  #[inline]
118  pub fn with_limit(buf: impl Into<ChunkWriter<B>>, limit: usize) -> Self {
119    let write_buf = buf.into();
120    Self::with_cursor_and_bounds_inner(write_buf, 0, Bound::Included(0), Bound::Excluded(limit))
121  }
122
123  /// Creates a new `Putter` constrained to a specific length.
124  ///
125  /// This is useful when you want to ensure the putter cannot write beyond
126  /// a certain number of bytes, providing additional safety for serialization operations.
127  ///
128  /// # Examples
129  ///
130  /// ```rust
131  /// use bufkit::{ChunkMut, Putter};
132  ///
133  /// let mut data = [0u8; 10];
134  /// let putter = Putter::const_with_limit((&mut data[..]).into(), 5); // Only write first 5 bytes
135  /// assert_eq!(putter.remaining_mut(), 5);
136  /// ```
137  #[inline]
138  pub const fn const_with_limit(buf: ChunkWriter<B>, limit: usize) -> Self {
139    Self::with_cursor_and_bounds_inner(buf, 0, Bound::Included(0), Bound::Excluded(limit))
140  }
141
142  /// Creates a new `Putter` with specific start and end bounds.
143  ///
144  /// This provides maximum flexibility in defining the putter's range,
145  /// allowing for more complex write scenarios.
146  ///
147  /// # Examples
148  ///
149  /// ```rust
150  /// use core::ops::Bound;
151  /// use bufkit::{ChunkMut, Putter};
152  ///
153  /// let mut data = [0u8; 10];
154  ///
155  /// // Write from index 2 to 7 (exclusive)
156  /// let putter = Putter::with_range(&mut data[..], 2..7);
157  /// assert_eq!(putter.remaining_mut(), 5);
158  /// ```
159  #[inline]
160  pub fn with_range(buf: impl Into<ChunkWriter<B>>, range: impl RangeBounds<usize>) -> Self
161  where
162    B: ChunkMut,
163  {
164    let start = range.start_bound().cloned();
165    let end = range.end_bound().cloned();
166    let write_buf = buf.into();
167    let start_pos = Self::resolve_start_bound(start, &write_buf);
168    Self::with_cursor_and_bounds_inner(write_buf, start_pos, Bound::Included(start_pos), end)
169  }
170
171  /// Returns the current position of the internal cursor relative to the start of the buffer.
172  ///
173  /// This represents how far the putter's cursor has advanced from its starting position.
174  ///
175  /// # Examples
176  ///
177  /// ```rust
178  /// use bufkit::{ChunkMut, Putter};
179  ///
180  /// let mut data = [0u8; 10];
181  /// let mut putter = Putter::new(&mut data[..]);
182  ///
183  /// assert_eq!(putter.position(), 0);
184  /// putter.write_u8(0x42);
185  /// assert_eq!(putter.position(), 1);
186  /// putter.advance_mut(2);
187  /// assert_eq!(putter.position(), 3);
188  /// ```
189  #[inline]
190  pub const fn position(&self) -> usize {
191    let start_pos = Self::resolve_start_bound_without_check(self.start);
192    self.cursor.saturating_sub(start_pos)
193  }
194
195  /// Returns the absolute position of the putter's cursor in the underlying buffer.
196  ///
197  /// This is the position relative to the start of the buffer, not just the putter's starting point.
198  /// This is useful for understanding where the putter is writing in the context of the entire buffer.
199  ///
200  /// # Examples
201  ///
202  /// ```rust
203  /// use bufkit::{ChunkMut, Putter};
204  /// use core::ops::Bound;
205  ///
206  /// let mut data = [0u8; 10];
207  /// let mut putter = Putter::with_range(&mut data[..], 3..7);
208  ///
209  /// putter.write_u8(0x42);
210  /// assert_eq!(putter.absolute_position(), 4); // 3 (offset) + 1 (written)
211  /// ```
212  #[inline]
213  pub const fn absolute_position(&self) -> usize {
214    self.cursor
215  }
216
217  /// Resets the putter's to the initial state.
218  ///
219  /// After calling this method, the putter will start writing from the same
220  /// position and the same limit where it was initially created.
221  ///
222  /// This method will not clean the dirty state of the putter, which means
223  /// any previously written data will still be present in the buffer. See also [`reset`](Putter::reset).
224  ///
225  /// # Examples
226  ///
227  /// ```rust
228  /// use bufkit::{ChunkMut, Putter};
229  ///
230  /// let mut data = [0u8; 10];
231  /// let mut putter = Putter::new(&mut data[..]);
232  ///
233  /// putter.advance_mut(3);
234  /// assert_eq!(putter.position(), 3);
235  ///
236  /// putter.reset_position();
237  /// assert_eq!(putter.position(), 0);
238  /// assert_eq!(putter.remaining_mut(), 10);
239  /// ```
240  #[inline]
241  pub const fn reset_position(&mut self) {
242    self.cursor = Self::resolve_start_bound_without_check(self.start);
243    self.limit = self.end;
244  }
245
246  /// Resets the putter's buffer to its initial state, filling the written area with zeros.
247  ///
248  /// This method clears the buffer's contents from the start position to the limit position,
249  /// effectively resetting the buffer to a clean state.
250  ///
251  /// # Examples
252  ///
253  /// ```rust
254  /// use bufkit::{ChunkMut, Putter};
255  ///
256  /// let mut data = [0u8; 10];
257  /// let mut putter = Putter::new(&mut data[..]);
258  ///
259  /// putter.put_u8(0x42);
260  /// assert_eq!(putter.buffer_mut()[0], 0x42);
261  ///
262  /// putter.reset();
263  /// assert_eq!(putter.buffer_mut()[0], 0x00); // The first byte is reset to zero
264  /// ```
265  pub fn reset(&mut self)
266  where
267    B: ChunkMut,
268  {
269    self.reset_position();
270    self.fill(0);
271  }
272
273  /// Consumes the putter and returns the underlying buffer.
274  ///
275  /// Any writes made through the putter will be reflected in the returned buffer.
276  /// This is useful when you want to finalize the writes and retrieve the modified buffer.
277  ///
278  /// # Examples
279  ///
280  /// ```rust
281  /// use bufkit::{ChunkMut, Putter};
282  ///
283  /// let mut data = [0u8; 10];
284  /// let mut putter = Putter::new(&mut data[..]);
285  /// putter.put_u8(0x42);
286  /// let buf = putter.into_inner();
287  /// assert_eq!(buf[0], 0x42);
288  /// ```
289  #[inline]
290  pub fn into_inner(self) -> B {
291    self.buf.0
292  }
293
294  #[inline]
295  const fn with_cursor_and_bounds_inner(
296    buf: ChunkWriter<B>,
297    cursor: usize,
298    start: Bound<usize>,
299    end: Bound<usize>,
300  ) -> Self {
301    Self {
302      buf,
303      cursor,
304      start,
305      end,
306      limit: end,
307    }
308  }
309
310  #[inline]
311  const fn resolve_start_bound_without_check(bound: Bound<usize>) -> usize {
312    match bound {
313      Bound::Included(n) => n,
314      Bound::Excluded(n) => n.saturating_add(1),
315      Bound::Unbounded => 0,
316    }
317  }
318}
319
320impl<B: ?Sized> Putter<B> {
321  #[inline]
322  fn resolve_start_bound(bound: Bound<usize>, buf: &ChunkWriter<B>) -> usize
323  where
324    B: ChunkMut,
325  {
326    let pos = match bound {
327      Bound::Included(n) => n,
328      Bound::Excluded(n) => n.saturating_add(1),
329      Bound::Unbounded => 0,
330    };
331    pos.min(buf.remaining_mut())
332  }
333
334  #[inline]
335  fn resolve_end_bound(&self, bound: Bound<usize>) -> usize
336  where
337    B: ChunkMut,
338  {
339    match bound {
340      Bound::Included(n) => (n.saturating_add(1)).min(self.buf.remaining_mut()),
341      Bound::Excluded(n) => n.min(self.buf.remaining_mut()),
342      Bound::Unbounded => self.buf.remaining_mut(),
343    }
344  }
345}
346
347impl<B: ChunkMut + ?Sized> ChunkMut for Putter<B> {
348  #[inline]
349  fn remaining_mut(&self) -> usize {
350    let end_pos = self.resolve_end_bound(self.limit);
351    end_pos.saturating_sub(self.cursor)
352  }
353
354  #[inline]
355  fn buffer_mut(&mut self) -> &mut [u8] {
356    let start = self.cursor.min(self.buf.remaining_mut());
357    let end_pos = self.resolve_end_bound(self.limit);
358    &mut self.buf.buffer_mut()[start..end_pos]
359  }
360
361  #[inline]
362  fn advance_mut(&mut self, cnt: usize) {
363    let remaining = self.remaining_mut();
364    if cnt > remaining {
365      panic_advance(&TryAdvanceError::new(must_non_zero(cnt), remaining));
366    }
367    self.cursor += cnt;
368  }
369
370  #[inline]
371  fn try_advance_mut(&mut self, cnt: usize) -> Result<(), TryAdvanceError> {
372    let remaining = self.remaining_mut();
373    if cnt > remaining {
374      return Err(TryAdvanceError::new(must_non_zero(cnt), remaining));
375    }
376    self.cursor += cnt;
377    Ok(())
378  }
379
380  #[inline]
381  fn truncate_mut(&mut self, new_len: usize) {
382    let current_remaining = self.remaining_mut();
383    if new_len >= current_remaining {
384      return; // No truncation needed
385    }
386
387    let new_end_pos = self.cursor + new_len;
388    let current_end_pos = self.resolve_end_bound(self.limit);
389
390    // Only truncate if the new limit is more restrictive than the current one
391    if new_end_pos < current_end_pos {
392      self.limit = Bound::Excluded(new_end_pos);
393    }
394  }
395}
396
397#[cfg(test)]
398mod tests {
399  use super::*;
400
401  #[test]
402  fn test_putter_basic_functionality() {
403    let mut data = [0u8; 10];
404    let mut putter = Putter::new(&mut data[..]);
405
406    assert_eq!(putter.remaining_mut(), 10);
407    assert_eq!(putter.position(), 0);
408
409    // Write a byte
410    putter.write_u8(0x42);
411    assert_eq!(putter.remaining_mut(), 9);
412    assert_eq!(putter.position(), 1);
413
414    // Check that the data was written
415    assert_eq!(data[0], 0x42);
416  }
417
418  #[test]
419  fn test_putter_ergonomic_api() {
420    // Test the improved ergonomics - no need for &mut &mut
421    let mut data = [0u8; 10];
422    let mut putter = Putter::new(&mut data[..]); // Clean!
423
424    putter.put_u8(0x11);
425    assert_eq!(data[0], 0x11);
426  }
427
428  #[test]
429  fn test_putter_with_limit() {
430    let mut data = [0u8; 10];
431    let mut putter = Putter::with_limit(&mut data[..], 3);
432
433    assert_eq!(putter.remaining_mut(), 3);
434
435    // Write within limit
436    putter.write_u8(0x11);
437    putter.write_u8(0x22);
438    putter.write_u8(0x33);
439
440    // Should be at limit now
441    assert_eq!(putter.remaining_mut(), 0);
442
443    // Check that data was written correctly
444    assert_eq!(data[0], 0x11);
445    assert_eq!(data[1], 0x22);
446    assert_eq!(data[2], 0x33);
447    assert_eq!(data[3], 0x00); // Unchanged
448  }
449
450  #[test]
451  fn test_putter_reset_position() {
452    let mut data = [0u8; 10];
453    let mut putter = Putter::new(&mut data[..]);
454
455    putter.advance_mut(3);
456    assert_eq!(putter.position(), 3);
457    assert_eq!(putter.remaining_mut(), 7);
458
459    putter.reset_position();
460    assert_eq!(putter.position(), 0);
461    assert_eq!(putter.remaining_mut(), 10);
462  }
463
464  #[test]
465  fn test_putter_truncate() {
466    let mut data = [0u8; 10];
467    let mut putter = Putter::new(&mut data[..]);
468
469    putter.truncate_mut(3);
470    assert_eq!(putter.remaining_mut(), 3);
471
472    // Should only be able to write 3 bytes
473    putter.write_u8(0x11);
474    putter.write_u8(0x22);
475    putter.write_u8(0x33);
476    assert_eq!(putter.remaining_mut(), 0);
477
478    // Check data
479    assert_eq!(data[0], 0x11);
480    assert_eq!(data[1], 0x22);
481    assert_eq!(data[2], 0x33);
482    assert_eq!(data[3], 0x00); // Unchanged
483  }
484
485  #[test]
486  fn test_putter_try_advance() {
487    let mut data = [0u8; 5];
488    let mut putter = Putter::new(&mut data[..]);
489
490    assert!(putter.try_advance_mut(2).is_ok());
491    assert_eq!(putter.position(), 2);
492
493    // Should fail when trying to advance beyond available space
494    assert!(putter.try_advance_mut(5).is_err());
495    assert_eq!(putter.position(), 2); // Should remain unchanged
496  }
497
498  #[test]
499  #[should_panic(expected = "advance")]
500  fn test_putter_advance_panic() {
501    let mut data = [0u8; 3];
502    let mut putter = Putter::new(&mut data[..]);
503
504    putter.advance_mut(5); // Should panic
505  }
506
507  #[test]
508  fn test_putter_with_different_integer_types() {
509    let mut data = [0u8; 20];
510    let mut putter = Putter::new(&mut data[..]);
511
512    // Test little-endian writes
513    putter.write_u16_le(0x1234);
514    putter.write_u32_le(0x56789ABC);
515    assert_eq!(putter.position(), 6);
516
517    // Verify the data was written correctly
518    assert_eq!(&data[0..2], &[0x34, 0x12]); // u16 LE
519    assert_eq!(&data[2..6], &[0xBC, 0x9A, 0x78, 0x56]); // u32 LE
520  }
521
522  #[test]
523  fn test_putter_write_slice() {
524    let mut data = [0u8; 10];
525    let mut putter = Putter::new(&mut data[..]);
526
527    let test_data = [0x11, 0x22, 0x33, 0x44];
528    putter.write_slice(&test_data);
529
530    assert_eq!(putter.position(), 4);
531    assert_eq!(&data[0..4], &test_data);
532    assert_eq!(data[4], 0x00); // Unchanged
533  }
534
535  #[test]
536  fn test_putter_with_advanced_buffer() {
537    let mut data = [0u8; 10];
538    let mut buf = &mut data[..];
539
540    // Advance the original buffer
541    buf.advance_mut(3);
542    assert_eq!(buf.remaining_mut(), 7);
543
544    // Putter should work with the advanced buffer
545    let mut putter = Putter::new(buf);
546    assert_eq!(putter.remaining_mut(), 7);
547    putter.put_u8(0x42);
548    // Should write to the correct position
549    assert_eq!(data[3], 0x42); // Position 3 in original array
550  }
551
552  #[test]
553  fn test_putter_buffer_mut_access() {
554    let mut data = [0u8; 10];
555    let mut putter = Putter::new(&mut data[..]);
556
557    // Write some data first
558    putter.write_u8(0x11);
559    putter.write_u8(0x22);
560
561    // Access the remaining buffer
562    let remaining_buffer = putter.buffer_mut();
563    remaining_buffer[0] = 0x99; // Should write to position 2 in original
564
565    assert_eq!(data[0], 0x11);
566    assert_eq!(data[1], 0x22);
567    assert_eq!(data[2], 0x99);
568  }
569
570  #[test]
571  fn test_putter_state_consistency() {
572    let mut data = [0u8; 10];
573    let mut putter = Putter::new(&mut data[..]);
574
575    // Verify invariants are maintained through operations
576    let initial_remaining = putter.remaining_mut();
577    let initial_written = putter.position();
578
579    // After advance
580    putter.advance_mut(3);
581    assert_eq!(
582      putter.remaining_mut() + putter.position(),
583      initial_remaining
584    );
585
586    // After truncate
587    putter.truncate_mut(5);
588    assert_eq!(putter.remaining_mut(), 5);
589    assert_eq!(putter.position(), 3);
590
591    // After reset_position
592    putter.reset_position();
593    assert_eq!(putter.position(), initial_written);
594    assert_eq!(putter.remaining_mut(), initial_remaining); // back to initial state
595  }
596
597  #[test]
598  fn test_putter_exhaustive_write() {
599    let mut data = [0u8; 3];
600    let mut putter = Putter::new(&mut data[..]);
601
602    // Write all bytes one by one
603    assert_eq!(putter.write_u8(0x11), 1);
604    assert_eq!(putter.remaining_mut(), 2);
605
606    assert_eq!(putter.write_u8(0x22), 1);
607    assert_eq!(putter.remaining_mut(), 1);
608
609    assert_eq!(putter.write_u8(0x33), 1);
610    assert_eq!(putter.remaining_mut(), 0);
611
612    // Verify data
613    assert_eq!(data, [0x11, 0x22, 0x33]);
614  }
615
616  #[test]
617  fn test_putter_from_trait() {
618    let mut data = [0u8; 10];
619
620    // Test From trait implementation
621    let putter: Putter<_> = (&mut data[..]).into();
622    assert_eq!(putter.remaining_mut(), 10);
623  }
624
625  #[test]
626  fn test_putter_endianness() {
627    let mut data = [0u8; 16];
628    let mut putter = Putter::new(&mut data[..]);
629
630    // Test different endianness
631    putter.write_u16_le(0x1234);
632    putter.write_u16_be(0x1234);
633    putter.write_u32_le(0x12345678);
634    putter.write_u32_be(0x12345678);
635
636    // Verify little-endian
637    assert_eq!(&data[0..2], &[0x34, 0x12]);
638    // Verify big-endian
639    assert_eq!(&data[2..4], &[0x12, 0x34]);
640    // Verify little-endian u32
641    assert_eq!(&data[4..8], &[0x78, 0x56, 0x34, 0x12]);
642    // Verify big-endian u32
643    assert_eq!(&data[8..12], &[0x12, 0x34, 0x56, 0x78]);
644  }
645
646  #[test]
647  fn test_putter_signed_values() {
648    let mut data = [0u8; 10];
649    let mut putter = Putter::new(&mut data[..]);
650
651    // Test signed values
652    putter.write_i8(-1);
653    putter.write_i16_le(-1);
654    putter.write_i32_be(-1);
655
656    // Verify i8
657    assert_eq!(data[0], 0xFF);
658    // Verify i16 LE
659    assert_eq!(&data[1..3], &[0xFF, 0xFF]);
660    // Verify i32 BE
661    assert_eq!(&data[3..7], &[0xFF, 0xFF, 0xFF, 0xFF]);
662  }
663
664  #[test]
665  fn test_putter_reset_position_preserves_limits() {
666    let mut data = [0u8; 10];
667    let mut putter = Putter::with_limit(&mut data[..], 5);
668
669    putter.advance_mut(3);
670    assert_eq!(putter.position(), 3);
671    assert_eq!(putter.remaining_mut(), 2);
672
673    putter.reset_position();
674    assert_eq!(putter.position(), 0);
675    assert_eq!(putter.remaining_mut(), 5); // Should still be limited to 5
676  }
677
678  #[test]
679  fn test_putter_truncate_after_advance() {
680    let mut data = [0u8; 10];
681    let mut putter = Putter::new(&mut data[..]);
682
683    putter.advance_mut(3);
684    assert_eq!(putter.remaining_mut(), 7);
685
686    putter.truncate_mut(4);
687    assert_eq!(putter.remaining_mut(), 4);
688
689    // Write to verify the truncation works
690    putter.write_slice(&[0x11, 0x22, 0x33, 0x44]);
691    assert_eq!(putter.remaining_mut(), 0);
692
693    // Verify data was written starting from position 3
694    assert_eq!(&data[3..7], &[0x11, 0x22, 0x33, 0x44]);
695  }
696
697  #[test]
698  fn test_putter_truncate_limited_putter() {
699    let mut data = [0u8; 10];
700    let mut putter = Putter::with_limit(&mut data[..], 6);
701
702    assert_eq!(putter.remaining_mut(), 6);
703
704    // Truncate within the limit
705    putter.truncate_mut(4);
706    assert_eq!(putter.remaining_mut(), 4);
707
708    // Further truncation
709    putter.truncate_mut(2);
710    assert_eq!(putter.remaining_mut(), 2);
711  }
712
713  #[test]
714  fn test_error_details() {
715    let mut data = [0u8; 3];
716    let mut putter = Putter::new(&mut data[..]);
717
718    // Test TryAdvanceError
719    let advance_err = putter.try_advance_mut(5).unwrap_err();
720    assert_eq!(advance_err.requested().get(), 5);
721    assert_eq!(advance_err.available(), 3);
722  }
723
724  #[test]
725  fn test_putter_large_data() {
726    // Test with larger buffer to ensure no performance issues
727    let mut large_data = [0u8; 1000];
728    let mut putter = Putter::new(&mut large_data[..]);
729
730    assert_eq!(putter.remaining_mut(), 1000);
731
732    // Write some data
733    for i in 0..100 {
734      putter.write_u8(i as u8);
735    }
736
737    assert_eq!(putter.remaining_mut(), 900);
738    assert_eq!(putter.position(), 100);
739
740    // Verify data
741    #[allow(clippy::needless_range_loop)]
742    for i in 0..100 {
743      assert_eq!(large_data[i], i as u8);
744    }
745  }
746
747  #[test]
748  fn test_putter_boundary_conditions() {
749    // Test with single byte buffer
750    let mut single_byte = [0u8; 1];
751    let mut putter = Putter::new(&mut single_byte[..]);
752
753    assert_eq!(putter.remaining_mut(), 1);
754    putter.write_u8(0x42);
755    assert_eq!(putter.remaining_mut(), 0);
756
757    // Operations on exhausted putter
758    assert!(putter.try_advance_mut(1).is_err());
759    assert_eq!(putter.put_u8_checked(0x99), None);
760    assert!(putter.try_put_u8(0x99).is_err());
761
762    // Reset should work
763    putter.reset_position();
764    assert_eq!(putter.remaining_mut(), 1);
765  }
766
767  #[test]
768  fn test_putter_with_limit_larger_than_buffer() {
769    let mut data = [0u8; 3];
770    let putter = Putter::with_limit(&mut data[..], 10);
771
772    // Should be limited by buffer size, not the requested limit
773    assert_eq!(putter.remaining_mut(), 3);
774  }
775
776  #[test]
777  fn test_putter_with_limit_zero() {
778    let mut data = [0u8; 5];
779    let putter = Putter::with_limit(&mut data[..], 0);
780
781    assert_eq!(putter.remaining_mut(), 0);
782  }
783
784  #[test]
785  fn test_written_calculation() {
786    let mut data = [0u8; 10];
787    let mut putter = Putter::new(&mut data[..]);
788
789    // Test written calculation with different operations
790    assert_eq!(putter.position(), 0);
791
792    putter.advance_mut(2);
793    assert_eq!(putter.position(), 2);
794
795    putter.write_u8(0x42);
796    assert_eq!(putter.position(), 3);
797
798    putter.write_slice(&[1, 2, 3]);
799    assert_eq!(putter.position(), 6);
800
801    // Reset and verify
802    putter.reset_position();
803    assert_eq!(putter.position(), 0);
804  }
805
806  #[test]
807  fn test_putter_ergonomic_comparison() {
808    let mut data = [0u8; 10];
809
810    // Before: awkward double reference
811    // let mut buf = &mut data[..];
812    // let mut putter = Putter::new(&mut buf);  // &mut &mut [u8]
813
814    // After: clean and natural
815    let mut putter = Putter::new(&mut data[..]); // Just &mut [u8]
816
817    putter.write_u8(0x42);
818    assert_eq!(putter.position(), 1);
819
820    assert_eq!(data[0], 0x42);
821  }
822
823  #[test]
824  fn test_putter_with_range() {
825    let mut data = [0u8; 10];
826    let mut putter = Putter::with_range(&mut data[..], 2..=8);
827
828    assert_eq!(putter.remaining_mut(), 7);
829
830    putter.write_u8(0x42);
831    assert_eq!(putter.position(), 1);
832    putter.reset();
833    assert_eq!(data[2], 0);
834
835    let mut putter = Putter::with_range(&mut data[..], ..7);
836    assert_eq!(putter.remaining_mut(), 7);
837    putter.write_u8(0x99);
838    assert_eq!(putter.position(), 1);
839    putter.reset();
840    assert_eq!(data[0], 0);
841
842    let mut putter = Putter::with_range(&mut data[..], (Bound::Excluded(1), Bound::Unbounded));
843    assert_eq!(putter.remaining_mut(), 8);
844    putter.write_u8(0x77);
845    assert_eq!(putter.position(), 1);
846    putter.reset();
847    assert_eq!(data[2], 0);
848  }
849}