Repository: sharksforarms/deku
Pull request #355 (2023-08-18 22:37:44)

Impl writer
Implement faster writing for bytes and support for `Write`. At the expense of some performance for only bit-wise writing.

# Tested
- https://github.com/wcampbell0x2a/backhand/pull/524
- https://github.com/wcampbell0x2a/asterix-rs/pull/9
- https://github.com/rsadsb/adsb_deku/pull/212
- https://github.com/wcampbell0x2a/cpio-deku

## Create Issue
- It would be nice to specialize for `&[u8]` and `Vec<u8>` so that results directly to one invocation of `writer.write_bytes(..)` instead of 1 byte per len. This is only possible for `Ctx = ()`?

See https://github.com/wcampbell0x2a/deku-bench/pull/6 (that uses https://github.com/sharksforarms/deku/pull/393)

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **725.8±0.70ns** | 776.2±0.91ns | **+6.94%** |
| deku_read_byte | **25.7±1.32ns** | 25.8±0.07ns | **+0.39%** |
| deku_read_enum | 10.7±0.10ns | **10.4±0.08ns** | **-2.80%** |
| deku_read_vec | 71.4±0.33ns | **65.3±0.13ns** | **-8.54%** |
| deku_write_bits | **102.8±0.44ns** | 268.7±1.57ns | **+161.38%** |
| deku_write_byte | 152.4±0.59ns | **37.7±0.17ns** | **-75.26%** |
| deku_write_enum | 101.5±0.49ns | **31.8±0.27ns** | **-68.67%** |
| deku_write_vec | 4.0±0.01µs | **523.4±2.79ns** | **-86.91%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **937.3±50.25ns** | 1014.1±58.33ns | **+8.19%** |
| deku_read_byte | **26.7±1.59ns** | 30.2±3.09ns | **+13.11%** |
| deku_read_enum | 12.7±0.88ns | 12.4±0.93ns | -2.36% |
| deku_read_vec | 75.5±4.17ns | 75.1±4.56ns | -0.53% |
| deku_write_bits | **132.7±9.29ns** | 357.4±24.82ns | **+169.33%** |
| deku_write_byte | 189.2±11.39ns | **41.5±2.83ns** | **-78.07%** |
| deku_write_enum | 122.8±6.30ns | **34.6±2.19ns** | **-71.82%** |
| deku_write_vec | 4.7±0.26µs | **577.2±119.10ns** | **-87.72%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1103.8±70.05ns | 1134.2±67.78ns | +2.75% |
| deku_read_byte | 31.0±1.20ns | 31.5±1.67ns | +1.61% |
| deku_read_enum | **14.4±0.69ns** | 15.1±1.91ns | **+4.86%** |
| deku_read_vec | 89.4±5.26ns | 88.4±5.63ns | -1.12% |
| deku_write_bits | **151.0±6.56ns** | 326.1±26.75ns | **+115.96%** |
| deku_write_byte | 219.6±22.20ns | **48.9±2.14ns** | **-77.73%** |
| deku_write_enum | 145.0±15.60ns | **42.1±1.32ns** | **-70.97%** |
| deku_write_vec | 5.4±0.31µs | **692.7±31.56ns** | **-87.17%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1052.9±1.05ns** | 1084.6±10.88ns | **+3.01%** |
| deku_read_byte | **31.3±0.72ns** | 33.0±0.50ns | **+5.43%** |
| deku_read_enum | **13.7±0.02ns** | 15.1±0.11ns | **+10.22%** |
| deku_read_vec | **83.3±0.45ns** | 83.9±0.17ns | **+0.72%** |
| deku_write_bits | **147.6±0.07ns** | 299.5±0.37ns | **+102.91%** |
| deku_write_byte | 202.0±1.07ns | **52.0±0.29ns** | **-74.26%** |
| deku_write_enum | 134.9±0.56ns | **45.4±0.12ns** | **-66.35%** |
| deku_write_vec | 5.1±0.02µs | **591.8±0.37ns** | **-88.40%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1053.9±1.30ns** | 1091.9±1.35ns | **+3.61%** |
| deku_read_byte | 31.1±0.14ns | **30.4±0.07ns** | **-2.25%** |
| deku_read_enum | **13.8±0.03ns** | 15.4±0.11ns | **+11.59%** |
| deku_read_vec | 83.1±0.25ns | **82.8±0.25ns** | **-0.36%** |
| deku_write_bits | **147.9±0.16ns** | 297.7±0.29ns | **+101.28%** |
| deku_write_byte | 201.8±2.31ns | **54.8±0.36ns** | **-72.84%** |
| deku_write_enum | 134.6±0.58ns | **46.3±0.18ns** | **-65.60%** |
| deku_write_vec | 5.1±0.02µs | **589.2±0.44ns** | **-88.45%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 779.1±0.85ns | **775.2±0.50ns** | **-0.50%** |
| deku_read_byte | **23.7±0.74ns** | 23.9±0.08ns | **+0.84%** |
| deku_read_enum | **9.9±0.07ns** | 10.0±0.31ns | **+1.01%** |
| deku_read_vec | 67.7±0.67ns | **67.3±0.13ns** | **-0.59%** |
| deku_write_bits | **109.4±0.51ns** | 251.6±1.02ns | **+129.98%** |
| deku_write_byte | 156.0±0.63ns | **42.6±0.18ns** | **-72.69%** |
| deku_write_enum | 106.9±0.90ns | **35.5±0.27ns** | **-66.79%** |
| deku_write_vec | 3.9±0.01µs | **622.0±9.60ns** | **-84.05%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **893.0±0.58ns** | 904.3±2.09ns | **+1.27%** |
| deku_read_byte | **23.0±0.16ns** | 24.8±0.18ns | **+7.83%** |
| deku_read_enum | 11.2±0.07ns | **11.1±0.09ns** | **-0.89%** |
| deku_read_vec | 68.7±0.14ns | 68.8±0.19ns | +0.15% |
| deku_write_bits | **127.4±0.09ns** | 251.1±1.50ns | **+97.10%** |
| deku_write_byte | 182.2±1.07ns | **43.6±0.28ns** | **-76.07%** |
| deku_write_enum | 119.2±0.48ns | **37.6±0.13ns** | **-68.46%** |
| deku_write_vec | 4.6±0.00µs | **507.2±2.27ns** | **-88.97%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 832.6±36.62ns | 860.2±48.40ns | +3.31% |
| deku_read_byte | **21.6±0.98ns** | 24.4±1.15ns | **+12.96%** |
| deku_read_enum | 11.3±0.56ns | **10.5±0.68ns** | **-7.08%** |
| deku_read_vec | 64.3±2.52ns | 64.9±2.73ns | +0.93% |
| deku_write_bits | **118.8±6.93ns** | 231.4±16.04ns | **+94.78%** |
| deku_write_byte | 172.5±8.25ns | **43.0±2.28ns** | **-75.07%** |
| deku_write_enum | 113.4±6.13ns | **36.8±1.66ns** | **-67.55%** |
| deku_write_vec | 4.3±0.22µs | **441.7±16.68ns** | **-89.73%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **776.4±1.38ns** | 781.3±0.62ns | **+0.63%** |
| deku_read_byte | **24.0±0.44ns** | 25.6±0.11ns | **+6.67%** |
| deku_read_enum | **10.1±0.09ns** | 10.2±0.08ns | **+0.99%** |
| deku_read_vec | **67.5±0.30ns** | 67.8±0.21ns | **+0.44%** |
| deku_write_bits | **106.5±0.44ns** | 228.2±7.17ns | **+114.27%** |
| deku_write_byte | 165.0±1.08ns | **40.8±0.19ns** | **-75.27%** |
| deku_write_enum | 105.7±0.46ns | **35.2±0.64ns** | **-66.70%** |
| deku_write_vec | 4.0±0.01µs | **509.7±7.92ns** | **-87.26%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1872.6±125.63ns | 1867.9±79.36ns | -0.25% |
| deku_read_byte | 26.6±1.25ns | 26.2±0.95ns | -1.50% |
| deku_read_enum | 12.4±0.78ns | 12.1±0.49ns | -2.42% |
| deku_read_vec | **73.6±2.35ns** | 76.2±5.11ns | **+3.53%** |
| deku_write_bits | **175.5±7.25ns** | 319.2±14.09ns | **+81.88%** |
| deku_write_byte | 193.4±5.91ns | **43.7±1.28ns** | **-77.40%** |
| deku_write_enum | 131.1±5.37ns | **37.9±1.17ns** | **-71.09%** |
| deku_write_vec | 4.5±0.26µs | **532.5±23.09ns** | **-88.17%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1404.3±1.14ns | **1402.9±7.37ns** | **-0.10%** |
| deku_read_byte | **25.4±0.22ns** | 25.9±0.11ns | **+1.97%** |
| deku_read_enum | **9.8±0.06ns** | 10.2±0.10ns | **+4.08%** |
| deku_read_vec | **66.0±0.28ns** | 70.3±0.36ns | **+6.52%** |
| deku_write_bits | **140.3±0.31ns** | 250.0±0.48ns | **+78.19%** |
| deku_write_byte | 158.0±0.58ns | **40.7±0.17ns** | **-74.24%** |
| deku_write_enum | 105.4±0.51ns | **34.5±0.17ns** | **-67.27%** |
| deku_write_vec | 3.9±0.01µs | **415.1±1.76ns** | **-89.36%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 2.0±0.01µs | 2.0±0.02µs | 0.00% |
| deku_read_byte | 33.0±1.97ns | **30.3±0.14ns** | **-8.18%** |
| deku_read_enum | **13.4±0.13ns** | 25.5±0.05ns | **+90.30%** |
| deku_read_vec | 83.3±3.20ns | **81.3±0.32ns** | **-2.40%** |
| deku_write_bits | **193.8±1.25ns** | 350.4±0.45ns | **+80.80%** |
| deku_write_byte | 207.3±1.13ns | **53.6±0.47ns** | **-74.14%** |
| deku_write_enum | 140.2±1.08ns | **48.2±0.64ns** | **-65.62%** |
| deku_write_vec | 5.3±0.02µs | **448.2±0.53ns** | **-91.54%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 2.2±0.10µs | 2.3±0.16µs | +4.55% |
| deku_read_byte | 32.5±1.91ns | 32.7±1.48ns | +0.62% |
| deku_read_enum | 14.4±0.89ns | 14.8±1.17ns | +2.78% |
| deku_read_vec | 88.3±7.06ns | 93.8±6.03ns | +6.23% |
| deku_write_bits | **214.6±11.27ns** | 401.0±16.73ns | **+86.86%** |
| deku_write_byte | 242.0±15.10ns | **52.9±1.98ns** | **-78.14%** |
| deku_write_enum | 157.1±5.69ns | **46.6±3.62ns** | **-70.34%** |
| deku_write_vec | 5.6±0.35µs | **526.2±27.53ns** | **-90.60%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1187.2±11.38ns** | 1297.4±17.88ns | **+9.28%** |
| deku_read_byte | 21.7±0.24ns | 21.6±0.34ns | -0.46% |
| deku_read_enum | **9.3±0.16ns** | 9.6±0.65ns | **+3.23%** |
| deku_read_vec | **57.7±0.75ns** | 58.6±0.89ns | **+1.56%** |
| deku_write_bits | **128.4±1.16ns** | 209.9±3.56ns | **+63.47%** |
| deku_write_byte | 136.4±4.24ns | **36.5±1.56ns** | **-73.24%** |
| deku_write_enum | 92.3±5.37ns | **30.7±0.33ns** | **-66.74%** |
| deku_write_vec | 3.2±0.15µs | **276.4±3.20ns** | **-91.36%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1217.2±12.65ns** | 1266.0±15.37ns | **+4.01%** |
| deku_read_byte | 21.5±0.48ns | 21.7±0.53ns | +0.93% |
| deku_read_enum | **9.3±0.10ns** | 9.5±0.70ns | **+2.15%** |
| deku_read_vec | **58.1±0.57ns** | 59.0±1.58ns | **+1.55%** |
| deku_write_bits | **155.5±4.64ns** | 200.1±5.83ns | **+28.68%** |
| deku_write_byte | 126.2±10.88ns | **20.8±0.31ns** | **-83.52%** |
| deku_write_enum | 83.7±1.78ns | **20.8±0.38ns** | **-75.15%** |
| deku_write_vec | 3.2±0.10µs | **300.7±3.34ns** | **-90.60%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1192.9±32.02ns** | 1235.1±17.87ns | **+3.54%** |
| deku_read_byte | 21.7±2.93ns | 21.7±1.29ns | 0.00% |
| deku_read_enum | 9.5±0.11ns | 9.4±0.11ns | -1.05% |
| deku_read_vec | 59.5±0.46ns | **58.5±0.78ns** | **-1.68%** |
| deku_write_bits | **151.7±4.27ns** | 245.3±28.80ns | **+61.70%** |
| deku_write_byte | 146.7±4.59ns | **21.1±0.20ns** | **-85.62%** |
| deku_write_enum | 96.9±4.03ns | **20.2±0.29ns** | **-79.15%** |
| deku_write_vec | 3.2±0.05µs | **299.1±3.78ns** | **-90.65%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1286.2±10.01ns | **1224.2±17.10ns** | **-4.82%** |
| deku_read_byte | 21.8±0.44ns | 21.7±0.22ns | -0.46% |
| deku_read_enum | 9.3±0.11ns | 9.4±0.24ns | +1.08% |
| deku_read_vec | 59.6±0.68ns | **58.8±0.78ns** | **-1.34%** |
| deku_write_bits | **140.4±4.96ns** | 204.6±6.64ns | **+45.73%** |
| deku_write_byte | 132.6±3.39ns | **20.8±0.26ns** | **-84.31%** |
| deku_write_enum | 85.4±4.49ns | **20.2±0.43ns** | **-76.35%** |
| deku_write_vec | 3.2±0.03µs | **293.8±3.75ns** | **-90.82%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1265.2±29.15ns | **1202.0±47.65ns** | **-5.00%** |
| deku_read_byte | 21.8±0.58ns | 21.8±0.57ns | 0.00% |
| deku_read_enum | 9.3±0.24ns | 9.4±0.10ns | +1.08% |
| deku_read_vec | 59.9±0.60ns | **58.8±0.56ns** | **-1.84%** |
| deku_write_bits | **129.6±2.22ns** | 205.2±5.37ns | **+58.33%** |
| deku_write_byte | 129.9±2.23ns | **20.9±0.67ns** | **-83.91%** |
| deku_write_enum | 87.2±11.64ns | **20.2±0.31ns** | **-76.83%** |
| deku_write_vec | 3.2±0.05µs | **294.3±3.82ns** | **-90.80%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1246.6±7.15ns** | 1301.1±16.34ns | **+4.37%** |
| deku_read_byte | 21.7±0.43ns | 21.8±0.40ns | +0.46% |
| deku_read_enum | 9.3±0.42ns | 9.4±0.16ns | +1.08% |
| deku_read_vec | 60.2±0.63ns | **58.8±0.81ns** | **-2.33%** |
| deku_write_bits | **144.1±5.12ns** | 203.7±4.61ns | **+41.36%** |
| deku_write_byte | 129.8±2.72ns | **20.8±0.42ns** | **-83.98%** |
| deku_write_enum | 84.9±2.19ns | **20.2±0.35ns** | **-76.21%** |
| deku_write_vec | 3.2±0.06µs | **293.9±2.04ns** | **-90.82%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1250.9±16.75ns | **1210.9±12.81ns** | **-3.20%** |
| deku_read_byte | 21.7±0.15ns | 21.7±0.20ns | 0.00% |
| deku_read_enum | 9.5±0.15ns | 9.6±0.14ns | +1.05% |
| deku_read_vec | 59.4±0.43ns | **58.0±1.19ns** | **-2.36%** |
| deku_write_bits | **158.9±3.63ns** | 207.1±14.57ns | **+30.33%** |
| deku_write_byte | 137.8±5.31ns | **21.1±0.14ns** | **-84.69%** |
| deku_write_enum | 91.9±3.93ns | **20.2±0.35ns** | **-78.02%** |
| deku_write_vec | 3.2±0.04µs | **300.7±4.06ns** | **-90.60%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1217.2±17.43ns | 1218.3±29.25ns | +0.09% |
| deku_read_byte | 21.4±0.88ns | 21.1±0.76ns | -1.40% |
| deku_read_enum | 9.5±0.14ns | **9.3±0.26ns** | **-2.11%** |
| deku_read_vec | 59.6±0.47ns | **58.9±0.60ns** | **-1.17%** |
| deku_write_bits | **143.9±5.64ns** | 210.0±11.87ns | **+45.93%** |
| deku_write_byte | 144.4±3.25ns | **20.9±0.48ns** | **-85.53%** |
| deku_write_enum | 106.7±4.84ns | **19.9±0.28ns** | **-81.35%** |
| deku_write_vec | 3.2±0.02µs | **299.1±4.77ns** | **-90.65%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1227.2±9.79ns** | 1350.9±11.07ns | **+10.08%** |
| deku_read_byte | 21.4±0.87ns | 21.4±0.23ns | 0.00% |
| deku_read_enum | 9.5±0.17ns | 9.5±0.14ns | 0.00% |
| deku_read_vec | 59.5±0.76ns | **58.3±0.75ns** | **-2.02%** |
| deku_write_bits | **139.7±4.48ns** | 206.6±3.32ns | **+47.89%** |
| deku_write_byte | 144.0±3.88ns | **20.8±0.17ns** | **-85.56%** |
| deku_write_enum | 84.7±2.29ns | **19.9±0.21ns** | **-76.51%** |
| deku_write_vec | 3.2±0.07µs | **299.6±2.60ns** | **-90.64%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1239.9±17.79ns | **1222.2±12.21ns** | **-1.43%** |
| deku_read_byte | **20.9±0.86ns** | 21.4±0.27ns | **+2.39%** |
| deku_read_enum | 9.6±0.15ns | **9.3±0.15ns** | **-3.12%** |
| deku_read_vec | **57.4±0.46ns** | 59.4±0.60ns | **+3.48%** |
| deku_write_bits | **125.4±7.93ns** | 199.9±6.87ns | **+59.41%** |
| deku_write_byte | 130.7±5.69ns | **21.4±0.35ns** | **-83.63%** |
| deku_write_enum | 90.5±5.86ns | **20.5±0.33ns** | **-77.35%** |
| deku_write_vec | 3.1±0.04µs | **263.6±2.55ns** | **-91.50%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1175.9±22.33ns | 1198.2±30.79ns | +1.90% |
| deku_read_byte | 21.1±0.83ns | 21.5±0.47ns | +1.90% |
| deku_read_enum | 9.6±0.18ns | 9.5±0.21ns | -1.04% |
| deku_read_vec | **57.6±0.81ns** | 59.5±0.70ns | **+3.30%** |
| deku_write_bits | **125.1±3.90ns** | 211.3±4.61ns | **+68.90%** |
| deku_write_byte | 135.7±4.66ns | **21.4±0.23ns** | **-84.23%** |
| deku_write_enum | 92.5±5.42ns | **20.8±0.35ns** | **-77.51%** |
| deku_write_vec | 3.1±0.04µs | **291.7±4.34ns** | **-90.59%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1238.6±14.99ns | **1207.0±46.87ns** | **-2.55%** |
| deku_read_byte | 20.9±0.40ns | 20.9±0.37ns | 0.00% |
| deku_read_enum | 9.5±0.14ns | **9.3±0.15ns** | **-2.11%** |
| deku_read_vec | **57.4±0.53ns** | 59.8±0.89ns | **+4.18%** |
| deku_write_bits | **126.3±3.10ns** | 195.1±3.94ns | **+54.47%** |
| deku_write_byte | 128.4±9.55ns | **21.4±0.19ns** | **-83.33%** |
| deku_write_enum | 94.4±3.11ns | **20.5±0.23ns** | **-78.28%** |
| deku_write_vec | 3.1±0.06µs | **293.0±3.06ns** | **-90.55%** |

  </details>
  
