
# tecs.data


Typed stores, JSON, UTF-8, UUIDs, hashes, and checksums.

## JSON

Encode and decode complete documents:

```teal
local text <const> = tecs.data.encodeJSON({
    jsonrpc = "2.0",
    id = 1,
    result = {entities = {12, 24, 36}},
})
local value <const> = tecs.data.decodeJSON(text)
```

Both functions raise on invalid input. Protect untrusted documents with
`pcall`.

Lua needs sentinels for two JSON values:

```teal
local value <const> = tecs.data.decodeJSON('{"name":null,"rows":[]}')

assert(value.name == tecs.data.null)

local rows <const> = setmetatable({}, tecs.data.array_mt)
return tecs.data.encodeJSON({rows = rows})
```

`null` preserves a present key whose JSON value equals null. An empty Lua
table normally encodes as an object; `empty_array` and `array_mt` express an
empty list. `newJSON` creates an independent configuration for protocols that
must not share process-wide settings.

The sentinel and option names retain lua-cjson spelling because they belong to
that library's compatibility surface.

## UUIDs

Generate RFC 9562 identifiers without a central allocator:

```teal
local objectId <const> = tecs.data.uuid4()
local eventId <const> = tecs.data.uuid7()
```

`uuid4` draws its random bits from the operating system. `uuid7` starts with
the current Unix timestamp in milliseconds and orders calls made by this
process, which makes it suitable for identifiers stored in an ordered index.
Both return the canonical lowercase 8-4-4-4-12 representation.

UUIDs identify values; they do not authenticate a player or make an
unguessable credential.

## UTF-8

Codepoint operations live under `tecs.data.utf8`:

```teal
local utf8 <const> = tecs.data.utf8
local codepoint, nextOffset = utf8.decodeAt("A€", 2)
assert(codepoint == 0x20ac)
assert(nextOffset == 5)
```

The child works over explicit byte lengths in strings and ByteViews, so
embedded NUL is U+0000 rather than a terminator. Malformed bytes decode as
U+FFFD and remain traversable. These are codepoint operations, not grapheme
segmentation.

## Hashes and checksums

`fnv1a64` supplies fast process and build identity as 16 lowercase hexadecimal
digits. `sha256` supplies a cryptographic digest as 64 lowercase hexadecimal
digits. `adler32` and `crc32` implement checksums required by formats that
name them. Pass the checksum from one call as the second argument to continue
over the next chunk without joining the chunks into one string:

```teal
local checksum = tecs.data.crc32(header)
checksum = tecs.data.crc32(body, checksum)
```

SHA-256 authenticates nothing by itself. Verify downloaded artifacts against a
digest obtained through a trusted channel or a signed manifest.

## Typed stores

Create typed keys once and use them with independent stores:

```teal
local SPEED <const>: tecs.data.Key<number> = tecs.data.Store.newKey(
    "game.speed"
)
local store <const> = tecs.data.newStore()
store[SPEED] = 120
```

`world.resources` is a `Store`. Stores are in-memory and independent; a named
key is the process-wide identity that lets hot reload and tooling find the same
value again. `Store.listKeys` reports the named keys registered by the process.

## Module contents

### Submodules

| Submodule | Description |
| --- | --- |
| [`tecs.data.utf8`](/modules/data/utf8/) | UTF-8 codepoint decoding, encoding, validation, and truncation |

### Constructors

