Repository: sharksforarms/deku
Pull request #405 (2024-01-17 05:16:42)

Add no-assert-string feature
When no-assert-string is enabled, remove string generation in .rodata section. When writing parsers made for embedded platforms that are limited in storage these strings add up when many assertions are added.

github-actions[bot]: ## Benchmark for e51d275
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1099.2±17.01ns** | 1144.4±14.90ns | **+4.11%** |
| deku_read_byte | 23.2±0.43ns | **22.7±0.39ns** | **-2.16%** |
| deku_read_enum | 9.4±0.16ns | 9.3±0.12ns | -1.06% |
| deku_read_vec | 53.3±0.83ns | 53.3±0.72ns | 0.00% |
| deku_write_bits | 113.0±1.29ns | **108.7±3.40ns** | **-3.81%** |
| deku_write_byte | 140.3±6.10ns | **122.7±2.41ns** | **-12.54%** |
| deku_write_enum | 92.7±6.25ns | **86.1±4.66ns** | **-7.12%** |
| deku_write_vec | 3.1±0.03µs | 3.1±0.03µs | 0.00% |

  </details>
  
github-actions[bot]: ## Benchmark for 7c221b9
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1124.9±13.34ns | **1109.6±18.58ns** | **-1.36%** |
| deku_read_byte | 23.4±0.97ns | **22.5±0.42ns** | **-3.85%** |
| deku_read_enum | 9.5±0.10ns | 9.5±0.07ns | 0.00% |
| deku_read_vec | 53.8±0.46ns | 54.2±0.87ns | +0.74% |
| deku_write_bits | 113.1±1.87ns | 111.8±8.35ns | -1.15% |
| deku_write_byte | 124.1±4.25ns | 125.7±7.15ns | +1.29% |
| deku_write_enum | 86.0±4.27ns | 87.0±4.84ns | +1.16% |
| deku_write_vec | 3.2±0.04µs | **3.0±0.03µs** | **-6.25%** |

  </details>
  
sempervictus: Wonder if some sort of rainbow table/heuristic dictionary or compression based approach would help provide middle ground for more usable string space with qualified lookup/decompression speed. Some sort of checksum dictionary stored in runtime might also be more efficient since its not really the bytes of the strings we're usually asserting but their equivalence to X or Y which matches pre-computed hashes.
wcampbell0x2a: > Wonder if some sort of rainbow table/heuristic dictionary or compression based approach would help provide middle ground for more usable string space with qualified lookup/decompression speed. Some sort of checksum dictionary stored in runtime might also be more efficient since its not really the bytes of the strings we're usually asserting but their equivalence to X or Y which matches pre-computed hashes.

Interesting idea, it could return a hash of both the struct/enum and field which could be reversed at runtime if you wanted that information. That would take some design, this is easier for me to just turn on and not have extra bytes of data..

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a18b9e8..2068fe0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,12 +3,25 @@
 ## [Unreleased]
 
 ## Changes
