# dista.codec

> A module of Dista's API.

Turning task arguments and results into bytes and back.

`encode` and `decode` work on any type, chosen at compile time:

- built-in scalars, `Bool` and `String` have fixed encodings (little-endian
  fixed width; `String` is an 8-byte length then its UTF-8 bytes);
- arrow-mlake's `RecordBatch` is an 8-byte length then one Arrow IPC stream
  holding the schema and the batch, so columnar data crosses processes in
  Arrow's own format;
- a type that conforms to `Codec` encodes itself;
- any other struct is encoded field by field, in declaration order, by
  reflection.

Built-ins are recognised by `reflect[T].name()`, because `Int` or `String`
can't adopt a dista trait. `List` and other generic containers aren't
supported: reflection can't recover their element type. Wrap them in a type
that implements `Codec`, or send columnar data as Arrow.

Both ends are the same binary, so there's no versioning or schema.

## Traits

### `Codec`

```mojo
undefined
```

A type that writes and reads its own bytes.

#### `encode`

```mojo
def encode(self, mut out: List[UInt8])
```

Appends this value's bytes to `out`.

#### `decode`

```mojo
def decode(buf: Span[UInt8], mut pos: Int) -> Self
```

Reads a value starting at `buf[pos]` and advances `pos` past it.

## Functions

### `put_scalar`

```mojo
def put_scalar[dt: DType](mut out: List[UInt8], v: Scalar[dt])
```

Appends `v` as its little-endian bytes.

### `get_scalar`

```mojo
def get_scalar[dt: DType](buf: Span[UInt8], mut pos: Int) -> Scalar[dt]
```

Reads a little-endian scalar at `buf[pos]` and advances `pos`.

### `encode`

```mojo
def encode[T: AnyType](ref value: T, mut out: List[UInt8])
```

Appends the bytes of `value` to `out`.

### `decode`

```mojo
def decode[T: Movable](buf: Span[UInt8], mut pos: Int) -> T
```

Reads a `T` starting at `buf[pos]` and advances `pos` past it.