| Constructor | Description |
| --- | --- |
| [`newStore`](/modules/data/#tecs.data.newStore) | Creates an independent typed store. |

### Types

| Type | Kind | Description |
| --- | --- | --- |
| [`ByteInput`](/modules/data/#tecs.data.ByteInput) | <span class="tealdoc-kind-badge tealdoc-kind-type">type</span> | ByteInput accepts a Lua byte string or an open Buffer or ByteView. |
| [`Key`](/modules/data/#tecs.data.Key) | <span class="tealdoc-kind-badge tealdoc-kind-interface">interface</span> | Key identifies one typed store value across every store in the process. |
| [`Store`](/modules/data/#tecs.data.Store) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Store holds typed values under process-wide keys and exposes their process-wide registry. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`adler32`](/modules/data/#tecs.data.adler32) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Computes Adler-32 over the bytes of text as a number in [0, 2^32). |
| [`crc32`](/modules/data/#tecs.data.crc32) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Computes CRC-32 over the bytes of text as a number in [0, 2^32). |
| [`fnv1a64`](/modules/data/#tecs.data.fnv1a64) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | FNV-1a over the bytes of text, 64-bit, as sixteen lowercase hex digits. |
| [`sha256`](/modules/data/#tecs.data.sha256) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Computes SHA-256 over the bytes of text. |
| [`uuid4`](/modules/data/#tecs.data.uuid4) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Generates a random RFC 9562 UUID version 4. |
| [`uuid7`](/modules/data/#tecs.data.uuid7) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Generates a time-ordered RFC 9562 UUID version 7. |

## Constructors

<a id="tecs.data.newStore"></a>
### tecs.data.newStore <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Creates an independent typed store.



```teal
function tecs.data.newStore(): Store
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| [`Store`](/modules/data/#tecs.data.Store) | Returns an empty store. You own it and its values. |

## Types

<a id="tecs.data.ByteInput"></a>
### tecs.data.ByteInput <span class="tealdoc-kind-badge tealdoc-kind-type">type</span>

`ByteInput` accepts a Lua byte string or an open Buffer or ByteView.


```teal
type tecs.data.ByteInput = string | ByteView
```

<a id="tecs.data.Key"></a>
### tecs.data.Key <span class="tealdoc-kind-badge tealdoc-kind-interface">interface</span>

`Key` identifies one typed store value across every store in the
process.


```teal
interface tecs.data.Key<T>
end
```

#### Type Parameters

| Name | Constraint | Description |
| --- | --- | --- |
| `T` |  |  |

<a id="tecs.data.Store"></a>
### tecs.data.Store <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Store` holds typed values under process-wide keys and exposes their
process-wide registry.


```teal
record tecs.data.Store
    findKey: function<T>(name: string): Key<T> | nil
    listKeys: function(): {string: integer}
    newKey: function<T>(name: string, forType: T): Key<T>
    metamethod __index: function<T>(self, key: Key<T>): T
    metamethod __newindex: function<T>(self, key: Key<T>, value: T)
end
```

<a id="tecs.data.Store.findKey"></a>
#### tecs.data.Store.findKey <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns a named key created by `newKey`.



```teal
function tecs.data.Store.findKey<T>(name: string): Key<T> | nil
```

##### Type Parameters

| Name | Constraint | Description |
| --- | --- | --- |
| `T` |  |  |

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | The name used during key creation. |

##### Returns

| Type | Description |
| --- | --- |
| [`Key`](/modules/data/#tecs.data.Key)<code>&lt;T&gt; &#124; nil</code> | Returns the existing key, or nil before any module creates it. |

<a id="tecs.data.Store.listKeys"></a>
#### tecs.data.Store.listKeys <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns all named keys as name-to-id entries.



```teal
function tecs.data.Store.listKeys(): {string: integer}
```

##### Arguments

None.

##### Returns

| Type | Description |
| --- | --- |
| `{string : integer}` | Returns a fresh process-wide table. |

<a id="tecs.data.Store.newKey"></a>
#### tecs.data.Store.newKey <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Creates a typed key.

Always pass a name. Named keys are discoverable through `findKey`
and `listKeys`, and repeating a name returns the same key so values
remain reachable when a module reloads.



```teal
function tecs.data.Store.newKey<T>(name: string, forType: T): Key<T>
```

##### Type Parameters

| Name | Constraint | Description |
| --- | --- | --- |
| `T` |  |  |

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | The namespaced identity, such as `"game.state"`. Omitting it creates an anonymous key and warns. |
| `forType` | `T` | Pass `nil as T` when Teal needs the value type carried explicitly. The function does not read it. |

##### Returns

| Type | Description |
| --- | --- |
| [`Key`](/modules/data/#tecs.data.Key)`<T>` | Returns the stable key for a name or a fresh anonymous key. |

<a id="tecs.data.Store.$meta"></a>
<a id="tecs.data.Store.$meta.__index"></a>
#### tecs.data.Store:__index <span class="tealdoc-kind-badge tealdoc-kind-metamethod">metamethod</span>

Returns the value associated with a typed key. An unwritten key
returns nil.

```teal
local SPEED <const>: tecs.data.Key<number> = tecs.data.Store.newKey(
    "game.speed"
)
local store <const> = tecs.data.newStore()
local speed <const> = store[SPEED]
```



```teal
metamethod tecs.data.Store.$meta.__index<T>(self, key: Key<T>): T
```

##### Type Parameters

| Name | Constraint | Description |
| --- | --- | --- |
| `T` |  |  |

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | [`Store`](/modules/data/#tecs.data.Store) | The Store to index. |
| `key` | [`Key`](/modules/data/#tecs.data.Key)`<T>` | The Key used to retrieve its value. |

##### Returns

| Type | Description |
| --- | --- |
| `T` | Returns the value for `key`, or nil when no value has been assigned. |

<a id="tecs.data.Store.$meta.__newindex"></a>
#### tecs.data.Store:__newindex <span class="tealdoc-kind-badge tealdoc-kind-metamethod">metamethod</span>

Associates a value with a typed key. Assigning nil removes the
value.

```teal
local SPEED <const>: tecs.data.Key<number> = tecs.data.Store.newKey(
    "game.speed"
)
local store <const> = tecs.data.newStore()
store[SPEED] = 120
```



```teal
metamethod tecs.data.Store.$meta.__newindex<T>(
    self, key: Key<T>, value: T
)
```

##### Type Parameters

| Name | Constraint | Description |
| --- | --- | --- |
| `T` |  |  |

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | [`Store`](/modules/data/#tecs.data.Store) | The Store to index. |
| `key` | [`Key`](/modules/data/#tecs.data.Key)`<T>` | The Key used to associate the value. |
| `value` | `T` | The value to associate with `key`. Passing nil removes the association. |

##### Returns

None.

## Functions

<a id="tecs.data.adler32"></a>
### tecs.data.adler32 <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Computes Adler-32 over the bytes of `text` as a number in [0, 2^32).

The checksum RFC 1950 puts in a zlib stream's trailer, which is the only
reason it is here. It is a poor content hash and is not offered as one: it
is a sum of a sum, it barely mixes on short inputs, and two files that
differ by reordering equal-length runs collide outright. Use `fnv1a64` for
identity and this only for the format that specifies it.

zlib computes it. The seed is 1, which is what RFC 1950 starts from and
what zlib's own documentation says to pass for the first call. Pass the
return value back as `previous` to continue the checksum over another
chunk. An empty chunk leaves `previous` unchanged.



```teal
function tecs.data.adler32(
    text: ByteInput, previous: integer
): integer
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `text` | [`ByteInput`](/modules/data/#tecs.data.ByteInput) | A byte string or open [`ByteView`](/modules/io/#tecs.io.ByteView). NUL and bytes above 127 contribute like any other. |
| `previous` | `integer` | The checksum returned after all preceding bytes. Omit it for the first chunk. Raises unless it is an unsigned 32-bit integer. |

#### Returns

| Type | Description |
| --- | --- |
| `integer` | The checksum after `text`, as an exact Lua number in [0, 2^32). |

<a id="tecs.data.crc32"></a>
### tecs.data.crc32 <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Computes CRC-32 over the bytes of `text` as a number in [0, 2^32).

The checksum PNG, gzip and ZIP put beside compressed bytes. It detects
accidental corruption and is not a cryptographic digest; use it only where
a format specifies CRC-32 or where an error-detecting checksum is enough.
Pass the return value back as `previous` to continue the checksum over
another chunk. An empty chunk leaves `previous` unchanged.



```teal
function tecs.data.crc32(
    text: ByteInput, previous: integer
): integer
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `text` | [`ByteInput`](/modules/data/#tecs.data.ByteInput) | A byte string or open [`ByteView`](/modules/io/#tecs.io.ByteView). NUL and bytes above 127 contribute like any other. |
| `previous` | `integer` | The checksum returned after all preceding bytes. Omit it for the first chunk. Raises unless it is an unsigned 32-bit integer. |

#### Returns

| Type | Description |
| --- | --- |
| `integer` | The checksum after `text`, as an exact Lua number in [0, 2^32). |

<a id="tecs.data.fnv1a64"></a>
### tecs.data.fnv1a64 <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

FNV-1a over the bytes of `text`, 64-bit, as sixteen lowercase hex digits.



```teal
function tecs.data.fnv1a64(text: ByteInput): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `text` | [`ByteInput`](/modules/data/#tecs.data.ByteInput) | A byte string or open [`ByteView`](/modules/io/#tecs.io.ByteView). NUL and bytes above 127 hash like any other. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Sixteen hex digits, high half first, so hashes sort as their values do and two of them compare as strings. |

<a id="tecs.data.sha256"></a>
### tecs.data.sha256 <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Computes SHA-256 over the bytes of `text`.

SHA-256 detects whether bytes match a trusted digest. It does not prove who
supplied either value, so artifact verification obtains the expected
digest through a trusted channel or a signed manifest.



```teal
function tecs.data.sha256(text: ByteInput): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `text` | [`ByteInput`](/modules/data/#tecs.data.ByteInput) | A byte string or open [`ByteView`](/modules/io/#tecs.io.ByteView). NUL and bytes above 127 hash like any other. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Sixty-four lowercase hexadecimal digits, high byte first. |

<a id="tecs.data.uuid4"></a>
### tecs.data.uuid4 <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Generates a random RFC 9562 UUID version 4.



```teal
function tecs.data.uuid4(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns a fresh UUID in canonical lowercase 8-4-4-4-12 form. |

<a id="tecs.data.uuid7"></a>
### tecs.data.uuid7 <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Generates a time-ordered RFC 9562 UUID version 7.

Calls made by this process remain ordered even when more than one falls
in the same millisecond. The timestamp makes the result unsuitable for
hiding when an identifier was created.



```teal
function tecs.data.uuid7(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns a fresh UUID in canonical lowercase 8-4-4-4-12 form. |