diff --git a/src/buffer.rs b/src/buffer.rs
new file mode 100644
index 0000000000000000000000000000000000000000..13db9eedd91d8c6ff8974cf0d2cf96f5d257c5b4
--- /dev/null
+++ b/src/buffer.rs
@@ -0,0 +1,122 @@
+use core::ops::{Deref, DerefMut};
+
+#[derive(Debug)]
+pub struct BufferOverflow;
+
+#[derive(Debug)]
+pub struct Buffer<'a, const N: usize> {
+    data: &'a mut [u8; N],
+    i: usize,
+}
+
+impl<'a, const N: usize> Buffer<'a, N> {
+    /// Constructs a new `Buffer`.
+    /// `data` is the backing array.
+    /// `i` is the number of elements in `data` that contain data (and should thus be exposed by `Buffer`)
+    pub fn new(data: &'a mut [u8; N], i: usize) -> Self {
+        assert!(i <= data.len());
+
+        Self { data, i }
+    }
+
+    pub fn new_full(data: &'a mut [u8; N]) -> Self {
+        let i = data.len();
+
+        Self::new(data, i)
+    }
+
+    pub fn new_empty(data: &'a mut [u8; N]) -> Self {
+        Self::new(data, 0)
+    }
+
+    pub const fn remaining_capacity(&self) -> usize {
+        N - self.i
+    }
+
+    pub fn try_push(&mut self, v: u8) -> Result<(), BufferOverflow> {
+        if self.i == N {
+            return Err(BufferOverflow);
+        }
+
+        self.data[self.i] = v;
+        self.i += 1;
+
+        Ok(())
+    }
+
+    pub fn push(&mut self, v: u8) {
+        self.try_push(v).unwrap();
+    }
+
+    pub fn try_extend_from_slice(&mut self, other: &[u8]) -> Result<(), BufferOverflow> {
+        if self.remaining_capacity() < other.len() {
+            return Err(BufferOverflow);
+        }
+
+        self.data[self.i..(self.i + other.len())].copy_from_slice(other);
+        self.i += other.len();
+
+        Ok(())
+    }
+
+    pub fn extend(&mut self, other: &[u8]) {
+        self.try_extend_from_slice(other).unwrap();
+    }
+
+    pub fn pop(&mut self) -> Option<u8> {
+        if self.i == 0 {
+            return None;
+        }
+
+        self.i -= 1;
+
+        Some(self.data[self.i])
+    }
+
+    pub fn truncate(&mut self, new_len: usize) {
+        assert!(self.i >= new_len);
+
+        self.i = new_len;
+    }
+
+    pub fn drain(&mut self, start: usize, end: usize) {
+        assert!(end >= start);
+        assert!(end <= self.i);
+        let delta = end - start;
+        let surplus = self.len() - end;
+
+        for i in start..(start + surplus) {
+            self[i] = self[i + delta];
+        }
+
+        self.i -= delta;
+    }
+
+    pub fn clone_backing<'b>(&self, buf: &'b mut [u8; N]) -> Buffer<'b, N> {
+        let mut out = Buffer::new_empty(buf);
+
+        out.extend(self);
+
+        out
+    }
+}
+
+impl<'a, const N: usize> From<&'a mut [u8; N]> for Buffer<'a, N> {
+    fn from(data: &'a mut [u8; N]) -> Self {
+        Self { data, i: 0 }
+    }
+}
+
+impl<'a, const N: usize> Deref for Buffer<'a, N> {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &self.data[..self.i]
+    }
+}
+
+impl<'a, const N: usize> DerefMut for Buffer<'a, N> {
+    fn deref_mut(&mut self) -> &mut [u8] {
+        &mut self.data[..self.i]
+    }
+}
diff --git a/src/interleaver.rs b/src/interleaver.rs
index 5661bb43b2b90d526157167c4ad8f5718984f806..5efeaaf7a3fdded2468749e22dd88dfe01383ff0 100644
--- a/src/interleaver.rs
+++ b/src/interleaver.rs
@@ -1,11 +1,13 @@
-use arrayvec::{ArrayVec, CapacityError};
 use bitvec::prelude::*;
 
