# Zerocopy Ecosystem Overview
## Issue tracking
- fxbug.dev
- cs.opensource.google/fuchsia
- Components:
- Connectivity>Libraries>net_types
- Connectivity>Libraries>packet
- Connectivity>Libraries>packet-formats
## Omnibus Build of Packet Dependencies
Add to this.
```
fx format-code && fx build src/connectivity/network/netstack3/core:{netstack3-core-instrumented,netstack3-core,netstack3-core-test-package,netstack3-core-testutils,netstack3-core-benchmarks,fuzz-single-device}
```
## Things We're Doing
- [ ] Remove `nonzero!` macro.
- [ ] Change `outer` argument of `Serialier` to `PacketConstraints`.
- [ ]
## Things we need
- update documentation
- long tail of little sharp edges
- open q: when to publish
- a: bias for publishing earlier than later
- fuchsia-specific stuff that needs to be disentangled
- most important crates:
- net_types
- packet
- packet-formats
## Miscellany
- https://fuchsia-docs.firebaseapp.com/rust/fuchsia_component/index.html
```shell
$ fx set core.x64 --no-ccache --cargo-toml-gen
$ fx build src/lib/network/packet:packet
# Might not need to do this line every time
$ fx build build/rust:cargo_toml_gen
$ fx gen-cargo //src/lib/network/packet:packet
```
## TODOs by crate
### Packet
### Patchsets
- ~~[[packet] Simplify execution of Serializers](https://fuchsia-review.googlesource.com/c/fuchsia/+/861377)~~
- ~~[[packet] Rename and simplify `SerializeBuffer](https://fuchsia-review.googlesource.com/c/fuchsia/+/862010)~~
- ~~[[packet] Simplify buffer trait hierarchy](https://fuchsia-review.googlesource.com/c/fuchsia/+/862776)~~
- ~~[[packet] Merge SerializeBuffer into GrowBufferMut](https://fuchsia-review.googlesource.com/c/fuchsia/+/874198)~~
#### Clean up trait hierarchy
- Rename `FragmentedBuffer` to `MaybeFragmentedBuffer`?
- Move `Buffer`/`BufferMut` into N3
- Add `testutil` module which provides tests that validate buffer implementations; mention this module in the docs for all buffer traits
#### Improve docs
- `#![deny(missing_docs)]`
- In `ParsablePacket::parse`'s `Padding` docs section, the phrase "consume any post-packet padding from the buffer's suffix" is misleading - it should be something more like "from the body and added to the suffix"
- Consider renaming `encapsulate` to `encapsulate_in` or `nest_in` or similar
- Consider renaming `Nested` now that it is only used for `Serializers`
- In `GrowBuffer`:
- This sentence is misleading: "The body of the buffer can shrink or grow as allowed by the capacity as packets are parsed or serialized." It is not guaranteed that a `GrowBuffer` can shrink.
- Clarify "guarantees never to discard"
- `undo_parse` can panic, but the docs claim it can't
- Add doc comment to `Serializer`
#### Impelemntation cleanup
- In `alloc_no_reuse`, change the following line to use checked arithmetic?
```
let need_capacity = prefix + body + suffix;
```
#### Miscellaneous
- Move `BufferProvider::alloc_no_reuse` to `BufferAlloc`? It was introduced by Zeling in https://fxrev.dev/786703
- Consider verifying behavior using [flux](https://github.com/flux-rs/flux)
- Make functions and methods `const`
- Merge N3's `transport::tcp::buffer::SendPayload` into `packet`?
- There should be an extension trait on slices that provides the ability to read and write zerocopy objects (e.g., like `write_obj_front`)
- Remove getters on `ParseMetadata` now that its fields are `pub`?
- Make sure naming is consistent (traits use full words and types use abbreviated words)
- Consolodate `zero` and `zero_iter`
- Consider using `either` crate instead of custom `Either` buffer type
- Consider replacing `ByteSliceInnerPacketBuilder` with a blanket impl
- Combine `Either` and `EitherSerializer`?
- Make `PacketConstraints: Copy`
## Hints that Josh gave Jack for implementing UDP
- Start with parsing; it's easier than serializing
- Implement the `ParsablePacket` trait
- For parsing, take a look at what the `BufferView` trait gives you
- For ease of implementation, start with `ParseArgs = ()`
- For generating the body, use `buffer.into_rest()`
- For `ParsablePacket::parse_metadata`, the body length field is the framework's notion of body length, not (necessarily) the length field from the UDP header
- "...", the body length is `self.body.len()`, not 0
- After Jack started implementing `PacketBuilder` for `UdpPacket`: Your `PacketBuilder` type does not need to be the same as your `ParsablePacket` type
- Think of a `PacketBuilder` as a builder - just whatever information is necessary to perform serialization
- Just introduce a new `UdpPacketBuilder` type which contains only those fields which you can't compute on the fly
- After Jack suggested putting a body field in the `UdpPacketBuilder` type: Look at the `SerializeBuffer` docs (my thinking was to dissuade him from this and point him towards the fact that the `SerializeBuffer` already has access to the body in the buffer)
- Remember that it is the caller's responsibility to provide the body - `PacketBuilder` doesn't know about it until `serialize` is called
## Potential future directions
- If we use GATs to support a type parameter that represents mutability, we could do something like:
```rust
struct UdpPacket<'a, M: Mutability> {
header: Ref<'a, M, UdpHeader>,
body: Ref<'a, M, [u8]>,
}
```
- Make `Serializer` methods which take a `PacketBuilder`/`PacketConstraints` `#[doc(hidden)]` (unless we think consumers would ever want to implement `Serializer`?)
## Concepts
### Buffers
A sequence of bytes logically segmented into a:
- prefix
- body
- suffix
The total size of these segments is the `capacity` of the buffer.
The size of the body is the `length` of the buffer.
## Before
```graphviz
digraph G {
Buffer [tooltip="A byte buffer used for parsing that can grow back to its original size."];
Buffer -> GrowBuffer, ParseBuffer [dir=back];
BufferMut [tooltip="A byte buffer used for parsing and serialization."];
BufferMut -> TargetBuffer, ParseBufferMut, Buffer [dir=back];
//BufferView [tooltip="A view into a ShrinkBuffer."];
//BufferView -> Sized, "AsRef<[u8]>" [dir=back];
//BufferViewMut [tooltip="A mutable view into a Buffer."];
//BufferViewMut -> BufferView, "AsMut<[u8]>" [dir=back];
ContiguousBuffer [tooltip="A buffer that is contiguous in memory."];
ContiguousBuffer -> "AsRef<[u8]>" [dir=back];
ContiguousBufferImpl [tooltip="A helper trait to implement FragmentedBuffer and ContiguousBuffer for any type that is AsRef<[u8]>."];
ContiguousBufferImpl -> "AsRef<[u8]>" [dir=back];
ContiguousBufferMut [tooltip="A mutable buffer that is contiguous in memory."];
ContiguousBufferMut -> ContiguousBuffer, "AsMut<[u8]>" [dir=back];
ContiguousBufferMutImpl [tooltip="A helper trait to implement FragmentedBufferMut and ContiguousBufferMut for any type that is AsMut<[u8]>."];
ContiguousBufferMutImpl -> ContiguousBuffer, "AsMut<[u8]>" [dir=back];
//Fragment [tooltip="A single byte slice fragment in a FragmentedByteSlice."];
//Fragment -> ByteSlice [dir=back];
FragmentedBuffer [tooltip="A buffer that may be fragmented in multiple parts which are discontiguous in memory."];
FragmentedBufferMut [tooltip="A FragmentedBuffer with mutable access to its contents."];
FragmentedBufferMut -> FragmentedBuffer [dir=back];
//FromRaw [tooltip="A packet that can be created from a raw form."];
//FromRaw -> Sized [dir=back];
GrowBuffer [tooltip="A buffer that can grow its body by taking space from its prefix and suffix."];
GrowBuffer -> FragmentedBuffer [dir=back];
GrowBufferMut [tooltip="A GrowBuffer which provides mutable access to its contents."];
GrowBufferMut -> GrowBuffer, FragmentedBufferMut [dir=back];
//ParsablePacket [tooltip="A packet which can be parsed from a buffer."];
//ParsablePacket -> Sized [dir=back];
ParseBuffer [tooltip="A byte buffer used for parsing."];
ParseBuffer -> ShrinkBuffer, ContiguousBuffer [dir=back];
ParseBufferMut [tooltip="A ParseBuffer which provides mutable access to its contents."];
ParseBufferMut -> ParseBuffer, FragmentedBufferMut, ContiguousBufferMut [dir=back];
ReusableBuffer [tooltip="A byte buffer that can be reused."];
ReusableBuffer -> TargetBuffer, ShrinkBuffer [dir=back];
ShrinkBuffer [tooltip="A buffer that can reduce its size."];
ShrinkBuffer -> FragmentedBuffer [dir=back];
TargetBuffer [tooltip="A buffer that can be serialized into."];
TargetBuffer -> GrowBufferMut [dir=back];
}
```
## After
```graphviz
digraph G {
//BufferView [tooltip="A view into a ShrinkBuffer."];
//BufferView -> Sized, "AsRef<[u8]>" [dir=back];
//BufferViewMut [tooltip="A mutable view into a Buffer."];
//BufferViewMut -> BufferView, "AsMut<[u8]>" [dir=back];
ContiguousBuffer [tooltip="A buffer that is contiguous in memory."];
ContiguousBuffer -> FragmentedBuffer, "AsRef<[u8]>" [dir=back];
ContiguousBufferMut [tooltip="A mutable buffer that is contiguous in memory."];
ContiguousBufferMut -> ContiguousBuffer, FragmentedBufferMut, "AsMut<[u8]>" [dir=back];
//Fragment [tooltip="A single byte slice fragment in a FragmentedByteSlice."];
//Fragment -> ByteSlice [dir=back];
FragmentedBuffer [tooltip="A buffer that may be fragmented in multiple parts which are discontiguous in memory."];
GrowBuffer [tooltip="A buffer that can grow its body by taking space from its prefix and suffix."];
GrowBuffer -> FragmentedBuffer [dir=back];
FragmentedBufferMut [tooltip="A FragmentedBuffer with mutable access to its contents."];
FragmentedBufferMut -> FragmentedBuffer [dir=back];
//FromRaw [tooltip="A packet that can be created from a raw form."];
//FromRaw -> Sized [dir=back];
GrowBufferMut [tooltip="A GrowBuffer which provides mutable access to its contents."];
GrowBufferMut -> GrowBuffer, FragmentedBufferMut [dir=back];
//ParsablePacket [tooltip="A packet which can be parsed from a buffer."];
//ParsablePacket -> Sized [dir=back];
ParseBuffer [tooltip="A byte buffer used for parsing."];
ParseBuffer -> ShrinkBuffer, ContiguousBuffer [dir=back];
ParseBufferMut [tooltip="A ParseBuffer which provides mutable access to its contents."];
ParseBufferMut -> ParseBuffer, ContiguousBufferMut [dir=back];
ReusableBuffer [tooltip="A byte buffer that can be reused."];
ReusableBuffer -> SerializeBuffer, ShrinkBuffer [dir=back];
ShrinkBuffer [tooltip="A buffer that can reduce its size."];
ShrinkBuffer -> FragmentedBuffer [dir=back];
SerializeBuffer [tooltip="A buffer that can be serialized into."];
SerializeBuffer -> GrowBufferMut [dir=back];
}
```
## Naming `packet`
### `buffer`
pros:
- boop
cons:
- moop