std/encoding/lib
std/encoding/src/lib.trb
Encoding and decoding: what other languages need reflection for (serialization, config mapping, database rows,
schemas, --help texts).
const json = Json()
const text = json.encode user
const back = json.decode<User>(text)?
A value is its constructor call. The compiler knows every type's constructor and offers it in three forms:
written (Encode), read (Decode) and described without a value (Describe). A format (Json, a database
driver, a command line parser) implements Encoder, Decoder or Describer and never sees a type. There is no tree
in between: a value is written while the type describes itself, and nothing is lost on the way.
All three forms are generated for a type whose constructor is usable from outside, over exactly the constructor's
parameters: a private field with a default is not one of them, so a cache stays out without an annotation. What
the compiler generates is ordinary code over the vocabulary, and could be written by hand:
extend User with Encode {
fn encode<Target: Encoder>(var target: Target) {
target.record "app/User"
target.field "name"
name.encode target
target.field "tags"
tags.encode target
target.finish()
}
}
The three traits take their target as a bound, not as a trait type: a format hands its own encoder over as a
var path and reads the result out of it afterwards, and the derived code is a direct call chain with no table.
EncodedValue is what a program holds where it has left the type behind, and Structure is a type's description as
a value. docs/design/ENCODING.md is the design.
This package also has the plainer kind of encoding: bytes as text and back. Base64Alphabet, Base32Alphabet and
Bytes.hexEncoded/String.hexDecoded/String.base64Decoded are RFC 4648, unrelated to Encode/Decode above - a value's own Encoder.bytes writes
raw bytes, and a format reaches for one of these only where the wire itself is text (a JWT, a !!binary YAML
scalar, a TOTP secret).
extend Bytes
extend Bytes
The operations on bytes and text that are RFC 4648's, written where they are imported from: a member another package
names is found in the module of the package that is imported, so use Buffer.hexEncoded from "std/encoding" needs
them here.
fn hexEncoded
fn hexEncoded(uppercase: Bool = false): String
These bytes as hexadecimal, two digits each, most significant nibble first: lower case unless uppercase.
extend String
extend String
The operations on text that read bytes back out of RFC 4648 text: hexadecimal and Base64. They are written here for
the same reason as the ones on Bytes: a member is found in the module of the package that is imported.
Examples
print "48 65 6c 6c 6f".hexDecoded()
print "SGVsbG8".base64Decoded()
fn hexDecoded
fn hexDecoded(): Result<Bytes, EncodingError>
The bytes this hexadecimal text stands for, upper and lower case digits both read, mixed or not, with whitespace inside it skipped.
Errors
EncodingErrorfor a character that is not a hexadecimal digit, or an odd number of them.
fn base64Decoded
fn base64Decoded(): Result<Bytes, EncodingError>
The bytes this Base64 text stands for, in either alphabet, padded or not, with whitespace inside it skipped - what a wrapped MIME block and a bare token both need read back.
Errors
EncodingErrorfor a character outside both alphabets, padding before the end, or a length no encoder writes.
trait Encode
trait Encode
A value writes itself into any format.
Generated for a type whose constructor is usable from outside and whose passable fields are all Encode, and for a
capsule through its conversion pair. The target is a bound, so the call is monomorphized per format.
Examples
type Point {
x: Int
y: Int
}
print Json().encode(Point(1, 2))
Related
fn encode
fn encode<Target: Encoder>(var target: Target)
Writes this value into target: the type's constructor call as record or variant, field and the field's own
encode, and finish; a scalar or a collection calls its one vocabulary method instead. Nothing is returned, the
result is read out of the target afterwards.
Examples
type Point {
x: Int
y: Int
}
var target = Values()
const point = Point 1, 2
point.encode target
print target.result()
trait Decode
trait Decode
A value reads itself back. Derived under the same condition as Encode, so what is written can always be read.
A field with a default may be missing from the input, and the default is only evaluated then.
Related
DecodeError- what a failure says, with the field chain it happened in.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
Reads one value of this type out of source, asking the decoder for exactly what the constructor expects.
Errors
DecodeErrorwhen the source holds something else than the type asks for, a field it needs is missing, or a narrow number does not fit; the error names the path of fields and positions it happened at.
Examples
var source = ValueDecoder.of EncodedValue.Sequence([EncodedValue.Int(1), EncodedValue.Int(2)])
print List<Int>.decode(source)
trait Describe
trait Describe
A type describes its structure without a value: what schema-driven formats need (a DDL statement, a .proto
file, a JSON Schema, a --help text). It is the same constructor again, with every field's doc comment and whether
the field has a default.
Related
Describer- what a schema format implements.structureOf- the description as aStructurevalue, for a format that wants a tree.
fn describe
static fn describe<Target: Describer>(var target: Target)
Describes this type into target, without a value: the same constructor as Encode.encode, walked as a shape.
A type that contains itself is described once and referred to by name after that.
Examples
var target = Structures()
Int.describe target
print target.result()
type DecodeError
type DecodeError with Show, Error
What went wrong reading a value, with the whole chain of fields and positions it went wrong in:
items[2].price.currency: a value is needed. A field is written after a ., a position of a sequence or a tuple in
brackets, and an entry of a map after a . with its key.
A derived decode reads past a field that failed and fails once at the end: its error carries every failed field in
problems, each with its own path, in the order the fields were read. Its own message and path are those of the
first problem, so code that looks at one problem only still finds one.
Examples
const problem = DecodeError("a value is needed").inside("currency").inside("price").at(2).inside("items")
print problem
field message
message: String
What was wrong, without the place.
field path
path: List<String> = []
The fields and positions from the outermost value down to where it went wrong.
field problems
problems: List<DecodeError> = []
Every failed field of a derived decode that read on after the first one, each with its whole path. Empty for an
error that stands for one problem only, such as one written by hand.
fn collecting
static fn collecting(problems: List<DecodeError>): Self
One error for the problems of a value, at least one: the first one's message and path, and all of them.
fn inside
fn inside(segment: String): Self
error.inside("email"): an error collects its path on the way out, outermost segment first.
fn each
fn each(): List<DecodeError>
The problems this error stands for: its problems, or the error itself where it stands for one.
fn at
fn at(position: Int): Self
error.at(2): the position of a sequence or a tuple the error happened in, shown as [2].
fn show
fn show(): String
One problem as items[2].name: a text is needed, and several as one such line each.
trait Encoder
trait Encoder
The writing half of a format. The scalar methods are named after the types of the language, so a format learns no second vocabulary; narrow numbers are widened on the way in and checked on the way back.
sequence, map, record and variant open, and finish closes the innermost of them. A record's field is
announced with field and its value is written right after. A record that announces no field at all is a wrapper
around the one value it holds, and a text format writes that value without anything around it.
Related
Decoder- the reading half, with the same method names.Values- the encoder that builds anEncodedValue.
fn nothing
var fn nothing()
An absent value: None. A field written as nothing may be left out, and its default applies on the way back.
fn bool
var fn bool(value: Bool)
A truth value.
fn int
var fn int(value: Int64)
A signed integer; every narrower signed or unsigned type arrives here widened to an Int64.
fn unsigned
var fn unsigned(value: UInt64)
An unsigned integer; it has its own method because the upper half of a UInt64 does not fit into an Int64.
fn float
var fn float(value: Float64)
A floating point number; a Float32 arrives widened to a Float64.
fn decimal
var fn decimal(value: Decimal)
An exact decimal number; a format without one has to spell it, as Values does with its text.
fn string
var fn string(value: String)
A text, which also stands for a Char.
fn bytes
var fn bytes(value: Bytes)
Raw bytes, not text: a format chooses how its wire carries them.
fn sequence
var fn sequence(length: Int?)
length is known for a collection and unknown for a pipeline: a binary format writes it in front.
fn map
var fn map(length: Int?)
A map of length entries, each written as its key and then its value.
fn record
var fn record(typeName: String)
typeName is the qualified name of the declaration ("app/orders/Price"): the key a format finds a mapping by.
fn variant
var fn variant(typeName: String, name: String)
One case of a type with cases. Its fields follow as a record's do.
fn field
var fn field(name: String)
The name of the field whose value comes next.
fn finish
var fn finish()
Closes the innermost sequence, map, record or variant.
fn writesPrivateFields
fn writesPrivateFields(): Bool
Whether a derived encode writes the fields that are no parameter of the constructor from outside, too: a
private field with a default. A format writes a type's data and answers false; a copy of the whole value
answers true (Values(privateFields: true)), such as the value a native binary hands a script in its VM
(std/sandbox, docs/design/SCRIPTS.md section 5). A field whose type is not Encode is left out either way.
fn flagsAsNames
fn flagsAsNames(): Bool
Whether a Flags is written as the list of the names of its cases (["Read", "Write"]) and not as its mask (3).
A format answers false, the number, unless it is told otherwise (Json(flagsAsNames: true)): how a set of flags
looks on the wire is a convention of the format, not a question the type asks.
trait Decoder
trait Decoder
The reading half of a format. Reading is driven by the type: it asks for what it expects, and gets a DecodeError
when something else is there. A format with unordered fields buffers one record at a time; a sequence streams.
Pitfalls
hasNext both tests and positions: it answers true while one more item of the open sequence (or one more key or
value of the open map) follows, and the next read takes it.
fn nothing
var fn nothing(): Bool
Consumes a "nothing" if one is there. This is how Option decides between None and Some.
fn bool
var fn bool(): Result<Bool, DecodeError>
Reads a truth value, or fails when the input holds something else.
fn int
var fn int(): Result<Int64, DecodeError>
Reads a signed integer as an Int64; a narrower type checks the range itself afterwards.
fn unsigned
var fn unsigned(): Result<UInt64, DecodeError>
Reads an unsigned integer as a UInt64, or fails for a negative or non-numeric input.
fn float
var fn float(): Result<Float64, DecodeError>
Reads a floating point number as a Float64.
fn decimal
var fn decimal(): Result<Decimal, DecodeError>
Reads an exact decimal number, or fails when the input is not one.
fn string
var fn string(): Result<String, DecodeError>
Reads a text.
fn bytes
var fn bytes(): Result<Bytes, DecodeError>
Reads raw bytes.
fn sequence
var fn sequence(): Result<Int?, DecodeError>
Opens a sequence and answers its length where the format writes one in front.
fn map
var fn map(): Result<Int?, DecodeError>
Opens a map. Its entries are read as a key, then a value, while hasNext answers true.
fn hasNext
var fn hasNext(): Result<Bool, DecodeError>
true while one more item of the open sequence or one more key or value of the open map follows.
fn record
var fn record(typeName: String): Result<Void, DecodeError>
Opens a record. A wrapper's record holds one value and no field.
fn variant
var fn variant(typeName: String): Result<String, DecodeError>
Opens a variant and answers which case is there.
fn field
var fn field(name: String): Result<Bool, DecodeError>
Positions on the field name. false when the input has no such field: the field's default applies.
fn finish
var fn finish(): Result<Void, DecodeError>
Closes the innermost sequence, map, record or variant.
fn readsPrivateFields
fn readsPrivateFields(): Bool
Whether a derived decode reads the fields that are no parameter of the constructor from outside, too, where the
input has them: the other half of Encoder.writesPrivateFields. A field the input does not have takes its default.
fn flagsAsNames
fn flagsAsNames(): Bool
Whether a Flags is read from the list of the names of its cases and not from its mask: the other half of
Encoder.flagsAsNames. With the option set only the names are read; a number is a DecodeError.
trait Describer
trait Describer
The describing half: the same method names as Encoder, without values. record and variant answer a Bool:
false means "this type is already known here", which is what makes a type that contains itself terminate - the
name has been handed over before the answer, so the target can refer to the type it declined.
Related
Structures- the describer that builds aStructure.
fn bool
var fn bool()
The type is a truth value.
fn int
var fn int()
The type is a signed integer; narrow signed and unsigned types describe themselves this way, too.
fn unsigned
var fn unsigned()
The type is an unsigned 64-bit integer.
fn float
var fn float()
The type is a floating point number.
fn decimal
var fn decimal()
The type is an exact decimal number.
fn string
var fn string()
The type is a text; a Char is one, too.
fn bytes
var fn bytes()
The type is a list of raw bytes.
fn optional
var fn optional()
The description that follows is the one that may be absent. finish closes it.
fn sequence
var fn sequence()
A sequence: the one description that follows is its item's. finish closes it.
fn map
var fn map()
A map: the two descriptions that follow are its key's and its value's. finish closes it.
fn record
var fn record(typeName: String): Bool
Opens a record. false: this target knows the type already and does not want the body again.
fn variant
var fn variant(typeName: String): Bool
Opens a type with cases, as record does.
fn variantCase
var fn variantCase(name: String)
One case of the open variant. Its fields follow, finish closes it.
fn field
var fn field(description: FieldDescription)
The next description belongs to this field.
fn finish
var fn finish()
Closes the innermost open description.
trait Format
trait Format<Failure>
What a whole format is, once: bytes in both directions, and the two Stages that make it work on a stream.
Encode/Decode and Encoder/Decoder stay synchronous, and that is the point. A decoder that could wait would
have to be written differently for every source, every derived implementation would have to change with it, and the
whole standard library would be coloured by it. Streaming happens one level up, at the level it happens in practice:
the element.
body.through(Json.items<User>()).checked() // bytes in, one `User` at a time out, memory = one user
Source.from(users).through(Json.encoded<User>()) // and back
items is a resumable framer: it finds where one element ends in whatever bytes have arrived - across chunk
borders - and decodes that element synchronously, through the ordinary Decode.
Failure is the type parameter because the language has no associated types: Json is a Format<JsonError>, and
a function that works for any format says fn load<Chosen: Format<Problem>, Problem>(...). The members are static
and use the format's default options; a format value with options has its own encode and decode.
fn encodeAll
static fn encodeAll<Value: Encode>(value: Value): Bytes
The whole value, as the bytes that go into a file or onto a wire.
fn decodeAll
static fn decodeAll<Value: Decode>(bytes: Bytes): Result<Value, Failure>
One whole value out of all of its bytes.
fn items
static fn items<Item: Decode>(): Stage<Bytes, Result<Item, Failure>>
Bytes to items. Fallible, so the items are Results - a Stage is synchronous and knows nothing about the
failure of the stream around it; source.checked() is where they become one.
fn encoded
static fn encoded<Item: Encode>(): Stage<Item, Bytes>
Items to bytes: the other direction, and the one that never fails.
extend Bool with Encode, Decode, Describe
extend Bool with Encode, Decode, Describe
Bool is the vocabulary's own bool, in all three forms.
extend Int64 with Encode, Decode, Describe
extend Int64 with Encode, Decode, Describe
Int64 is the vocabulary's own int, in all three forms.
extend Int8 with Encode, Decode, Describe
extend Int8 with Encode, Decode, Describe
A narrow integer travels as an Int64 and is checked on the way back: a value that does not fit is an error.
extend Int16 with Encode, Decode, Describe
extend Int16 with Encode, Decode, Describe
A narrow integer travels as an Int64 and is checked on the way back: a value that does not fit is an error.
extend Int32 with Encode, Decode, Describe
extend Int32 with Encode, Decode, Describe
A narrow integer travels as an Int64 and is checked on the way back: a value that does not fit is an error.
extend UInt8 with Encode, Decode, Describe
extend UInt8 with Encode, Decode, Describe
A narrow unsigned integer travels as an Int64, which holds every one of its values, and is checked on the way back.
extend UInt16 with Encode, Decode, Describe
extend UInt16 with Encode, Decode, Describe
A narrow unsigned integer travels as an Int64, which holds every one of its values, and is checked on the way back.
extend UInt32 with Encode, Decode, Describe
extend UInt32 with Encode, Decode, Describe
A narrow unsigned integer travels as an Int64, which holds every one of its values, and is checked on the way back.
extend UInt64 with Encode, Decode, Describe
extend UInt64 with Encode, Decode, Describe
UInt64 is the vocabulary's own unsigned: its upper half does not fit into an Int64.
extend Float32 with Encode, Describe
extend Float32 with Encode, Describe
A Float32 travels as a Float64, which holds it exactly. It has no Decode yet: one would read the Float64 and
narrow it with Float32.tryFrom, which rounds to the nearest Float32 and fails only beyond its range.
extend Float64 with Encode, Decode, Describe
extend Float64 with Encode, Decode, Describe
Float64 is the vocabulary's own float, in all three forms.
extend Decimal with Encode, Decode, Describe
extend Decimal with Encode, Decode, Describe
Decimal is the vocabulary's own decimal: an exact number stays exact in a format that can say so.
extend Char with Encode, Decode, Describe
extend Char with Encode, Decode, Describe
A Char travels as a text of one character: the formats of the language have no character of their own.
extend String with Encode, Decode, Describe
extend String with Encode, Decode, Describe
String is the vocabulary's own string, in all three forms.
extend Option<Value> with Encode
extend<Value: Encode> Option<Value> with Encode
Some is its value, and None is nothing() - which a field may leave out, so its default applies.
fn encode
fn encode<Target: Encoder>(var target: Target)
extend Option<Value> with Decode
extend<Value: Decode> Option<Value> with Decode
A "nothing" is None, and anything else is the value's own decode.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Value?, DecodeError>
extend Option<Value> with Describe
extend<Value: Describe> Option<Value> with Describe
An optional value describes as optional() around the value's own structure.
fn describe
static fn describe<Target: Describer>(var target: Target)
extend List<Item> with Encode
extend<Item: Encode> List<Item> with Encode
A list is a sequence: its length in front, then every item with its own encode.
fn encode
fn encode<Target: Encoder>(var target: Target)
extend List<Item> with Decode
extend<Item: Decode> List<Item> with Decode
A sequence, item by item, each through the item's own decode. An error names the position it happened at, and an
item that failed does not stop the ones after it: the list fails at its end, with every item's problems.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
extend List<Item> with Describe
extend<Item: Describe> List<Item> with Describe
A list describes as a sequence around its item's structure: the shape is one item, repeated.
fn describe
static fn describe<Target: Describer>(var target: Target)
extend ArrayList<Item> with Decode
extend<Item: Decode> ArrayList<Item> with Decode
An ArrayList reads back as the list it is written as - a sequence, item by item - so a field of one does not keep
the type around it from having a derived decode (Chunk of the bytecode, whose code is one). It writes through the
encode of List.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
extend ArrayList<Item> with Describe
extend<Item: Describe> ArrayList<Item> with Describe
An ArrayList describes as the sequence of List.
fn describe
static fn describe<Target: Describer>(var target: Target)
extend Buffer<Item> with Decode
extend<Item: Decode> Buffer<Item> with Decode
A Buffer reads back as the list it is written as, for the reason an ArrayList does: a field of one, Bytes in
the first place, does not keep the type around it from having a derived decode.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
extend Buffer<Item> with Describe
extend<Item: Describe> Buffer<Item> with Describe
A Buffer describes as the sequence of List.
fn describe
static fn describe<Target: Describer>(var target: Target)
extend Flags<Case> with Encode
extend<Case: RawValue & Encode> Flags<Case> with Encode
Flags are their mask, a number, unless the target asks for names (Encoder.flagsAsNames): then they are a sequence
of the case names, each through the case's own encode, the lowest bit first.
fn encode
fn encode<Target: Encoder>(var target: Target)
extend Flags<Case> with Decode
extend<Case: RawValue & Decode> Flags<Case> with Decode
The way back of Flags' encode: a mask through fromBits, which refuses a bit no case stands for, or - where the
source asks for names (Decoder.flagsAsNames) - a sequence of case names, each through the case's own decode. A
name that is no case, and a value that is no sequence, are a DecodeError with the place it happened at.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
extend Set<Item> with Encode
extend<Item: Encode> Set<Item> with Encode
A set is a sequence of its items.
fn encode
fn encode<Target: Encoder>(var target: Target)
extend Set<Item> with Decode
extend<Item: Decode & Hash> Set<Item> with Decode
A sequence read into a set: an item that is there twice is there once.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
extend Set<Item> with Describe
extend<Item: Describe> Set<Item> with Describe
A set describes as a sequence of its item.
fn describe
static fn describe<Target: Describer>(var target: Target)
extend Map<Key, Value> with Encode
extend<Key: Encode, Value: Encode> Map<Key, Value> with Encode
A map is a map: its length in front, then every entry as its key and its value.
fn encode
fn encode<Target: Encoder>(var target: Target)
extend Map<Key, Value> with Decode
extend<Key: Decode & Hash & Show, Value: Decode> Map<Key, Value> with Decode
A map, entry by entry: a key, then its value. An error in a value names the key it belongs to, and the entries after it are read all the same, as a list's items are.
fn decode
static fn decode<Source: Decoder>(var source: Source): Result<Self, DecodeError>
extend Map<Key, Value> with Describe
extend<Key: Describe, Value: Describe> Map<Key, Value> with Describe
A map describes as a map around its key's and its value's structure.
fn describe
static fn describe<Target: Describer>(var target: Target)