-use crate::error::EncodeError;
+use crate::{
+    buffer::{Buffer, BufferOverflow},
+    error::EncodeError,
+};
 
 pub(crate) fn interleave<const N: usize>(
     data: &[u8],
-    out: &mut ArrayVec<u8, N>,
+    out: &mut Buffer<N>,
 ) -> Result<(), EncodeError> {
     let bv = data.view_bits::<Msb0>();
 
@@ -33,10 +35,10 @@ pub(crate) fn interleave<const N: usize>(
 
 pub(crate) fn uninterleave<const N: usize>(
     data: &[u8],
-) -> Result<ArrayVec<u8, N>, CapacityError<u8>> {
+    out: &mut Buffer<N>,
+) -> Result<(), BufferOverflow> {
     let bv = data.view_bits::<Msb0>();
 
-    let mut out: ArrayVec<u8, N> = ArrayVec::new();
     for _ in 0..data.len() {
         out.try_push(0)?;
     }
@@ -55,7 +57,7 @@ pub(crate) fn uninterleave<const N: usize>(
         }
     }
 
-    Ok(out)
+    Ok(())
 }
 
 #[cfg(test)]
@@ -64,17 +66,22 @@ mod tests {
 
     #[test]
     fn interleaver_works() {
-        let orig =
-            ArrayVec::try_from([0x84, 0x73, 0x12, 0xA3, 0xFF, 0x00, 0xC2, 0x1B, 0x77]).unwrap();
-        let mut interleaved: ArrayVec<u8, 10> = ArrayVec::new();
+        let mut data = [0x84, 0x73, 0x12, 0xA3, 0xFF, 0x00, 0xC2, 0x1B, 0x77];
+        let orig = Buffer::new_full(&mut data);
+
+        let mut interleaved = [0; 10];
+        let mut interleaved = Buffer::new(&mut interleaved, 0);
         interleaved.push(b'H');
+
         interleave(&orig, &mut interleaved).unwrap();
 
         let expected = [b'H', 0xCD, 0xB5, 0xDB, 0x2A, 0x0A, 0x52, 0x0C, 0x89, 0x4F];
         assert_eq!(expected, interleaved[..]);
 
-        let uninterleaved = uninterleave(&interleaved[1..]).unwrap();
+        let mut uninterleaved = [0; 10];
+        let mut uninterleaved = Buffer::new(&mut uninterleaved, 0);
+        uninterleave(&interleaved[1..], &mut uninterleaved).unwrap();
 
-        assert_eq!(orig, uninterleaved);
+        assert_eq!(*orig, *uninterleaved);
     }
 }
diff --git a/src/ldpc.rs b/src/ldpc.rs
index d085bf4a6269b2c757539918e73680444e8eedc9..a3cda75036ccfb1cadf6a4159707913c59198dc5 100644
--- a/src/ldpc.rs
+++ b/src/ldpc.rs
@@ -1,7 +1,6 @@
-use arrayvec::ArrayVec;
 use labrador_ldpc::LDPCCode;
 
-use crate::error::EncodeError;
+use crate::{buffer::Buffer, error::EncodeError};
 
 macro_rules! enc_chunk {
     ($d:ident, $i:ident, $t:ident, $n:literal) => {
@@ -38,7 +37,7 @@ macro_rules! dec_chunk {
 }
 
 // On failure this still modifies the data array!
-pub(crate) fn encode<const N: usize>(data: &mut ArrayVec<u8, N>) -> Result<(), EncodeError> {
+pub(crate) fn encode<const N: usize>(data: &mut Buffer<N>) -> Result<(), EncodeError> {
     let mut i = 0;
     let n = data.len();
 
@@ -84,7 +83,7 @@ pub(crate) fn encode<const N: usize>(data: &mut ArrayVec<u8, N>) -> Result<(), E
     Ok(())
 }
 
-pub(crate) fn decode<const N: usize>(data_av: &mut ArrayVec<u8, N>) -> Option<()> {
+pub(crate) fn decode<const N: usize>(data_av: &mut Buffer<N>) -> Option<()> {
     if data_av.len() < 2 {
         return None;
     }
@@ -157,25 +156,18 @@ pub(crate) fn decode<const N: usize>(data_av: &mut ArrayVec<u8, N>) -> Option<()
 #[cfg(test)]
 mod tests {
     use super::*;
-    use arrayvec::ArrayVec;
 
     #[test]
     fn len_test() {
         // from the example in the docs
-        let mut data: ArrayVec<u8, 8191> = ArrayVec::new();
+        let mut buf = [0; 8191];
+        let mut data = Buffer::new_empty(&mut buf);
 
         for _ in 0..41 {
-            data.extend(
-                b"Example packet data  wueirpqwerwrywqoeiruy29346129384761"
-                    .iter()
-                    .cloned(),
-            );
+            data.extend(b"Example packet data  wueirpqwerwrywqoeiruy29346129384761");
         }
-        data.extend(
-            b"Example packet data  wueirpqwerwrywqoeiru346129384761"
-                .iter()
-                .cloned(),
-        );
+        data.extend(b"Example packet data  wueirpqwerwrywqoeiru346129384761");
+
         assert_eq!(2349, data.len());
 
         encode(&mut data).unwrap();
@@ -185,51 +177,50 @@ mod tests {
 
     #[test]
     fn basic_encode_decode_short() {
-        let mut data: ArrayVec<u8, 32> = ArrayVec::new();
+        let mut buf = [0; 32];
+        let mut buf2 = [0; 32];
+        let mut data = Buffer::new_empty(&mut buf);
         data.try_extend_from_slice(b"Hello world!").unwrap();
-        let orig = data.clone();
+        let orig = data.clone_backing(&mut buf2);
 
         encode(&mut data).unwrap();
 
         decode(&mut data).unwrap();
 
-        assert_eq!(orig, data);
+        assert_eq!(*orig, *data);
     }
 
     #[test]
     fn basic_encode_decode() {
-        let mut data: ArrayVec<u8, 8191> = ArrayVec::new();
+        let mut buf = [0; 8191];
+        let mut buf2 = [0; 8191];
+        let mut data = Buffer::new_empty(&mut buf);
         for _ in 0..50 {
-            data.extend(
-                b"This is a test packet. jsalksjd093809324JASLD:LKD*#$)(*#@)"
-                    .iter()
-                    .cloned(),
-            );
+            data.extend(b"This is a test packet. jsalksjd093809324JASLD:LKD*#$)(*#@)");
         }
-        let orig = data.clone();
+        let orig = data.clone_backing(&mut buf2);
 
         encode(&mut data).unwrap();
-        assert_ne!(orig, data);
+        assert_ne!(*orig, *data);
 
         decode(&mut data).unwrap();
 
-        assert_eq!(orig, data);
+        assert_eq!(*orig, *data);
     }
 
     #[test]
     fn encode_decode_with_bit_flips() {
-        let mut data: ArrayVec<u8, 8191> = ArrayVec::new();
+        let mut buf = [0; 8191];
+        let mut buf2 = [0; 8191];
+        let mut data = Buffer::new_empty(&mut buf);
+
         for _ in 0..50 {
-            data.extend(
-                b"jsalksjd093809324JASLD:LKD*#$)(*#@) Another test packet"
-                    .iter()
-                    .cloned(),
-            );
+            data.extend(b"jsalksjd093809324JASLD:LKD*#$)(*#@) Another test packet");
         }
-        let orig = data.clone();
+        let orig = data.clone_backing(&mut buf2);
 
         encode(&mut data).unwrap();
-        assert_ne!(orig, data);
+        assert_ne!(*orig, *data);
 
         data[234] ^= 0x55;
         data[0] ^= 0xAA;
@@ -237,6 +228,6 @@ mod tests {
 
         decode(&mut data).unwrap();
 
-        assert_eq!(orig, data);
+        assert_eq!(*orig, *data);
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 79eebf023d26c464a8773b0a66dd14b975024ef7..feeaa23f790c92af2322ee1727132f366bdddb0a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 #![no_std]
 
+pub mod buffer;
 pub mod error;
 pub mod interleaver;
 pub mod ldpc;
diff --git a/src/packet.rs b/src/packet.rs
index a671484362d1b2a41148797b844eaca62a7bcc9f..6ad9e01630fde26843aff00f211de96a43497099 100644
--- a/src/packet.rs
+++ b/src/packet.rs
@@ -1,9 +1,9 @@
 use core::fmt::Debug;
 
-use arrayvec::{ArrayVec, CapacityError};
 use crc::{Crc, CRC_16_IBM_SDLC};
 
 use crate::{
+    buffer::{Buffer, BufferOverflow},
     error::{CommentError, DecodeError, EncodeError},
     interleaver, ldpc, utf8,
     whisker::{
@@ -30,7 +30,7 @@ macro_rules! uniq_whisker {
                     return Err(EncodeError::DuplicateData);
                 }
 
-                try_lock(&mut self.data, |data| {
+                try_lock(&mut self.buf, |data| {
 					let mut buf = [0; 256];
                     // safe to unwrap since we know we have enough space
                     let out = w.encode(&mut buf).unwrap();
@@ -66,7 +66,7 @@ macro_rules! poly_whisker {
             }
 
 			pub fn [<add_ $t:lower>](&mut self, w: $t) -> Result<(), EncodeError> {
-				try_lock(&mut self.data, |data| {
+				try_lock(&mut self.buf, |data| {
 					let mut buf = [0; 256];
                     // safe to unwrap since we know we have enough space
                     let out = w.encode(&mut buf).unwrap();
@@ -89,22 +89,32 @@ macro_rules! poly_whisker {
 
 /// N is the maximum packet size we can handle
 /// Panics if N >= 8191
-#[derive(Default, Clone)]
-pub struct Packet<const N: usize> {
-    data: ArrayVec<u8, N>,
+pub struct Packet<'a, const N: usize> {
+    buf: Buffer<'a, N>,
 }
 
-// TODO need to have methods for removing whiskers from the packet
-impl<const N: usize> Packet<N> {
-    pub fn decode(data: ArrayVec<u8, N>) -> Result<Self, DecodeError> {
+impl<'a, const N: usize> Packet<'a, N> {
+    pub fn new(buf: &'a mut [u8; N]) -> Self {
+        Self { buf: buf.into() }
+    }
+
+    pub fn clone_backing<'b>(&self, buf: &'b mut [u8; N]) -> Packet<'b, N> {
+        Packet {
+            buf: self.buf.clone_backing(buf),
+        }
+    }
+
+    /// Expects bytes in the `buf`
+    /// `buf` is used as the backing buffer for the Packet
+    pub fn decode(buf: Buffer<'a, N>) -> Result<Self, DecodeError> {
         assert!(N <= 8191);
 
         // validate the data
-        for w in WhiskerIter::new(&data) {
+        for w in WhiskerIter::new(&buf) {
             w?;
         }
 
-        let comment_iter = WhiskerIter::new(&data)
+        let comment_iter = WhiskerIter::new(&buf)
             .filter_map(|w| match w.unwrap() {
                 Whisker::Comment(c) => Some(c.internal_data().clone()),
                 _ => None,
@@ -115,18 +125,18 @@ impl<const N: usize> Packet<N> {
             return Err(DecodeError::InvalidComment);
         }
 
-        Ok(Self { data })
+        Ok(Self { buf })
     }
 
     pub fn encode(&self) -> &[u8] {
-        &self.data
+        &self.buf
     }
 
     /// Directly after the CRC block in The Pipeline
-    pub fn semi_encode(mut self) -> Result<ArrayVec<u8, N>, (EncodeError, Self)> {
-        let crc = X25.checksum(&self.data).to_le_bytes();
+    pub fn semi_encode(mut self) -> Result<Buffer<'a, N>, (EncodeError, Self)> {
+        let crc = X25.checksum(&self.buf).to_le_bytes();
 
-        let res: Result<(), CapacityError<u8>> = try_lock(&mut self.data, |data| {
+        let res: Result<(), BufferOverflow> = try_lock(&mut self.buf, |data| {
             data.try_push(crc[0])?;
             data.try_push(crc[1])?;
 
@@ -134,52 +144,57 @@ impl<const N: usize> Packet<N> {
         });
 
         match res {
-            Ok(()) => Ok(self.data),
+            Ok(()) => Ok(self.buf),
             Err(_) => Err((EncodeError::CatsOverflow, self)),
         }
     }
 
     /// Directly after the CRC block in The Pipeline
-    pub fn semi_decode(mut data: ArrayVec<u8, N>) -> Result<Self, DecodeError> {
-        let crc1 = data.pop().ok_or(DecodeError::UnexpectedEndOfInput)?;
-        let crc0 = data.pop().ok_or(DecodeError::UnexpectedEndOfInput)?;
+    /// Expects bytes in the `buf`
+    /// `buf` is used as the backing buffer for the Packet
+    pub fn semi_decode(mut buf: Buffer<'a, N>) -> Result<Self, DecodeError> {
+        let crc1 = buf.pop().ok_or(DecodeError::UnexpectedEndOfInput)?;
+        let crc0 = buf.pop().ok_or(DecodeError::UnexpectedEndOfInput)?;
         let crc_expected = u16::from_le_bytes([crc0, crc1]);
-        let crc_actual = X25.checksum(&data);
+        let crc_actual = X25.checksum(&buf);
 
         if crc_expected != crc_actual {
             return Err(DecodeError::CrcMismatch);
         }
 
-        Self::decode(data)
+        Self::decode(buf)
     }
 
     /// Encodes packet for transmission on the air.
     /// Includes the data length L, but does not include the preamble or sync word.
-    pub fn fully_encode(self) -> Result<ArrayVec<u8, N>, EncodeError> {
+    pub fn fully_encode(self, out: &mut Buffer<N>) -> Result<(), EncodeError> {
         let mut data = self.semi_encode().map_err(|(err, _)| err)?;
         whitener::whiten(&mut data);
         ldpc::encode(&mut data)?;
-        let mut out: ArrayVec<u8, N> = ArrayVec::new();
+
         // safe to unwrap - length must be below 8191
         out.try_extend_from_slice(&u16::try_from(data.len()).unwrap().to_le_bytes())
             .map_err(|_| EncodeError::CatsOverflow)?;
-        interleaver::interleave(&data, &mut out)?;
+        interleaver::interleave(&data, out)?;
 
-        Ok(out)
+        Ok(())
     }
 
     /// Decodes packet that was received over the air.
     /// Packet shouldn't have preamble, sync word, or data langth L.
-    pub fn fully_decode(data: &[u8]) -> Result<Self, DecodeError> {
-        let mut data = interleaver::uninterleave(data).map_err(|_| DecodeError::Overflow)?;
-        ldpc::decode(&mut data).ok_or(DecodeError::LdpcError)?;
-        whitener::whiten(&mut data);
-
-        Self::semi_decode(data)
+    /// Expects bytes in the `buf`
+    /// `buf` is used as the backing buffer for the Packet
+    pub fn fully_decode(data: &[u8], buf: &'a mut [u8; N]) -> Result<Self, DecodeError> {
+        let mut buf = Buffer::new_empty(buf);
+        interleaver::uninterleave(data, &mut buf).map_err(|_| DecodeError::Overflow)?;
+        ldpc::decode(&mut buf).ok_or(DecodeError::LdpcError)?;
+        whitener::whiten(&mut buf);
+
+        Self::semi_decode(buf)
     }
 
     pub fn iter(&self) -> ValidatedWhiskerIter {
-        ValidatedWhiskerIter::new(&self.data)
+        ValidatedWhiskerIter::new(&self.buf)
     }
 
     uniq_whisker!(Identification);
@@ -189,7 +204,7 @@ impl<const N: usize> Packet<N> {
     poly_whisker!(Destination);
     poly_whisker!(Arbitrary);
 
-    pub fn comment<'a>(&self, buf: &'a mut [u8]) -> Result<&'a str, CommentError> {
+    pub fn comment<'b>(&self, buf: &'b mut [u8]) -> Result<&'b str, CommentError> {
         let iter = self.iter().filter_map(|w| match w {
             Whisker::Comment(c) => Some(c),
             _ => None,
@@ -221,7 +236,7 @@ impl<const N: usize> Packet<N> {
             return Err(EncodeError::DuplicateData);
         }
 
-        try_lock(&mut self.data, |data| {
+        try_lock(&mut self.buf, |data| {
             let mut comment = comment.as_bytes();
 
             while comment.len() > 255 {
@@ -258,12 +273,12 @@ impl<const N: usize> Packet<N> {
 
     fn clear_by_type(&mut self, whisker_type: u8, all: bool) {
         let mut i = 0;
-        while i < self.data.len() {
-            let t = self.data[i];
-            let step = usize::from(self.data[i + 1]) + 2;
+        while i < self.buf.len() {
+            let t = self.buf[i];
+            let step = usize::from(self.buf[i + 1]) + 2;
 
             if t == whisker_type {
-                self.data.drain(i..(i + step));
+                self.buf.drain(i, i + step);
 
                 if !all {
                     return;
@@ -275,26 +290,26 @@ impl<const N: usize> Packet<N> {
     }
 }
 
-impl<const N: usize> Debug for Packet<N> {
+impl<'a, const N: usize> Debug for Packet<'a, N> {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         f.debug_list()
-            .entries(ValidatedWhiskerIter::new(&self.data))
+            .entries(ValidatedWhiskerIter::new(&self.buf))
             .finish()
     }
 }
 
 // if the function returns an error, we roll back the array
 // by rolling it back, we really just pop bytes off the end until the length matches
-fn try_lock<const N: usize, T, E, F: Fn(&mut ArrayVec<u8, N>) -> Result<T, E>>(
-    arr: &mut ArrayVec<u8, N>,
+fn try_lock<'a, const N: usize, T, E, F: Fn(&mut Buffer<'a, N>) -> Result<T, E>>(
+    buf: &mut Buffer<'a, N>,
     f: F,
 ) -> Result<T, E> {
-    let len = arr.len();
+    let len = buf.len();
 
-    match f(arr) {
+    match f(buf) {
         Ok(x) => Ok(x),
         Err(e) => {
-            arr.truncate(len);
+            buf.truncate(len);
 
             Err(e)
         }
@@ -303,7 +318,7 @@ fn try_lock<const N: usize, T, E, F: Fn(&mut ArrayVec<u8, N>) -> Result<T, E>>(
 
 #[cfg(test)]
 mod tests {
-    use arrayvec::ArrayString;
+    use arrayvec::{ArrayString, ArrayVec};
 
     use super::*;
 
@@ -312,7 +327,8 @@ mod tests {
         let d1 = Destination::new(false, 7, "CALL1", 23).unwrap();
         let d2 = Destination::new(true, 23, "CALL2", 2).unwrap();
 
-        let mut packet: Packet<1024> = Packet::default();
+        let mut buf = [0; 1024];
+        let mut packet: Packet<1024> = Packet::new(&mut buf);
         packet.add_destination(d1.clone()).unwrap();
         packet.add_destination(d2.clone()).unwrap();
 
@@ -324,7 +340,8 @@ mod tests {
 
     #[test]
     fn route_clear() {
-        let mut p: Packet<1024> = Packet::default();
+        let mut buf = [0; 1024];
+        let mut p: Packet<1024> = Packet::new(&mut buf);
         p.add_identification(Identification::new(123, "call", 43).unwrap())
             .unwrap();
         let mut r = Route::new(8);
@@ -347,7 +364,8 @@ mod tests {
     fn semi_e2e() {
         let comment = "Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.";
 
-        let mut packet = Packet::<2048>::default();
+        let mut buf = [0; 2048];
+        let mut packet = Packet::new(&mut buf);
         packet
             .add_identification(Identification {
                 icon: 123,
@@ -385,7 +403,8 @@ mod tests {
     fn full_e2e() {
         let comment = "Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.";
 
-        let mut packet = Packet::<4096>::default();
+        let mut buf = [0; 4096];
+        let mut packet = Packet::new(&mut buf);
         packet
             .add_identification(Identification {
                 icon: 123,
@@ -403,13 +422,16 @@ mod tests {
         });
         assert!(matches!(res, Err(EncodeError::DuplicateData)));
 
-        let mut fully = packet.fully_encode().unwrap();
+        let mut buf2 = [0; 4096];
+        let mut fully = Buffer::new_empty(&mut buf2);
+        packet.fully_encode(&mut fully).unwrap();
 
         fully[40] ^= 0x55;
         fully[844] ^= 0x7B;
 
         // exclude length
-        let packet2: Packet<8191> = Packet::fully_decode(&fully[2..]).unwrap();
+        let mut buf3 = [0; 8191];
+        let packet2: Packet<8191> = Packet::fully_decode(&fully[2..], &mut buf3).unwrap();
         assert_eq!(
             Identification {
                 icon: 123,
@@ -476,7 +498,8 @@ mod tests {
             118, 118, 118, 118, 118, 118, 118, 0, 0, 0, 0, 96, 96,
         ];
 
-        let _ = Packet::<1024>::fully_decode(&data);
+        let mut buf = [0; 1024];
+        let _ = Packet::<1024>::fully_decode(&data, &mut buf);
     }
 
     #[test]
@@ -524,13 +547,15 @@ mod tests {
         )
         .unwrap();
 
-        let _ = Packet::<1024>::semi_decode(data);
+        let mut buf = [0; 1024];
+        let _ = Packet::<1024>::fully_decode(&data, &mut buf);
     }
 
     #[test]
     fn decode_fuzz_tests() {
-        let data = ArrayVec::try_from(&[4, 0, 0, 0][..]).unwrap();
+        let data = [4, 0, 0, 0];
 
-        let _ = Packet::<1024>::decode(data);
+        let mut buf = [0; 1024];
+        let _ = Packet::<1024>::fully_decode(&data, &mut buf);
     }
 }
diff --git a/src/whitener.rs b/src/whitener.rs
index 46123fe9902a969ed061c6c7004a7576af503526..79d21d97d4a8fa58b6bc6c616e3041fdbd560b77 100644
--- a/src/whitener.rs
+++ b/src/whitener.rs
@@ -1,10 +1,8 @@
-use arrayvec::ArrayVec;
-
 const WHITE: [u8; 16] = [
     0xe9, 0xcf, 0x67, 0x20, 0x19, 0x1a, 0x07, 0xdc, 0xc0, 0x72, 0x79, 0x97, 0x51, 0xf7, 0xdd, 0x93,
 ];
 
-pub(crate) fn whiten<const N: usize>(data: &mut ArrayVec<u8, N>) {
+pub(crate) fn whiten(data: &mut [u8]) {
     for (i, d) in data.iter_mut().enumerate() {
         *d ^= WHITE[i % 15];
     }
@@ -16,9 +14,9 @@ mod tests {
 
     #[test]
     fn basic() {
-        let mut data: ArrayVec<u8, 512> =
-            ArrayVec::try_from(&b"Hello world! The quick brown fox jumped over the lazy dog"[..])
-                .unwrap();
+        let mut data = [0; 64];
+        data[0..57]
+            .clone_from_slice(&b"Hello world! The quick brown fox jumped over the lazy dog"[..]);
         let orig = data.clone();
 
         whiten(&mut data);