# tecs.data Typed stores, JSON, UTF-8, UUIDs, hashes, and checksums. ## JSON Encode and decode complete documents: ```teal local text = tecs.data.encodeJSON({ jsonrpc = "2.0", id = 1, result = {entities = {12, 24, 36}}, }) local value = 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 = tecs.data.decodeJSON('{"name":null,"rows":[]}') assert(value.name == tecs.data.null) local rows = 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 = tecs.data.uuid4() local eventId = 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 = 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 : tecs.data.Key = tecs.data.Store.newKey( "game.speed" ) local store = 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) | type | ByteInput accepts a Lua byte string or an open Buffer or ByteView. | | [`Key`](/modules/data/#tecs.data.Key) | interface | Key identifies one typed store value across every store in the process. | | [`Store`](/modules/data/#tecs.data.Store) | record | Store holds typed values under process-wide keys and exposes their process-wide registry. | ### Functions | Function | Kind | Description | | --- | --- | --- | | [`adler32`](/modules/data/#tecs.data.adler32) | Static | Computes Adler-32 over the bytes of text as a number in [0, 2^32). | | [`crc32`](/modules/data/#tecs.data.crc32) | Static | Computes CRC-32 over the bytes of text as a number in [0, 2^32). | | [`fnv1a64`](/modules/data/#tecs.data.fnv1a64) | Static | FNV-1a over the bytes of text, 64-bit, as sixteen lowercase hex digits. | | [`sha256`](/modules/data/#tecs.data.sha256) | Static | Computes SHA-256 over the bytes of text. | | [`uuid4`](/modules/data/#tecs.data.uuid4) | Static | Generates a random RFC 9562 UUID version 4. | | [`uuid7`](/modules/data/#tecs.data.uuid7) | Static | Generates a time-ordered RFC 9562 UUID version 7. | ## Constructors ### tecs.data.newStore Static 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 ### tecs.data.ByteInput type `ByteInput` accepts a Lua byte string or an open Buffer or ByteView. ```teal type tecs.data.ByteInput = string | ByteView ``` ### tecs.data.Key interface `Key` identifies one typed store value across every store in the process. ```teal interface tecs.data.Key end ``` #### Type Parameters | Name | Constraint | Description | | --- | --- | --- | | `T` | | | ### tecs.data.Store record `Store` holds typed values under process-wide keys and exposes their process-wide registry. ```teal record tecs.data.Store findKey: function(name: string): Key | nil listKeys: function(): {string: integer} newKey: function(name: string, forType: T): Key metamethod __index: function(self, key: Key): T metamethod __newindex: function(self, key: Key, value: T) end ``` #### tecs.data.Store.findKey Static Returns a named key created by `newKey`. ```teal function tecs.data.Store.findKey(name: string): Key | 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)<T> | nil | Returns the existing key, or nil before any module creates it. | #### tecs.data.Store.listKeys Static 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. | #### tecs.data.Store.newKey Static 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(name: string, forType: T): Key ``` ##### 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)`` | Returns the stable key for a name or a fresh anonymous key. | #### tecs.data.Store:__index metamethod Returns the value associated with a typed key. An unwritten key returns nil. ```teal local SPEED : tecs.data.Key = tecs.data.Store.newKey( "game.speed" ) local store = tecs.data.newStore() local speed = store[SPEED] ``` ```teal metamethod tecs.data.Store.$meta.__index(self, key: Key): 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)`` | The Key used to retrieve its value. | ##### Returns | Type | Description | | --- | --- | | `T` | Returns the value for `key`, or nil when no value has been assigned. | #### tecs.data.Store:__newindex metamethod Associates a value with a typed key. Assigning nil removes the value. ```teal local SPEED : tecs.data.Key = tecs.data.Store.newKey( "game.speed" ) local store = tecs.data.newStore() store[SPEED] = 120 ``` ```teal metamethod tecs.data.Store.$meta.__newindex( self, key: Key, 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)`` | The Key used to associate the value. | | `value` | `T` | The value to associate with `key`. Passing nil removes the association. | ##### Returns None. ## Functions ### tecs.data.adler32 Static 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). | ### tecs.data.crc32 Static 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). | ### tecs.data.fnv1a64 Static 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. | ### tecs.data.sha256 Static 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. | ### tecs.data.uuid4 Static 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. | ### tecs.data.uuid7 Static 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. |