-[#390](https://github.com/sharksforarms/deku/pull/390) added MSRV for `1.67.1`.
-[#389](https://github.com/sharksforarms/deku/pull/389) changed edition to 2021
-[#352](https://github.com/sharksforarms/deku/pull/352) added a new function `from_reader` that uses `io::Read`.
-`io::Read` is also now used internally, bringing massive performance and usability improvements.
+- Added MSRV for `1.67.1` ([#390](https://github.com/sharksforarms/deku/pull/390))
+- Changed edition to 2021 ([#389](https://github.com/sharksforarms/deku/pull/389))
+- Refactored `logging` feature with massive usability increases ([#352](https://github.com/sharksforarms/deku/pull/352)), ([#355](https://github.com/sharksforarms/deku/pull/355))
+- Bumped the `syn` library to 2.0, which required replacing `type` for Enums with `id_type` ([#386](https://github.com/sharksforarms/deku/pull/386))
+```diff,rust
+ #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+-#[deku(type = "u8")]
++#[deku(id_type = "u8")]
+ enum DekuTest {
+     #[deku(id_pat = "_")]
+     VariantC((u8, u8)),
+ }
+```
+
+### Updated Reader API
+- Changed API of reading to use `io::Read`, bringing massive performance and usability improvements ([#352](https://github.com/sharksforarms/deku/pull/352))
+- Changed the trait `DekuRead` to `DekuReader`
 
-### New `from_reader`
+For example:
 ```rust
 use std::io::{Seek, SeekFrom, Read};
 use std::fs::File;
@@ -31,7 +44,7 @@ With the switch to internal streaming, the variables `deku::input`, `deku::input
 `deku::reader` is a replacement for some of the functionality.
 See [examples/deku_input.rs](examples/deku_input.rs) for a new example of caching all reads.
 
-old:
+Old:
 ```rust
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 struct DekuTest {
@@ -56,7 +69,7 @@ fn custom_read(
 }
 ```
 
-new:
+New:
 ```rust
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 struct DekuTest {
@@ -83,7 +96,7 @@ fn custom_read<R: std::io::Read>(
 
 - With the addition of using `Read`, containing a byte slice with a reference is not supported:
 
-old
+Old
 ```rust
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
 struct TestStruct<'a> {
@@ -94,7 +107,7 @@ struct TestStruct<'a> {
 }
 ```
 
-new
+New
 ```rust
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
 struct TestStruct {
@@ -108,20 +121,20 @@ struct TestStruct {
 - `id_pat` is now required to be the same type as stored id.
 This also disallows using tuples for storing the id:
 
-old:
+Old:
 ```rust
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     #[deku(id_pat = "_")]
     VariantC((u8, u8)),
 }
 ```
 
-new:
+New:
 ```rust
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     #[deku(id_pat = "_")]
     VariantC {
@@ -133,6 +146,81 @@ enum DekuTest {
 
 - The feature `const_generics` was removed and is enabled by default.
 
+### Updated Writer API
+- Changed API of writing to use `io::Write`, bringing massive performance and usability improvements ([#355](https://github.com/sharksforarms/deku/pull/355))
+- Changed the trait `DekuWrite` to `DekuWriter`
+- The more internal (with context) `write(..)` was replaced with `to_writer(..)`.
+With the switch to internal streaming, the variables `deku::output` are now not possible and were removed. `deku::writer` is a replacement for some of the functionality.
+
+Old:
+```rust
+fn bit_flipper_write(
+    field_a: u8,
+    field_b: u8,
+    output: &mut BitVec<u8, Msb0>,
+    bit_size: BitSize,
+) -> Result<(), DekuError> {
+    // Access to previously written fields
+    println!("field_a = 0x{:X}", field_a);
+
+    // value of field_b
+    println!("field_b = 0x{:X}", field_b);
+
+    // Size of the current field
+    println!("bit_size: {:?}", bit_size);
+
+    // flip the bits on value if field_a is 0x01
+    let value = if field_a == 0x01 { !field_b } else { field_b };
+
+    value.write(output, bit_size)
+}
+
+#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    field_a: u8,
+
+    #[deku(
+        writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))"
+    )]
+    field_b: u8,
+}
+````
+
+New:
+```rust
+fn bit_flipper_write<W: Write>(
+    field_a: u8,
+    field_b: u8,
+    writer: &mut Writer<W>,
+    bit_size: BitSize,
+) -> Result<(), DekuError> {
+    // Access to previously written fields
+    println!("field_a = 0x{:X}", field_a);
+
+    // value of field_b
+    println!("field_b = 0x{:X}", field_b);
+
+    // Size of the current field
+    println!("bit_size: {:?}", bit_size);
+
+    // flip the bits on value if field_a is 0x01
+    let value = if field_a == 0x01 { !field_b } else { field_b };
+
+    value.to_writer(writer, bit_size)
+}
+
+#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    field_a: u8,
+
+    #[deku(
+        writer = "bit_flipper_write(*field_a, *field_b, deku::writer, BitSize(8))"
+    )]
+    field_b: u8,
+}
+```
+- Added `DekuError::Write` to denote `io::Write` errors
+
 ## [0.16.0] - 2023-02-28
 
 ### Changes
diff --git a/Cargo.toml b/Cargo.toml
index 21c42b1..bf3139f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,7 @@ default = ["std"]
 std = ["deku_derive/std", "bitvec/std", "alloc", "no_std_io/std"]
 alloc = ["bitvec/alloc"]
 logging = ["deku_derive/logging", "log"]
+no-assert-string = ["deku_derive/no-assert-string"]
 
 [dependencies]
 deku_derive = { version = "^0.16.0", path = "deku-derive", default-features = false}
@@ -32,13 +33,14 @@ log = { version = "0.4.17", optional = true }
 no_std_io = { version = "0.5.0", default-features = false, features = ["alloc"] }
 
 [dev-dependencies]
-rstest = "0.16.0"
+rstest = "0.18.0"
 hexlit = "0.5.5"
 criterion = "0.4.0"
 alloc_counter = "0.0.4"
 trybuild = "1.0.77"
 rustc-hash = "1.1.0"
 env_logger = "0.10.0"
+assert_hex = "0.2.2"
 
 [[bench]]
 name = "deku"
diff --git a/benches/deku.rs b/benches/deku.rs
index d6bad65..b047d7b 100644
--- a/benches/deku.rs
+++ b/benches/deku.rs
@@ -21,7 +21,7 @@ struct DekuBytes {
 }
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuEnum {
     #[deku(id = "0x01")]
     VariantA(u8),
@@ -34,40 +34,13 @@ struct DekuVec {
     data: Vec<u8>,
 }
 
-fn deku_read_bits(mut reader: impl Read) {
-    let mut reader = Reader::new(&mut reader);
-    let _v = DekuBits::from_reader_with_ctx(&mut reader, ()).unwrap();
-}
-
-fn deku_write_bits(input: &DekuBits) {
-    let _v = input.to_bytes().unwrap();
-}
-
-fn deku_read_byte(mut reader: impl Read) {
-    let mut reader = Reader::new(&mut reader);
-    let _v = DekuBytes::from_reader_with_ctx(&mut reader, ()).unwrap();
-}
-
-fn deku_write_byte(input: &DekuBytes) {
+fn deku_write<T: DekuContainerWrite>(input: &T) {
     let _v = input.to_bytes().unwrap();
 }
 
-fn deku_read_enum(mut reader: impl Read) {
+fn deku_read<T: for<'a> DekuContainerRead<'a>>(mut reader: impl Read) {
     let mut reader = Reader::new(&mut reader);
-    let _v = DekuEnum::from_reader_with_ctx(&mut reader, ()).unwrap();
-}
-
-fn deku_write_enum(input: &DekuEnum) {
-    let _v = input.to_bytes().unwrap();
-}
-
-fn deku_read_vec(mut reader: impl Read) {
-    let mut reader = Reader::new(&mut reader);
-    let _v = DekuVec::from_reader_with_ctx(&mut reader, ()).unwrap();
-}
-
-fn deku_write_vec(input: &DekuVec) {
-    let _v = input.to_bytes().unwrap();
+    let _v = T::from_reader_with_ctx(&mut reader, ()).unwrap();
 }
 
 fn criterion_benchmark(c: &mut Criterion) {
@@ -75,13 +48,13 @@ fn criterion_benchmark(c: &mut Criterion) {
         let reader = Cursor::new(&[0x01; 1 + 2 + 4]);
         b.iter_batched(
             || reader.clone(),
-            |mut reader| deku_read_byte(&mut reader),
+            |mut reader| deku_read::<DekuBytes>(&mut reader),
             BatchSize::SmallInput,
         )
     });
     c.bench_function("deku_write_byte", |b| {
         b.iter(|| {
-            deku_write_byte(black_box(&DekuBytes {
+            deku_write(black_box(&DekuBytes {
                 data_00: 0x00,
                 data_01: 0x02,
                 data_02: 0x03,
@@ -92,13 +65,13 @@ fn criterion_benchmark(c: &mut Criterion) {
         let reader = Cursor::new(&[0x01; 1]);
         b.iter_batched(
             || reader.clone(),
-            |mut reader| deku_read_bits(&mut reader),
+            |mut reader| deku_read::<DekuBits>(&mut reader),
             BatchSize::SmallInput,
         )
     });
     c.bench_function("deku_write_bits", |b| {
         b.iter(|| {
-            deku_write_bits(black_box(&DekuBits {
+            deku_write(black_box(&DekuBits {
                 data_01: 0x0f,
                 data_02: 0x00,
                 data_03: 0x01,
@@ -110,12 +83,12 @@ fn criterion_benchmark(c: &mut Criterion) {
         let reader = Cursor::new(&[0x01; 2]);
         b.iter_batched(
             || reader.clone(),
-            |mut reader| deku_read_enum(&mut reader),
+            |mut reader| deku_read::<DekuEnum>(&mut reader),
             BatchSize::SmallInput,
         )
     });
     c.bench_function("deku_write_enum", |b| {
-        b.iter(|| deku_write_enum(black_box(&DekuEnum::VariantA(0x02))))
+        b.iter(|| deku_write(black_box(&DekuEnum::VariantA(0x02))))
     });
 
     let deku_write_vec_input = DekuVec {
@@ -126,12 +99,12 @@ fn criterion_benchmark(c: &mut Criterion) {
         let reader = Cursor::new(&[0x08; 8 + 1]);
         b.iter_batched(
             || reader.clone(),
-            |mut reader| deku_read_vec(&mut reader),
+            |mut reader| deku_read::<DekuVec>(&mut reader),
             BatchSize::SmallInput,
         )
     });
     c.bench_function("deku_write_vec", |b| {
-        b.iter(|| deku_write_vec(black_box(&deku_write_vec_input)))
+        b.iter(|| deku_write(black_box(&deku_write_vec_input)))
     });
 }
 
diff --git a/deku-derive/Cargo.toml b/deku-derive/Cargo.toml
index 5d47534..0e022f1 100644
--- a/deku-derive/Cargo.toml
+++ b/deku-derive/Cargo.toml
@@ -15,15 +15,16 @@ proc-macro = true
 [features]
 std = ["proc-macro-crate"]
 logging = []
+no-assert-string = []
 
 [dependencies]
 quote = "1.0"
-syn = "1.0"
+syn = "2.0"
 # extra-traits gives us Debug
 # syn = {version = "1.0", features = ["extra-traits"]}
 proc-macro2 = "1.0"
-darling = "0.14"
+darling = "0.20"
 proc-macro-crate = { version = "1.3.0", optional = true }
 
 [dev-dependencies]
-rstest = "0.16"
+rstest = "0.18"
diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs
index 061a0c2..13abbe7 100644
--- a/deku-derive/src/lib.rs
+++ b/deku-derive/src/lib.rs
@@ -12,7 +12,6 @@ use proc_macro2::TokenStream;
 use quote::quote;
 use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
-use syn::AttributeArgs;
 
 use crate::macros::deku_read::emit_deku_read;
 use crate::macros::deku_write::emit_deku_write;
@@ -208,7 +207,10 @@ impl DekuData {
             ast::Data::Struct(_) => {
                 // Validate id_* attributes are being used on an enum
                 if data.id_type.is_some() {
-                    Err(cerror(data.id_type.span(), "`type` only supported on enum"))
+                    Err(cerror(
+                        data.id_type.span(),
+                        "`id_type` only supported on enum",
+                    ))
                 } else if data.id.is_some() {
                     Err(cerror(data.id.span(), "`id` only supported on enum"))
                 } else if data.bytes.is_some() {
@@ -220,19 +222,19 @@ impl DekuData {
                 }
             }
             ast::Data::Enum(_) => {
-                // Validate `type` or `id` is specified
+                // Validate `id_type` or `id` is specified
                 if data.id_type.is_none() && data.id.is_none() {
                     return Err(cerror(
                         data.ident.span(),
-                        "`type` or `id` must be specified on enum",
+                        "`id_type` or `id` must be specified on enum",
                     ));
                 }
 
-                // Validate either `type` or `id` is specified
+                // Validate either `id_type` or `id` is specified
                 if data.id_type.is_some() && data.id.is_some() {
                     return Err(cerror(
                         data.ident.span(),
-                        "conflicting: both `type` and `id` specified on enum",
+                        "conflicting: both `id_type` and `id` specified on enum",
                     ));
                 }
 
@@ -390,6 +392,9 @@ struct FieldData {
     /// a predicate to decide when to stop reading elements into the container
     until: Option<TokenStream>,
 
+    /// read until `reader.end()`
+    read_all: bool,
+
     /// apply a function to the field after it's read
     map: Option<TokenStream>,
 
@@ -457,6 +462,7 @@ impl FieldData {
             bits_read: receiver.bits_read?,
             bytes_read: receiver.bytes_read?,
             until: receiver.until?,
+            read_all: receiver.read_all,
             map: receiver.map?,
             ctx,
             update: receiver.update?,
@@ -524,6 +530,18 @@ impl FieldData {
             ));
         }
 
+        // Validate usage of read_all
+        if data.read_all
+            && (data.until.is_some()
+                || data.count.is_some()
+                || (data.bits_read.is_some() || data.bytes_read.is_some()))
+        {
+            return Err(cerror(
+                data.bits.span(),
+                "conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`",
+            ));
+        }
+
         Ok(())
     }
 
@@ -638,11 +656,7 @@ struct DekuReceiver {
     id: Option<Id>,
 
     /// enum only: type of the enum `id`
-    #[darling(
-        rename = "type",
-        default = "default_res_opt",
-        map = "map_litstr_as_tokenstream"
-    )]
+    #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
     id_type: Result<Option<TokenStream>, ReplacementError>,
 
     /// enum only: bit size of the enum `id`
@@ -671,7 +685,7 @@ fn apply_replacements(input: &syn::LitStr) -> Result<Cow<'_, syn::LitStr>, Repla
 
     let input_str = input_value
         .replace("deku::reader", "__deku_reader")
-        .replace("deku::output", "__deku_output") // part of the public API `write`
+        .replace("deku::writer", "__deku_writer")
         .replace("deku::bit_offset", "__deku_bit_offset")
         .replace("deku::byte_offset", "__deku_byte_offset");
 
@@ -762,6 +776,10 @@ struct DekuFieldReceiver {
     #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
     until: Result<Option<TokenStream>, ReplacementError>,
 
+    /// read until `reader.end()`
+    #[darling(default)]
+    read_all: bool,
+
     /// apply a function to the field after it's read
     #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
     map: Result<Option<TokenStream>, ReplacementError>,
@@ -878,7 +896,7 @@ pub fn proc_deku_write(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
 }
 
 fn is_not_deku(attr: &syn::Attribute) -> bool {
-    attr.path
+    attr.path()
         .get_ident()
         .map(|ident| ident != "deku" && ident != "deku_derive")
         .unwrap_or(true)
@@ -942,8 +960,8 @@ pub fn deku_derive(
     item: proc_macro::TokenStream,
 ) -> proc_macro::TokenStream {
     // Parse `deku_derive` attribute
-    let attr_args = syn::parse_macro_input!(attr as AttributeArgs);
-    let args = match DekuDerive::from_list(&attr_args) {
+    let nested_meta = darling::ast::NestedMeta::parse_meta_list(attr.into()).unwrap();
+    let args = match DekuDerive::from_list(&nested_meta) {
         Ok(v) => v,
         Err(e) => {
             return proc_macro::TokenStream::from(e.write_errors());
@@ -1042,9 +1060,9 @@ mod tests {
         }"#),
 
         // Valid Enum
-        case::enum_empty(r#"#[deku(type = "u8")] enum Test {}"#),
+        case::enum_empty(r#"#[deku(id_type = "u8")] enum Test {}"#),
         case::enum_all(r#"
-        #[deku(type = "u8")]
+        #[deku(id_type = "u8")]
         enum Test {
             #[deku(id = "1")]
             A,
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index d5b20d0..fbc7139 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -11,6 +11,8 @@ use crate::macros::{
 };
 use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id};
 
+use super::assertion_failed;
+
 pub(crate) fn emit_deku_read(input: &DekuData) -> Result<TokenStream, syn::Error> {
     match &input.data {
         Data::Enum(_) => emit_enum(input),
@@ -566,31 +568,19 @@ fn emit_field_read(
     let internal_field_ident = gen_internal_field_ident(&field_ident);
 
     let field_assert = f.assert.as_ref().map(|v| {
+        let return_error = assertion_failed(v, &ident, &field_ident_str, None);
         quote! {
             if (!(#v)) {
-                // assertion is false, raise error
-                return Err(::#crate_::DekuError::Assertion(format!(
-                            "{}.{} field failed assertion: {}",
-                            #ident,
-                            #field_ident_str,
-                            stringify!(#v)
-                        )));
-            } else {
-                // do nothing
+                #return_error
             }
         }
     });
 
     let field_assert_eq = f.assert_eq.as_ref().map(|v| {
+        let return_error = assertion_failed(v, &ident, &field_ident_str, Some(&field_ident));
         quote! {
             if (!(#internal_field_ident == (#v))) {
-                // assertion is false, raise error
-                return Err(::#crate_::DekuError::Assertion(format!(
-                            "{}.{} field failed assertion: {}",
-                            #ident,
-                            #field_ident_str,
-                            stringify!(#field_ident == #v)
-                        )));
+                #return_error
             } else {
                 // do nothing
             }
@@ -599,7 +589,7 @@ fn emit_field_read(
 
     let trace_field_log = if cfg!(feature = "logging") {
         quote! {
-            log::trace!("Reading: {}::{}", #ident, #field_ident_str);
+            log::trace!("Reading: {}.{}", #ident, #field_ident_str);
         }
     } else {
         quote! {}
@@ -679,6 +669,17 @@ fn emit_field_read(
                     (::#crate_::ctx::Limit::new_until(#field_until), (#read_args))
                 )?
             }
+        } else if f.read_all {
+            quote! {
+                {
+                    use core::borrow::Borrow;
+                    #type_as_deku_read::from_reader_with_ctx
+                    (
+                        __deku_reader,
+                        (::#crate_::ctx::Limit::end(), (#read_args))
+                    )?
+                }
+            }
         } else {
             quote! {
                 #type_as_deku_read::from_reader_with_ctx
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index 6e0b715..bc2166c 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -5,8 +5,8 @@ use proc_macro2::TokenStream;
 use quote::quote;
 
 use crate::macros::{
-    gen_ctx_types_and_arg, gen_field_args, gen_struct_destruction, pad_bits, token_contains_string,
-    wrap_default_ctx,
+    assertion_failed, gen_ctx_types_and_arg, gen_field_args, gen_struct_destruction, pad_bits,
+    token_contains_string, wrap_default_ctx,
 };
 use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id};
 
@@ -47,26 +47,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
 
     // Implement `DekuContainerWrite` for types that don't need a context
     if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
-        let to_bits_body = wrap_default_ctx(
-            quote! {
-                match *self {
-                    #destructured => {
-                        let mut __deku_acc: ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> = ::#crate_::bitvec::BitVec::new();
-                        let __deku_output = &mut __deku_acc;
-
-                        #magic_write
-                        #(#field_writes)*
-
-                    Ok(__deku_acc)
-                    }
-                }
-            },
-            &input.ctx,
-            &input.ctx_default,
-        );
-
         tokens.extend(quote! {
-            impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
+             impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
                 type Error = ::#crate_::DekuError;
 
                 fn try_from(input: #ident) -> core::result::Result<Self, Self::Error> {
@@ -82,17 +64,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
 
-            impl #imp DekuContainerWrite for #ident #wher {
-                fn to_bytes(&self) -> core::result::Result<Vec<u8>, ::#crate_::DekuError> {
-                    let mut acc: ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> = self.to_bits()?;
-                    Ok(acc.into_vec())
-                }
-
-                #[allow(unused_variables)]
-                fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0>, ::#crate_::DekuError> {
-                    #to_bits_body
-                }
-            }
+            impl #imp DekuContainerWrite for #ident #wher {}
         });
     }
 
@@ -122,9 +94,9 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             }
         }
 
-        impl #imp DekuWrite<#ctx_types> for #ident #wher {
+        impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher {
             #[allow(unused_variables)]
-            fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0>, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> {
+            fn to_writer<W: ::#crate_::no_std_io::Write>(&self, __deku_writer: &mut ::#crate_::writer::Writer<W>, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> {
                 #write_body
             }
         }
@@ -134,9 +106,9 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default);
 
         tokens.extend(quote! {
-            impl #imp DekuWrite for #ident #wher {
+            impl #imp ::#crate_::DekuWriter for #ident #wher {
                 #[allow(unused_variables)]
-                fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0>, _: ()) -> core::result::Result<(), ::#crate_::DekuError> {
+                fn to_writer<W: ::#crate_::no_std_io::Write>(&self, __deku_writer: &mut ::#crate_::writer::Writer<W>, _: ()) -> core::result::Result<(), ::#crate_::DekuError> {
                     #write_body
                 }
             }
@@ -200,19 +172,19 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                     Id::TokenStream(v) => {
                         quote! {
                             let mut __deku_variant_id: #id_type = #v;
-                            __deku_variant_id.write(__deku_output, (#id_args))?;
+                            __deku_variant_id.to_writer(__deku_writer, (#id_args))?;
                         }
                     }
                     Id::Int(v) => {
                         quote! {
                             let mut __deku_variant_id: #id_type = #v;
-                            __deku_variant_id.write(__deku_output, (#id_args))?;
+                            __deku_variant_id.to_writer(__deku_writer, (#id_args))?;
                         }
                     }
                     Id::LitByteStr(v) => {
                         quote! {
                             let mut __deku_variant_id: #id_type = *#v;
-                            __deku_variant_id.write(__deku_output, (#id_args))?;
+                            __deku_variant_id.to_writer(__deku_writer, (#id_args))?;
                         }
                     }
                 }
@@ -221,7 +193,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             } else if has_discriminant {
                 quote! {
                     let mut __deku_variant_id: #id_type = Self::#variant_ident as #id_type;
-                    __deku_variant_id.write(__deku_output, (#id_args))?;
+                    __deku_variant_id.to_writer(__deku_writer, (#id_args))?;
                 }
             } else {
                 return Err(syn::Error::new(
@@ -266,25 +238,8 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
 
     // Implement `DekuContainerWrite` for types that don't need a context
     if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
-        let to_bits_body = wrap_default_ctx(
-            quote! {
-                let mut __deku_acc: ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> = ::#crate_::bitvec::BitVec::new();
-                let __deku_output = &mut __deku_acc;
-
-                #magic_write
-
-                match self {
-                    #(#variant_writes),*
-                }
-
-                Ok(__deku_acc)
-            },
-            &input.ctx,
-            &input.ctx_default,
-        );
-
         tokens.extend(quote! {
-            impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
+             impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
                 type Error = ::#crate_::DekuError;
 
                 fn try_from(input: #ident) -> core::result::Result<Self, Self::Error> {
@@ -300,17 +255,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
 
-            impl #imp DekuContainerWrite for #ident #wher {
-                fn to_bytes(&self) -> core::result::Result<Vec<u8>, ::#crate_::DekuError> {
-                    let mut acc: ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> = self.to_bits()?;
-                    Ok(acc.into_vec())
-                }
-
-                #[allow(unused_variables)]
-                fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0>, ::#crate_::DekuError> {
-                    #to_bits_body
-                }
-            }
+            impl #imp DekuContainerWrite for #ident #wher {}
         })
     }
 
@@ -342,9 +287,9 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             }
         }
 
-        impl #imp DekuWrite<#ctx_types> for #ident #wher {
+        impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher {
             #[allow(unused_variables)]
-            fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0>, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> {
+            fn to_writer<W: ::#crate_::no_std_io::Write>(&self, __deku_writer: &mut ::#crate_::writer::Writer<W>, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> {
                 #write_body
             }
         }
@@ -354,9 +299,9 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default);
 
         tokens.extend(quote! {
-            impl #imp DekuWrite for #ident #wher {
+            impl #imp ::#crate_::DekuWriter for #ident #wher {
                 #[allow(unused_variables)]
-                fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0>, _: ()) -> core::result::Result<(), ::#crate_::DekuError> {
+                fn to_writer<W: ::#crate_::no_std_io::Write>(&self, __deku_writer: &mut ::#crate_::writer::Writer<W>, _: ()) -> core::result::Result<(), ::#crate_::DekuError> {
                     #write_body
                 }
             }
@@ -368,9 +313,10 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
 }
 
 fn emit_magic_write(input: &DekuData) -> TokenStream {
+    let crate_ = super::get_crate_name();
     if let Some(magic) = &input.magic {
         quote! {
-            #magic.write(__deku_output, ())?;
+            ::#crate_::DekuWriter::to_writer(#magic, __deku_writer, ())?;
         }
     } else {
         quote! {}
@@ -432,7 +378,7 @@ fn emit_bit_byte_offsets(
         .any(|v| token_contains_string(v, "__deku_byte_offset"))
     {
         Some(quote! {
-            let __deku_byte_offset = __deku_bit_offset / 8;
+            let __deku_byte_offset = __deku_writer.bits_written / 8;
         })
     } else {
         None
@@ -444,7 +390,7 @@ fn emit_bit_byte_offsets(
         || byte_offset.is_some()
     {
         Some(quote! {
-            let __deku_bit_offset = __deku_output.len();
+            let __deku_bit_offset = __deku_writer.bits_written;
         })
     } else {
         None
@@ -464,8 +410,7 @@ fn emit_padding(bit_size: &TokenStream) -> TokenStream {
                     stringify!(#bit_size)
                 ))
             )?;
-            let new_len = __deku_output.len() + __deku_pad;
-            __deku_output.resize(new_len, false);
+            __deku_writer.write_bits(::#crate_::bitvec::bitvec![u8, ::#crate_::bitvec::Msb0; 0; __deku_pad].as_bitslice())?;
         }
     }
 }
@@ -497,15 +442,10 @@ fn emit_field_write(
     let field_ident_str = field_ident.to_string();
 
     let field_assert = f.assert.as_ref().map(|v| {
+        let return_error = assertion_failed(v, ident, &field_ident_str, None);
         quote! {
             if (!(#v)) {
-                // assertion is false, raise error
-                return Err(::#crate_::DekuError::Assertion(format!(
-                            "{}.{} field failed assertion: {}",
-                            #ident,
-                            #field_ident_str,
-                            stringify!(#v)
-                        )));
+                #return_error
             } else {
                 // do nothing
             }
@@ -513,21 +453,24 @@ fn emit_field_write(
     });
 
     let field_assert_eq = f.assert_eq.as_ref().map(|v| {
+        let return_error = assertion_failed(v, ident, &field_ident_str, Some(&field_ident));
         quote! {
             if (!(*(#field_ident) == (#v))) {
-                // assertion is false, raise error
-                return Err(::#crate_::DekuError::Assertion(format!(
-                            "{}.{} field failed assertion: {}",
-                            #ident,
-                            #field_ident_str,
-                            stringify!(#field_ident == #v)
-                        )));
+                #return_error
             } else {
                 // do nothing
             }
         }
     });
 
+    let trace_field_log = if cfg!(feature = "logging") {
+        quote! {
+            log::trace!("Writing: {}.{}", #ident, #field_ident_str);
+        }
+    } else {
+        quote! {}
+    };
+
     let field_write_func = if field_writer.is_some() {
         quote! { #field_writer }
     } else {
@@ -543,13 +486,13 @@ fn emit_field_write(
                 let field_type = &f.ty;
                 quote! {
                     let #field_ident: #field_type = #temp_value;
-                    ::#crate_::DekuWrite::write(#object_prefix &#field_ident, __deku_output, (#write_args))
+                    ::#crate_::DekuWriter::to_writer(#object_prefix &#field_ident, __deku_writer, (#write_args))
                 }
             } else {
                 quote! { core::result::Result::<(), ::#crate_::DekuError>::Ok(()) }
             }
         } else {
-            quote! { ::#crate_::DekuWrite::write(#object_prefix #field_ident, __deku_output, (#write_args)) }
+            quote! { ::#crate_::DekuWriter::to_writer(#object_prefix #field_ident, __deku_writer, (#write_args)) }
         }
     };
 
@@ -568,11 +511,20 @@ fn emit_field_write(
         #field_write_func ?;
     };
 
+    let skipping_log = if cfg!(feature = "logging") {
+        quote! {
+            log::trace!("skipping");
+        }
+    } else {
+        quote! {}
+    };
+
     let field_write_tokens = match (f.skip, &f.cond) {
         (true, Some(field_cond)) => {
             // #[deku(skip, cond = "...")] ==> `skip` if `cond`
             quote! {
                 if (#field_cond) {
+                    #skipping_log
                    // skipping, no write
                 } else {
                     #field_write_normal
@@ -582,6 +534,7 @@ fn emit_field_write(
         (true, None) => {
             // #[deku(skip)] ==> `skip`
             quote! {
+                #skipping_log
                 // skipping, no write
             }
         }
@@ -598,6 +551,7 @@ fn emit_field_write(
         #bit_offset
         #byte_offset
 
+        #trace_field_log
         #field_assert
         #field_assert_eq
 
diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs
index 2fa865c..256137c 100644
--- a/deku-derive/src/macros/mod.rs
+++ b/deku-derive/src/macros/mod.rs
@@ -344,3 +344,35 @@ fn pad_bits(
         (None, None) => quote!(),
     }
 }
+
+/// assertion is false, raise error
+fn assertion_failed(
+    v: &TokenStream,
+    ident: &str,
+    field_ident_str: &str,
+    field_ident: Option<&TokenStream>,
+) -> TokenStream {
+    let crate_ = get_crate_name();
+    let stringify = if let Some(field_ident) = field_ident {
+        quote! { stringify!(#field_ident == #v) }
+    } else {
+        quote! { stringify!(#v) }
+    };
+    #[cfg(feature = "no-assert-string")]
+    {
+        quote! {
+            return Err(::#crate_::DekuError::AssertionNoStr);
+        }
+    }
+    #[cfg(not(feature = "no-assert-string"))]
+    {
+        quote! {
+            return Err(::#crate_::DekuError::Assertion(format!(
+                "{}.{} field failed assertion: {}",
+                #ident,
+                #field_ident_str,
+                #stringify,
+            )));
+        }
+    }
+}
diff --git a/examples/custom_reader_and_writer.rs b/examples/custom_reader_and_writer.rs
index 908696c..10a5511 100644
--- a/examples/custom_reader_and_writer.rs
+++ b/examples/custom_reader_and_writer.rs
@@ -1,8 +1,9 @@
 use std::convert::TryInto;
 
-use deku::bitvec::{BitVec, Msb0};
 use deku::ctx::BitSize;
-use deku::prelude::*;
+use deku::writer::Writer;
+use deku::{prelude::*, DekuWriter};
+use no_std_io::io::Write;
 
 fn bit_flipper_read<R: std::io::Read>(
     field_a: u8,
@@ -24,10 +25,10 @@ fn bit_flipper_read<R: std::io::Read>(
     Ok(value)
 }
 
-fn bit_flipper_write(
+fn bit_flipper_write<W: Write>(
     field_a: u8,
     field_b: u8,
-    output: &mut BitVec<u8, Msb0>,
+    writer: &mut Writer<W>,
     bit_size: BitSize,
 ) -> Result<(), DekuError> {
     // Access to previously written fields
@@ -42,7 +43,7 @@ fn bit_flipper_write(
     // flip the bits on value if field_a is 0x01
     let value = if field_a == 0x01 { !field_b } else { field_b };
 
-    value.write(output, bit_size)
+    value.to_writer(writer, bit_size)
 }
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
@@ -51,7 +52,7 @@ struct DekuTest {
 
     #[deku(
         reader = "bit_flipper_read(*field_a, deku::reader, BitSize(8))",
-        writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))"
+        writer = "bit_flipper_write(*field_a, *field_b, deku::writer, BitSize(8))"
     )]
     field_b: u8,
 }
diff --git a/examples/enums.rs b/examples/enums.rs
index 296ffc5..722c8aa 100644
--- a/examples/enums.rs
+++ b/examples/enums.rs
@@ -6,7 +6,7 @@ use hexlit::hex;
 const THREE: u8 = 3;
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     #[deku(id = 0)]
     Var1,
diff --git a/examples/enums_catch_all.rs b/examples/enums_catch_all.rs
index 8126d1e..882be0f 100644
--- a/examples/enums_catch_all.rs
+++ b/examples/enums_catch_all.rs
@@ -4,7 +4,7 @@ use deku::prelude::*;
 use hexlit::hex;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, DekuWrite, DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 #[non_exhaustive]
 #[repr(u8)]
 pub enum DekuTest {
diff --git a/examples/read_all.rs b/examples/read_all.rs
new file mode 100644
index 0000000..b3c57f8
--- /dev/null
+++ b/examples/read_all.rs
@@ -0,0 +1,23 @@
+use deku::prelude::*;
+use std::convert::{TryFrom, TryInto};
+
+fn main() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    struct TestStruct {
+        #[deku(read_all)]
+        data: Vec<(u8, u8)>,
+    }
+
+    let test_data: Vec<u8> = [0xaa, 0xbb, 0xcc, 0xdd].to_vec();
+
+    let ret_read = TestStruct::try_from(test_data.as_slice()).unwrap();
+    assert_eq!(
+        TestStruct {
+            data: vec![(0xaa, 0xbb), (0xcc, 0xdd)]
+        },
+        ret_read
+    );
+
+    let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+    assert_eq!(test_data, ret_write);
+}
diff --git a/src/attributes.rs b/src/attributes.rs
index bd90cdd..ba66f25 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -42,6 +42,7 @@ enum DekuEnum {
 | [bits_read](#bits_read) | field | Set the field representing the number of bits to read into a container
 | [bytes_read](#bytes_read) | field | Set the field representing the number of bytes to read into a container
 | [until](#until) | field | Set a predicate returning when to stop reading elements into a container
+| [read_all](#read_all) | field | Read until [reader.end()] returns `true`
 | [update](#update) | field | Apply code over the field when `.update()` is called
 | [temp](#temp) | field | Read the field but exclude it from the struct/enum
 | [temp_value](#temp_value) | field | Write the field but exclude it from the struct/enum
@@ -412,6 +413,43 @@ assert_eq!(
     },
     value
 );
+
+```
+# read_all
+
+Read values into the container until [reader.end()] returns `true`.
+
+Example:
+```rust
+# use deku::prelude::*;
+# use std::convert::{TryInto, TryFrom};
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct InnerDekuTest {
+    field_a: u8,
+    field_b: u8
+}
+
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    #[deku(read_all)]
+    items: Vec<InnerDekuTest>,
+}
+
+let data: &[u8] = &[0xAB, 0xBC, 0xDE, 0xEF];
+
+let value = DekuTest::try_from(data).unwrap();
+
+assert_eq!(
+    DekuTest {
+       items: vec![
+           InnerDekuTest{field_a: 0xAB, field_b: 0xBC},
+           InnerDekuTest{field_a: 0xDE, field_b: 0xEF}],
+    },
+    value
+);
+
+let value: Vec<u8> = value.try_into().unwrap();
+assert_eq!(&*data, value);
 ```
 
 
@@ -803,7 +841,7 @@ use deku::prelude::*;
 struct DekuTest {
     #[deku(
         reader = "DekuTest::read(deku::reader)",
-        writer = "DekuTest::write(deku::output, &self.field_a)"
+        writer = "DekuTest::write(deku::writer, &self.field_a)"
     )]
     field_a: String,
 }
@@ -818,9 +856,9 @@ impl DekuTest {
     }
 
     /// Parse from String to u8 and write
-    fn write(output: &mut BitVec<u8, Msb0>, field_a: &str) -> Result<(), DekuError> {
+    fn write<W: std::io::Write>(writer: &mut Writer<W>, field_a: &str) -> Result<(), DekuError> {
         let value = field_a.parse::<u8>().unwrap();
-        value.write(output, ())
+        value.to_writer(writer, ())
     }
 }
 
@@ -1011,7 +1049,7 @@ or [id (top-level)](#id-top-level)
 
 **Note**:
     - If no `id` is specified, it is defaulted to the discriminant value.
-    - The discriminant value is retreived using the `as` keyword.
+    - The discriminant value is retrieved using the `as` keyword.
 
 Example:
 ```rust
@@ -1019,7 +1057,7 @@ Example:
 # use std::io::Cursor;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     #[deku(id = 0x01)]
     VariantA(u8),
@@ -1057,7 +1095,7 @@ Example discriminant
 # use std::io::Cursor;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     VariantA = 0x01,
     VariantB,
@@ -1099,7 +1137,7 @@ Example:
 # use std::io::Cursor;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     #[deku(id = 0x01)]
     VariantA(u8),
@@ -1151,7 +1189,7 @@ Example:
 # use std::io::Cursor;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8", bits = 4)]
+#[deku(id_type = "u8", bits = 4)]
 enum DekuTest {
     #[deku(id = 0b1001)]
     VariantA( #[deku(bits = 4)] u8, u8),
@@ -1182,7 +1220,7 @@ Example:
 # use deku::prelude::*;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u32", bytes = 2)]
+#[deku(id_type = "u32", bytes = 2)]
 enum DekuTest {
     #[deku(id = 0xBEEF)]
     VariantA(u8),
@@ -1201,5 +1239,5 @@ let value: Vec<u8> = value.try_into().unwrap();
 assert_eq!(data, value);
 ```
 
-
+[reader.end()]: crate::reader::Reader::end()
 */
diff --git a/src/ctx.rs b/src/ctx.rs
index 3728328..5565be2 100644
--- a/src/ctx.rs
+++ b/src/ctx.rs
@@ -95,6 +95,9 @@ pub enum Limit<T, Predicate: FnMut(&T) -> bool> {
 
     /// Read until a given quantity of bits have been read
     BitSize(BitSize),
+
+    /// Read until `reader.end()` is true. Used for `read_all` attribute.
+    End,
 }
 
 impl<T> From<usize> for Limit<T, fn(&T) -> bool> {
@@ -135,6 +138,14 @@ impl<T, Predicate: for<'a> FnMut(&'a T) -> bool> Limit<T, Predicate> {
     }
 }
 
+impl<T> Limit<T, fn(&T) -> bool> {
+    /// Read until `reader.end()` is true
+    #[inline]
+    pub fn end() -> Self {
+        Self::End
+    }
+}
+
 impl<T> Limit<T, fn(&T) -> bool> {
     /// Constructs a new Limit that reads until the given number of elements are read
     #[inline]
diff --git a/src/error.rs b/src/error.rs
index 061f58d..930208a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -42,8 +42,12 @@ pub enum DekuError {
     Unexpected(String),
     /// Assertion error from `assert` or `assert_eq` attributes
     Assertion(String),
+    /// Assertion error from `assert` or `assert_eq` attributes, without string
+    AssertionNoStr,
     /// Could not resolve `id` for variant
     IdVariantNotFound,
+    /// IO error while writing
+    Write,
 }
 
 impl From<core::num::TryFromIntError> for DekuError {
@@ -77,7 +81,9 @@ impl core::fmt::Display for DekuError {
             DekuError::InvalidParam(ref err) => write!(f, "Invalid param error: {err}"),
             DekuError::Unexpected(ref err) => write!(f, "Unexpected error: {err}"),
             DekuError::Assertion(ref err) => write!(f, "Assertion error: {err}"),
+            DekuError::AssertionNoStr => write!(f, "Assertion error"),
             DekuError::IdVariantNotFound => write!(f, "Could not resolve `id` for variant"),
+            DekuError::Write => write!(f, "write error"),
         }
     }
 }
@@ -99,7 +105,9 @@ impl From<DekuError> for std::io::Error {
             DekuError::InvalidParam(_) => io::Error::new(io::ErrorKind::InvalidInput, error),
             DekuError::Unexpected(_) => io::Error::new(io::ErrorKind::Other, error),
             DekuError::Assertion(_) => io::Error::new(io::ErrorKind::InvalidData, error),
+            DekuError::AssertionNoStr => io::Error::from(io::ErrorKind::InvalidData),
             DekuError::IdVariantNotFound => io::Error::new(io::ErrorKind::NotFound, error),
+            DekuError::Write => io::Error::new(io::ErrorKind::BrokenPipe, error),
         }
     }
 }
diff --git a/src/impls/bool.rs b/src/impls/bool.rs
index 71e85a1..34fdaba 100644
--- a/src/impls/bool.rs
+++ b/src/impls/bool.rs
@@ -1,11 +1,11 @@
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
 #[cfg(feature = "alloc")]
 use alloc::format;
 
-use bitvec::prelude::*;
-
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 impl<'a, Ctx> DekuReader<'a, Ctx> for bool
 where
@@ -13,7 +13,7 @@ where
     u8: DekuReader<'a, Ctx>,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         inner_ctx: Ctx,
     ) -> Result<bool, DekuError> {
         let val = u8::from_reader_with_ctx(reader, inner_ctx)?;
@@ -28,15 +28,15 @@ where
     }
 }
 
-impl<Ctx> DekuWrite<Ctx> for bool
+impl<Ctx> DekuWriter<Ctx> for bool
 where
-    u8: DekuWrite<Ctx>,
+    u8: DekuWriter<Ctx>,
 {
     /// wrapper around u8::write with consideration to context, such as bit size
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, inner_ctx: Ctx) -> Result<(), DekuError> {
         match self {
-            true => (0x01u8).write(output, inner_ctx),
-            false => (0x00u8).write(output, inner_ctx),
+            true => (0x01u8).to_writer(writer, inner_ctx),
+            false => (0x00u8).to_writer(writer, inner_ctx),
         }
     }
 }
@@ -47,7 +47,7 @@ mod tests {
     use no_std_io::io::Cursor;
     use rstest::rstest;
 
-    use crate::reader::Reader;
+    use crate::{ctx::BitSize, reader::Reader};
 
     use super::*;
 
@@ -72,9 +72,20 @@ mod tests {
         let mut reader = Reader::new(&mut cursor);
         let res_read = bool::from_reader_with_ctx(&mut reader, crate::ctx::BitSize(2)).unwrap();
         assert!(res_read);
+    }
+
+    #[test]
+    fn test_writer() {
+        let mut writer = Writer::new(vec![]);
+        true.to_writer(&mut writer, BitSize(1)).unwrap();
+        assert_eq!(vec![true], writer.rest());
+
+        let mut writer = Writer::new(vec![]);
+        true.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(vec![1], writer.inner);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, ()).unwrap();
-        assert_eq!(vec![0b01], res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        false.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(vec![0], writer.inner);
     }
 }
diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs
index bd733ef..a24a47f 100644
--- a/src/impls/boxed.rs
+++ b/src/impls/boxed.rs
@@ -1,12 +1,12 @@
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
 use alloc::boxed::Box;
 use alloc::vec::Vec;
 
-use bitvec::prelude::*;
-
 use crate::ctx::Limit;
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 impl<'a, T, Ctx> DekuReader<'a, Ctx> for Box<T>
 where
@@ -14,7 +14,7 @@ where
     Ctx: Copy,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         inner_ctx: Ctx,
     ) -> Result<Self, DekuError> {
         let val = <T>::from_reader_with_ctx(reader, inner_ctx)?;
@@ -22,17 +22,6 @@ where
     }
 }
 
-impl<T, Ctx> DekuWrite<Ctx> for Box<T>
-where
-    T: DekuWrite<Ctx>,
-    Ctx: Copy,
-{
-    /// Write T from box
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
-        self.as_ref().write(output, inner_ctx)
-    }
-}
-
 impl<'a, T, Ctx, Predicate> DekuReader<'a, (Limit<T, Predicate>, Ctx)> for Box<[T]>
 where
     T: DekuReader<'a, Ctx>,
@@ -40,7 +29,7 @@ where
     Predicate: FnMut(&T) -> bool,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         (limit, inner_ctx): (Limit<T, Predicate>, Ctx),
     ) -> Result<Self, DekuError> {
         // use Vec<T>'s implementation and convert to Box<[T]>
@@ -49,20 +38,32 @@ where
     }
 }
 
-impl<T, Ctx> DekuWrite<Ctx> for Box<[T]>
+impl<T, Ctx> DekuWriter<Ctx> for Box<[T]>
 where
-    T: DekuWrite<Ctx>,
+    T: DekuWriter<Ctx>,
     Ctx: Copy,
 {
     /// Write all `T`s to bits
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         for v in self.as_ref() {
-            v.write(output, ctx)?;
+            v.to_writer(writer, ctx)?;
         }
         Ok(())
     }
 }
 
+impl<T, Ctx> DekuWriter<Ctx> for Box<T>
+where
+    T: DekuWriter<Ctx>,
+    Ctx: Copy,
+{
+    /// Write all `T`s to bits
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
+        self.as_ref().to_writer(writer, ctx)?;
+        Ok(())
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use no_std_io::io::Cursor;
@@ -72,6 +73,7 @@ mod tests {
     use crate::ctx::*;
     use crate::native_endian;
     use crate::reader::Reader;
+    use bitvec::prelude::*;
 
     #[rstest(input, expected,
         case(
@@ -85,9 +87,9 @@ mod tests {
         let res_read = <Box<u16>>::from_reader_with_ctx(&mut reader, ()).unwrap();
         assert_eq!(expected, res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, ()).unwrap();
-        assert_eq!(input.to_vec(), res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(input.to_vec(), writer.inner);
     }
 
     // Note: Copied tests from vec.rs impl
@@ -105,7 +107,7 @@ mod tests {
         bit_size: Option<usize>,
         limit: Limit<u16, Predicate>,
         expected: Box<[u16]>,
-        expected_rest_bits: &BitSlice<u8, Msb0>,
+        expected_rest_bits: &bitvec::slice::BitSlice<u8, bitvec::prelude::Msb0>,
         expected_rest_bytes: &[u8],
         expected_write: Vec<u8>,
     ) {
@@ -126,11 +128,13 @@ mod tests {
         cursor.read_to_end(&mut buf).unwrap();
         assert_eq!(expected_rest_bytes, buf);
 
-        let mut res_write = bitvec![u8, Msb0;];
+        assert_eq!(input[..expected_write.len()].to_vec(), expected_write);
+
+        let mut writer = Writer::new(vec![]);
         res_read
-            .write(&mut res_write, (endian, BitSize(bit_size)))
+            .to_writer(&mut writer, (endian, BitSize(bit_size)))
             .unwrap();
-        assert_eq!(expected_write, res_write.into_vec());
+        assert_eq!(expected_write, writer.inner);
 
         assert_eq!(input[..expected_write.len()].to_vec(), expected_write);
     }
diff --git a/src/impls/cow.rs b/src/impls/cow.rs
index d7a4ff5..c2461ab 100644
--- a/src/impls/cow.rs
+++ b/src/impls/cow.rs
@@ -1,10 +1,10 @@
 use std::borrow::{Borrow, Cow};
 
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
-use bitvec::prelude::*;
-
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 impl<'a, T, Ctx> DekuReader<'a, Ctx> for Cow<'a, T>
 where
@@ -12,7 +12,7 @@ where
     Ctx: Copy,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         inner_ctx: Ctx,
     ) -> Result<Self, DekuError> {
         let val = <T>::from_reader_with_ctx(reader, inner_ctx)?;
@@ -20,14 +20,14 @@ where
     }
 }
 
-impl<T, Ctx> DekuWrite<Ctx> for Cow<'_, T>
+impl<T, Ctx> DekuWriter<Ctx> for Cow<'_, T>
 where
-    T: DekuWrite<Ctx> + Clone,
+    T: DekuWriter<Ctx> + Clone,
     Ctx: Copy,
 {
     /// Write T from Cow<T>
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
-        (self.borrow() as &T).write(output, inner_ctx)
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, inner_ctx: Ctx) -> Result<(), DekuError> {
+        (self.borrow() as &T).to_writer(writer, inner_ctx)
     }
 }
 
@@ -51,8 +51,8 @@ mod tests {
         let res_read = <Cow<u16>>::from_reader_with_ctx(&mut reader, ()).unwrap();
         assert_eq!(expected, res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, ()).unwrap();
-        assert_eq!(input.to_vec(), res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(input.to_vec(), writer.inner);
     }
 }
diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs
index c95e41f..cf636b5 100644
--- a/src/impls/cstring.rs
+++ b/src/impls/cstring.rs
@@ -1,18 +1,18 @@
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 use std::ffi::CString;
 
-use bitvec::prelude::*;
-
+use crate::reader::Reader;
+use crate::writer::Writer;
 use crate::{ctx::*, DekuReader};
-use crate::{DekuError, DekuWrite};
+use crate::{DekuError, DekuWriter};
 
-impl<Ctx: Copy> DekuWrite<Ctx> for CString
+impl<Ctx: Copy> DekuWriter<Ctx> for CString
 where
-    u8: DekuWrite<Ctx>,
+    u8: DekuWriter<Ctx>,
 {
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         let bytes = self.as_bytes_with_nul();
-        bytes.write(output, ctx)
+        bytes.to_writer(writer, ctx)
     }
 }
 
@@ -21,7 +21,7 @@ where
     u8: DekuReader<'a, Ctx>,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         inner_ctx: Ctx,
     ) -> Result<Self, DekuError> {
         let bytes =
@@ -67,8 +67,8 @@ mod tests {
         cursor.read_to_end(&mut buf).unwrap();
         assert_eq!(expected_rest, buf);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, ()).unwrap();
-        assert_eq!(vec![b't', b'e', b's', b't', b'\0'], res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(vec![b't', b'e', b's', b't', b'\0'], writer.inner);
     }
 }
diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs
index b51ea80..7851810 100644
--- a/src/impls/hashmap.rs
+++ b/src/impls/hashmap.rs
@@ -1,11 +1,11 @@
 use std::collections::HashMap;
 use std::hash::{BuildHasher, Hash};
 
-use bitvec::prelude::*;
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
 use crate::ctx::*;
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 /// Read `K, V`s into a hashmap until a given predicate returns true
 /// * `capacity` - an optional capacity to pre-allocate the hashmap with
@@ -42,6 +42,30 @@ where
     Ok(res)
 }
 
+fn from_reader_with_ctx_hashmap_to_end<'a, K, V, S, Ctx, R: Read>(
+    reader: &mut crate::reader::Reader<R>,
+    capacity: Option<usize>,
+    ctx: Ctx,
+) -> Result<HashMap<K, V, S>, DekuError>
+where
+    K: DekuReader<'a, Ctx> + Eq + Hash,
+    V: DekuReader<'a, Ctx>,
+    S: BuildHasher + Default,
+    Ctx: Copy,
+{
+    let mut res = HashMap::with_capacity_and_hasher(capacity.unwrap_or(0), S::default());
+
+    loop {
+        if reader.end() {
+            break;
+        }
+        let val = <(K, V)>::from_reader_with_ctx(reader, ctx)?;
+        res.insert(val.0, val.1);
+    }
+
+    Ok(res)
+}
+
 impl<'a, K, V, S, Ctx, Predicate> DekuReader<'a, (Limit<(K, V), Predicate>, Ctx)>
     for HashMap<K, V, S>
 where
@@ -136,6 +160,9 @@ where
                     move |read_bits, _| read_bits == bit_size,
                 )
             }
+
+            // Read until `reader.end()` is true
+            Limit::End => from_reader_with_ctx_hashmap_to_end(reader, None, inner_ctx),
         }
     }
 }
@@ -159,7 +186,7 @@ where
     }
 }
 
-impl<K: DekuWrite<Ctx>, V: DekuWrite<Ctx>, S, Ctx: Copy> DekuWrite<Ctx> for HashMap<K, V, S> {
+impl<K: DekuWriter<Ctx>, V: DekuWriter<Ctx>, S, Ctx: Copy> DekuWriter<Ctx> for HashMap<K, V, S> {
     /// Write all `K, V`s in a `HashMap` to bits.
     /// * **inner_ctx** - The context required by `K, V`.
     /// Note: depending on the Hasher `S`, the order in which the `K, V` pairs are
@@ -167,19 +194,21 @@ impl<K: DekuWrite<Ctx>, V: DekuWrite<Ctx>, S, Ctx: Copy> DekuWrite<Ctx> for Hash
     /// instead of the default RandomState hasher if you don't want the order written to change.
     /// # Examples
     /// ```rust
-    /// # use deku::{ctx::Endian, DekuWrite};
+    /// # use deku::{ctx::Endian, DekuWriter};
+    /// # use deku::writer::Writer;
     /// # use deku::bitvec::{Msb0, bitvec};
     /// # use std::collections::HashMap;
-    /// let mut output = bitvec![u8, Msb0;];
+    /// let mut out_buf = vec![];
+    /// let mut writer = Writer::new(&mut out_buf);
     /// let mut map = HashMap::<u8, u32>::default();
     /// map.insert(100, 0x04030201);
-    /// map.write(&mut output, Endian::Big).unwrap();
+    /// map.to_writer(&mut writer, Endian::Big).unwrap();
     /// let expected: Vec<u8> = vec![100, 4, 3, 2, 1];
-    /// assert_eq!(expected, output.into_vec())
+    /// assert_eq!(expected, out_buf);
     /// ```
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, inner_ctx: Ctx) -> Result<(), DekuError> {
         for kv in self {
-            kv.write(output, inner_ctx)?;
+            kv.to_writer(writer, inner_ctx)?;
         }
         Ok(())
     }
@@ -194,6 +223,7 @@ mod tests {
     use crate::reader::Reader;
 
     use super::*;
+    use bitvec::prelude::*;
 
     // Macro to create a deterministic HashMap for tests
     // This is needed for tests since the default HashMap Hasher
@@ -217,7 +247,12 @@ mod tests {
         case::count_1([0x01, 0xAA, 0x02, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0x02, 0xbb]),
         case::count_2([0x01, 0xAA, 0x02, 0xBB, 0xBB].as_ref(), Endian::Little, Some(8), 2.into(), fxhashmap!{0x01 => 0xAA, 0x02 => 0xBB}, bits![u8, Msb0;], &[0xbb]),
         case::until_null([0x01, 0xAA, 0, 0, 0xBB].as_ref(), Endian::Little, None, (|kv: &(u8, u8)| kv.0 == 0u8 && kv.1 == 0u8).into(), fxhashmap!{0x01 => 0xAA, 0 => 0}, bits![u8, Msb0;], &[0xbb]),
+        case::until_empty_bits([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(0).into(), FxHashMap::default(), bits![u8, Msb0;], &[0x01, 0xaa, 0xbb]),
+        case::until_empty_bytes([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(0).into(), FxHashMap::default(), bits![u8, Msb0;], &[0x01, 0xaa, 0xbb]),
         case::until_bits([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(16).into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]),
+        case::read_all([0x01, 0xAA].as_ref(), Endian::Little, None, Limit::end(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[]),
+        case::until_bytes([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(2).into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]),
+        case::until_count([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::from(1), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]),
         case::bits_6([0b0000_0100, 0b1111_0000, 0b1000_0000].as_ref(), Endian::Little, Some(6), 2.into(), fxhashmap!{0x01 => 0x0F, 0x02 => 0}, bits![u8, Msb0;], &[]),
         #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")]
         case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), FxHashMap::default(), bits![u8, Msb0;], &[]),
@@ -267,44 +302,8 @@ mod tests {
         case::normal(fxhashmap!{0x11u8 => 0xAABBu16, 0x23u8 => 0xCCDDu16}, Endian::Little, vec![0x11, 0xBB, 0xAA, 0x23, 0xDD, 0xCC]),
     )]
     fn test_hashmap_write(input: FxHashMap<u8, u16>, endian: Endian, expected: Vec<u8>) {
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
-    }
-
-    // Note: These tests also exist in boxed.rs
-    #[rstest(input, endian, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write,
-        case::normal_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, 2.into(), fxhashmap!{0xBBAA => 0, 0xDDCC => 0}, bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0, 0xAA, 0xBB, 0]),
-        case::normal_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, 2.into(), fxhashmap!{0xAABB => 0, 0xCCDD => 0}, bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0, 0xAA, 0xBB, 0]),
-        case::predicate_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, (|kv: &(u16, u8)| kv.0 == 0xBBAA && kv.1 == 0).into(), fxhashmap!{0xBBAA => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]),
-        case::predicate_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, (|kv: &(u16, u8)| kv.0 == 0xAABB && kv.1 == 0).into(), fxhashmap!{0xAABB => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]),
-        case::bytes_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, BitSize(24).into(), fxhashmap!{0xBBAA => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]),
-        case::bytes_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, BitSize(24).into(), fxhashmap!{0xAABB => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]),
-    )]
-    fn test_hashmap_read_write<Predicate: FnMut(&(u16, u8)) -> bool + Copy>(
-        input: &[u8],
-        endian: Endian,
-        limit: Limit<(u16, u8), Predicate>,
-        expected: FxHashMap<u16, u8>,
-        expected_rest_bits: &BitSlice<u8, Msb0>,
-        expected_rest_bytes: &[u8],
-        expected_write: Vec<u8>,
-    ) {
-        let mut cursor = Cursor::new(input);
-        let mut reader = Reader::new(&mut cursor);
-        let res_read =
-            FxHashMap::<u16, u8>::from_reader_with_ctx(&mut reader, (limit, endian)).unwrap();
-        assert_eq!(expected, res_read);
-        assert_eq!(
-            reader.rest(),
-            expected_rest_bits.iter().by_vals().collect::<Vec<bool>>()
-        );
-        let mut buf = vec![];
-        cursor.read_to_end(&mut buf).unwrap();
-        assert_eq!(expected_rest_bytes, buf);
-
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected_write, res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(expected, writer.inner);
     }
 }
diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs
index 16c4dea..39f61ce 100644
--- a/src/impls/hashset.rs
+++ b/src/impls/hashset.rs
@@ -1,11 +1,11 @@
 use std::collections::HashSet;
 use std::hash::{BuildHasher, Hash};
 
-use bitvec::prelude::*;
-use no_std_io::io::Read;
+use crate::writer::Writer;
+use no_std_io::io::{Read, Write};
 
 use crate::ctx::*;
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::{DekuError, DekuReader, DekuWriter};
 
 /// Read `T`s into a hashset until a given predicate returns true
 /// * `capacity` - an optional capacity to pre-allocate the hashset with
@@ -41,6 +41,29 @@ where
     Ok(res)
 }
 
+fn from_reader_with_ctx_hashset_to_end<'a, T, S, Ctx, R: Read>(
+    reader: &mut crate::reader::Reader<R>,
+    capacity: Option<usize>,
+    ctx: Ctx,
+) -> Result<HashSet<T, S>, DekuError>
+where
+    T: DekuReader<'a, Ctx> + Eq + Hash,
+    S: BuildHasher + Default,
+    Ctx: Copy,
+{
+    let mut res = HashSet::with_capacity_and_hasher(capacity.unwrap_or(0), S::default());
+
+    loop {
+        if reader.end() {
+            break;
+        }
+        let val = <T>::from_reader_with_ctx(reader, ctx)?;
+        res.insert(val);
+    }
+
+    Ok(res)
+}
+
 impl<'a, T, S, Ctx, Predicate> DekuReader<'a, (Limit<T, Predicate>, Ctx)> for HashSet<T, S>
 where
     T: DekuReader<'a, Ctx> + Eq + Hash,
@@ -131,6 +154,9 @@ where
                     move |read_bits, _| read_bits == bit_size,
                 )
             }
+
+            // Read until `reader.end()` is true
+            Limit::End => from_reader_with_ctx_hashset_to_end(reader, None, inner_ctx),
         }
     }
 }
@@ -150,7 +176,7 @@ impl<'a, T: DekuReader<'a> + Eq + Hash, S: BuildHasher + Default, Predicate: FnM
     }
 }
 
-impl<T: DekuWrite<Ctx>, S, Ctx: Copy> DekuWrite<Ctx> for HashSet<T, S> {
+impl<T: DekuWriter<Ctx>, S, Ctx: Copy> DekuWriter<Ctx> for HashSet<T, S> {
     /// Write all `T`s in a `HashSet` to bits.
     /// * **inner_ctx** - The context required by `T`.
     /// Note: depending on the Hasher `S`, the order in which the `T`'s are
@@ -158,17 +184,19 @@ impl<T: DekuWrite<Ctx>, S, Ctx: Copy> DekuWrite<Ctx> for HashSet<T, S> {
     /// instead of the default RandomState hasher if you don't want the order written to change.
     /// # Examples
     /// ```rust
-    /// # use deku::{ctx::Endian, DekuWrite};
+    /// # use deku::{ctx::Endian, DekuWriter};
+    /// # use deku::writer::Writer;
     /// # use deku::bitvec::{Msb0, bitvec};
     /// # use std::collections::HashSet;
+    /// let mut out_buf = vec![];
+    /// let mut writer = Writer::new(&mut out_buf);
     /// let set: HashSet<u8> = vec![1].into_iter().collect();
-    /// let mut output = bitvec![u8, Msb0;];
-    /// set.write(&mut output, Endian::Big).unwrap();
-    /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1])
+    /// set.to_writer(&mut writer, Endian::Big).unwrap();
+    /// assert_eq!(out_buf, vec![1]);
     /// ```
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, inner_ctx: Ctx) -> Result<(), DekuError> {
         for v in self {
-            v.write(output, inner_ctx)?;
+            v.to_writer(writer, inner_ctx)?;
         }
         Ok(())
     }
@@ -176,6 +204,7 @@ impl<T: DekuWrite<Ctx>, S, Ctx: Copy> DekuWrite<Ctx> for HashSet<T, S> {
 
 #[cfg(test)]
 mod tests {
+    use crate::bitvec::{bits, BitSlice, Msb0};
     use no_std_io::io::Cursor;
     use rstest::rstest;
     use rustc_hash::FxHashSet;
@@ -189,7 +218,12 @@ mod tests {
         case::count_1([0xAA, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
         case::count_2([0xAA, 0xBB, 0xCC].as_ref(), Endian::Little, Some(8), 2.into(), vec![0xAA, 0xBB].into_iter().collect(), bits![u8, Msb0;], &[0xcc]),
         case::until_null([0xAA, 0, 0xBB].as_ref(), Endian::Little, None, (|v: &u8| *v == 0u8).into(), vec![0xAA, 0].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
+        case::until_empty_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(0).into(), HashSet::default(), bits![u8, Msb0;], &[0xaa, 0xbb]),
+        case::until_empty_bytes([0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(0).into(), HashSet::default(), bits![u8, Msb0;], &[0xaa, 0xbb]),
         case::until_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(8).into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
+        case::read_all([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::end(), vec![0xAA, 0xBB].into_iter().collect(), bits![u8, Msb0;], &[]),
+        case::until_bytes([0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(1).into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
+        case::until_count([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::from(1), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
         case::bits_6([0b0110_1001, 0b1110_1001].as_ref(), Endian::Little, Some(6), 2.into(), vec![0b00_011010, 0b00_011110].into_iter().collect(), bits![u8, Msb0; 1, 0, 0, 1], &[]),
         #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")]
         case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), FxHashSet::default(), bits![u8, Msb0;], &[]),
@@ -237,9 +271,9 @@ mod tests {
         case::normal(vec![0xAABB, 0xCCDD].into_iter().collect(), Endian::Little, vec![0xDD, 0xCC, 0xBB, 0xAA]),
     )]
     fn test_hashset_write(input: FxHashSet<u16>, endian: Endian, expected: Vec<u8>) {
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(expected, writer.inner);
     }
 
     // Note: These tests also exist in boxed.rs
@@ -280,10 +314,10 @@ mod tests {
         cursor.read_to_end(&mut buf).unwrap();
         assert_eq!(expected_rest_bytes, buf);
 
-        let mut res_write = bitvec![u8, Msb0;];
+        let mut writer = Writer::new(vec![]);
         res_read
-            .write(&mut res_write, (endian, BitSize(bit_size)))
+            .to_writer(&mut writer, (endian, BitSize(bit_size)))
             .unwrap();
-        assert_eq!(expected_write, res_write.into_vec());
+        assert_eq!(expected_write, writer.inner);
     }
 }
diff --git a/src/impls/ipaddr.rs b/src/impls/ipaddr.rs
index 9ad5bf2..36e3053 100644
--- a/src/impls/ipaddr.rs
+++ b/src/impls/ipaddr.rs
@@ -1,17 +1,17 @@
 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
-use bitvec::prelude::*;
-
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 impl<'a, Ctx> DekuReader<'a, Ctx> for Ipv4Addr
 where
     u32: DekuReader<'a, Ctx>,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         inner_ctx: Ctx,
     ) -> Result<Self, DekuError> {
         let ip = u32::from_reader_with_ctx(reader, inner_ctx)?;
@@ -19,13 +19,13 @@ where
     }
 }
 
-impl<Ctx> DekuWrite<Ctx> for Ipv4Addr
+impl<Ctx> DekuWriter<Ctx> for Ipv4Addr
 where
-    u32: DekuWrite<Ctx>,
+    u32: DekuWriter<Ctx>,
 {
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         let ip: u32 = (*self).into();
-        ip.write(output, ctx)
+        ip.to_writer(writer, ctx)
     }
 }
 
@@ -34,7 +34,7 @@ where
     u128: DekuReader<'a, Ctx>,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         inner_ctx: Ctx,
     ) -> Result<Self, DekuError> {
         let ip = u128::from_reader_with_ctx(reader, inner_ctx)?;
@@ -42,25 +42,25 @@ where
     }
 }
 
-impl<Ctx> DekuWrite<Ctx> for Ipv6Addr
+impl<Ctx> DekuWriter<Ctx> for Ipv6Addr
 where
-    u128: DekuWrite<Ctx>,
+    u128: DekuWriter<Ctx>,
 {
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         let ip: u128 = (*self).into();
-        ip.write(output, ctx)
+        ip.to_writer(writer, ctx)
     }
 }
 
-impl<Ctx> DekuWrite<Ctx> for IpAddr
+impl<Ctx> DekuWriter<Ctx> for IpAddr
 where
-    Ipv6Addr: DekuWrite<Ctx>,
-    Ipv4Addr: DekuWrite<Ctx>,
+    Ipv6Addr: DekuWriter<Ctx>,
+    Ipv4Addr: DekuWriter<Ctx>,
 {
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         match self {
-            IpAddr::V4(ipv4) => ipv4.write(output, ctx),
-            IpAddr::V6(ipv6) => ipv6.write(output, ctx),
+            IpAddr::V4(ipv4) => ipv4.to_writer(writer, ctx),
+            IpAddr::V6(ipv6) => ipv6.to_writer(writer, ctx),
         }
     }
 }
@@ -83,9 +83,9 @@ mod tests {
         let res_read = Ipv4Addr::from_reader_with_ctx(&mut reader, endian).unwrap();
         assert_eq!(expected, res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, endian).unwrap();
-        assert_eq!(input.to_vec(), res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(input.to_vec(), writer.inner);
     }
 
     #[rstest(input, endian, expected,
@@ -98,27 +98,28 @@ mod tests {
         let res_read = Ipv6Addr::from_reader_with_ctx(&mut reader, endian).unwrap();
         assert_eq!(expected, res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, endian).unwrap();
-        assert_eq!(input.to_vec(), res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(input.to_vec(), writer.inner);
     }
 
     #[test]
     fn test_ip_addr_write() {
         let ip_addr = IpAddr::V4(Ipv4Addr::new(145, 254, 160, 237));
-        let mut ret_write = bitvec![u8, Msb0;];
-        ip_addr.write(&mut ret_write, Endian::Little).unwrap();
-        assert_eq!(vec![237, 160, 254, 145], ret_write.into_vec());
+
+        let mut writer = Writer::new(vec![]);
+        ip_addr.to_writer(&mut writer, Endian::Little).unwrap();
+        assert_eq!(vec![237, 160, 254, 145], writer.inner);
 
         let ip_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x02ff));
-        let mut ret_write = bitvec![u8, Msb0;];
-        ip_addr.write(&mut ret_write, Endian::Little).unwrap();
+        let mut writer = Writer::new(vec![]);
+        ip_addr.to_writer(&mut writer, Endian::Little).unwrap();
         assert_eq!(
             vec![
                 0xff, 0x02, 0x0a, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00
             ],
-            ret_write.into_vec()
+            writer.inner
         );
     }
 }
diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs
index d85b245..e51c1cd 100644
--- a/src/impls/nonzero.rs
+++ b/src/impls/nonzero.rs
@@ -1,18 +1,18 @@
 #[cfg(feature = "alloc")]
 use alloc::format;
 use core::num::*;
-use no_std_io::io::Read;
-
-use bitvec::prelude::*;
+use no_std_io::io::{Read, Write};
 
 use crate::ctx::*;
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 macro_rules! ImplDekuTraitsCtx {
     ($typ:ty, $readtype:ty, $ctx_arg:tt, $ctx_type:tt) => {
         impl DekuReader<'_, $ctx_type> for $typ {
             fn from_reader_with_ctx<R: Read>(
-                reader: &mut crate::reader::Reader<R>,
+                reader: &mut Reader<R>,
                 $ctx_arg: $ctx_type,
             ) -> Result<Self, DekuError> {
                 let value = <$readtype>::from_reader_with_ctx(reader, $ctx_arg)?;
@@ -25,14 +25,14 @@ macro_rules! ImplDekuTraitsCtx {
             }
         }
 
-        impl DekuWrite<$ctx_type> for $typ {
-            fn write(
+        impl DekuWriter<$ctx_type> for $typ {
+            fn to_writer<W: Write>(
                 &self,
-                output: &mut BitVec<u8, Msb0>,
+                writer: &mut Writer<W>,
                 $ctx_arg: $ctx_type,
             ) -> Result<(), DekuError> {
                 let value = self.get();
-                value.write(output, $ctx_arg)
+                value.to_writer(writer, $ctx_arg)
             }
         }
     };
@@ -68,6 +68,7 @@ mod tests {
     use crate::reader::Reader;
 
     use super::*;
+    use bitvec::prelude::*;
 
     #[rstest(input, expected,
         case(&hex!("FF"), NonZeroU8::new(0xFF).unwrap()),
@@ -82,8 +83,8 @@ mod tests {
         let res_read = NonZeroU8::from_reader_with_ctx(&mut reader, ()).unwrap();
         assert_eq!(expected, res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, ()).unwrap();
-        assert_eq!(input.to_vec(), res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(input.to_vec(), writer.inner);
     }
 }
diff --git a/src/impls/option.rs b/src/impls/option.rs
index 3eabefa..7800e7d 100644
--- a/src/impls/option.rs
+++ b/src/impls/option.rs
@@ -1,7 +1,6 @@
-use bitvec::prelude::*;
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::{writer::Writer, DekuError, DekuReader, DekuWriter};
 
 impl<'a, T: DekuReader<'a, Ctx>, Ctx: Copy> DekuReader<'a, Ctx> for Option<T> {
     fn from_reader_with_ctx<R: Read>(
@@ -13,20 +12,10 @@ impl<'a, T: DekuReader<'a, Ctx>, Ctx: Copy> DekuReader<'a, Ctx> for Option<T> {
     }
 }
 
-impl<T: DekuWrite<Ctx>, Ctx: Copy> DekuWrite<Ctx> for Option<T> {
-    /// Write T if Some
-    /// * **inner_ctx** - The context required by `T`.
-    /// # Examples
-    /// ```rust
-    /// # use deku::{ctx::Endian, DekuWrite};
-    /// # use deku::bitvec::{bitvec, Msb0};
-    /// let data = Some(1u8);
-    /// let mut output = bitvec![u8, Msb0;];
-    /// data.write(&mut output, Endian::Big).unwrap();
-    /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1])
-    /// ```
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
-        self.as_ref().map_or(Ok(()), |v| v.write(output, inner_ctx))
+impl<T: DekuWriter<Ctx>, Ctx: Copy> DekuWriter<Ctx> for Option<T> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, inner_ctx: Ctx) -> Result<(), DekuError> {
+        self.as_ref()
+            .map_or(Ok(()), |v| v.to_writer(writer, inner_ctx))
     }
 }
 
@@ -38,7 +27,7 @@ mod tests {
     use crate::reader::Reader;
 
     #[test]
-    fn test_option() {
+    fn test_option_read() {
         use crate::ctx::*;
         let input = &[1u8, 2, 3, 4];
         let mut cursor = Cursor::new(input);
@@ -46,4 +35,11 @@ mod tests {
         let v = Option::<u32>::from_reader_with_ctx(&mut reader, Endian::Little).unwrap();
         assert_eq!(v, Some(0x04030201))
     }
+
+    #[test]
+    fn test_option_write() {
+        let mut writer = Writer::new(vec![]);
+        Some(true).to_writer(&mut writer, ()).unwrap();
+        assert_eq!(vec![1], writer.inner);
+    }
 }
diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs
index 63aeeb0..5320b35 100644
--- a/src/impls/primitive.rs
+++ b/src/impls/primitive.rs
@@ -5,11 +5,12 @@ use alloc::string::ToString;
 use core::convert::TryInto;
 
 use bitvec::prelude::*;
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
 use crate::ctx::*;
 use crate::reader::{Reader, ReaderRet};
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 
 /// "Read" trait: read bits and construct type
 trait DekuRead<'a, Ctx = ()> {
@@ -31,6 +32,32 @@ trait DekuRead<'a, Ctx = ()> {
         Self: Sized;
 }
 
+/// "Writer" trait: write from type to bits
+trait DekuWrite<Ctx = ()> {
+    /// Write type to bits
+    /// * **output** - Sink to store resulting bits
+    /// * **ctx** - A context required by context-sensitive reading. A unit type `()` means no context
+    /// needed.
+    fn write(
+        &self,
+        output: &mut crate::bitvec::BitVec<u8, crate::bitvec::Msb0>,
+        ctx: Ctx,
+    ) -> Result<(), DekuError>;
+}
+
+/// Implements DekuWrite for references of types that implement DekuWrite
+impl<T, Ctx> DekuWrite<Ctx> for &T
+where
+    T: DekuWrite<Ctx>,
+    Ctx: Copy,
+{
+    /// Write value of type to bits
+    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+        <T>::write(self, output, ctx)?;
+        Ok(())
+    }
+}
+
 // specialize u8 for ByteSize
 impl DekuRead<'_, (Endian, ByteSize)> for u8 {
     #[inline]
@@ -392,11 +419,11 @@ macro_rules! ForwardDekuRead {
 
 macro_rules! ImplDekuWrite {
     ($typ:ty) => {
-        impl DekuWrite<(Endian, BitSize)> for $typ {
+        impl DekuWriter<(Endian, BitSize)> for $typ {
             #[inline]
-            fn write(
+            fn to_writer<W: Write>(
                 &self,
-                output: &mut BitVec<u8, Msb0>,
+                writer: &mut Writer<W>,
                 (endian, size): (Endian, BitSize),
             ) -> Result<(), DekuError> {
                 let input = match endian {
@@ -422,27 +449,27 @@ macro_rules! ImplDekuWrite {
                     let mut remaining_bits = bit_size;
                     for chunk in input_bits.chunks(8) {
                         if chunk.len() > remaining_bits {
-                            output.extend_from_bitslice(&chunk[chunk.len() - remaining_bits..]);
+                            writer.write_bits(&chunk[chunk.len() - remaining_bits..])?;
                             break;
                         } else {
-                            output.extend_from_bitslice(chunk)
+                            writer.write_bits(&chunk)?;
                         }
                         remaining_bits -= chunk.len();
                     }
                 } else {
                     // Example read 10 bits u32 [0xAB, 0b11_000000]
                     // => [00000000, 00000000, 00000010, 10101111]
-                    output.extend_from_bitslice(&input_bits[input_bits.len() - bit_size..]);
+                    writer.write_bits(&input_bits[input_bits.len() - bit_size..])?;
                 }
                 Ok(())
             }
         }
 
-        impl DekuWrite<(Endian, ByteSize)> for $typ {
+        impl DekuWriter<(Endian, ByteSize)> for $typ {
             #[inline]
-            fn write(
+            fn to_writer<W: Write>(
                 &self,
-                output: &mut BitVec<u8, Msb0>,
+                writer: &mut Writer<W>,
                 (endian, size): (Endian, ByteSize),
             ) -> Result<(), DekuError> {
                 let input = match endian {
@@ -450,53 +477,37 @@ macro_rules! ImplDekuWrite {
                     Endian::Big => self.to_be_bytes(),
                 };
 
-                let bit_size: usize = size.0 * 8;
-
-                let input_bits = input.view_bits::<Msb0>();
-
-                if bit_size > input_bits.len() {
+                const TYPE_SIZE: usize = core::mem::size_of::<$typ>();
+                if size.0 > TYPE_SIZE {
                     return Err(DekuError::InvalidParam(format!(
-                        "bit size {} is larger than input {}",
-                        bit_size,
-                        input_bits.len()
+                        "byte size {} is larger then input {}",
+                        size.0, TYPE_SIZE
                     )));
                 }
 
-                if matches!(endian, Endian::Little) {
-                    // Example read 10 bits u32 [0xAB, 0b11_000000]
-                    // => [10101011, 00000011, 00000000, 00000000]
-                    let mut remaining_bits = bit_size;
-                    for chunk in input_bits.chunks(8) {
-                        if chunk.len() > remaining_bits {
-                            output.extend_from_bitslice(&chunk[chunk.len() - remaining_bits..]);
-                            break;
-                        } else {
-                            output.extend_from_bitslice(chunk)
-                        }
-                        remaining_bits -= chunk.len();
-                    }
+                let input = if matches!(endian, Endian::Big) {
+                    &input[TYPE_SIZE - size.0 as usize..]
                 } else {
-                    // Example read 10 bits u32 [0xAB, 0b11_000000]
-                    // => [00000000, 00000000, 00000010, 10101111]
-                    output.extend_from_bitslice(&input_bits[input_bits.len() - bit_size..]);
-                }
+                    &input[..size.0 as usize]
+                };
+
+                writer.write_bytes(&input)?;
                 Ok(())
             }
         }
 
-        // Only have `endian`, return all input
-        impl DekuWrite<Endian> for $typ {
-            #[inline]
-            fn write(
+        impl DekuWriter<Endian> for $typ {
+            #[inline(always)]
+            fn to_writer<W: Write>(
                 &self,
-                output: &mut BitVec<u8, Msb0>,
+                writer: &mut Writer<W>,
                 endian: Endian,
             ) -> Result<(), DekuError> {
                 let input = match endian {
                     Endian::Little => self.to_le_bytes(),
                     Endian::Big => self.to_be_bytes(),
                 };
-                output.extend_from_bitslice(input.view_bits::<Msb0>());
+                writer.write_bytes(&input)?;
                 Ok(())
             }
         }
@@ -505,34 +516,32 @@ macro_rules! ImplDekuWrite {
 
 macro_rules! ForwardDekuWrite {
     ($typ:ty) => {
-        // Only have `bit_size`, set `endian` to `Endian::default`.
-        impl DekuWrite<BitSize> for $typ {
+        impl DekuWriter<BitSize> for $typ {
             #[inline]
-            fn write(
+            fn to_writer<W: Write>(
                 &self,
-                output: &mut BitVec<u8, Msb0>,
+                writer: &mut Writer<W>,
                 bit_size: BitSize,
             ) -> Result<(), DekuError> {
-                <$typ>::write(self, output, (Endian::default(), bit_size))
+                <$typ>::to_writer(self, writer, (Endian::default(), bit_size))
             }
         }
 
-        // Only have `bit_size`, set `endian` to `Endian::default`.
-        impl DekuWrite<ByteSize> for $typ {
+        impl DekuWriter<ByteSize> for $typ {
             #[inline]
-            fn write(
+            fn to_writer<W: Write>(
                 &self,
-                output: &mut BitVec<u8, Msb0>,
-                bit_size: ByteSize,
+                writer: &mut Writer<W>,
+                byte_size: ByteSize,
             ) -> Result<(), DekuError> {
-                <$typ>::write(self, output, (Endian::default(), bit_size))
+                <$typ>::to_writer(self, writer, (Endian::default(), byte_size))
             }
         }
 
-        impl DekuWrite for $typ {
+        impl DekuWriter for $typ {
             #[inline]
-            fn write(&self, output: &mut BitVec<u8, Msb0>, _: ()) -> Result<(), DekuError> {
-                <$typ>::write(self, output, Endian::default())
+            fn to_writer<W: Write>(&self, writer: &mut Writer<W>, _: ()) -> Result<(), DekuError> {
+                <$typ>::to_writer(self, writer, Endian::default())
             }
         }
     };
@@ -615,9 +624,9 @@ mod tests {
                 let res_read = <$typ>::from_reader_with_ctx(&mut reader, ENDIAN).unwrap();
                 assert_eq!($expected, res_read);
 
-                let mut res_write = bitvec![u8, Msb0;];
-                res_read.write(&mut res_write, ENDIAN).unwrap();
-                assert_eq!($input, res_write.into_vec());
+                let mut writer = Writer::new(vec![]);
+                res_read.to_writer(&mut writer, ENDIAN).unwrap();
+                assert_eq!($input, writer.inner);
             }
         };
     }
@@ -791,23 +800,49 @@ mod tests {
         assert_eq!(expected_rest_bytes, buf);
     }
 
-    #[rstest(input, endian, bit_size, expected,
-        case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD]),
-        case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA]),
-        case::bit_size_le_smaller(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]),
-        case::bit_size_be_smaller(0x03AB, Endian::Big, Some(10), vec![0b11_1010_10, 0b11_000000]),
+    #[rstest(input, endian, bit_size, expected, expected_leftover,
+        case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD], vec![]),
+        case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA], vec![]),
+        case::bit_size_le_smaller(0x03AB, Endian::Little, Some(10), vec![0xAB], vec![true, true]),
+        case::bit_size_be_smaller(0x03AB, Endian::Big, Some(10), vec![0b11_1010_10], vec![true, true]),
         #[should_panic(expected = "InvalidParam(\"bit size 100 is larger than input 32\")")]
-        case::bit_size_le_bigger(0x03AB, Endian::Little, Some(100), vec![0xAB, 0b11_000000]),
+        case::bit_size_le_bigger(0x03AB, Endian::Little, Some(100), vec![0xAB, 0b11_000000], vec![true, true]),
     )]
-    fn test_bit_write(input: u32, endian: Endian, bit_size: Option<usize>, expected: Vec<u8>) {
-        let mut res_write = bitvec![u8, Msb0;];
+    fn test_bit_writer(
+        input: u32,
+        endian: Endian,
+        bit_size: Option<usize>,
+        expected: Vec<u8>,
+        expected_leftover: Vec<bool>,
+    ) {
+        let mut writer = Writer::new(vec![]);
         match bit_size {
             Some(bit_size) => input
-                .write(&mut res_write, (endian, BitSize(bit_size)))
+                .to_writer(&mut writer, (endian, BitSize(bit_size)))
                 .unwrap(),
-            None => input.write(&mut res_write, endian).unwrap(),
+            None => input.to_writer(&mut writer, endian).unwrap(),
         };
-        assert_eq!(expected, res_write.into_vec());
+        assert_eq!(expected_leftover, writer.rest());
+        assert_eq!(expected, writer.inner);
+    }
+
+    #[rstest(input, endian, byte_size, expected,
+        case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD]),
+        case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA]),
+        case::byte_size_le_smaller(0x00ffABAA, Endian::Little, Some(2), vec![0xaa, 0xab]),
+        case::byte_size_be_smaller(0x00ffABAA, Endian::Big, Some(2), vec![0xab, 0xaa]),
+        #[should_panic(expected = "InvalidParam(\"byte size 10 is larger then input 4\")")]
+        case::byte_size_le_bigger(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]),
+    )]
+    fn test_byte_writer(input: u32, endian: Endian, byte_size: Option<usize>, expected: Vec<u8>) {
+        let mut writer = Writer::new(vec![]);
+        match byte_size {
+            Some(byte_size) => input
+                .to_writer(&mut writer, (endian, ByteSize(byte_size)))
+                .unwrap(),
+            None => input.to_writer(&mut writer, endian).unwrap(),
+        };
+        assert_hex::assert_eq_hex!(expected, writer.inner);
     }
 
     #[rstest(input, endian, bit_size, expected, expected_write,
@@ -831,15 +866,14 @@ mod tests {
         };
         assert_eq!(expected, res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
+        let mut writer = Writer::new(vec![]);
         match bit_size {
             Some(bit_size) => res_read
-                .write(&mut res_write, (endian, BitSize(bit_size)))
+                .to_writer(&mut writer, (endian, BitSize(bit_size)))
                 .unwrap(),
-            None => res_read.write(&mut res_write, endian).unwrap(),
+            None => res_read.to_writer(&mut writer, endian).unwrap(),
         };
-
-        assert_eq!(expected_write, res_write.into_vec());
+        assert_hex::assert_eq_hex!(expected_write, writer.inner);
     }
 
     macro_rules! TestSignExtending {
diff --git a/src/impls/slice.rs b/src/impls/slice.rs
index a6b6040..b9df306 100644
--- a/src/impls/slice.rs
+++ b/src/impls/slice.rs
@@ -1,20 +1,16 @@
 //! Implementations of DekuRead and DekuWrite for [T; N] where 0 < N <= 32
 
-use crate::{DekuError, DekuWrite};
-use bitvec::prelude::*;
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
 use core::mem::MaybeUninit;
-use no_std_io::io::Read;
-
-use crate::DekuReader;
+use no_std_io::io::{Read, Write};
 
 impl<'a, Ctx: Copy, T, const N: usize> DekuReader<'a, Ctx> for [T; N]
 where
     T: DekuReader<'a, Ctx>,
 {
-    fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
-        ctx: Ctx,
-    ) -> Result<Self, DekuError>
+    fn from_reader_with_ctx<R: Read>(reader: &mut Reader<R>, ctx: Ctx) -> Result<Self, DekuError>
     where
         Self: Sized,
     {
@@ -39,32 +35,44 @@ where
         }
 
         let val = unsafe {
-            // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/80908
+            // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/96097
             (core::ptr::addr_of!(slice) as *const [T; N]).read()
         };
         Ok(val)
     }
 }
 
-impl<Ctx: Copy, T, const N: usize> DekuWrite<Ctx> for [T; N]
+impl<Ctx: Copy, T, const N: usize> DekuWriter<Ctx> for [T; N]
 where
-    T: DekuWrite<Ctx>,
+    T: DekuWriter<Ctx>,
 {
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         for v in self {
-            v.write(output, ctx)?;
+            v.to_writer(writer, ctx)?;
         }
         Ok(())
     }
 }
 
-impl<Ctx: Copy, T> DekuWrite<Ctx> for &[T]
+impl<Ctx: Copy, T> DekuWriter<Ctx> for &[T]
 where
-    T: DekuWrite<Ctx>,
+    T: DekuWriter<Ctx>,
 {
-    fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
         for v in *self {
-            v.write(output, ctx)?;
+            v.to_writer(writer, ctx)?;
+        }
+        Ok(())
+    }
+}
+
+impl<Ctx: Copy, T> DekuWriter<Ctx> for [T]
+where
+    T: DekuWriter<Ctx>,
+{
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
+        for v in self {
+            v.to_writer(writer, ctx)?;
         }
         Ok(())
     }
@@ -72,15 +80,17 @@ where
 
 #[cfg(test)]
 mod tests {
-    use crate::DekuWrite;
+    use super::*;
     use bitvec::prelude::*;
     use rstest::rstest;
 
-    use crate::{ctx::Endian, reader::Reader, DekuReader};
+    use crate::{ctx::Endian, reader::Reader, writer::Writer, DekuReader};
 
     #[rstest(input,endian,expected,
         case::normal_le([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Little, [0xCCDD, 0xAABB]),
         case::normal_be([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Big, [0xDDCC, 0xBBAA]),
+        #[should_panic(expected = "Incomplete(NeedSize { bits: 16 })")]
+        case::normal_be([0xDD, 0xCC].as_ref(), Endian::Big, [0xDDCC, 0xBBAA]),
     )]
     fn test_bit_read(input: &[u8], endian: Endian, expected: [u16; 2]) {
         let mut bit_slice = input.view_bits::<Msb0>();
@@ -95,51 +105,12 @@ mod tests {
         case::normal_be([0xDDCC, 0xBBAA], Endian::Big, vec![0xDD, 0xCC, 0xBB, 0xAA]),
     )]
     fn test_bit_write(input: [u16; 2], endian: Endian, expected: Vec<u8>) {
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
-
-        // test &slice
-        let input = input.as_ref();
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
-    }
-
-    #[cfg(feature = "const_generics")]
-    #[rstest(input,endian,expected,expected_rest,
-        case::normal_le(
-            [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(),
-            Endian::Little,
-            [[0xCCDD, 0xAABB], [0x8899, 0x6677]],
-            bits![u8, Msb0;],
-        ),
-        case::normal_le(
-            [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(),
-            Endian::Big,
-            [[0xDDCC, 0xBBAA], [0x9988, 0x7766]],
-            bits![u8, Msb0;],
-        ),
-    )]
-    fn test_nested_array_bit_read(
-        input: &[u8],
-        endian: Endian,
-        expected: [[u16; 2]; 2],
-        expected_rest: &BitSlice<u8, Msb0>,
-    ) {
-        use no_std_io::io::Cursor;
-
-        use crate::reader::Reader;
-
-        let bit_slice = input.view_bits::<Msb0>();
-
-        let mut cursor = Cursor::new(input);
-        let mut reader = Reader::new(&mut cursor);
-        let res_read = <[[u16; 2]; 2]>::from_reader_with_ctx(&mut reader, endian).unwrap();
-        assert_eq!(expected, res_read);
+        // test writer
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(expected, writer.inner);
     }
 
-    #[cfg(feature = "const_generics")]
     #[rstest(input,endian,expected,
         case::normal_le(
             [[0xCCDD, 0xAABB], [0x8899, 0x6677]],
@@ -153,14 +124,15 @@ mod tests {
         ),
     )]
     fn test_nested_array_bit_write(input: [[u16; 2]; 2], endian: Endian, expected: Vec<u8>) {
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
+        // test writer
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(expected, writer.inner);
 
         // test &slice
         let input = input.as_ref();
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(expected, writer.inner);
     }
 }
diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs
index d527d39..677942a 100644
--- a/src/impls/tuple.rs
+++ b/src/impls/tuple.rs
@@ -1,9 +1,10 @@
 //! Implementations of DekuRead and DekuWrite for tuples of length 1 to 11
 
-use bitvec::prelude::*;
-use no_std_io::io::Read;
+use crate::writer::Writer;
 
-use crate::{DekuError, DekuReader, DekuWrite};
+use no_std_io::io::{Read, Write};
+
+use crate::{DekuError, DekuReader, DekuWriter};
 
 // Trait to help us build intermediate tuples while DekuRead'ing each element
 // from the tuple
@@ -54,13 +55,13 @@ macro_rules! ImplDekuTupleTraits {
             }
         }
 
-        impl<Ctx: Copy, $($T:DekuWrite<Ctx>),+> DekuWrite<Ctx> for ($($T,)+)
+        impl<Ctx: Copy, $($T:DekuWriter<Ctx>),+> DekuWriter<Ctx> for ($($T,)+)
         {
             #[allow(non_snake_case)]
-            fn write(&self, output: &mut BitVec<u8, Msb0>, ctx: Ctx) -> Result<(), DekuError> {
+            fn to_writer<W: Write>(&self, writer: &mut Writer<W>, ctx: Ctx) -> Result<(), DekuError> {
                 let ($(ref $T,)+) = *self;
                 $(
-                    $T.write(output, ctx)?;
+                    $T.to_writer(writer, ctx)?;
                 )+
                 Ok(())
             }
@@ -94,10 +95,10 @@ mod tests {
     )]
     fn test_tuple_write<T>(input: T, expected: Vec<u8>)
     where
-        T: DekuWrite,
+        T: DekuWriter,
     {
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, ()).unwrap();
-        assert_eq!(expected, res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(expected, writer.inner);
     }
 }
diff --git a/src/impls/unit.rs b/src/impls/unit.rs
index d5e3d89..7d3c93b 100644
--- a/src/impls/unit.rs
+++ b/src/impls/unit.rs
@@ -1,20 +1,23 @@
-use bitvec::prelude::*;
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
-use crate::{DekuError, DekuReader, DekuWrite};
+use crate::{reader::Reader, writer::Writer, DekuError, DekuReader, DekuWriter};
 
 impl<Ctx: Copy> DekuReader<'_, Ctx> for () {
     fn from_reader_with_ctx<R: Read>(
-        _reader: &mut crate::reader::Reader<R>,
+        _reader: &mut Reader<R>,
         _inner_ctx: Ctx,
     ) -> Result<Self, DekuError> {
         Ok(())
     }
 }
 
-impl<Ctx: Copy> DekuWrite<Ctx> for () {
+impl<Ctx: Copy> DekuWriter<Ctx> for () {
     /// NOP on write
-    fn write(&self, _output: &mut BitVec<u8, Msb0>, _inner_ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(
+        &self,
+        _writer: &mut Writer<W>,
+        _inner_ctx: Ctx,
+    ) -> Result<(), DekuError> {
         Ok(())
     }
 }
@@ -37,8 +40,8 @@ mod tests {
         let res_read = <()>::from_reader_with_ctx(&mut reader, ()).unwrap();
         assert_eq!((), res_read);
 
-        let mut res_write = bitvec![u8, Msb0;];
-        res_read.write(&mut res_write, ()).unwrap();
-        assert_eq!(0, res_write.len());
+        let mut writer = Writer::new(vec![]);
+        res_read.to_writer(&mut writer, ()).unwrap();
+        assert!(writer.inner.is_empty());
     }
 }
diff --git a/src/impls/vec.rs b/src/impls/vec.rs
index c19abea..664ffff 100644
--- a/src/impls/vec.rs
+++ b/src/impls/vec.rs
@@ -1,12 +1,12 @@
-use no_std_io::io::Read;
+use no_std_io::io::{Read, Write};
 
 #[cfg(feature = "alloc")]
 use alloc::vec::Vec;
 
-use bitvec::prelude::*;
-
+use crate::reader::Reader;
+use crate::writer::Writer;
 use crate::{ctx::*, DekuReader};
-use crate::{DekuError, DekuWrite};
+use crate::{DekuError, DekuWriter};
 
 /// Read `T`s into a vec until a given predicate returns true
 /// * `capacity` - an optional capacity to pre-allocate the vector with
@@ -16,7 +16,7 @@ use crate::{DekuError, DekuWrite};
 /// and a borrow of the latest value to have been read. It should return `true` if reading
 /// should now stop, and `false` otherwise
 fn reader_vec_with_predicate<'a, T, Ctx, Predicate, R: Read>(
-    reader: &mut crate::reader::Reader<R>,
+    reader: &mut Reader<R>,
     capacity: Option<usize>,
     ctx: Ctx,
     mut predicate: Predicate,
@@ -44,6 +44,27 @@ where
     Ok(res)
 }
 
+fn reader_vec_to_end<'a, T, Ctx, R: Read>(
+    reader: &mut crate::reader::Reader<R>,
+    capacity: Option<usize>,
+    ctx: Ctx,
+) -> Result<Vec<T>, DekuError>
+where
+    T: DekuReader<'a, Ctx>,
+    Ctx: Copy,
+{
+    let mut res = capacity.map_or_else(Vec::new, Vec::with_capacity);
+    loop {
+        if reader.end() {
+            break;
+        }
+        let val = <T>::from_reader_with_ctx(reader, ctx)?;
+        res.push(val);
+    }
+
+    Ok(res)
+}
+
 impl<'a, T, Ctx, Predicate> DekuReader<'a, (Limit<T, Predicate>, Ctx)> for Vec<T>
 where
     T: DekuReader<'a, Ctx>,
@@ -51,7 +72,7 @@ where
     Predicate: FnMut(&T) -> bool,
 {
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         (limit, inner_ctx): (Limit<T, Predicate>, Ctx),
     ) -> Result<Self, DekuError>
     where
@@ -104,6 +125,8 @@ where
                     read_bits == bit_size
                 })
             }
+
+            Limit::End => reader_vec_to_end(reader, None, inner_ctx),
         }
     }
 }
@@ -113,7 +136,7 @@ impl<'a, T: DekuReader<'a>, Predicate: FnMut(&T) -> bool> DekuReader<'a, Limit<T
 {
     /// Read `T`s until the given limit from input for types which don't require context.
     fn from_reader_with_ctx<R: Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         limit: Limit<T, Predicate>,
     ) -> Result<Self, DekuError>
     where
@@ -123,21 +146,23 @@ impl<'a, T: DekuReader<'a>, Predicate: FnMut(&T) -> bool> DekuReader<'a, Limit<T
     }
 }
 
-impl<T: DekuWrite<Ctx>, Ctx: Copy> DekuWrite<Ctx> for Vec<T> {
+impl<T: DekuWriter<Ctx>, Ctx: Copy> DekuWriter<Ctx> for Vec<T> {
     /// Write all `T`s in a `Vec` to bits.
     /// * **inner_ctx** - The context required by `T`.
     /// # Examples
     /// ```rust
-    /// # use deku::{ctx::Endian, DekuWrite};
+    /// # use deku::{ctx::Endian, DekuWriter};
+    /// # use deku::writer::Writer;
     /// # use deku::bitvec::{Msb0, bitvec};
     /// let data = vec![1u8];
-    /// let mut output = bitvec![u8, Msb0;];
-    /// data.write(&mut output, Endian::Big).unwrap();
-    /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1])
+    /// let mut out_buf = vec![];
+    /// let mut writer = Writer::new(&mut out_buf);
+    /// data.to_writer(&mut writer, Endian::Big).unwrap();
+    /// assert_eq!(data, out_buf.to_vec());
     /// ```
-    fn write(&self, output: &mut BitVec<u8, Msb0>, inner_ctx: Ctx) -> Result<(), DekuError> {
+    fn to_writer<W: Write>(&self, writer: &mut Writer<W>, inner_ctx: Ctx) -> Result<(), DekuError> {
         for v in self {
-            v.write(output, inner_ctx)?;
+            v.to_writer(writer, inner_ctx)?;
         }
         Ok(())
     }
@@ -145,18 +170,47 @@ impl<T: DekuWrite<Ctx>, Ctx: Copy> DekuWrite<Ctx> for Vec<T> {
 
 #[cfg(test)]
 mod tests {
+    use crate::bitvec::{bits, BitSlice, Msb0};
     use rstest::rstest;
 
     use crate::reader::Reader;
 
     use super::*;
 
-    #[rstest(input,endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes,
+    #[rstest(input, limit, expected, expected_rest_bits, expected_rest_bytes,
+        case::count_0([0xAA].as_ref(), 0.into(), vec![], bits![u8, Msb0;], &[0xaa]),
+        case::count_1([0xAA, 0xBB].as_ref(), 1.into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]),
+        case::count_2([0xAA, 0xBB, 0xCC].as_ref(), 2.into(), vec![0xAA, 0xBB], bits![u8, Msb0;], &[0xcc]),
+        case::until_null([0xAA, 0, 0xBB].as_ref(), (|v: &u8| *v == 0u8).into(), vec![0xAA, 0], bits![u8, Msb0;], &[0xbb]),
+        case::until_bits([0xAA, 0xBB].as_ref(), BitSize(8).into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]),
+    )]
+    fn test_vec_reader_no_ctx<Predicate: FnMut(&u8) -> bool>(
+        mut input: &[u8],
+        limit: Limit<u8, Predicate>,
+        expected: Vec<u8>,
+        expected_rest_bits: &BitSlice<u8, Msb0>,
+        expected_rest_bytes: &[u8],
+    ) {
+        let mut reader = Reader::new(&mut input);
+        let res_read = Vec::<u8>::from_reader_with_ctx(&mut reader, limit).unwrap();
+        assert_eq!(expected, res_read);
+        assert_eq!(
+            reader.rest(),
+            expected_rest_bits.iter().by_vals().collect::<Vec<bool>>()
+        );
+        let mut buf = vec![];
+        input.read_to_end(&mut buf).unwrap();
+        assert_eq!(expected_rest_bytes, buf);
+    }
+
+    #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes,
         case::count_0([0xAA].as_ref(), Endian::Little, Some(8), 0.into(), vec![], bits![u8, Msb0;], &[0xaa]),
         case::count_1([0xAA, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]),
         case::count_2([0xAA, 0xBB, 0xCC].as_ref(), Endian::Little, Some(8), 2.into(), vec![0xAA, 0xBB], bits![u8, Msb0;], &[0xcc]),
         case::until_null([0xAA, 0, 0xBB].as_ref(), Endian::Little, None, (|v: &u8| *v == 0u8).into(), vec![0xAA, 0], bits![u8, Msb0;], &[0xbb]),
         case::until_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(8).into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]),
+        case::end([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::end(), vec![0xaa, 0xbb], bits![u8, Msb0;], &[]),
+        case::end_bitsize([0xf0, 0xf0].as_ref(), Endian::Little, Some(4), Limit::end(), vec![0xf, 0x0, 0x0f, 0x0], bits![u8, Msb0;], &[]),
         case::bits_6([0b0110_1001, 0b1110_1001].as_ref(), Endian::Little, Some(6), 2.into(), vec![0b00_011010, 0b00_011110], bits![u8, Msb0; 1, 0, 0, 1], &[]),
         #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")]
         case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), vec![], bits![u8, Msb0;], &[]),
@@ -202,9 +256,9 @@ mod tests {
         case::normal(vec![0xAABB, 0xCCDD], Endian::Little, vec![0xBB, 0xAA, 0xDD, 0xCC]),
     )]
     fn test_vec_write(input: Vec<u16>, endian: Endian, expected: Vec<u8>) {
-        let mut res_write = bitvec![u8, Msb0;];
-        input.write(&mut res_write, endian).unwrap();
-        assert_eq!(expected, res_write.into_vec());
+        let mut writer = Writer::new(vec![]);
+        input.to_writer(&mut writer, endian).unwrap();
+        assert_eq!(expected, writer.inner);
     }
 
     // Note: These tests also exist in boxed.rs
@@ -243,11 +297,11 @@ mod tests {
         input.read_to_end(&mut buf).unwrap();
         assert_eq!(expected_rest_bytes, buf);
 
-        let mut res_write = bitvec![u8, Msb0;];
+        let mut writer = Writer::new(vec![]);
         res_read
-            .write(&mut res_write, (endian, BitSize(bit_size)))
+            .to_writer(&mut writer, (endian, BitSize(bit_size)))
             .unwrap();
-        assert_eq!(expected_write, res_write.into_vec());
+        assert_eq!(expected_write, writer.inner);
 
         assert_eq!(input_clone[..expected_write.len()].to_vec(), expected_write);
     }
diff --git a/src/lib.rs b/src/lib.rs
index 64d36de..aef9804 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -155,8 +155,8 @@ the incoming data.
 First the "type" is read using the `type`, then is matched against the
 variants given `id`. What happens after is the same as structs!
 
-This is implemented with the [id](/attributes/index.html#id),
-[id_pat](/attributes/index.html#id_pat), [default](/attributes/index.html#default) and
+This is implemented with the [id](attributes#id),
+[id_pat](attributes#id_pat), [default](attributes#default) and
 [type](attributes#type) attributes. See these for more examples.
 
 If no `id` is specified, the variant will default to it's discriminant value.
@@ -174,7 +174,7 @@ Example:
 use deku::prelude::*;
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum DekuTest {
     #[deku(id = 0x01)]
     VariantA,
@@ -238,7 +238,7 @@ using `no_std`.
 # use std::io::{Seek, SeekFrom, Read};
 # use std::fs::File;
 # use deku::prelude::*;
-#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Hash)]
+#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)]
 #[deku(endian = "big")]
 struct EcHdr {
     magic: [u8; 4],
@@ -250,6 +250,23 @@ let mut file = File::options().read(true).open("file").unwrap();
 let ec = EcHdr::from_reader((&mut file, 0)).unwrap();
 ```
 
+# `Write` enabled
+Parsers can be created that directly write to a source implementing [Write](crate::no_std_io::Write).
+
+```rust, no_run
+# use std::io::{Seek, SeekFrom, Read};
+# use std::fs::File;
+# use deku::prelude::*;
+#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)]
+#[deku(endian = "big")]
+struct Hdr {
+    version: u8,
+}
+let hdr = Hdr { version: 0xf0 };
+let mut file = File::options().write(true).open("file").unwrap();
+hdr.to_writer(&mut Writer::new(file), ());
+```
+
 # Internal variables and previously read fields
 
 Along similar lines to [Context](#context) variables, previously read variables
@@ -273,8 +290,8 @@ tokens such as `reader`, `writer`, `map`, `count`, etc.
 These are provided as a convenience to the user.
 
 Always included:
-- `deku::reader: &mut Reader` - Current [Reader](crate::reader::Reader)
-- `deku::output: &mut BitSlice<u8, Msb0>` - The output bit stream
+- `deku::reader: &mut Reader` - Current [Reader]
+- `deku::writer: &mut Writer` - Current [Writer]
 
 Conditionally included if referenced:
 - `deku::bit_offset: usize` - Current bit offset from the input
@@ -331,6 +348,7 @@ pub mod no_std_io {
     pub use no_std_io::io::Cursor;
     pub use no_std_io::io::Read;
     pub use no_std_io::io::Result;
+    pub use no_std_io::io::Write;
 }
 
 /// re-export of bitvec
@@ -347,8 +365,11 @@ pub mod error;
 mod impls;
 pub mod prelude;
 pub mod reader;
+pub mod writer;
 
 pub use crate::error::DekuError;
+use crate::reader::Reader;
+use crate::writer::Writer;
 
 /// "Reader" trait: read bytes and bits from [`no_std_io::Read`]er
 pub trait DekuReader<'a, Ctx = ()> {
@@ -360,7 +381,7 @@ pub trait DekuReader<'a, Ctx = ()> {
     /// # use std::fs::File;
     /// # use deku::prelude::*;
     /// # use deku::ctx::Endian;
-    /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Hash)]
+    /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)]
     /// #[deku(endian = "ctx_endian", ctx = "ctx_endian: Endian")]
     /// struct EcHdr {
     ///     magic: [u8; 4],
@@ -373,7 +394,7 @@ pub trait DekuReader<'a, Ctx = ()> {
     /// let ec = EcHdr::from_reader_with_ctx(&mut reader, Endian::Big).unwrap();
     /// ```
     fn from_reader_with_ctx<R: no_std_io::Read>(
-        reader: &mut crate::reader::Reader<R>,
+        reader: &mut Reader<R>,
         ctx: Ctx,
     ) -> Result<Self, DekuError>
     where
@@ -396,7 +417,7 @@ pub trait DekuContainerRead<'a>: DekuReader<'a, ()> {
     /// # use std::io::{Seek, SeekFrom, Read};
     /// # use std::fs::File;
     /// # use deku::prelude::*;
-    /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Hash)]
+    /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)]
     /// #[deku(endian = "big")]
     /// struct EcHdr {
     ///     magic: [u8; 4],
@@ -422,27 +443,73 @@ pub trait DekuContainerRead<'a>: DekuReader<'a, ()> {
         Self: Sized;
 }
 
-/// "Writer" trait: write from type to bits
-pub trait DekuWrite<Ctx = ()> {
-    /// Write type to bits
-    /// * **output** - Sink to store resulting bits
-    /// * **ctx** - A context required by context-sensitive reading. A unit type `()` means no context
-    /// needed.
-    fn write(
+/// "Writer" trait: write from type to bytes
+pub trait DekuWriter<Ctx = ()> {
+    /// Write type to bytes
+    fn to_writer<W: no_std_io::Write>(
         &self,
-        output: &mut bitvec::BitVec<u8, bitvec::Msb0>,
+        writer: &mut Writer<W>,
         ctx: Ctx,
     ) -> Result<(), DekuError>;
 }
 
 /// "Writer" trait: implemented on DekuWrite struct and enum containers. A `container` is a type which
 /// doesn't need any context information.
-pub trait DekuContainerWrite: DekuWrite<()> {
+pub trait DekuContainerWrite: DekuWriter<()> {
     /// Write struct/enum to Vec<u8>
-    fn to_bytes(&self) -> Result<Vec<u8>, DekuError>;
+    ///
+    /// ```rust
+    /// # use deku::prelude::*;
+    /// #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+    /// #[deku(endian = "little")]
+    /// struct S {
+    ///    data_00: u8,
+    ///    data_01: u16,
+    ///    data_02: u32,
+    /// }
+    ///
+    /// let s = S { data_00: 0x01, data_01: 0x02, data_02: 0x03 };
+    /// let bytes = s.to_bytes().unwrap();
+    /// assert_eq!(bytes, [0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00]);
+    /// ````
+    fn to_bytes(&self) -> Result<Vec<u8>, DekuError> {
+        let mut out_buf = Vec::new();
+        let mut __deku_writer = Writer::new(&mut out_buf);
+        DekuWriter::to_writer(self, &mut __deku_writer, ())?;
+        __deku_writer.finalize()?;
+        Ok(out_buf)
+    }
 
     /// Write struct/enum to BitVec
-    fn to_bits(&self) -> Result<bitvec::BitVec<u8, bitvec::Msb0>, DekuError>;
+    ///
+    /// ```rust
+    /// # use deku::prelude::*;
+    /// # use deku::bitvec::Lsb0;
+    /// #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    /// #[deku(endian = "little")]
+    /// pub struct TestOver {
+    ///     #[deku(bits = "4")]
+    ///     pub a: u8,
+    ///     #[deku(bits = "4")]
+    ///     pub b: u8,
+    ///     #[deku(bits = "1")]
+    ///     pub c: u8,
+    /// }
+    ///
+    /// let test_data: &[u8] = &[0xf1, 0x80];
+    /// let test = TestOver::from_bytes((test_data, 0)).unwrap().1;
+    /// let bits = test.to_bits().unwrap();
+    /// assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits);
+    /// ```
+    fn to_bits(&self) -> Result<bitvec::BitVec<u8, bitvec::Msb0>, DekuError> {
+        let mut out_buf = Vec::new();
+        let mut __deku_writer = Writer::new(&mut out_buf);
+        DekuWriter::to_writer(self, &mut __deku_writer, ())?;
+        let mut leftover = __deku_writer.leftover;
+        let mut bv = bitvec::BitVec::from_slice(&out_buf);
+        bv.append(&mut leftover);
+        Ok(bv)
+    }
 }
 
 /// "Updater" trait: apply mutations to a type
@@ -457,19 +524,17 @@ pub trait DekuEnumExt<'a, T> {
     fn deku_id(&self) -> Result<T, DekuError>;
 }
 
-/// Implements DekuWrite for references of types that implement DekuWrite
-impl<T, Ctx> DekuWrite<Ctx> for &T
+impl<T, Ctx> DekuWriter<Ctx> for &T
 where
-    T: DekuWrite<Ctx>,
+    T: DekuWriter<Ctx>,
     Ctx: Copy,
 {
-    /// Write value of type to bits
-    fn write(
+    fn to_writer<W: no_std_io::Write>(
         &self,
-        output: &mut bitvec::BitVec<u8, bitvec::Msb0>,
+        writer: &mut Writer<W>,
         ctx: Ctx,
     ) -> Result<(), DekuError> {
-        <T>::write(self, output, ctx)?;
+        <T>::to_writer(self, writer, ctx)?;
         Ok(())
     }
 }
diff --git a/src/prelude.rs b/src/prelude.rs
index 6a70b6d..da52e98 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -4,6 +4,6 @@
 */
 pub use crate::error::{DekuError, NeedSize};
 pub use crate::{
-    deku_derive, reader::Reader, DekuContainerRead, DekuContainerWrite, DekuEnumExt, DekuRead,
-    DekuReader, DekuUpdate, DekuWrite,
+    deku_derive, reader::Reader, writer::Writer, DekuContainerRead, DekuContainerWrite,
+    DekuEnumExt, DekuRead, DekuReader, DekuUpdate, DekuWrite, DekuWriter,
 };
diff --git a/src/reader.rs b/src/reader.rs
index f31ed1b..e800042 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -42,6 +42,12 @@ impl<'a, R: Read> Reader<'a, R> {
         }
     }
 
+    /// Consume self, returning inner Reader
+    #[inline]
+    pub fn into_inner(self) -> &'a mut R {
+        self.inner
+    }
+
     /// Return the unused bits
     ///
     /// Once the parsing is complete for a struct, if the total size of the field using the `bits` attribute
@@ -78,7 +84,8 @@ impl<'a, R: Read> Reader<'a, R> {
         self.leftover.iter().by_vals().collect()
     }
 
-    /// Return true if we are at the end of a reader and there are no cached bits in the reader
+    /// Return true if we are at the end of a reader and there are no cached bits in the reader.
+    /// Since this uses [Read] internally, this will return true when [Read] returns [ErrorKind::UnexpectedEof].
     ///
     /// The byte that was read will be internally buffered
     #[inline]
@@ -106,7 +113,7 @@ impl<'a, R: Read> Reader<'a, R> {
     }
 
     /// Used at the beginning of `from_reader`.
-    /// TODO: maybe send into read_bytes() if amt >= 8
+    // TODO: maybe send into read_bytes() if amt >= 8
     #[inline]
     pub fn skip_bits(&mut self, amt: usize) -> Result<(), DekuError> {
         #[cfg(feature = "logging")]
@@ -218,7 +225,7 @@ impl<'a, R: Read> Reader<'a, R> {
             self.bits_read += amt * 8;
 
             #[cfg(feature = "logging")]
-            log::trace!("read_bytes: returning {buf:02x?}");
+            log::trace!("read_bytes: returning {:02x?}", &buf[..amt]);
 
             Ok(ReaderRet::Bytes)
         } else {
diff --git a/src/writer.rs b/src/writer.rs
new file mode 100644
index 0000000..a8ffc04
--- /dev/null
+++ b/src/writer.rs
@@ -0,0 +1,199 @@
+//! Writer for writer functions
+
+use bitvec::bitvec;
+use bitvec::{field::BitField, prelude::*};
+use no_std_io::io::Write;
+
+#[cfg(feature = "logging")]
+use log;
+
+use crate::DekuError;
+
+const fn bits_of<T>() -> usize {
+    core::mem::size_of::<T>().saturating_mul(<u8>::BITS as usize)
+}
+
+/// Container to use with `to_writer`
+pub struct Writer<W: Write> {
+    pub(crate) inner: W,
+    /// Leftover bits
+    pub leftover: BitVec<u8, Msb0>,
+    /// Total bits written
+    pub bits_written: usize,
+}
+
+impl<W: Write> Writer<W> {
+    /// Create a new `Writer`
+    #[inline]
+    pub fn new(inner: W) -> Self {
+        Self {
+            inner,
+            leftover: BitVec::new(),
+            bits_written: 0,
+        }
+    }
+
+    /// Return the unused bits
+    #[inline]
+    pub fn rest(&mut self) -> alloc::vec::Vec<bool> {
+        self.leftover.iter().by_vals().collect()
+    }
+
+    /// Write all `bits` to `Writer` buffer if bits can fit into a byte buffer.
+    ///
+    /// Any leftover bits will be written before `bits`, and non-written bits will
+    /// be stored in `self.leftover`.
+    #[inline]
+    pub fn write_bits(&mut self, bits: &BitSlice<u8, Msb0>) -> Result<(), DekuError> {
+        #[cfg(feature = "logging")]
+        log::trace!("attempting {} bits", bits.len());
+
+        // quick return if we can't write to the bytes buffer
+        if (self.leftover.len() + bits.len()) < 8 {
+            self.leftover.extend_from_bitslice(bits);
+            return Ok(());
+        }
+
+        // pre-pend the previous attempt to write if needed
+        let mut bits = if self.leftover.is_empty() {
+            bits
+        } else {
+            #[cfg(feature = "logging")]
+            log::trace!("pre-pending {} bits", self.leftover.len());
+
+            self.leftover.extend_from_bitslice(bits);
+            &mut self.leftover
+        };
+
+        // one shot impl of BitSlice::read(no read_exact), but for no_std
+        let mut buf = alloc::vec![0x00; bits.len() / 8];
+        let mut count = 0;
+        bits.chunks_exact(bits_of::<u8>())
+            .zip(buf.iter_mut())
+            .for_each(|(byte, slot)| {
+                *slot = byte.load_be();
+                count += 1;
+            });
+
+        // SAFETY: This does not have a safety comment in bitvec. But this is safe
+        // because of `count` here will always still be within the bounds
+        // of `bits`
+        bits = unsafe { bits.get_unchecked(count * bits_of::<u8>()..) };
+
+        self.leftover = bits.to_bitvec();
+        if self.inner.write_all(&buf).is_err() {
+            return Err(DekuError::Write);
+        }
+
+        self.bits_written += buf.len() * 8;
+        #[cfg(feature = "logging")]
+        log::trace!("wrote {} bits: {buf:02x?}", buf.len() * 8);
+
+        Ok(())
+    }
+
+    /// Write `buf` into `Writer`
+    ///
+    /// If no `self.leftover`, this will write directly into `Writer`, and if not will write
+    /// `buf` using `Self::write_bits()`.
+    // The following inline(always) helps performance significantly
+    #[inline(always)]
+    pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), DekuError> {
+        #[cfg(feature = "logging")]
+        log::trace!("writing {} bytes: {buf:02x?}", buf.len());
+
+        if !self.leftover.is_empty() {
+            #[cfg(feature = "logging")]
+            log::trace!("leftover exists");
+
+            // TODO(perf): we could check here and only send the required bits to finish the byte,
+            // instead of sending the entire thing. The rest would be through self.inner.write.
+            self.write_bits(&BitVec::from_slice(buf))?;
+        } else {
+            if self.inner.write_all(buf).is_err() {
+                return Err(DekuError::Write);
+            }
+            self.bits_written += buf.len() * 8;
+        }
+
+        Ok(())
+    }
+
+    /// Write all remaining bits into `Writer`, adding empty bits to the end so that we can write
+    /// into a byte buffer
+    #[inline]
+    pub fn finalize(&mut self) -> Result<(), DekuError> {
+        if !self.leftover.is_empty() {
+            #[cfg(feature = "logging")]
+            log::trace!("finalized: {} bits leftover", self.leftover.len());
+
+            // add bits to be byte aligned so we can write
+            self.leftover
+                .extend_from_bitslice(&bitvec![u8, Msb0; 0; 8 - self.leftover.len()]);
+            let mut buf = alloc::vec![0x00; self.leftover.len() / 8];
+
+            // write as many leftover to the buffer. Because of the previous extend,
+            // this will include all the bits in self.leftover
+            self.leftover
+                .chunks_exact(bits_of::<u8>())
+                .zip(buf.iter_mut())
+                .for_each(|(byte, slot)| {
+                    *slot = byte.load_be();
+                });
+
+            if self.inner.write_all(&buf).is_err() {
+                return Err(DekuError::Write);
+            }
+            self.bits_written += buf.len() * 8;
+
+            #[cfg(feature = "logging")]
+            log::trace!("finalized: wrote {} bits: {buf:02x?}", buf.len() * 8);
+        }
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use hexlit::hex;
+
+    #[test]
+    fn test_writer() {
+        let mut out_buf = vec![];
+        let mut writer = Writer::new(&mut out_buf);
+
+        let mut input = hex!("aa");
+        writer.write_bytes(&mut input).unwrap();
+
+        let mut bv = BitVec::<u8, Msb0>::from_slice(&[0xbb]);
+        writer.write_bits(&mut bv).unwrap();
+
+        let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1];
+        writer.write_bits(&mut bv).unwrap();
+        let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1];
+        writer.write_bits(&mut bv).unwrap();
+
+        let mut input = hex!("aa");
+        writer.write_bytes(&mut input).unwrap();
+
+        let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1];
+        writer.write_bits(&mut bv).unwrap();
+        let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1];
+        writer.write_bits(&mut bv).unwrap();
+
+        let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1];
+        writer.write_bits(&mut bv).unwrap();
+
+        let mut input = hex!("aa");
+        writer.write_bytes(&mut input).unwrap();
+
+        let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1];
+        writer.write_bits(&mut bv).unwrap();
+
+        assert_eq!(
+            &mut out_buf,
+            &mut vec![0xaa, 0xbb, 0xf1, 0xaa, 0x1f, 0x1a, 0xaf]
+        );
+    }
+}


Test patch:
diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs
index 32dd937..0ac56ab 100644
--- a/tests/test_alloc.rs
+++ b/tests/test_alloc.rs
@@ -14,14 +14,14 @@ struct NestedStruct {
 }
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-#[deku(type = "u8", ctx = "_endian: Endian")]
+#[deku(id_type = "u8", ctx = "_endian: Endian")]
 enum NestedEnum {
     #[deku(id = "0x01")]
     VarA(u8),
 }
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-#[deku(type = "u32", bytes = 2, ctx = "_endian: Endian")]
+#[deku(id_type = "u32", bytes = 2, ctx = "_endian: Endian")]
 enum NestedEnum2 {
     #[deku(id = "0x01")]
     VarA(u8),
diff --git a/tests/test_attributes/test_ctx.rs b/tests/test_attributes/test_ctx.rs
index 3d16fcd..f945cd3 100644
--- a/tests/test_attributes/test_ctx.rs
+++ b/tests/test_attributes/test_ctx.rs
@@ -5,6 +5,7 @@ use bitvec::bitvec;
 use deku::bitvec::Msb0;
 use deku::prelude::*;
 use deku::reader::Reader;
+use deku::writer::Writer;
 
 /// General smoke tests for ctx
 /// TODO: These should be divided into smaller units
@@ -16,7 +17,7 @@ fn test_ctx_struct() {
     struct SubTypeNeedCtx {
         #[deku(
             reader = "(u8::from_reader_with_ctx(deku::reader,()).map(|c|(a+b+c) as usize))",
-            writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(self.i as u8)"
+            writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(self.i as u8)"
         )]
         i: usize,
     }
@@ -50,13 +51,13 @@ fn test_ctx_struct() {
 #[test]
 fn test_top_level_ctx_enum() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u8", ctx = "a: u8, b: u8")]
+    #[deku(id_type = "u8", ctx = "a: u8, b: u8")]
     enum TopLevelCtxEnum {
         #[deku(id = "1")]
         VariantA(
             #[deku(
                 reader = "(u8::from_reader_with_ctx(deku::reader,()).map(|c|(a+b+c)))",
-                writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(field_0)"
+                writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(field_0)"
             )]
             u8,
         ),
@@ -70,21 +71,22 @@ fn test_top_level_ctx_enum() {
     .unwrap();
     assert_eq!(ret_read, TopLevelCtxEnum::VariantA(0x06));
 
-    let mut ret_write = bitvec![u8, Msb0;];
-    ret_read.write(&mut ret_write, (1, 2)).unwrap();
-    assert_eq!(ret_write.into_vec(), &test_data[..]);
+    let mut out_buf = vec![];
+    let mut writer = Writer::new(&mut out_buf);
+    ret_read.to_writer(&mut writer, (1, 2)).unwrap();
+    assert_eq!(out_buf.to_vec(), &test_data[..]);
 }
 
 #[test]
 fn test_top_level_ctx_enum_default() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u8", ctx = "a: u8, b: u8", ctx_default = "1,2")]
+    #[deku(id_type = "u8", ctx = "a: u8, b: u8", ctx_default = "1,2")]
     enum TopLevelCtxEnumDefault {
         #[deku(id = "1")]
         VariantA(
             #[deku(
                 reader = "(u8::from_reader_with_ctx(deku::reader, ()).map(|c|(a+b+c)))",
-                writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(field_0)"
+                writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(field_0)"
             )]
             u8,
         ),
@@ -106,9 +108,10 @@ fn test_top_level_ctx_enum_default() {
     )
     .unwrap();
     assert_eq!(ret_read, TopLevelCtxEnumDefault::VariantA(0x06));
-    let mut ret_write = bitvec![u8, Msb0;];
-    ret_read.write(&mut ret_write, (1, 2)).unwrap();
-    assert_eq!(test_data.to_vec(), ret_write.into_vec());
+    let mut out_buf = vec![];
+    let mut writer = Writer::new(&mut out_buf);
+    ret_read.to_writer(&mut writer, (1, 2)).unwrap();
+    assert_eq!(test_data.to_vec(), out_buf.to_vec());
 }
 
 #[test]
@@ -228,14 +231,16 @@ fn test_ctx_default_struct() {
     .unwrap();
     assert_eq!(expected, ret_read);
     let mut ret_write = bitvec![u8, Msb0;];
-    ret_read.write(&mut ret_write, (1, 2)).unwrap();
-    assert_eq!(test_data.to_vec(), ret_write.into_vec());
+    let mut out_buf = vec![];
+    let mut writer = Writer::new(&mut out_buf);
+    ret_read.to_writer(&mut writer, (1, 2)).unwrap();
+    assert_eq!(test_data.to_vec(), out_buf.to_vec());
 }
 
 #[test]
 fn test_enum_endian_ctx() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u32", endian = "endian", ctx = "endian: deku::ctx::Endian")]
+    #[deku(id_type = "u32", endian = "endian", ctx = "endian: deku::ctx::Endian")]
     enum EnumTypeEndianCtx {
         #[deku(id = "0xDEADBEEF")]
         VarA(u8),
diff --git a/tests/test_attributes/test_limits/mod.rs b/tests/test_attributes/test_limits/mod.rs
index 48aa212..c7e74d0 100644
--- a/tests/test_attributes/test_limits/mod.rs
+++ b/tests/test_attributes/test_limits/mod.rs
@@ -1,4 +1,5 @@
 mod test_bits_read;
 mod test_bytes_read;
 mod test_count;
+mod test_read_all;
 mod test_until;
diff --git a/tests/test_attributes/test_limits/test_read_all.rs b/tests/test_attributes/test_limits/test_read_all.rs
new file mode 100644
index 0000000..4e91388
--- /dev/null
+++ b/tests/test_attributes/test_limits/test_read_all.rs
@@ -0,0 +1,46 @@
+use std::convert::{TryFrom, TryInto};
+
+use deku::prelude::*;
+
+#[test]
+fn test_read_all() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    struct TestStruct1 {
+        #[deku(read_all)]
+        data: Vec<u8>,
+    }
+
+    let test_data: Vec<u8> = [0xaa, 0xbb].to_vec();
+
+    let ret_read = TestStruct1::try_from(test_data.as_slice()).unwrap();
+    assert_eq!(
+        TestStruct1 {
+            data: test_data.to_vec()
+        },
+        ret_read
+    );
+
+    let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+    assert_eq!(test_data, ret_write);
+
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    struct TestStruct2 {
+        first: u8,
+        #[deku(read_all)]
+        data: Vec<u8>,
+    }
+
+    let test_data: Vec<u8> = [0xff, 0xaa, 0xbb].to_vec();
+
+    let ret_read = TestStruct2::try_from(test_data.as_slice()).unwrap();
+    assert_eq!(
+        TestStruct2 {
+            first: 0xff,
+            data: test_data[1..].to_vec()
+        },
+        ret_read
+    );
+
+    let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+    assert_eq!(test_data, ret_write);
+}
diff --git a/tests/test_attributes/test_temp.rs b/tests/test_attributes/test_temp.rs
index 2a0cb44..c4040b2 100644
--- a/tests/test_attributes/test_temp.rs
+++ b/tests/test_attributes/test_temp.rs
@@ -104,7 +104,7 @@ fn test_temp_field_unnamed_write() {
 fn test_temp_enum_field() {
     #[deku_derive(DekuRead, DekuWrite)]
     #[derive(PartialEq, Debug)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum TestEnum {
         #[deku(id = "0xAB")]
         VarA {
@@ -133,7 +133,7 @@ fn test_temp_enum_field() {
 fn test_temp_enum_field_write() {
     #[deku_derive(DekuRead, DekuWrite)]
     #[derive(PartialEq, Debug)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum TestEnum {
         #[deku(id = "0xAB")]
         VarA {
diff --git a/tests/test_box.rs b/tests/test_box.rs
new file mode 100644
index 0000000..d9cb6fc
--- /dev/null
+++ b/tests/test_box.rs
@@ -0,0 +1,14 @@
+use deku::prelude::*;
+
+#[derive(DekuRead, DekuWrite)]
+struct TestStruct {
+    field: Box<u8>,
+}
+
+#[test]
+fn test_box_smoke_test() {
+    let test_data: &[u8] = &[0xf0];
+    let a = TestStruct::try_from(test_data).unwrap();
+    let new_bytes = a.to_bytes().unwrap();
+    assert_eq!(test_data, &*new_bytes);
+}
diff --git a/tests/test_catch_all.rs b/tests/test_catch_all.rs
index 1565cf0..62bc4cf 100644
--- a/tests/test_catch_all.rs
+++ b/tests/test_catch_all.rs
@@ -6,7 +6,7 @@ mod test {
 
     /// Basic test struct
     #[derive(Clone, Copy, PartialEq, Eq, Debug, DekuWrite, DekuRead)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     #[non_exhaustive]
     #[repr(u8)]
     pub enum BasicMapping {
@@ -21,7 +21,7 @@ mod test {
 
     /// Advanced test struct
     #[derive(Clone, Copy, PartialEq, Eq, Debug, DekuWrite, DekuRead)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     #[non_exhaustive]
     #[repr(u8)]
     pub enum AdvancedRemapping {
diff --git a/tests/test_compile/cases/bits_bytes_conflict.rs b/tests/test_compile/cases/bits_bytes_conflict.rs
index bc43d3b..a6c18c8 100644
--- a/tests/test_compile/cases/bits_bytes_conflict.rs
+++ b/tests/test_compile/cases/bits_bytes_conflict.rs
@@ -1,11 +1,11 @@
 use deku::prelude::*;
 
 #[derive(DekuRead)]
-#[deku(type = "u8", bits = 1, bytes = 2)]
+#[deku(id_type = "u8", bits = 1, bytes = 2)]
 enum Test1 {}
 
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum Test2 {
     A(#[deku(bits = 1, bytes = 2)] u8),
     B {
diff --git a/tests/test_compile/cases/bits_bytes_conflict.stderr b/tests/test_compile/cases/bits_bytes_conflict.stderr
index ff8f9d5..8ea8495 100644
--- a/tests/test_compile/cases/bits_bytes_conflict.stderr
+++ b/tests/test_compile/cases/bits_bytes_conflict.stderr
@@ -1,8 +1,8 @@
 error: conflicting: both `bits` and `bytes` specified on enum
- --> $DIR/bits_bytes_conflict.rs:4:28
+ --> $DIR/bits_bytes_conflict.rs:4:31
   |
-4 | #[deku(type = "u8", bits = 1, bytes = 2)]
-  |                            ^
+4 | #[deku(id_type = "u8", bits = 1, bytes = 2)]
+  |                               ^
 
 error: conflicting: both `bits` and `bytes` specified on field
   --> $DIR/bits_bytes_conflict.rs:10:21
diff --git a/tests/test_compile/cases/catch_all_multiple.rs b/tests/test_compile/cases/catch_all_multiple.rs
index cdc80f5..72a1f53 100644
--- a/tests/test_compile/cases/catch_all_multiple.rs
+++ b/tests/test_compile/cases/catch_all_multiple.rs
@@ -1,7 +1,7 @@
 use deku::prelude::*;
 
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum Test1 {
     #[deku(default)]
     A = 1,
diff --git a/tests/test_compile/cases/count_read_conflict.rs b/tests/test_compile/cases/count_read_conflict.rs
index 7054b6d..a9cfe62 100644
--- a/tests/test_compile/cases/count_read_conflict.rs
+++ b/tests/test_compile/cases/count_read_conflict.rs
@@ -24,5 +24,22 @@ struct Test4 {
     a: Vec<u8>,
 }
 
+#[derive(DekuRead)]
+struct Test5 {
+    #[deku(count = "1", read_all)]
+    a: Vec<u8>,
+}
+
+#[derive(DekuRead)]
+struct Test6 {
+    #[deku(bytes_read = "1", read_all)]
+    a: Vec<u8>,
+}
+
+#[derive(DekuRead)]
+struct Test7 {
+    #[deku(bits_read = "1", read_all)]
+    a: Vec<u8>,
+}
 
 fn main() {}
diff --git a/tests/test_compile/cases/count_read_conflict.stderr b/tests/test_compile/cases/count_read_conflict.stderr
index 92e9aa9..19f29cf 100644
--- a/tests/test_compile/cases/count_read_conflict.stderr
+++ b/tests/test_compile/cases/count_read_conflict.stderr
@@ -21,3 +21,27 @@ error: conflicting: both `count` and `bytes_read` specified on field
    |
 23 |     #[deku(count = "1", bytes_read = "2")]
    |                    ^^^
+
+error: conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`
+  --> $DIR/count_read_conflict.rs:27:10
+   |
+27 | #[derive(DekuRead)]
+   |          ^^^^^^^^
+   |
+   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`
+  --> $DIR/count_read_conflict.rs:33:10
+   |
+33 | #[derive(DekuRead)]
+   |          ^^^^^^^^
+   |
+   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`
+  --> $DIR/count_read_conflict.rs:39:10
+   |
+39 | #[derive(DekuRead)]
+   |          ^^^^^^^^
+   |
+   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/test_compile/cases/enum_validation.rs b/tests/test_compile/cases/enum_validation.rs
index cf1aedc..7adafe4 100644
--- a/tests/test_compile/cases/enum_validation.rs
+++ b/tests/test_compile/cases/enum_validation.rs
@@ -4,22 +4,22 @@ use deku::prelude::*;
 #[derive(DekuRead)]
 enum Test1 {}
 
-// test conflict `type` and `id`
+// test conflict `type` and `id_type`
 #[derive(DekuRead)]
-#[deku(type = "u8", id = "test")]
+#[deku(id_type = "u8", id = "test")]
 enum Test2 {}
 
-// test conflict `id` and `id_pat`
+// test conflict `id_type` and `id_pat`
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum Test3 {
     #[deku(id = "1", id_pat = "2..=3")]
     A(u8),
 }
 
-// test `type` only allowed on enum
+// test `id_type` only allowed on enum
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 struct Test4 {
     a: u8,
 }
@@ -38,38 +38,38 @@ struct Test6 {
     a: u8,
 }
 
-// test `id` only allowed on enum
+// test `id_type` only allowed on enum
 #[derive(DekuRead)]
-#[deku(id = "test")]
+#[deku(id_type = "test")]
 struct Test7 {
     a: u8,
 }
 
-// test `bits` cannot be used with `id`
+// test `bits` cannot be used with `id_type`
 #[derive(DekuRead)]
-#[deku(id = "test", bits = 4)]
+#[deku(id_type = "test", bits = 4)]
 enum Test8 {
     A,
 }
 
-// test `bytes` cannot be used with `id`
+// test `bytes` cannot be used with `id_type`
 #[derive(DekuRead)]
-#[deku(id = "test", bytes = 4)]
+#[deku(id_type = "test", bytes = 4)]
 enum Test9 {
     A,
 }
 
-// test `id` cannot be `_`
+// test `type_id` cannot be `_`
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum Test10 {
     #[deku(id = "_")]
     A,
 }
 
-// test missing `id`
+// test missing `id_type`
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum Test11 {
     #[deku(id = "1")]
     A,
diff --git a/tests/test_compile/cases/enum_validation.stderr b/tests/test_compile/cases/enum_validation.stderr
index 1ebfc69..a68d775 100644
--- a/tests/test_compile/cases/enum_validation.stderr
+++ b/tests/test_compile/cases/enum_validation.stderr
@@ -1,65 +1,65 @@
-error: `type` or `id` must be specified on enum
- --> $DIR/enum_validation.rs:5:6
+error: `id_type` or `id` must be specified on enum
+ --> tests/test_compile/cases/enum_validation.rs:5:6
   |
 5 | enum Test1 {}
   |      ^^^^^
 
-error: conflicting: both `type` and `id` specified on enum
-  --> $DIR/enum_validation.rs:10:6
+error: conflicting: both `id_type` and `id` specified on enum
+  --> tests/test_compile/cases/enum_validation.rs:10:6
    |
 10 | enum Test2 {}
    |      ^^^^^
 
 error: conflicting: both `id` and `id_pat` specified on variant
-  --> $DIR/enum_validation.rs:16:17
+  --> tests/test_compile/cases/enum_validation.rs:16:17
    |
 16 |     #[deku(id = "1", id_pat = "2..=3")]
    |                 ^^^
 
-error: `type` only supported on enum
-  --> $DIR/enum_validation.rs:22:15
+error: `id_type` only supported on enum
+  --> tests/test_compile/cases/enum_validation.rs:22:18
    |
-22 | #[deku(type = "u8")]
-   |               ^^^^
+22 | #[deku(id_type = "u8")]
+   |                  ^^^^
 
 error: `bits` only supported on enum
-  --> $DIR/enum_validation.rs:29:15
+  --> tests/test_compile/cases/enum_validation.rs:29:15
    |
 29 | #[deku(bits = 1)]
    |               ^
 
 error: `bits` only supported on enum
-  --> $DIR/enum_validation.rs:36:15
+  --> tests/test_compile/cases/enum_validation.rs:36:15
    |
 36 | #[deku(bits = 1)]
    |               ^
 
-error: `id` only supported on enum
-  --> $DIR/enum_validation.rs:43:13
+error: `id_type` only supported on enum
+  --> tests/test_compile/cases/enum_validation.rs:43:18
    |
-43 | #[deku(id = "test")]
-   |             ^^^^^^
+43 | #[deku(id_type = "test")]
+   |                  ^^^^^^
 
-error: error: cannot use `bits` with `id`
-  --> $DIR/enum_validation.rs:51:6
+error: DekuRead: `id` must be specified on non-unit variants
+  --> tests/test_compile/cases/enum_validation.rs:52:5
    |
-51 | enum Test8 {
-   |      ^^^^^
+52 |     A,
+   |     ^
 
-error: error: cannot use `bytes` with `id`
-  --> $DIR/enum_validation.rs:58:6
+error: DekuRead: `id` must be specified on non-unit variants
+  --> tests/test_compile/cases/enum_validation.rs:59:5
    |
-58 | enum Test9 {
-   |      ^^^^^
+59 |     A,
+   |     ^
 
 error: error: `id_pat` should be used for `_`
-  --> $DIR/enum_validation.rs:67:5
+  --> tests/test_compile/cases/enum_validation.rs:67:5
    |
 67 |     A,
    |     ^
 
 error: DekuRead: `id` must be specified on non-unit variants
-  --> $DIR/enum_validation.rs:76:5
+  --> tests/test_compile/cases/enum_validation.rs:76:5
    |
 76 |     B(u8),
    |     ^
diff --git a/tests/test_compile/cases/internal_variables.rs b/tests/test_compile/cases/internal_variables.rs
index 0108c68..561f910 100644
--- a/tests/test_compile/cases/internal_variables.rs
+++ b/tests/test_compile/cases/internal_variables.rs
@@ -1,4 +1,3 @@
-use deku::bitvec::{BitVec, Msb0};
 use deku::prelude::*;
 
 #[derive(DekuRead, DekuWrite)]
@@ -73,13 +72,13 @@ struct TestCtx {
     field_b: ChildCtx,
 }
 
-fn dummy_writer(_offset: usize, _output: &mut BitVec<u8, Msb0>) -> Result<(), DekuError> {
+fn dummy_writer<W: std::io::Write>(_offset: usize, _writer: &mut deku::writer::Writer<W>) -> Result<(), DekuError> {
     Ok(())
 }
 #[derive(DekuRead, DekuWrite)]
 struct TestWriter {
     field_a: u8,
-    #[deku(writer = "dummy_writer(deku::byte_offset, deku::output)")]
+    #[deku(writer = "dummy_writer(deku::byte_offset, deku::writer)")]
     field_b: usize,
 }
 
diff --git a/tests/test_compile/cases/internal_variables.stderr b/tests/test_compile/cases/internal_variables.stderr
index 891a145..7853121 100644
--- a/tests/test_compile/cases/internal_variables.stderr
+++ b/tests/test_compile/cases/internal_variables.stderr
@@ -1,5 +1,5 @@
 error: Unexpected meta-item format `attribute cannot contain `__deku_` these are internal variables. Please use the `deku::` instead.`
-  --> tests/test_compile/cases/internal_variables.rs:89:19
+  --> tests/test_compile/cases/internal_variables.rs:88:19
    |
-89 |     #[deku(cond = "__deku_bit_offset == *field_a as usize")]
+88 |     #[deku(cond = "__deku_bit_offset == *field_a as usize")]
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/test_compile/cases/temp_field.stderr b/tests/test_compile/cases/temp_field.stderr
index 86b0716..b64d3d8 100644
--- a/tests/test_compile/cases/temp_field.stderr
+++ b/tests/test_compile/cases/temp_field.stderr
@@ -1,5 +1,5 @@
 error[E0063]: missing field `field_a` in initializer of `Test1`
- --> $DIR/temp_field.rs:4:10
+ --> tests/test_compile/cases/temp_field.rs:4:10
   |
 4 | #[derive(DekuRead, DekuWrite)]
   |          ^^^^^^^^ missing `field_a`
@@ -7,7 +7,7 @@ error[E0063]: missing field `field_a` in initializer of `Test1`
   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: pattern requires `..` due to inaccessible fields
- --> $DIR/temp_field.rs:4:20
+ --> tests/test_compile/cases/temp_field.rs:4:20
   |
 4 | #[derive(DekuRead, DekuWrite)]
   |                    ^^^^^^^^^
diff --git a/tests/test_compile/cases/unknown_endian.rs b/tests/test_compile/cases/unknown_endian.rs
index de47726..6d787a3 100644
--- a/tests/test_compile/cases/unknown_endian.rs
+++ b/tests/test_compile/cases/unknown_endian.rs
@@ -13,11 +13,11 @@ struct Test2 {
 }
 
 #[derive(DekuRead)]
-#[deku(type = "u8", endian = "variable")]
+#[deku(id_type = "u8", endian = "variable")]
 enum Test3 {}
 
 #[derive(DekuRead)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum Test4 {
     #[deku(id = "1")]
     A(#[deku(endian = "variable")] u8),
diff --git a/tests/test_deku_id.rs b/tests/test_deku_id.rs
index 6d0bc1d..633847f 100644
--- a/tests/test_deku_id.rs
+++ b/tests/test_deku_id.rs
@@ -3,7 +3,7 @@ use deku::prelude::*;
 #[test]
 fn test_regular() {
     #[derive(Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum Request1 {
         #[deku(id = "0x01")]
         Cats { toy: u8 },
@@ -19,7 +19,7 @@ fn test_regular() {
 #[test]
 fn test_custom_type() {
     #[derive(Debug, DekuRead, PartialEq, DekuWrite)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum Request2 {
         #[deku(id = "0x01")]
         Cats,
@@ -29,7 +29,7 @@ fn test_custom_type() {
     }
 
     #[derive(Debug, DekuRead, DekuWrite)]
-    #[deku(type = "Request2")]
+    #[deku(id_type = "Request2")]
     enum Request3 {
         #[deku(id = "Request2::Cats")]
         Cats,
@@ -56,7 +56,7 @@ fn test_ctx() {
     assert_eq!(Ok(1), EnumId::VarA(0).deku_id());
 
     #[derive(Copy, Clone, PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum Nice {
         True = 0x00,
         False = 0x01,
@@ -78,7 +78,7 @@ fn test_ctx() {
 #[test]
 fn test_ctx_and_type() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u8", ctx = "_a: u8, _b: u8")]
+    #[deku(id_type = "u8", ctx = "_a: u8, _b: u8")]
     enum TopLevelCtxEnum {
         #[deku(id = "1")]
         VariantA(u8),
@@ -90,7 +90,7 @@ fn test_ctx_and_type() {
 #[test]
 fn test_litbytestr() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "[u8; 3]")]
+    #[deku(id_type = "[u8; 3]")]
     enum TestEnumArray {
         #[deku(id = b"123")]
         VarA,
@@ -105,7 +105,7 @@ fn test_litbytestr() {
 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: IdVariantNotFound")]
 fn test_no_id_discriminant() {
     #[derive(Debug, DekuRead, PartialEq, DekuWrite)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum Discriminant {
         Cats = 0x01,
         Dogs,
diff --git a/tests/test_enum.rs b/tests/test_enum.rs
index a3ec4d7..cbe3947 100644
--- a/tests/test_enum.rs
+++ b/tests/test_enum.rs
@@ -8,7 +8,7 @@ use rstest::*;
 /// TODO: These should be divided into smaller tests
 
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum TestEnum {
     #[deku(id = "1")]
     VarA(u8),
@@ -54,7 +54,7 @@ fn test_enum(input: &[u8], expected: TestEnum) {
 #[should_panic(expected = "Parse(\"Could not match enum variant id = 2 on enum `TestEnum`\")")]
 fn test_enum_error() {
     #[derive(DekuRead)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum TestEnum {
         #[deku(id = "1")]
         VarA(u8),
@@ -65,7 +65,7 @@ fn test_enum_error() {
 }
 
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum TestEnumDiscriminant {
     VarA = 0x00,
     VarB,
@@ -92,7 +92,7 @@ fn test_enum_discriminant(input: &[u8], expected: TestEnumDiscriminant) {
 #[test]
 fn test_enum_array_type() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "[u8; 3]")]
+    #[deku(id_type = "[u8; 3]")]
     enum TestEnumArray {
         #[deku(id = b"123")]
         VarA,
diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs
index b40e529..a992c5b 100644
--- a/tests/test_from_bytes.rs
+++ b/tests/test_from_bytes.rs
@@ -31,7 +31,7 @@ fn test_from_bytes_struct() {
 #[test]
 fn test_from_bytes_enum() {
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = 4)]
+    #[deku(id_type = "u8", bits = 4)]
     enum TestDeku {
         #[deku(id = "0b0110")]
         VariantA(#[deku(bits = 4)] u8),
diff --git a/tests/test_from_reader.rs b/tests/test_from_reader.rs
index eb7780f..02c927d 100644
--- a/tests/test_from_reader.rs
+++ b/tests/test_from_reader.rs
@@ -37,7 +37,7 @@ fn test_from_reader_struct() {
 #[test]
 fn test_from_reader_enum() {
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = 4)]
+    #[deku(id_type = "u8", bits = 4)]
     enum TestDeku {
         #[deku(id = "0b0110")]
         VariantA(#[deku(bits = 4)] u8),
diff --git a/tests/test_generic.rs b/tests/test_generic.rs
index dfb3b8f..a3826dc 100644
--- a/tests/test_generic.rs
+++ b/tests/test_generic.rs
@@ -7,7 +7,7 @@ fn test_generic_struct() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
     struct TestStruct<T>
     where
-        T: deku::DekuWrite + for<'a> deku::DekuReader<'a>,
+        T: deku::DekuWriter + for<'a> deku::DekuReader<'a>,
     {
         field_a: T,
     }
@@ -24,10 +24,10 @@ fn test_generic_struct() {
 #[test]
 fn test_generic_enum() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(type = "u8")]
+    #[deku(id_type = "u8")]
     enum TestEnum<T>
     where
-        T: deku::DekuWrite + for<'a> deku::DekuReader<'a>,
+        T: deku::DekuWriter + for<'a> deku::DekuReader<'a>,
     {
         #[deku(id = "1")]
         VariantT(T),
diff --git a/tests/test_magic.rs b/tests/test_magic.rs
index 29d55c8..b87f017 100644
--- a/tests/test_magic.rs
+++ b/tests/test_magic.rs
@@ -58,7 +58,7 @@ fn test_magic_struct(input: &[u8]) {
 )]
 fn test_magic_enum(input: &[u8]) {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(magic = b"deku", type = "u8")]
+    #[deku(magic = b"deku", id_type = "u8")]
     enum TestEnum {
         #[deku(id = "0")]
         Variant,
diff --git a/tests/test_regression.rs b/tests/test_regression.rs
index 84ff990..57e5ac4 100644
--- a/tests/test_regression.rs
+++ b/tests/test_regression.rs
@@ -18,7 +18,7 @@ fn issue_224() {
     }
 
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = 2)]
+    #[deku(id_type = "u8", bits = 2)]
     pub enum One {
         Start = 0,
         Go = 1,
@@ -26,7 +26,7 @@ fn issue_224() {
     }
 
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = 4)]
+    #[deku(id_type = "u8", bits = 4)]
     pub enum Two {
         #[deku(id = "0b0000")]
         Put(Op1),
@@ -370,7 +370,7 @@ fn issue_397() {
     }
 
     #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", endian = "big")]
+    #[deku(id_type = "u8", endian = "big")]
     enum PacketType {
         #[deku(id = 0)]
         Zero,
diff --git a/tests/test_struct.rs b/tests/test_struct.rs
index 9880309..41e3b3d 100644
--- a/tests/test_struct.rs
+++ b/tests/test_struct.rs
@@ -179,3 +179,28 @@ fn test_raw_identifiers_struct() {
     let ret_write: Vec<u8> = ret_read.try_into().unwrap();
     assert_eq!(test_data, ret_write);
 }
+
+#[test]
+fn test_big_endian() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    pub struct A {
+        #[deku(bytes = "3", endian = "big")]
+        address: u32,
+    }
+
+    let bytes = [0x11, 0x22, 0x33];
+    let a = A::from_bytes((&bytes, 0)).unwrap().1;
+    let new_bytes = a.to_bytes().unwrap();
+    assert_eq!(bytes, &*new_bytes);
+
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    pub struct B {
+        #[deku(bytes = "2", endian = "big")]
+        address: u32,
+    }
+
+    let bytes = [0x00, 0xff, 0xab, 0xaa];
+    let a = B::from_bytes((&bytes, 0)).unwrap().1;
+    let new_bytes = a.to_bytes().unwrap();
+    assert_eq!(&bytes[..2], &*new_bytes);
+}
diff --git a/tests/test_to_bits.rs b/tests/test_to_bits.rs
new file mode 100644
index 0000000..1515a40
--- /dev/null
+++ b/tests/test_to_bits.rs
@@ -0,0 +1,53 @@
+use std::convert::TryFrom;
+
+use deku::bitvec::Lsb0;
+use deku::prelude::*;
+
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+pub struct Test {
+    #[deku(bits = "4")]
+    pub a: u8,
+    #[deku(bits = "4")]
+    pub b: u8,
+}
+
+#[test]
+fn test_to_bits_correct() {
+    let test_data: &[u8] = &[0xf1];
+    let test = Test::try_from(test_data).unwrap();
+    let bits = test.to_bits().unwrap();
+    assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1], bits);
+}
+
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+pub struct TestOver {
+    #[deku(bits = "4")]
+    pub a: u8,
+    #[deku(bits = "4")]
+    pub b: u8,
+    #[deku(bits = "1")]
+    pub c: u8,
+}
+
+#[test]
+fn test_to_bits_correct_over() {
+    let test_data: &[u8] = &[0xf1, 0x80];
+    let test = TestOver::from_bytes((test_data, 0)).unwrap().1;
+    let bits = test.to_bits().unwrap();
+    assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits);
+}
+
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+#[deku(id_type = "u8", bits = "4")]
+enum TestEnum {
+    #[deku(id = "0b1010")]
+    VarA,
+}
+
+#[test]
+fn test_to_bits_enum() {
+    let test_data: &[u8] = &[0b1010_0000];
+    let test = TestEnum::from_bytes((test_data, 0)).unwrap().1;
+    let bits = test.to_bits().unwrap();
+    assert_eq!(deku::bitvec::bitvec![1, 0, 1, 0], bits);
+}
diff --git a/tests/test_tuple.rs b/tests/test_tuple.rs
index be95529..7c38bb7 100644
--- a/tests/test_tuple.rs
+++ b/tests/test_tuple.rs
@@ -5,7 +5,7 @@ use hexlit::hex;
 use rstest::*;
 
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
 enum TestEnum {
     #[deku(id = "1")]
     VarA((u8, u16)),