gcode/
buffers.rs

1//! Buffer Management.
2//!
3//! This module is mainly intended for use cases when the amount of space that
4//! can be consumed by buffers needs to be defined at compile time. For most
5//! users, the [`DefaultBuffers`] alias should be suitable.
6//!
7//! For most end users it is probably simpler to determine a "good enough"
8//! buffer size and create type aliases of [`GCode`] and [`Line`] for that size.
9
10use crate::{Comment, GCode, Word};
11use arrayvec::{Array, ArrayVec};
12use core::{
13    fmt::{self, Debug, Display, Formatter},
14    marker::PhantomData,
15};
16
17#[allow(unused_imports)] // for rustdoc links
18use crate::Line;
19
20cfg_if::cfg_if! {
21    if #[cfg(feature = "std")] {
22        /// The default buffer type for this platform.
23        ///
24        /// This is a type alias for [`VecBuffers`] because the crate is compiled
25        /// with the *"std"* feature.
26        pub type DefaultBuffers = VecBuffers;
27
28        /// The default [`Buffer`] to use for a [`GCode`]'s arguments.
29        ///
30        /// This is a type alias for [`Vec<Word>`] because the crate is compiled
31        /// with the *"std"* feature.
32        pub type DefaultArguments = Vec<Word>;
33    } else {
34        /// The default buffer type for this platform.
35        ///
36        /// This is a type alias for [`SmallFixedBuffers`] because the crate is compiled
37        /// without the *"std"* feature.
38        pub type DefaultBuffers = SmallFixedBuffers;
39
40        /// The default [`Buffer`] to use for a [`GCode`]'s arguments.
41        ///
42        /// This is a type alias for [`ArrayVec`] because the crate is compiled
43        /// without the *"std"* feature.
44        pub type DefaultArguments = ArrayVec<[Word; 5]>;
45    }
46}
47
48/// A set of type aliases defining the types to use when storing data.
49pub trait Buffers<'input> {
50    /// The [`Buffer`] used to store [`GCode`] arguments.
51    type Arguments: Buffer<Word> + Default;
52    /// The [`Buffer`] used to store [`GCode`]s.
53    type Commands: Buffer<GCode<Self::Arguments>> + Default;
54    /// The [`Buffer`] used to store [`Comment`]s.
55    type Comments: Buffer<Comment<'input>> + Default;
56}
57
58/// Something which can store items sequentially in memory. This doesn't
59/// necessarily require dynamic memory allocation.
60pub trait Buffer<T> {
61    /// Try to add another item to this [`Buffer`], returning the item if there
62    /// is no more room.
63    fn try_push(&mut self, item: T) -> Result<(), CapacityError<T>>;
64
65    /// The items currently stored in the [`Buffer`].
66    fn as_slice(&self) -> &[T];
67}
68
69impl<T, A: Array<Item = T>> Buffer<T> for ArrayVec<A> {
70    fn try_push(&mut self, item: T) -> Result<(), CapacityError<T>> {
71        ArrayVec::try_push(self, item).map_err(|e| CapacityError(e.element()))
72    }
73
74    fn as_slice(&self) -> &[T] { &self }
75}
76
77/// The smallest usable set of [`Buffers`].
78///
79/// ```rust
80/// # use gcode::{Line, GCode, buffers::{Buffers, SmallFixedBuffers}};
81/// let line_size = std::mem::size_of::<Line<'_, SmallFixedBuffers>>();
82/// assert!(line_size <= 350, "Got {}", line_size);
83///
84/// // the explicit type for a `GCode` backed by `SmallFixedBuffers`
85/// type SmallBufferGCode<'a> = GCode<<SmallFixedBuffers as Buffers<'a>>::Arguments>;
86///
87/// let gcode_size = std::mem::size_of::<SmallBufferGCode<'_>>();
88/// assert!(gcode_size  <= 250, "Got {}", gcode_size);
89/// ```
90#[derive(Debug, Copy, Clone, PartialEq)]
91pub enum SmallFixedBuffers {}
92
93impl<'input> Buffers<'input> for SmallFixedBuffers {
94    type Arguments = DefaultArguments;
95    type Commands = ArrayVec<[GCode<Self::Arguments>; 1]>;
96    type Comments = ArrayVec<[Comment<'input>; 1]>;
97}
98
99with_std! {
100    /// A [`Buffers`] implementation which uses [`std::vec::Vec`] for storing items.
101    ///
102    /// In terms of memory usage, this has the potential to use a lot less overall
103    /// than something like [`SmallFixedBuffers`] because we've traded deterministic
104    /// memory usage for only allocating memory when it is required.
105    #[derive(Debug, Copy, Clone, PartialEq)]
106    pub enum VecBuffers {}
107
108    impl<'input> Buffers<'input> for VecBuffers {
109        type Arguments = DefaultArguments;
110        type Commands = Vec<GCode<Self::Arguments>>;
111        type Comments = Vec<Comment<'input>>;
112    }
113
114    impl<T> Buffer<T> for Vec<T> {
115        fn try_push(&mut self, item: T) -> Result<(), CapacityError<T>> {
116            self.push(item);
117            Ok(())
118        }
119
120        fn as_slice(&self) -> &[T] { &self }
121    }
122}
123
124/// An error returned when [`Buffer::try_push()`] fails.
125///
126/// When a [`Buffer`] can't add an item, it will use [`CapacityError`] to pass
127/// the original item back to the caller.
128#[derive(Debug, Copy, Clone, PartialEq)]
129pub struct CapacityError<T>(pub T);
130
131impl<T: Debug> Display for CapacityError<T> {
132    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
133        write!(f, "insufficient capacity")
134    }
135}
136
137with_std! {
138    impl<T: Debug> std::error::Error for CapacityError<T> {}
139}
140
141/// Debug *any* [`Buffer`] when the item is [`Debug`].
142pub(crate) fn debug<'a, T, B>(buffer: &'a B) -> impl Debug + 'a
143where
144    B: Buffer<T> + 'a,
145    T: Debug + 'a,
146{
147    DebugBuffer::new(buffer)
148}
149
150struct DebugBuffer<'a, B, T> {
151    buffer: &'a B,
152    _item: PhantomData<&'a T>,
153}
154
155impl<'a, T, B: Buffer<T>> DebugBuffer<'a, B, T> {
156    fn new(buffer: &'a B) -> Self {
157        DebugBuffer {
158            buffer,
159            _item: PhantomData,
160        }
161    }
162}
163
164impl<'a, B, T> Debug for DebugBuffer<'a, B, T>
165where
166    B: Buffer<T>,
167    T: Debug,
168{
169    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
170        let entries =
171            self.buffer.as_slice().iter().map(|item| item as &dyn Debug);
172
173        f.debug_list().entries(entries).finish()
174    }
175}