Skip to content

Encoding Dynamic Values

TheFlyingFiddle edited this page Jul 11, 2015 · 9 revisions

This tutorial show how to encode dynamic values, that is values whose type is not known in advance. The mapping we will be focusing on in this tutorial is standard.dynamic which is located in the tier.standard submodule.

In the previous tutorials we have learned how to encode values that have a variety of different forms. In this tutorial we will not expand upon the different kinds of values that we can encode but instead show an alternative way to encode them.

When encoding dynamic types with the standard.dynamic mapping we can encode the following lua values: booleans, numbers, strings, nil and tables. The standard mapping can't encode userdata, functions or thread values. In a later tutorial Creating Generic Lua Value Mappings we will see how to encode those types of values dynamically as well.

There are two primary reasons to encode with dynamic typing. The first reason is convenience, the standard.dynamic mapping can encode most lua values and for simple use cases this can be good enough. The second reason is that there are times when we don't know what kind of data we are encoding. An example of such a scenario that is common when writing C style libraries is that types in these libraries contain a user data pointer or variable. In this variable users of the library can store whatever custom information they want, this custom information could be of any type and the library writer cannot make assumptions of the type when creating mappings.

In previous tutorials we have sometimes done decoding without specifying a mapping to use for decoding, when we do this what happens is that the encoding.decode method defaults to the standard.dynamic mapping for decoding. This mapping looks for metadata in the stream and uses that data to decode the value. How this works will be covered in the more advanced tutorial Creating a Dynamic Decoder.

Unlike other mappings in the encoding.standard module the standard.dynamic is not an aggregate type but a primitive type, that is standard.dynamic is a mapping and not a function that creates a mapping.

Usage Examples

local tier = require"tier"

--The dynamic mapping
local dynamic = tier.standard.dynamic

local output = io.open("Dynamics.dat", "wb")

--Encode a string
tier.encode(output, "some string", dynamic)

--Encode a number
--The tier.encode function defaults to using dynamic tier
--so the _dynamic_ mapping can be eluded if wanted.
tier.encode(output, 132)

--Encode a boolean
tier.encode(output, true)

--Encode nothing
tier.encode(output, nil)

--Encode a more complex table
tier.encode(output, { a = "Hello", b = 412, c = false, d = { 1, 2, 3 } })
output:close()

local input = io.open("Dynamics.dat", "rb")

--The decoding function defaults to using dynamic decoding.
--This decoding is the same as standard.dynamic so it can be 
--eluded. 
local string  = tier.decode(input, dynamic)
local number  = tier.decode(input)
local boolean = tier.decode(input)
local null    = tier.decode(input)
local table   = tier.decode(input)
input:close()

Guidelines

If you are at all concerned with efficiency you should only use dynamic encoding when you don't know what values are going to be encoded. Encoding values dynamically takes time since we have to do some runtime reflection on the values before we can encode them. The output stream can also be much larger (about 1.5x to 5x depending on what is encoded) then when values are encoded with an appropriate mapping.

This marks the end for this tutorial in the next tutorial Encoding Graphs we will cover encoding of cyclic data data-structures such as trees and graphs.

Clone this wiki locally