# iceberg.values

> A module of iceberg's API.

Typed single values (`Datum`) and Iceberg's two single-value encodings.

A `Datum` is one primitive value tagged with its Iceberg type. It is what
predicates compare against, what a partition transform consumes and produces,
and what a manifest's `lower_bound` / `upper_bound` bytes decode into.

Two serializations matter for reading:

* **Appendix D, binary** — `lower_bounds`, `upper_bounds` and partition field
  summaries. `datum_from_bytes` / `datum_to_bytes`.
* **Appendix D, JSON** — `initial-default`, `write-default` and the JSON forms
  of partition values. `datum_from_json`.

Integers, dates, times and timestamps all ride in `Datum.i` (`Int64`); floats
and doubles in `Datum.f`; strings in `Datum.s`; everything byte-shaped
(`binary`, `fixed`, `uuid`, and a decimal's unscaled two's-complement) in
`Datum.b`.

Decimals carry both forms: `b` holds the minimal big-endian two's-complement
unscaled value (what bucket hashing and the binary encoding need) and `i` holds
the same value as an `Int64` when precision ≤ 18 (what truncation needs).
Comparison always goes through the bytes, so decimals wider than 64 bits still
compare correctly even though this build cannot do arithmetic on them.

## Structs

### `Datum`

```mojo
struct Datum
```

One primitive Iceberg value, tagged with its type.

Implements: `Copyable`, `Movable`, `Writable`

Fields:

- `kind: UInt8`
- `i: Int64`
- `f: Float64`
- `s: String`
- `b: List[UInt8]`
- `precision: Int`
- `scale: Int`
- `length: Int`
- `valid: Bool`: False for "no value" — an absent bound, a null partition value.

#### `none`

```mojo
def none() -> Self
```

#### `bool_`

```mojo
def bool_(v: Bool) -> Self
```

#### `int_`

```mojo
def int_(v: Int64) -> Self
```

#### `long_`

```mojo
def long_(v: Int64) -> Self
```

#### `integral`

```mojo
def integral(kind: UInt8, v: Int64) -> Self
```

`int`/`long`/`date`/`time`/`timestamp[_ns][tz]` — all carried as Int64.

#### `float_`

```mojo
def float_(v: Float64) -> Self
```

#### `double_`

```mojo
def double_(v: Float64) -> Self
```

#### `string_`

```mojo
def string_(var v: String) -> Self
```

#### `binary_`

```mojo
def binary_(var v: List[UInt8]) -> Self
```

#### `fixed_`

```mojo
def fixed_(var v: List[UInt8]) -> Self
```

#### `uuid_`

```mojo
def uuid_(var v: List[UInt8]) -> Self
```

#### `decimal_`

```mojo
def decimal_(var unscaled_be: List[UInt8], precision: Int, scale: Int) -> Self
```

#### `decimal_int`

```mojo
def decimal_int(unscaled: Int64, precision: Int, scale: Int) -> Self
```

#### `is_nan`

```mojo
def is_nan(self) -> Bool
```

#### `write_to`

```mojo
def write_to(self, mut writer: T)
```

#### `repr_`

```mojo
def repr_(self) -> String
```

#### `to_json`

```mojo
def to_json(self) -> String
```

Appendix D, JSON half.

## Functions

### `compare`

```mojo
def compare(a: Datum, b: Datum) -> Int
```

Iceberg's natural order: -1 / 0 / 1. Both values must be comparable.

### `cmp_unsigned`

```mojo
def cmp_unsigned(a: List[UInt8], b: List[UInt8]) -> Int
```

Lexicographic unsigned-byte order (uuid, fixed, binary).

### `cmp_be_twos`

```mojo
def cmp_be_twos(a: List[UInt8], b: List[UInt8]) -> Int
```

Signed compare of two minimal big-endian two's-complement integers.

### `be_twos_to_int64`

