bufkit/buf_mut/
putter.rs

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