wcampbell0x2a: @sharksforarms this is ready for review!
github-actions[bot]: ## Benchmark for 19ac421
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1304.6±16.95ns | **1231.1±10.18ns** | **-5.63%** |
| deku_read_byte | 21.0±0.94ns | 21.2±0.49ns | +0.95% |
| deku_read_enum | 9.6±0.16ns | **9.3±0.09ns** | **-3.12%** |
| deku_read_vec | **57.6±0.50ns** | 59.3±0.43ns | **+2.95%** |
| deku_write_bits | **134.7±5.21ns** | 196.6±3.72ns | **+45.95%** |
| deku_write_byte | 134.7±5.09ns | **21.6±0.31ns** | **-83.96%** |
| deku_write_enum | 95.5±6.96ns | **20.5±0.32ns** | **-78.53%** |
| deku_write_vec | 3.1±0.04µs | **315.2±3.88ns** | **-89.83%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1216.1±9.56ns** | 1278.5±12.77ns | **+5.13%** |
| deku_read_byte | **20.8±0.35ns** | 21.2±0.49ns | **+1.92%** |
| deku_read_enum | 9.6±0.19ns | 9.5±0.16ns | -1.04% |
| deku_read_vec | **57.4±0.87ns** | 59.2±0.45ns | **+3.14%** |
| deku_write_bits | **125.2±5.48ns** | 199.4±22.62ns | **+59.27%** |
| deku_write_byte | 130.7±3.47ns | **21.4±0.38ns** | **-83.63%** |
| deku_write_enum | 95.6±3.05ns | **20.5±0.39ns** | **-78.56%** |
| deku_write_vec | 3.1±0.04µs | **389.6±2.86ns** | **-87.43%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1274.3±20.31ns | **1240.7±25.69ns** | **-2.64%** |
| deku_read_byte | **20.8±0.20ns** | 21.5±0.35ns | **+3.37%** |
| deku_read_enum | 9.5±0.28ns | 9.5±0.11ns | 0.00% |
| deku_read_vec | **57.5±0.89ns** | 59.2±0.86ns | **+2.96%** |
| deku_write_bits | **131.3±4.15ns** | 195.2±3.50ns | **+48.67%** |
| deku_write_byte | 131.3±3.96ns | **21.4±0.22ns** | **-83.70%** |
| deku_write_enum | 93.5±4.12ns | **20.6±0.52ns** | **-77.97%** |
| deku_write_vec | 3.1±0.08µs | **297.8±4.18ns** | **-90.39%** |

  </details>
  
