bufkit/chunk_mut/
putter.rs

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