1use std::fmt::{Debug, Formatter};
2use scroll::{Endian, Pwrite, ctx::TryIntoCtx};
3use std::ops::*;
4
5pub struct DynamicBuffer {
63 pub(crate) buffer: Vec<u8>,
64 alloc_increment: usize,
65 pub(crate) start_offset: usize,
66 write_end: usize,
67}
68
69impl DynamicBuffer {
70 pub fn new() -> DynamicBuffer {
72 Self::with_increment(1)
73 }
74
75 pub fn with_increment(alloc_increment: usize) -> DynamicBuffer {
77 DynamicBuffer {
78 buffer: vec![],
79 alloc_increment,
80 start_offset: 0,
81 write_end: 0,
82 }
83 }
84
85 pub fn get(&self) -> &[u8] {
97 &self.buffer[..self.write_end]
98 }
99
100 pub fn into_vec(mut self) -> Vec<u8> {
111 self.buffer.truncate(self.write_end);
112 self.buffer
113 }
114
115 pub fn clear(&mut self) {
133 self.buffer.clear();
134 self.start_offset = 0;
135 self.write_end = 0;
136 }
137}
138
139impl Debug for DynamicBuffer {
140 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
141 Debug::fmt(self.get(), f)
142 }
143}
144
145impl Index<usize> for DynamicBuffer {
146 type Output = u8;
147
148 fn index(&self, index: usize) -> &Self::Output {
149 self.buffer.index(self.start_offset + index)
150 }
151}
152impl IndexMut<usize> for DynamicBuffer {
153 fn index_mut(&mut self, mut index: usize) -> &mut Self::Output {
154 index += self.start_offset;
155
156 if let Some(diff) = index.checked_sub(self.buffer.len()) {
157 self.buffer
158 .extend(std::iter::repeat(0).take(std::cmp::max(self.alloc_increment, diff + 1)));
159 }
160
161 self.write_end = self.write_end.max(index + 1);
162
163 self.buffer.index_mut(index)
164 }
165}
166
167impl<Ctx: Copy, E> Pwrite<Ctx, E> for DynamicBuffer {
168 fn pwrite_with<N: TryIntoCtx<Ctx, Self, Error = E>>(
169 &mut self,
170 n: N,
171 offset: usize,
172 ctx: Ctx,
173 ) -> Result<usize, E> {
174 self.start_offset += offset;
175
176 let end = n.try_into_ctx(self, ctx)?;
177
178 self.start_offset -= offset;
179
180 Ok(end)
181 }
182}
183
184impl TryIntoCtx<(), DynamicBuffer> for &'_ [u8] {
185 type Error = scroll::Error; fn try_into_ctx(self, into: &mut DynamicBuffer, _: ()) -> Result<usize, Self::Error> {
188 let len = self.len();
189
190 into[len - 1] = 0;
193
194 into.buffer[into.start_offset..into.start_offset + len].copy_from_slice(self);
197
198 Ok(len)
199 }
200}
201
202macro_rules! num_impl {
203 ($t:ty) => {
204 impl TryIntoCtx<scroll::Endian, DynamicBuffer> for $t {
205 type Error = scroll::Error; fn try_into_ctx(
208 self,
209 buf: &mut DynamicBuffer,
210 ctx: Endian,
211 ) -> Result<usize, Self::Error> {
212 (&if ctx.is_little() {
213 self.to_le_bytes()
214 } else {
215 self.to_be_bytes()
216 }).try_into_ctx(buf, ())
217 }
218 }
219
220 impl TryIntoCtx<scroll::Endian, DynamicBuffer> for &$t {
221 type Error = scroll::Error;
222
223 fn try_into_ctx(
224 self,
225 buf: &mut DynamicBuffer,
226 ctx: Endian,
227 ) -> Result<usize, Self::Error> {
228 (*self).try_into_ctx(buf, ctx)
229 }
230 }
231 };
232}
233
234num_impl!(i8);
235num_impl!(i16);
236num_impl!(i32);
237num_impl!(i64);
238num_impl!(i128);
239
240num_impl!(u8);
241num_impl!(u16);
242num_impl!(u32);
243num_impl!(u64);
244num_impl!(u128);
245
246num_impl!(f32);
247num_impl!(f64);
248
249#[cfg(test)]
250mod tests {
251 use super::DynamicBuffer;
252 use scroll::{Endian, Pwrite, ctx::TryIntoCtx};
253
254 struct Test {
255 a: u16,
256 b: u32,
257 c: u64,
258 }
259
260 #[test]
261 fn int_write() {
262 let mut buf = DynamicBuffer::new();
263
264 buf.pwrite_with(0x1234u16, 0, Endian::Little).unwrap();
265 buf.pwrite_with(0x5678i16, 2, Endian::Big).unwrap();
266
267 assert_eq!(buf.get(), [0x34, 0x12, 0x56, 0x78]);
268 }
269
270 #[test]
271 fn offset_write() {
272 let mut buf = DynamicBuffer::new();
273
274 buf.pwrite_with(0x1234u16, 2, Endian::Big).unwrap();
275
276 assert_eq!(buf.get(), [0, 0, 0x12, 0x34]);
277 }
278
279 #[test]
280 fn slice_write() {
281 let mut buf = DynamicBuffer::new();
282
283 buf.pwrite([1u8; 4].as_slice(), 0).unwrap();
284 assert_eq!(buf.get(), [1, 1, 1, 1]);
285
286 buf.pwrite([2u8; 2].as_slice(), 2).unwrap();
287 assert_eq!(buf.get(), [1, 1, 2, 2]);
288 }
289
290 #[test]
291 fn basic_write() {
292 impl TryIntoCtx<Endian, DynamicBuffer> for Test {
293 type Error = scroll::Error;
294
295 fn try_into_ctx(
296 self,
297 buf: &mut DynamicBuffer,
298 ctx: Endian,
299 ) -> Result<usize, Self::Error> {
300 let offset = &mut 0;
301
302 buf.gwrite_with(self.a, offset, ctx)?;
303 buf.gwrite_with(self.b, offset, ctx)?;
304 buf.gwrite_with(self.c, offset, ctx)?;
305
306 Ok(*offset)
307 }
308 }
309
310 let mut buf = DynamicBuffer::new();
311
312 buf.pwrite_with(Test { a: 1, b: 2, c: 3 }, 0, Endian::Little)
313 .unwrap();
314
315 assert_eq!(buf.get(), [1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]);
316 }
317
318 #[test]
319 fn dyn_size_write() {
320 impl TryIntoCtx<(bool, Endian), DynamicBuffer> for Test {
321 type Error = scroll::Error;
322
323 fn try_into_ctx(
324 self,
325 buf: &mut DynamicBuffer,
326 (is16, ctx): (bool, Endian),
327 ) -> Result<usize, Self::Error> {
328 let offset = &mut 0;
329
330 if is16 {
331 buf.gwrite_with(self.a, offset, ctx)?;
332 } else {
333 buf.gwrite_with(self.a as u32, offset, ctx)?;
334 }
335
336 buf.gwrite_with(self.b, offset, ctx)?;
337 buf.gwrite_with(self.c, offset, ctx)?;
338
339 Ok(*offset)
340 }
341 }
342
343 let mut buf1 = DynamicBuffer::new();
344 buf1.pwrite_with(Test { a: 1, b: 2, c: 3 }, 0, (true, Endian::Little))
345 .unwrap();
346 assert_eq!(buf1.get(), [1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]);
347
348 let mut buf1 = DynamicBuffer::new();
349 buf1.pwrite_with(Test { a: 1, b: 2, c: 3 }, 0, (false, Endian::Little))
350 .unwrap();
351 assert_eq!(buf1.get(), [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]);
352 }
353
354 #[test]
355 fn alloc_size() {
356 let mut buf = DynamicBuffer::with_increment(8);
357
358 buf.pwrite_with(0xbabecafe_u32, 0, Endian::Big).unwrap();
359
360 assert_eq!(buf.buffer.len(), 8);
361 assert_eq!(buf.get(), [0xba, 0xbe, 0xca, 0xfe]);
362 }
363}
364
365#[cfg(doctest)]
366doc_comment::doctest!("../README.md");