-
Notifications
You must be signed in to change notification settings - Fork 1
Encoding References
This tutorial show how to encode value references. The mapping we will be focusing on in this tutorial is standard.object which is located in the tier.standard submodule.
Unlike many other popular encoding formats TIER supports encoding of value references. When encoding the same value as a reference, the full value will only be encoded the first time, after the first time values will instead be encoded as references to that first encoded value. A reference mapping is an aggregate mapping that uses a single type mapping to encode and decode the full values of a particular type. The type mapping can be any kind of mapping excluding another reference mapping. A reference to a reference does not make any sense in TIER.
Encoding references has two primary purposes. The first is to make it possible to encode data structures that can contain cycles, such as graphs. The second is to save space by only encoding the same value a single time in the encoded stream. The first case will not be covered in this tutorial, instead it's covered in the Encoding Graphs tutorial.
Creation
--Creates a reference mapping
--mapping a mapping for a type we want to encode
-- as a reference
standard.object(mapping)Usage Examples
local tier = require"tier"
local standard = tier.standard
local primitive = tier.primitive
--A reference mapping of string values
local string_ref_mapping = standard.object(primitive.string)
--
local output = io.open("References.dat", "wb")
local string_list = standard.list(string_ref_mapping)
local fruit_basket =
{
"Apple", "Orange", "Apple",
"Banana", "Orange", "Pineapple",
"Banana", "Apple"
}
--Will only encode the full values "Apple", "Orange", "Banana" and "Pineapple" a
--single time. The other times will simply be instream references.
tier.encode(output, fruit_basket, string_list)
local person_ref_mapping = standard.object(standard.tuple
{
{ key = "first_name", mapping = primitive.string },
{ key = "last_name", mapping = primitive.string },
{ key = "age", mapping = primitive.uint8 }
})
local task_mapping = standard.tuple
{
{ key = "assigned", mapping = person_ref_mapping },
{ key = "description", mapping = primitive.string }
}
local task_list = standard.list(task_mapping)
local persons =
{
john = { first_name = "John", last_name = "Doe", age = 23 },
jane = { first_name = "Jane", last_name = "Doe", age = 32 }
}
local tasks =
{
{ assigned = persons.john, description = "Build a Time machine" },
{ assigned = persons.john, description = "Travel back in time" },
{ assigned = persons.jane, description = "Invent warp drive" },
{ assigned = persons.jane, description = "Visit Orion" }
}
--Encode the tasks, the persons john and jane will only be encoded a single time.
--Thus their references will be valid even after the decoding.
tier.encode(output, tasks, task_list)
output:close()
local input = io.open("References.dat", "rb")
local fruits = tier.decode(input, string_list)
local tasks = tier.decode(input, task_list)
--Make sure that the referential integrity is still there.
assert(tasks[1].assigned == tasks[2].assigned)This marks the end of this tutorial. In the next tutorial Encoding Dynamic Values we will look at how we can encode values that we don't know the type of in advance.