sharksforarms: @wcampbell0x2a ack. I may not get to review it until the new year 
wcampbell0x2a: Butterfingers closing the mr 😂

No problem! I also might bike-shed the names back to `read` and `write` instead of `to_writer`. Idk.
github-actions[bot]: ## Benchmark for e323734
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1213.5±37.96ns | 1226.0±16.02ns | +1.03% |
| deku_read_byte | 21.1±0.45ns | 21.1±0.53ns | 0.00% |
| deku_read_enum | 9.6±0.15ns | 9.5±0.14ns | -1.04% |
| deku_read_vec | **58.2±0.44ns** | 59.1±0.43ns | **+1.55%** |
| deku_write_bits | **125.9±4.66ns** | 194.4±2.03ns | **+54.41%** |
| deku_write_byte | 130.4±6.15ns | **21.5±0.41ns** | **-83.51%** |
| deku_write_enum | 90.7±5.51ns | **20.6±1.35ns** | **-77.29%** |
| deku_write_vec | 3.1±0.07µs | **293.2±7.28ns** | **-90.54%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1102.4±10.87ns** | 1212.6±30.33ns | **+10.00%** |
| deku_read_byte | **19.3±0.35ns** | 20.1±0.28ns | **+4.15%** |
| deku_read_enum | 9.4±0.13ns | 9.5±0.16ns | +1.06% |
| deku_read_vec | 54.1±1.11ns | 54.0±0.75ns | -0.18% |
| deku_write_bits | **113.4±1.58ns** | 179.5±3.53ns | **+58.29%** |
| deku_write_byte | 124.3±2.85ns | **21.3±0.33ns** | **-82.86%** |
| deku_write_enum | 86.3±5.15ns | **20.2±0.31ns** | **-76.59%** |
| deku_write_vec | 3.0±0.05µs | **264.1±1.73ns** | **-91.20%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1168.8±9.71ns** | 1245.5±18.43ns | **+6.56%** |
| deku_read_byte | **18.9±0.39ns** | 20.2±0.59ns | **+6.88%** |
| deku_read_enum | 9.3±0.17ns | 9.4±0.17ns | +1.08% |
| deku_read_vec | 53.3±0.33ns | 53.3±0.53ns | 0.00% |
| deku_write_bits | **112.1±1.18ns** | 187.7±4.28ns | **+67.44%** |
| deku_write_byte | 125.3±3.64ns | **21.2±0.39ns** | **-83.08%** |
| deku_write_enum | 89.4±5.12ns | **20.2±0.41ns** | **-77.40%** |
| deku_write_vec | 5.0±0.03µs | **299.5±1.11ns** | **-94.01%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1203.8±18.27ns | **1106.5±16.74ns** | **-8.08%** |
| deku_read_byte | 23.1±0.84ns | **22.0±0.48ns** | **-4.76%** |
| deku_read_enum | 9.3±0.15ns | 9.3±0.15ns | 0.00% |
| deku_read_vec | 53.6±0.81ns | 53.6±0.79ns | 0.00% |
| deku_write_bits | **109.0±1.89ns** | 188.8±17.81ns | **+73.21%** |
| deku_write_byte | 126.4±3.24ns | **20.8±0.21ns** | **-83.54%** |
| deku_write_enum | 84.6±2.62ns | **20.2±0.33ns** | **-76.12%** |
| deku_write_vec | 3.0±0.07µs | **265.0±28.27ns** | **-91.17%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1227.7±8.75ns | **1146.8±18.09ns** | **-6.59%** |
| deku_read_byte | 23.0±0.36ns | **22.1±0.47ns** | **-3.91%** |
| deku_read_enum | 9.3±0.13ns | 9.4±0.23ns | +1.08% |
| deku_read_vec | 53.4±0.82ns | 53.5±0.65ns | +0.19% |
| deku_write_bits | **108.9±1.77ns** | 181.5±4.34ns | **+66.67%** |
| deku_write_byte | 121.7±2.30ns | **20.6±0.32ns** | **-83.07%** |
| deku_write_enum | 86.6±10.19ns | **19.6±0.45ns** | **-77.37%** |
| deku_write_vec | 3.0±0.04µs | **298.2±3.66ns** | **-90.06%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1184.4±12.63ns | **1150.3±11.76ns** | **-2.88%** |
| deku_read_byte | 23.0±0.27ns | **22.1±0.33ns** | **-3.91%** |
| deku_read_enum | 9.3±0.15ns | 9.4±0.15ns | +1.08% |
| deku_read_vec | 53.5±0.67ns | 53.2±0.54ns | -0.56% |
| deku_write_bits | **113.1±4.14ns** | 199.1±4.26ns | **+76.04%** |
| deku_write_byte | 121.4±2.36ns | **20.6±0.20ns** | **-83.03%** |
| deku_write_enum | 85.3±3.76ns | **19.4±0.39ns** | **-77.26%** |
| deku_write_vec | 3.1±0.04µs | **304.6±5.71ns** | **-90.17%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1198.8±11.17ns | **1079.3±20.78ns** | **-9.97%** |
| deku_read_byte | 22.8±0.70ns | **21.5±0.34ns** | **-5.70%** |
| deku_read_enum | 9.3±0.13ns | 9.4±0.13ns | +1.08% |
| deku_read_vec | 53.3±0.63ns | 53.4±0.74ns | +0.19% |
| deku_write_bits | **108.6±1.51ns** | 197.9±5.05ns | **+82.23%** |
| deku_write_byte | 122.3±3.46ns | **20.5±0.36ns** | **-83.24%** |
| deku_write_enum | 85.4±3.41ns | **19.2±0.34ns** | **-77.52%** |
| deku_write_vec | 3.0±0.04µs | **300.5±3.91ns** | **-89.98%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1242.5±24.81ns | **1160.3±18.15ns** | **-6.62%** |
| deku_read_byte | **20.3±0.51ns** | 20.9±0.64ns | **+2.96%** |
| deku_read_enum | **9.3±0.08ns** | 9.4±0.17ns | **+1.08%** |
| deku_read_vec | 59.2±0.58ns | **58.1±0.59ns** | **-1.86%** |
| deku_write_bits | **122.8±6.65ns** | 191.6±6.69ns | **+56.03%** |
| deku_write_byte | 122.3±3.33ns | **21.1±0.37ns** | **-82.75%** |
| deku_write_enum | 87.3±3.93ns | **20.5±0.19ns** | **-76.52%** |
| deku_write_vec | 3.0±0.06µs | **281.1±3.14ns** | **-90.63%** |

  </details>
  

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..432138d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,13 +32,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..402e506 100644
--- a/deku-derive/Cargo.toml
+++ b/deku-derive/Cargo.toml
@@ -18,12 +18,12 @@ logging = []
 
 [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 a2a0de3..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",
                     ));
                 }
 