```mojo
def be_twos_to_int64(b: List[UInt8]) -> Int64
```

Big-endian two's complement → Int64. Wider-than-64-bit input saturates.

### `int64_to_be_twos`

```mojo
def int64_to_be_twos(v: Int64) -> List[UInt8]
```

Int64 → the *minimal* big-endian two's-complement byte string.

### `hex_text`

```mojo
def hex_text(b: List[UInt8]) -> String
```

### `hex_bytes`

```mojo
def hex_bytes(s: String) -> List[UInt8]
```

### `uuid_text`

```mojo
def uuid_text(b: List[UInt8]) -> String
```

### `uuid_bytes`

```mojo
def uuid_bytes(s: String) -> List[UInt8]
```

### `decimal_text`

```mojo
def decimal_text(unscaled_be: List[UInt8], scale: Int) -> String
```

Render a decimal from its unscaled two's-complement value and scale.

### `decimal_from_text`

```mojo
def decimal_from_text(s: String, precision: Int, scale: Int) -> Datum
```

Parse decimal text at the column's scale, to full 38-digit width.

### `floor_div`

```mojo
def floor_div(a: Int64, b: Int64) -> Int64
```

Division that rounds toward negative infinity.

Mojo's `//` on integers already floors (Python semantics, verified on both
stable 1.0.0 and the nightly: `-1 // 86400000000 == -1`, `-7 // 2 == -4`),
so this is a rename that documents the intent. Every date and timestamp
calculation in Iceberg needs floor, not truncation, or pre-epoch values
land a day early.

### `floor_mod`

```mojo
def floor_mod(a: Int64, b: Int64) -> Int64
```

Remainder with the sign of the divisor — `%` already does this in Mojo.

### `civil_from_days`

```mojo
def civil_from_days(z_in: Int64) -> List[Int64]
```

Days since 1970-01-01 → [year, month, day]. Howard Hinnant's algorithm.

### `days_from_civil`

```mojo
def days_from_civil(y_in: Int64, m: Int64, d: Int64) -> Int64
```

### `iso_text`

```mojo
def iso_text(kind: UInt8, v: Int64) -> String
```

ISO-8601 rendering used by the JSON single-value form.

### `parse_iso`

```mojo
def parse_iso(kind: UInt8, text: String) -> Int64
```

ISO-8601 → the integer Iceberg stores for `kind`.

### `datum_from_bytes`

```mojo
def datum_from_bytes(store: TypeStore, type_idx: Int, b: List[UInt8]) -> Datum
```

Decode Appendix-D binary bytes as the type at `type_idx`.

Byte length disambiguates *promoted* columns: a `long` column whose bounds
were written while it was still an `int` carries 4 bytes, a `double`
promoted from `float` carries 4, and a `timestamp` promoted from `date`
carries 4. The spec requires readers to infer the original type from the
length rather than mis-decode.

### `datum_from_bytes_prim`

```mojo
def datum_from_bytes_prim(prim: UInt8, precision: Int, scale: Int, length: Int, b: List[UInt8]) -> Datum
```

### `datum_to_bytes`

```mojo
def datum_to_bytes(d: Datum) -> List[UInt8]
```

Encode a `Datum` in Appendix-D binary form.

### `datum_from_json`

```mojo
def datum_from_json(store: TypeStore, type_idx: Int, doc: Json, i: Int) -> Datum
```

### `datum_from_json_prim`

```mojo
def datum_from_json_prim(prim: UInt8, precision: Int, scale: Int, length: Int, doc: Json, i: Int) -> Datum
```

## Aliases

### `MICROS_PER_DAY`

```mojo
comptime MICROS_PER_DAY = Int64(86400000000)
```

### `MICROS_PER_HOUR`

```mojo
comptime MICROS_PER_HOUR = Int64(3600000000)
```

### `NANOS_PER_MICRO`

```mojo
comptime NANOS_PER_MICRO = Int64(1000)
```