@@ -654,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`
@@ -687,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");
 
@@ -898,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)
@@ -962,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());
@@ -1062,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 5d76c3f..1f2afb3 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -599,7 +599,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! {}
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index 6e0b715..7ecb47a 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -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())?;
         }
     }
 }
@@ -528,6 +473,14 @@ fn emit_field_write(
         }
     });
 
+    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 +496,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 +521,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 +544,7 @@ fn emit_field_write(
         (true, None) => {
             // #[deku(skip)] ==> `skip`
             quote! {
+                #skipping_log
                 // skipping, no write
             }
         }
@@ -598,6 +561,7 @@ fn emit_field_write(
         #bit_offset
         #byte_offset
 
+        #trace_field_log
         #field_assert
         #field_assert_eq
 
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/src/attributes.rs b/src/attributes.rs
index ee596a0..ba66f25 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -841,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,
 }
@@ -856,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, ())
     }
 }
 
@@ -1049,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
@@ -1057,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),
@@ -1095,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,
@@ -1137,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),
@@ -1189,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),
@@ -1220,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),
diff --git a/src/error.rs b/src/error.rs
index 061f58d..f39e1a2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -44,6 +44,8 @@ pub enum DekuError {
     Assertion(String),
     /// Could not resolve `id` for variant
     IdVariantNotFound,
+    /// IO error while writing
+    Write,
 }
 
 impl From<core::num::TryFromIntError> for DekuError {
@@ -78,6 +80,7 @@ impl core::fmt::Display for DekuError {
             DekuError::Unexpected(ref err) => write!(f, "Unexpected error: {err}"),
             DekuError::Assertion(ref err) => write!(f, "Assertion error: {err}"),
             DekuError::IdVariantNotFound => write!(f, "Could not resolve `id` for variant"),
+            DekuError::Write => write!(f, "write error"),
         }
     }
 }
@@ -100,6 +103,7 @@ impl From<DekuError> for std::io::Error {
             DekuError::Unexpected(_) => io::Error::new(io::ErrorKind::Other, error),
             DekuError::Assertion(_) => io::Error::new(io::ErrorKind::InvalidData, error),
             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 9d6e4fa..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
@@ -186,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
@@ -194,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(())
     }
@@ -221,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
@@ -244,8 +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;], &[]),
@@ -295,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 fc2a69a..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
@@ -176,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
@@ -184,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(())
     }
@@ -202,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;
@@ -215,8 +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;], &[]),
@@ -264,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
@@ -307,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 adcd290..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,44 +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> 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(())
     }
@@ -84,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>();
@@ -107,47 +105,10 @@ 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());
-    }
-
-    #[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);
     }
 
     #[rstest(input,endian,expected,
@@ -163,23 +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());
-    }
-
-    #[test]
-    fn test_issue270() {
-        let num = [1, 1];
-        let mut res_write = bitvec![u8, Msb0;];
-        num.write(&mut res_write, ()).unwrap();
-        <[u8]>::write(num.as_ref(), &mut res_write, ()).unwrap();
-        <&[u8]>::write(&num.as_ref(), &mut res_write, ()).unwrap();
+        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 3d17976..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,
@@ -72,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
@@ -136,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
@@ -146,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(())
     }
@@ -168,13 +170,40 @@ 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]),
@@ -227,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
@@ -268,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 42a49f1..e800042 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -225,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_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/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)),