Integrate BACKBEAT SDK and resolve KACHING license validation

Major integrations and fixes:
- Added BACKBEAT SDK integration for P2P operation timing
- Implemented beat-aware status tracking for distributed operations
- Added Docker secrets support for secure license management
- Resolved KACHING license validation via HTTPS/TLS
- Updated docker-compose configuration for clean stack deployment
- Disabled rollback policies to prevent deployment failures
- Added license credential storage (CHORUS-DEV-MULTI-001)

Technical improvements:
- BACKBEAT P2P operation tracking with phase management
- Enhanced configuration system with file-based secrets
- Improved error handling for license validation
- Clean separation of KACHING and CHORUS deployment stacks

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-09-06 07:56:26 +10:00
parent 543ab216f9
commit 9bdcbe0447
4730 changed files with 1480093 additions and 1916 deletions

79
vendor/github.com/polydawn/refmt/obj/atlas/atlas.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
package atlas
import (
"fmt"
"reflect"
)
type Atlas struct {
// Map typeinfo to a static description of how that type should be handled.
// (The internal machinery that will wield this information, and has memory of
// progress as it does so, is configured using the AtlasEntry, but allocated separately.
// The machinery is stateful and mutable; the AtlasEntry is not.)
//
// We use 'var rtid uintptr = reflect.ValueOf(rt).Pointer()' -- pointer of the
// value of the reflect.Type info -- as an index.
// This is both unique and correctly converges when recomputed, and much
// faster to compare against than reflect.Type (which is an interface that
// tends to contain fairly large structures).
mappings map[uintptr]*AtlasEntry
// Mapping of tag ints to atlasEntry for quick lookups when the
// unmarshaller hits a tag. Values are a subset of `mappings`.
tagMappings map[int]*AtlasEntry
// MapMorphism specifies the default map sorting scheme
defaultMapMorphism *MapMorphism
}
func Build(entries ...*AtlasEntry) (Atlas, error) {
atl := Atlas{
mappings: make(map[uintptr]*AtlasEntry),
tagMappings: make(map[int]*AtlasEntry),
defaultMapMorphism: &MapMorphism{KeySortMode_Default},
}
for _, entry := range entries {
rtid := reflect.ValueOf(entry.Type).Pointer()
if _, exists := atl.mappings[rtid]; exists {
return Atlas{}, fmt.Errorf("repeated entry for type %v", entry.Type)
}
atl.mappings[rtid] = entry
if entry.Tagged == true {
if prev, exists := atl.tagMappings[entry.Tag]; exists {
return Atlas{}, fmt.Errorf("repeated tag %v on type %v (already mapped to type %v)", entry.Tag, entry.Type, prev.Type)
}
atl.tagMappings[entry.Tag] = entry
}
}
return atl, nil
}
func MustBuild(entries ...*AtlasEntry) Atlas {
atl, err := Build(entries...)
if err != nil {
panic(err)
}
return atl
}
func (atl Atlas) WithMapMorphism(m MapMorphism) Atlas {
atl.defaultMapMorphism = &m
return atl
}
// Gets the AtlasEntry for a typeID. Used by obj package, not meant for user facing.
func (atl Atlas) Get(rtid uintptr) (*AtlasEntry, bool) {
ent, ok := atl.mappings[rtid]
return ent, ok
}
// Gets the AtlasEntry for a tag int. Used by obj package, not meant for user facing.
func (atl Atlas) GetEntryByTag(tag int) (*AtlasEntry, bool) {
ent, ok := atl.tagMappings[tag]
return ent, ok
}
// Gets the default map morphism config. Used by obj package, not meant for user facing.
func (atl Atlas) GetDefaultMapMorphism() *MapMorphism {
return atl.defaultMapMorphism
}

View File

@@ -0,0 +1,10 @@
package atlas
// A type to enumerate key sorting modes.
type KeySortMode string
const (
KeySortMode_Default = KeySortMode("default") // the default mode -- for structs, this is the source-order of the fields; for maps, it's identify to "strings" sort mode.
KeySortMode_Strings = KeySortMode("strings") // lexical sort by strings. this *is* the default for maps; it overrides source-order sorting for structs.
KeySortMode_RFC7049 = KeySortMode("rfc7049") // "Canonical" as proposed by rfc7049 § 3.9 (shorter byte sequences sort to top).
)

View File

@@ -0,0 +1,150 @@
package atlas
import (
"reflect"
)
/*
The AtlasEntry is a declarative roadmap of what we should do for
marshal and unmarshal of a single object, keyed by type.
There are a lot of paths your mappings might want to take:
- For a struct type, you may simply want to specify some alternate keys, or some to leave out, etc.
- For an interface type, you probably want to specify one of our interface muxing strategies
with a mapping between enumstr:typeinfo (and, what to do if we get a struct we don't recognize).
- For a string, int, or other primitive, you don't need to say anything: defaults will DTRT.
- For a typedef'd string, int, or other primitive, you *still* don't need to say anything: but,
if you want custom behavior (say, transform the string to an int at the last second, and back again),
you can specify transformer functions for that.
- For a struct type that you want to turn into a whole different kind (like a string): use
those same transform functions. (You'll no longer need a FieldMap.)
- For the most esoteric needs, you can fall all the way back to providing a custom MarshalMachine
(but avoid that; it's a lot of work, and one of these other transform methods should suffice).
*/
type AtlasEntry struct {
// The reflect info of the type this morphism is regarding.
Type reflect.Type
// --------------------------------------------------------
// The big escape valves: wanna map to some other kind completely?
// --------------------------------------------------------
// Transforms the value we reached by walking (the 'live' value -- which
// must be of `this.Type`) into another value (the 'serialable' value --
// which will be of `this.MarshalTransformTargetType`).
//
// The target type may be anything, even of a completely different Kind!
//
// This transform func runs first, then the resulting value is
// serialized (by running through the path through Atlas again, so
// chaining of transform funcs is supported, though not recommended).
MarshalTransformFunc MarshalTransformFunc
// The type of value we expect after using the MarshalTransformFunc.
//
// The match between transform func and target type should be checked
// during construction of this AtlasEntry.
MarshalTransformTargetType reflect.Type
// Expects a different type (the 'serialable' value -- which will be of
// 'this.UnmarshalTransformTargetType') than the value we reached by
// walking (the 'live' value -- which must be of `this.Type`).
//
// The target type may be anything, even of a completely different Kind!
//
// The unmarshal of that target type will be run first, then the
// resulting value is fed through this function to produce the real value,
// which is then placed correctly into bigger mid-unmarshal object tree.
//
// For non-primitives, unmarshal of the target type will always target
// an empty pointer or empty slice, roughly as per if it was
// operating on a value produced by `TargetType.New()`.
UnmarshalTransformFunc UnmarshalTransformFunc
// The type of value we will manufacture an instance of and unmarshal
// into, then when done provide to the UnmarshalTransformFunc.
//
// The match between transform func and target type should be checked
// during construction of this AtlasEntry.
UnmarshalTransformTargetType reflect.Type
// --------------------------------------------------------
// Standard options for how to map (varies by Kind)
// --------------------------------------------------------
// A "tag" to emit when marshalling this type of value;
// and when unmarshalling, this tag will cause unmarshal to pick
// this atlas (and if there's conflicting type info, error).
Tag int
// Flag for whether the Tag feature should be used (zero is a valid tag).
Tagged bool
// A mapping of fields in a struct to serial keys.
// Only valid if `this.Type.Kind() == Struct`.
StructMap *StructMap
// Configuration for how to traverse a map kind.
// Only valid if `this.Type.Kind() == Map`.
MapMorphism *MapMorphism
// Configuration for how to pick concrete types to fill a union interface.
// Only valid if `this.Type.Kind() == Interface`.
UnionKeyedMorphism *UnionKeyedMorphism
// FUTURE: enum-ish primitives, multiplexers for interfaces,
// lots of such things will belong here.
// --------------------------------------------------------
// Hooks, validate helpers
// --------------------------------------------------------
// A validation function which will be called for the whole value
// after unmarshalling reached the end of the object.
// If it returns an error, the entire unmarshal will error.
//
// Not used in marshalling.
// Not reachable if an UnmarshalTransform is set.
ValidateFn func(v interface{}) error
}
func BuildEntry(typeHintObj interface{}) *BuilderCore {
rt := reflect.TypeOf(typeHintObj)
if rt.Kind() == reflect.Ptr {
if rt.Elem().Kind() == reflect.Interface {
rt = rt.Elem()
} else {
panic("invalid atlas build: use the bare object, not a pointer (refmt will handle pointers automatically)")
}
}
return &BuilderCore{
&AtlasEntry{Type: rt},
}
}
/*
Intermediate step in building an AtlasEntry: use `BuildEntry` to
get one of these to start with, then call one of the methods
on this type to get a specialized builder which has the methods
relevant for setting up that specific kind of mapping.
One full example of using this builder may look like the following:
atlas.BuildEntry(Formula{}).StructMap().Autogenerate().Complete()
Some intermediate manipulations may be performed on this object,
for example setting the "tag" (if you want to use cbor tagging),
before calling the specializer method.
In this case, just keep chaining the configuration calls like so:
atlas.BuildEntry(Formula{}).UseTag(4000)
.StructMap().Autogenerate().Complete()
*/
type BuilderCore struct {
entry *AtlasEntry
}
func (x *BuilderCore) UseTag(tag int) *BuilderCore {
x.entry.Tagged = true
x.entry.Tag = tag
return x
}

45
vendor/github.com/polydawn/refmt/obj/atlas/doc.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
/*
Atlas types are used to define how to map Go values into refmt token streams.
Atlas information may be autogenerated based on struct tags automatically,
but you can also specify custom AtlasEntry info to use advanced features
and define custom transformations.
An Atlas is a collection of AtlasEntry (plus some internal indexing).
Typical usage is to declare an AtlasEntry for your structs (often near by the
struct definition), then
Building an AtlasEntry for some type called `Formula` looks like this:
atlas.BuildEntry(Formula{}).StructMap().Autogenerate().Complete()
Building an AtlasEntry always starts with `atlas.BuildEntry(x)` where `x` is
a dummy object used to convey type information.
The next function in the chain declares what kind of behavior we're going
to use to turn that type of object into its serial form.
(In the above example, we're declaring that we want refmt to see the `Formula`
type as a struct and traverse its fields. There are many other options!)
Subsequent functions are specific to what kind of walking and mapping we've
chosen. For struct walking, this may involve declaring fields and custom serial
names to map them to; for a "Transform" we'd instead have to provide callbacks
to do the transformation from the `Formula` type to some other type; etcetera.
The final function in the chain is always called `Complete`, and returns
a ready-to-use AtlasEntry.
Building a complete Atlas for a whole suite of serializable types is as
easy as putting a bunch of them together:
atlas.Build(
atlas.BuildEntry(Foo{}).StructMap().Autogenerate().Complete(),
atlas.BuildEntry(Bar{}).StructMap().Autogenerate().Complete(),
atlas.BuildEntry(Baz{}).StructMap().Autogenerate().Complete(),
)
You can put your entire protocol into one Atlas.
It's also possible to build several different Atlases each with different
sets of AtlasEntry. This may be useful if you have a protocol where some
messages are not valid during some phases of communication, and you would
like to use the Atlas as a form of whitelisting for what can be
marshalled/unmarshalled.
*/
package atlas

14
vendor/github.com/polydawn/refmt/obj/atlas/errors.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
package atlas
// Error type raised when initializing an Atlas, and field entries do
// not resolve against the type.
// (If you recently refactored names of fields in your types, check
// to make sure you updated any references to those fields by name to match!)
type ErrStructureMismatch struct {
TypeName string
Reason string
}
func (e ErrStructureMismatch) Error() string {
return "structure mismatch: " + e.TypeName + " " + e.Reason
}

View File

@@ -0,0 +1,38 @@
package atlas
import (
"fmt"
"reflect"
)
type MapMorphism struct {
KeySortMode KeySortMode
}
func (x *BuilderCore) MapMorphism() *BuilderMapMorphism {
if x.entry.Type.Kind() != reflect.Map {
panic(fmt.Errorf("cannot use mapMorphism for type %q, which is kind %s", x.entry.Type, x.entry.Type.Kind()))
}
x.entry.MapMorphism = &MapMorphism{
KeySortMode_Default,
}
return &BuilderMapMorphism{x.entry}
}
type BuilderMapMorphism struct {
entry *AtlasEntry
}
func (x *BuilderMapMorphism) Complete() *AtlasEntry {
return x.entry
}
func (x *BuilderMapMorphism) SetKeySortMode(km KeySortMode) *BuilderMapMorphism {
switch km {
case KeySortMode_Default, KeySortMode_Strings, KeySortMode_RFC7049:
x.entry.MapMorphism.KeySortMode = km
default:
panic(fmt.Errorf("invalid key sort mode %q", km))
}
return x
}

View File

@@ -0,0 +1,46 @@
package atlas
import "reflect"
type StructMap struct {
// A slice of descriptions of each field in the type.
// Each entry specifies the name by which each field should be referenced
// when serialized, and defines a way to get an address to the field.
Fields []StructMapEntry
}
type StructMapEntry struct {
// The field name; will be emitted as token during marshal, and used for
// lookup during unmarshal. Required.
SerialName string
// If true, a key token with this SerialName will be ignored during unmarshal.
// (By default, if there's no StructMapEntry for a key token, it's an error.)
// If true, the ReflectRoute, Type, etc fields are irrelevant and may be nil.
Ignore bool
ReflectRoute ReflectRoute // reflection generates these.
Type reflect.Type // type to expect on the far side of the ReflectRoute.
tagged bool // used during autogen.
// Theoretical feature which would be alternative to ReflectRoute. Support dropped for the moment.
//addrFunc func(interface{}) interface{} // custom user function.
// If true, marshalling will skip this field if it's the zero value.
OmitEmpty bool
}
type ReflectRoute []int
func (rr ReflectRoute) TraverseToValue(v reflect.Value) reflect.Value {
for _, i := range rr {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{}
}
v = v.Elem()
}
v = v.Field(i)
}
return v
}

View File

@@ -0,0 +1,320 @@
package atlas
import (
"fmt"
"reflect"
"sort"
"strings"
"unicode"
)
func AutogenerateStructMapEntry(rt reflect.Type) *AtlasEntry {
return AutogenerateStructMapEntryUsingTags(rt, "refmt", KeySortMode_Default)
}
func AutogenerateStructMapEntryUsingTags(rt reflect.Type, tagName string, sorter KeySortMode) *AtlasEntry {
if rt.Kind() != reflect.Struct {
panic(fmt.Errorf("cannot use structMap for type %q, which is kind %s", rt, rt.Kind()))
}
entry := &AtlasEntry{
Type: rt,
StructMap: &StructMap{Fields: exploreFields(rt, tagName, sorter)},
}
return entry
}
// exploreFields returns a list of fields that StructAtlas should recognize for the given type.
// The algorithm is breadth-first search over the set of structs to include - the top struct
// and then any reachable anonymous structs.
func exploreFields(rt reflect.Type, tagName string, sorter KeySortMode) []StructMapEntry {
// Anonymous fields to explore at the current level and the next.
current := []StructMapEntry{}
next := []StructMapEntry{{Type: rt}}
// Count of queued names for current level and the next.
count := map[reflect.Type]int{}
nextCount := map[reflect.Type]int{}
// Types already visited at an earlier level.
visited := map[reflect.Type]bool{}
// Fields found.
var fields []StructMapEntry
for len(next) > 0 {
current, next = next, current[:0]
count, nextCount = nextCount, map[reflect.Type]int{}
for _, f := range current {
if visited[f.Type] {
continue
}
visited[f.Type] = true
// Scan f.Type for fields to include.
for i := 0; i < f.Type.NumField(); i++ {
sf := f.Type.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
tag := sf.Tag.Get(tagName)
if tag == "-" {
continue
}
name, opts := parseTag(tag)
if !isValidTag(name) {
name = ""
}
route := make([]int, len(f.ReflectRoute)+1)
copy(route, f.ReflectRoute)
route[len(f.ReflectRoute)] = i
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
// Follow pointer.
ft = ft.Elem()
}
// Record found field and index sequence.
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
tagged := name != ""
if name == "" {
name = downcaseFirstLetter(sf.Name)
}
fields = append(fields, StructMapEntry{
SerialName: name,
ReflectRoute: route,
Type: sf.Type,
tagged: tagged,
OmitEmpty: opts.Contains("omitempty"),
})
if count[f.Type] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don't bother generating any more copies.
fields = append(fields, fields[len(fields)-1])
}
continue
}
// Record new anonymous struct to explore in next round.
nextCount[ft]++
if nextCount[ft] == 1 {
next = append(next, StructMapEntry{
ReflectRoute: route,
Type: ft,
})
}
}
}
}
sort.Sort(StructMapEntry_byName(fields))
// Delete all fields that are hidden by the Go rules for embedded fields,
// except that fields with JSON tags are promoted.
// The fields are sorted in primary order of name, secondary order
// of field index length. Loop over names; for each name, delete
// hidden fields by choosing the one dominant field that survives.
out := fields[:0]
for advance, i := 0, 0; i < len(fields); i += advance {
// One iteration per name.
// Find the sequence of fields with the name of this first field.
fi := fields[i]
name := fi.SerialName
for advance = 1; i+advance < len(fields); advance++ {
fj := fields[i+advance]
if fj.SerialName != name {
break
}
}
if advance == 1 { // Only one field with this name
out = append(out, fi)
continue
}
dominant, ok := dominantField(fields[i : i+advance])
if ok {
out = append(out, dominant)
}
}
fields = out
switch sorter {
case KeySortMode_Default:
sort.Sort(StructMapEntry_byFieldRoute(fields))
case KeySortMode_Strings:
//sort.Sort(StructMapEntry_byName(fields))
// it's already in this order, though, so, pass
case KeySortMode_RFC7049:
sort.Sort(StructMapEntry_RFC7049(fields))
default:
panic("invalid struct sorter option")
}
return fields
}
// If the first character of the string is uppercase, return a string
// where it is switched to lowercase.
// We use this to make go field names look more like what everyone else
// in the universe expects their json to look like by default: snakeCase.
func downcaseFirstLetter(s string) string {
if s == "" {
return ""
}
r := rune(s[0]) // if multibyte chars: you're left alone.
if !unicode.IsUpper(r) {
return s
}
return string(unicode.ToLower(r)) + s[1:]
}
// dominantField looks through the fields, all of which are known to
// have the same name, to find the single field that dominates the
// others using Go's embedding rules, modified by the presence of
// JSON tags. If there are multiple top-level fields, the boolean
// will be false: This condition is an error in Go and we skip all
// the fields.
func dominantField(fields []StructMapEntry) (StructMapEntry, bool) {
// The fields are sorted in increasing index-length order. The winner
// must therefore be one with the shortest index length. Drop all
// longer entries, which is easy: just truncate the slice.
length := len(fields[0].ReflectRoute)
tagged := -1 // Index of first tagged field.
for i, f := range fields {
if len(f.ReflectRoute) > length {
fields = fields[:i]
break
}
if f.tagged {
if tagged >= 0 {
// Multiple tagged fields at the same level: conflict.
// Return no field.
return StructMapEntry{}, false
}
tagged = i
}
}
if tagged >= 0 {
return fields[tagged], true
}
// All remaining fields have the same length. If there's more than one,
// we have a conflict (two fields named "X" at the same level) and we
// return no field.
if len(fields) > 1 {
return StructMapEntry{}, false
}
return fields[0], true
}
// StructMapEntry_byName sorts field by name,
// breaking ties with depth,
// then breaking ties with "name came from tag",
// then breaking ties with FieldRoute sequence.
type StructMapEntry_byName []StructMapEntry
func (x StructMapEntry_byName) Len() int { return len(x) }
func (x StructMapEntry_byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x StructMapEntry_byName) Less(i, j int) bool {
if x[i].SerialName != x[j].SerialName {
return x[i].SerialName < x[j].SerialName
}
if len(x[i].ReflectRoute) != len(x[j].ReflectRoute) {
return len(x[i].ReflectRoute) < len(x[j].ReflectRoute)
}
if x[i].tagged != x[j].tagged {
return x[i].tagged
}
return StructMapEntry_byFieldRoute(x).Less(i, j)
}
// StructMapEntry_RFC7049 sorts fields as specified in RFC7049,
type StructMapEntry_RFC7049 []StructMapEntry
func (x StructMapEntry_RFC7049) Len() int { return len(x) }
func (x StructMapEntry_RFC7049) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x StructMapEntry_RFC7049) Less(i, j int) bool {
il, jl := len(x[i].SerialName), len(x[j].SerialName)
switch {
case il < jl:
return true
case il > jl:
return false
default:
return x[i].SerialName < x[j].SerialName
}
}
// StructMapEntry_byFieldRoute sorts field by FieldRoute sequence
// (e.g., roughly source declaration order within each type).
type StructMapEntry_byFieldRoute []StructMapEntry
func (x StructMapEntry_byFieldRoute) Len() int { return len(x) }
func (x StructMapEntry_byFieldRoute) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x StructMapEntry_byFieldRoute) Less(i, j int) bool {
for k, xik := range x[i].ReflectRoute {
if k >= len(x[j].ReflectRoute) {
return false
}
if xik != x[j].ReflectRoute[k] {
return xik < x[j].ReflectRoute[k]
}
}
return len(x[i].ReflectRoute) < len(x[j].ReflectRoute)
}
// tagOptions is the string following a comma in a struct field's
// tag, or the empty string. It does not include the leading comma.
type tagOptions string
// parseTag splits a struct field's tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx], tagOptions(tag[idx+1:])
}
return tag, tagOptions("")
}
// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {
if len(o) == 0 {
return false
}
s := string(o)
for s != "" {
var next string
i := strings.Index(s, ",")
if i >= 0 {
s, next = s[:i], s[i+1:]
}
if s == optionName {
return true
}
s = next
}
return false
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
// Backslash and quote chars are reserved, but
// otherwise any punctuation chars are allowed
// in a tag name.
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
}
}
return true
}

View File

@@ -0,0 +1,96 @@
package atlas
import (
"fmt"
"reflect"
"strings"
)
func (x *BuilderCore) StructMap() *BuilderStructMap {
if x.entry.Type.Kind() != reflect.Struct {
panic(fmt.Errorf("cannot use structMap for type %q, which is kind %s", x.entry.Type, x.entry.Type.Kind()))
}
x.entry.StructMap = &StructMap{}
return &BuilderStructMap{x.entry}
}
type BuilderStructMap struct {
entry *AtlasEntry
}
func (x *BuilderStructMap) Complete() *AtlasEntry {
return x.entry
}
/*
Add a field to the mapping based on its name.
Given a struct:
struct{
X int
Y struct{ Z int }
}
`AddField("X", {"x", ...}) will cause that field to be serialized as key "x";
`AddField("Y.Z", {"z", ...})` will cause that *nested* field to be serialized
as key "z" in the same object (e.g. "x" and "z" will be siblings).
Returns the mutated builder for convenient call chaining.
If the fieldName string doesn't map onto the structure type info,
a panic will be raised.
*/
func (x *BuilderStructMap) AddField(fieldName string, mapping StructMapEntry) *BuilderStructMap {
fieldNameSplit := strings.Split(fieldName, ".")
rr, rt, err := fieldNameToReflectRoute(x.entry.Type, fieldNameSplit)
if err != nil {
panic(err) // REVIEW: now that we have the builder obj, we could just curry these into it until 'Complete' is called (or, thus, 'MustComplete'!).
}
mapping.ReflectRoute = rr
mapping.Type = rt
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, mapping)
return x
}
func (x *BuilderStructMap) IgnoreKey(serialKeyName string) *BuilderStructMap {
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, StructMapEntry{
SerialName: serialKeyName,
Ignore: true,
})
return x
}
func fieldNameToReflectRoute(rt reflect.Type, fieldNameSplit []string) (rr ReflectRoute, _ reflect.Type, _ error) {
for _, fn := range fieldNameSplit {
rf, ok := rt.FieldByName(fn)
if !ok {
return nil, nil, ErrStructureMismatch{rt.Name(), "does not have field named " + fn}
}
rr = append(rr, rf.Index...)
rt = rf.Type
}
return rr, rt, nil
}
/*
Automatically generate mappings by looking at the struct type info,
taking any hints from tags, and appending that to the builder.
You may use autogeneration in concert with manually adding field mappings,
though if doing so be mindful not to map the same fields twice.
*/
func (x *BuilderStructMap) Autogenerate() *BuilderStructMap {
autoEntry := AutogenerateStructMapEntry(x.entry.Type)
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, autoEntry.StructMap.Fields...)
return x
}
/*
Automatically generate mappings using a given struct field sorting scheme
*/
func (x *BuilderStructMap) AutogenerateWithSortingScheme(sorting KeySortMode) *BuilderStructMap {
autoEntry := AutogenerateStructMapEntryUsingTags(x.entry.Type, "refmt", sorting)
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, autoEntry.StructMap.Fields...)
return x
}

View File

@@ -0,0 +1,30 @@
package atlas
import (
"reflect"
)
func (x *BuilderCore) Transform() *BuilderTransform {
// no checks on x.entry.Type.Kind() here -- transforms can be pretty much any<->any
return &BuilderTransform{x.entry}
}
type BuilderTransform struct {
entry *AtlasEntry
}
func (x *BuilderTransform) Complete() *AtlasEntry {
return x.entry
}
func (x *BuilderTransform) TransformMarshal(trFunc MarshalTransformFunc, toType reflect.Type) *BuilderTransform {
x.entry.MarshalTransformFunc = trFunc
x.entry.MarshalTransformTargetType = toType
return x
}
func (x *BuilderTransform) TransformUnmarshal(trFunc UnmarshalTransformFunc, toType reflect.Type) *BuilderTransform {
x.entry.UnmarshalTransformFunc = trFunc
x.entry.UnmarshalTransformTargetType = toType
return x
}

View File

@@ -0,0 +1,68 @@
package atlas
import "reflect"
type MarshalTransformFunc func(liveForm reflect.Value) (serialForm reflect.Value, err error)
type UnmarshalTransformFunc func(serialForm reflect.Value) (liveForm reflect.Value, err error)
var err_rt = reflect.TypeOf((*error)(nil)).Elem()
/*
Takes a wildcard object which must be `func (live T1) (serialable T2, error)`
and returns a MarshalTransformFunc and the typeinfo of T2.
*/
func MakeMarshalTransformFunc(fn interface{}) (MarshalTransformFunc, reflect.Type) {
fn_rv := reflect.ValueOf(fn)
if fn_rv.Kind() != reflect.Func {
panic("no")
}
fn_rt := fn_rv.Type()
if fn_rt.NumIn() != 1 {
panic("no")
}
if fn_rt.NumOut() != 2 {
panic("no")
}
if !fn_rt.Out(1).AssignableTo(err_rt) {
panic("no")
}
// nothing to do for `fn_rt.In(0)` -- whatever type it is... TODO redesign to make less sketchy; we should most certainly be able to check this in the builder
out_rt := fn_rt.Out(0)
return func(liveForm reflect.Value) (serialForm reflect.Value, err error) {
results := fn_rv.Call([]reflect.Value{liveForm})
if results[1].IsNil() {
return results[0], nil
}
return results[0], results[1].Interface().(error)
}, out_rt
}
/*
Takes a wildcard object which must be `func (serialable T1) (live T2, error)`
and returns a UnmarshalTransformFunc and the typeinfo of T1.
*/
func MakeUnmarshalTransformFunc(fn interface{}) (UnmarshalTransformFunc, reflect.Type) {
fn_rv := reflect.ValueOf(fn)
if fn_rv.Kind() != reflect.Func {
panic("no")
}
fn_rt := fn_rv.Type()
if fn_rt.NumIn() != 1 {
panic("no")
}
if fn_rt.NumOut() != 2 {
panic("no")
}
if !fn_rt.Out(1).AssignableTo(err_rt) {
panic("no")
}
// nothing to do for checking `fn_rf.Out(0)` -- because we don't know what entry we're about to be used for. TODO redesign to make less sketchy.
in_rt := fn_rt.In(0)
return func(serialForm reflect.Value) (liveForm reflect.Value, err error) {
results := fn_rv.Call([]reflect.Value{serialForm})
if results[1].IsNil() {
return results[0], nil
}
return results[0], results[1].Interface().(error)
}, in_rt
}

View File

@@ -0,0 +1,45 @@
package atlas
import (
"fmt"
"reflect"
"sort"
)
type UnionKeyedMorphism struct {
// Mapping of typehint key strings to atlasEntry that should be delegated to.
Elements map[string]*AtlasEntry
// Mapping of rtid to string (roughly the dual of the Elements map).
Mappings map[uintptr]string
// Purely to have in readiness for error messaging.
KnownMembers []string
}
func (x *BuilderCore) KeyedUnion() *BuilderUnionKeyedMorphism {
if x.entry.Type.Kind() != reflect.Interface {
panic(fmt.Errorf("cannot use union morphisms for type %q, which is kind %s", x.entry.Type, x.entry.Type.Kind()))
}
x.entry.UnionKeyedMorphism = &UnionKeyedMorphism{
Elements: make(map[string]*AtlasEntry),
Mappings: make(map[uintptr]string),
}
return &BuilderUnionKeyedMorphism{x.entry}
}
type BuilderUnionKeyedMorphism struct {
entry *AtlasEntry
}
func (x *BuilderUnionKeyedMorphism) Of(elements map[string]*AtlasEntry) *AtlasEntry {
cfg := x.entry.UnionKeyedMorphism
for hint, ent := range elements {
// FIXME: check that all the delegates are... well struct or map machines really, but definitely blacklisting other delegating machinery.
// FIXME: and sanity check that they can all be assigned to the interface ffs.
cfg.Elements[hint] = ent
cfg.Mappings[reflect.ValueOf(ent.Type).Pointer()] = hint
cfg.KnownMembers = append(cfg.KnownMembers, hint)
}
sort.Strings(cfg.KnownMembers)
return x.entry
}

24
vendor/github.com/polydawn/refmt/obj/builtins.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package obj
import (
. "reflect"
)
var (
rtid_bool = ValueOf(TypeOf(false)).Pointer()
rtid_string = ValueOf(TypeOf("")).Pointer()
rtid_bytes = ValueOf(TypeOf([]byte{})).Pointer()
rtid_int = ValueOf(TypeOf(int(0))).Pointer()
rtid_int8 = ValueOf(TypeOf(int8(0))).Pointer()
rtid_int16 = ValueOf(TypeOf(int16(0))).Pointer()
rtid_int32 = ValueOf(TypeOf(int32(0))).Pointer()
rtid_int64 = ValueOf(TypeOf(int64(0))).Pointer()
rtid_uint = ValueOf(TypeOf(uint(0))).Pointer()
rtid_uint8 = ValueOf(TypeOf(uint8(0))).Pointer()
rtid_uint16 = ValueOf(TypeOf(uint16(0))).Pointer()
rtid_uint32 = ValueOf(TypeOf(uint32(0))).Pointer()
rtid_uint64 = ValueOf(TypeOf(uint64(0))).Pointer()
rtid_uintptr = ValueOf(TypeOf(uintptr(0))).Pointer()
rtid_float32 = ValueOf(TypeOf(float32(0))).Pointer()
rtid_float64 = ValueOf(TypeOf(float64(0))).Pointer()
)

6
vendor/github.com/polydawn/refmt/obj/doc.go generated vendored Normal file
View File

@@ -0,0 +1,6 @@
/*
The `obj` package defines Marshaller and Unmarshaller types,
which can be used to convert in-memory values to token streams,
and token streams to unpack in-memory values.
*/
package obj

31
vendor/github.com/polydawn/refmt/obj/empty.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package obj
import "reflect"
// The missing definition of 'reflect.IsZero' you've always wanted.
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
case reflect.Struct:
// Note: we can't rely on a *literal* "is zero" check because
// non-zero pointers may still point to *empty* things.
for i := 0; i < v.NumField(); i++ {
if !isEmptyValue(v.Field(i)) {
return false
}
}
return true
}
return false
}

82
vendor/github.com/polydawn/refmt/obj/errors.go generated vendored Normal file
View File

@@ -0,0 +1,82 @@
package obj
import (
"fmt"
"reflect"
. "github.com/polydawn/refmt/tok"
)
// General note: avoid using reflect.Type here. It doesn't do well with `go-cmp`,
// which in turn makes our tests for error paths a lot jankier.
// ErrInvalidUnmarshalTarget describes an invalid argument passed to Unmarshaller.Bind.
// (Unmarshalling must target a non-nil pointer so that it can address the value.)
type ErrInvalidUnmarshalTarget struct {
Type reflect.Type
}
func (e ErrInvalidUnmarshalTarget) Error() string {
if e.Type == nil {
return "unmarshal error: invalid target (nil)"
}
if e.Type.Kind() != reflect.Ptr {
return "unmarshal error: invalid target (non-pointer " + e.Type.String() + ")"
}
return "unmarshal error: invalid target (nil " + e.Type.String() + ")"
}
// ErrUnmarshalTypeCantFit is the error returned when unmarshalling cannot
// coerce the tokens in the stream into the kind of variables the unmarshal is targetting,
// for example if a map open token comes when an int is expected,
// or an int token comes when a string is expected.
type ErrUnmarshalTypeCantFit struct {
Token Token
Value reflect.Value
LenLim int // Set only if Value.Kind == Array and Token is bytes of a mismatch length.
}
func (e ErrUnmarshalTypeCantFit) Error() string {
switch e.LenLim {
case 0:
return fmt.Sprintf("unmarshal error: cannot assign %s to %s field", e.Token, e.Value.Kind())
default:
return fmt.Sprintf("unmarshal error: cannot assign %s to fixed length=%d byte array", e.Token, e.Value.Len())
}
}
// ErrMalformedTokenStream is the error returned when unmarshalling recieves ae
// completely invalid transition, such as when a map value is expected, but the
// map suddenly closes, or an array close is recieved with no matching array open.
type ErrMalformedTokenStream struct {
Got TokenType // Token in the stream that triggered the error.
Expected string // Freeform string describing valid token types. Often a summary like "array close or start of value", or "map close or key".
}
func (e ErrMalformedTokenStream) Error() string {
return fmt.Sprintf("malformed stream: invalid appearance of %s token; expected %s", e.Got, e.Expected)
}
// ErrNoSuchField is the error returned when unmarshalling into a struct and
// the token stream for the map contains a key which is not defined for the struct.
type ErrNoSuchField struct {
Name string // Field name from the token.
Type string // Type name of the struct we're operating on.
}
func (e ErrNoSuchField) Error() string {
return fmt.Sprintf("unmarshal error: stream contains key %q, but there's no such field in structs of type %s", e.Name, e.Type)
}
// ErrNoSuchUnionMember is the error returned when unmarshalling into a union
// interface and the token stream contains a key which does not name any of the
// known members of the union.
type ErrNoSuchUnionMember struct {
Name string // Key name from the token.
Type reflect.Type // The interface type we're trying to fill.
KnownMembers []string // Members we expected isntead.
}
func (e ErrNoSuchUnionMember) Error() string {
return fmt.Sprintf("unmarshal error: cannot unmarshal into union %s: %q is not one of the known members (expected one of %s)", e.Type, e.Name, e.KnownMembers)
}

110
vendor/github.com/polydawn/refmt/obj/marshal.go generated vendored Normal file
View File

@@ -0,0 +1,110 @@
package obj
import (
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
/*
Allocates the machinery for treating an object like a `TokenSource`.
This machinery will walk over structures in memory,
emitting tokens representing values and fields as it visits them.
Initialization must be finished by calling `Bind` to set the value to visit;
after this, the `Step` function is ready to be pumped.
Subsequent calls to `Bind` do a full reset, leaving `Step` ready to call
again and making all of the machinery reusable without re-allocating.
*/
func NewMarshaller(atl atlas.Atlas) *Marshaller {
d := &Marshaller{
marshalSlab: marshalSlab{
atlas: atl,
rows: make([]marshalSlabRow, 0, 10),
},
stack: make([]MarshalMachine, 0, 10),
}
return d
}
func (d *Marshaller) Bind(v interface{}) error {
d.stack = d.stack[0:0]
d.marshalSlab.rows = d.marshalSlab.rows[0:0]
rv := reflect.ValueOf(v)
if !rv.IsValid() {
// if given an untyped nil, swap in a less spikey nil thunk instead.
rv = nil_rv
}
rt := rv.Type()
d.step = d.marshalSlab.requisitionMachine(rt)
return d.step.Reset(&d.marshalSlab, rv, rt)
}
type Marshaller struct {
marshalSlab marshalSlab
stack []MarshalMachine
step MarshalMachine
}
type MarshalMachine interface {
Reset(*marshalSlab, reflect.Value, reflect.Type) error
Step(*Marshaller, *marshalSlab, *Token) (done bool, err error)
}
type marshalMachineStep func(*Marshaller, *marshalSlab, *Token) (done bool, err error)
func (d *Marshaller) Step(tok *Token) (bool, error) {
tok.Tagged = false
// fmt.Printf("> next step is %#v\n", d.step)
done, err := d.step.Step(d, &d.marshalSlab, tok)
// fmt.Printf(">> yield is %#v\n", TokenToString(*tok))
// If the step errored: out, entirely.
if err != nil {
return true, err
}
// If the step wasn't done, return same status.
if !done {
return false, nil
}
// If it WAS done, pop next, or if stack empty, we're entirely done.
nSteps := len(d.stack) - 1
if nSteps == -1 {
return true, nil // that's all folks
}
// fmt.Printf(">> popping up from %#v\n", d.stack)
d.step = d.stack[nSteps]
d.stack = d.stack[0:nSteps]
return false, nil
}
/*
Starts the process of recursing marshalling over value `rv`.
Caller provides the machine to use (this is an optimization for maps and slices,
which already know the machine and keep reusing it for all their entries).
This method pushes the first step with `tok` (the upstream tends to have peeked at
it in order to decide what to do, but if recursing, it belongs to the next obj),
then saves this new machine onto the driver's stack: future calls to step
the driver will then continuing stepping the new machine it returns a done status,
at which point we'll finally "return" by popping back to the last machine on the stack
(which is presumably the same one that just called this Recurse method).
In other words, your MarshalMachine calls this when it wants to deal
with an object, and by the time we call back to your machine again,
that object will be traversed and the stream ready for you to continue.
*/
func (d *Marshaller) Recurse(tok *Token, rv reflect.Value, rt reflect.Type, nextMach MarshalMachine) (err error) {
// fmt.Printf(">>> pushing into recursion with %#v\n", nextMach)
// Push the current machine onto the stack (we'll resume it when the new one is done),
d.stack = append(d.stack, d.step)
// Initialize the machine for this new target value.
err = nextMach.Reset(&d.marshalSlab, rv, rt)
if err != nil {
return
}
d.step = nextMach
// Immediately make a step (we're still the delegate in charge of someone else's step).
_, err = d.Step(tok)
return
}

View File

@@ -0,0 +1,92 @@
package obj
import (
"reflect"
. "github.com/polydawn/refmt/tok"
)
type ptrDerefDelegateMarshalMachine struct {
MarshalMachine
peelCount int
isNil bool
}
func (mach *ptrDerefDelegateMarshalMachine) Reset(slab *marshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.isNil = false
for i := 0; i < mach.peelCount; i++ {
if rv.IsNil() {
mach.isNil = true
return nil
}
rv = rv.Elem()
}
return mach.MarshalMachine.Reset(slab, rv, rv.Type()) // REVIEW: we could have cached the peeled rt at mach conf time; worth it?
}
func (mach *ptrDerefDelegateMarshalMachine) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
if mach.isNil {
tok.Type = TNull
return true, nil
}
return mach.MarshalMachine.Step(driver, slab, tok)
}
type marshalMachinePrimitive struct {
kind reflect.Kind
rv reflect.Value
}
func (mach *marshalMachinePrimitive) Reset(_ *marshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.rv = rv
return nil
}
func (mach *marshalMachinePrimitive) Step(_ *Marshaller, _ *marshalSlab, tok *Token) (done bool, err error) {
switch mach.kind {
case reflect.Bool:
tok.Type = TBool
tok.Bool = mach.rv.Bool()
return true, nil
case reflect.String:
tok.Type = TString
tok.Str = mach.rv.String()
return true, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
tok.Type = TInt
tok.Int = mach.rv.Int()
return true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
tok.Type = TUint
tok.Uint = mach.rv.Uint()
return true, nil
case reflect.Float32, reflect.Float64:
tok.Type = TFloat64
tok.Float64 = mach.rv.Float()
return true, nil
case reflect.Slice: // implicitly bytes; no other slices are "primitive"
if mach.rv.IsNil() {
tok.Type = TNull
return true, nil
}
tok.Type = TBytes
tok.Bytes = mach.rv.Bytes()
return true, nil
case reflect.Array: // implicitly bytes; no other arrays are "primitive"
tok.Type = TBytes
// Unfortunately, there does not seem to be any efficient way to extract the contents of a byte array into a slice via reflect.
// Since the lengths are part of the type, it is almost understandable that the stdlib reflect package has a hard time expressing this;
// however, it drives me somewhat up the wall that they do not provide a case for arrays inside the `Value.Bytes` method, and panic.
// Attempting to `Value.Convert(Type)` from a fixed-length array to a slice of the same type is also rejected.
// Nor does `reflect.AppendSlice` accept an array kind as the second parameter; no, only slices there too.
// So... we iterate. If anyone knows a better way to do this, PRs extremely welcome.
n := mach.rv.Len()
tok.Bytes = make([]byte, n)
for i := 0; i < n; i++ {
tok.Bytes[i] = byte(mach.rv.Index(i).Uint())
}
return true, nil
default:
panic("unhandled")
}
}

View File

@@ -0,0 +1,148 @@
package obj
import (
"fmt"
"reflect"
"sort"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type marshalMachineMapWildcard struct {
morphism *atlas.MapMorphism // set on initialization
target_rv reflect.Value
value_rt reflect.Type
keyStringer atlas.MarshalTransformFunc
valueMach MarshalMachine
keys []wildcardMapStringyKey
index int
value bool
}
func (mach *marshalMachineMapWildcard) Reset(slab *marshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
// Pick machinery for handling the value types.
mach.value_rt = rt.Elem()
mach.valueMach = slab.requisitionMachine(mach.value_rt)
// Enumerate all the keys (must do this up front, one way or another),
// flip them into strings,
// and sort them (optional, arguably, but right now you're getting it).
key_rt := rt.Key()
switch key_rt.Kind() {
case reflect.String:
// continue.
// note: stdlib json.marshal supports all the int types here as well, and will
// tostring them. but this is not supported symmetrically; so we simply... don't.
// we could also consider supporting anything that uses a MarshalTransformFunc
// to become a string kind; that's a fair bit of code, perhaps later.
mach.keyStringer = nil
case reflect.Struct:
// composite keys requires some fancy footwork, but we can do it.
// Interestingly enough, we don't need full-on machinery here; because the
// tokenized form is restricted to being a string, the transform func is enough.
rtid := reflect.ValueOf(key_rt).Pointer()
atlEnt, ok := slab.atlas.Get(rtid)
if !ok || atlEnt.MarshalTransformTargetType == nil || atlEnt.MarshalTransformTargetType.Kind() != reflect.String {
return fmt.Errorf("unsupported map key type %q (if you want to use struct keys, your atlas needs a transform to string)", key_rt.Name())
}
mach.keyStringer = atlEnt.MarshalTransformFunc
default:
return fmt.Errorf("unsupported map key type %q", key_rt.Name())
}
keys_rv := mach.target_rv.MapKeys()
mach.keys = make([]wildcardMapStringyKey, len(keys_rv))
for i, v := range keys_rv {
mach.keys[i].rv = v
if mach.keyStringer == nil {
mach.keys[i].s = v.String()
} else {
trans_rv, err := mach.keyStringer(v)
if err != nil {
return fmt.Errorf("unsupported map key type %q: errors in stringifying: %s", key_rt.Name(), err)
}
mach.keys[i].s = trans_rv.String()
}
}
ksm := atlas.KeySortMode_Default
if mach.morphism != nil {
ksm = mach.morphism.KeySortMode
}
switch ksm {
case atlas.KeySortMode_Default:
sort.Sort(wildcardMapStringyKey_byString(mach.keys))
case atlas.KeySortMode_Strings:
sort.Sort(wildcardMapStringyKey_byString(mach.keys))
case atlas.KeySortMode_RFC7049:
sort.Sort(wildcardMapStringyKey_RFC7049(mach.keys))
default:
panic(fmt.Errorf("unknown map key sort mode %q", ksm))
}
mach.index = -1
return nil
}
func (mach *marshalMachineMapWildcard) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
if mach.index < 0 {
if mach.target_rv.IsNil() {
tok.Type = TNull
mach.index++
return true, nil
}
tok.Type = TMapOpen
tok.Length = mach.target_rv.Len()
mach.index++
return false, nil
}
if mach.index == len(mach.keys) {
tok.Type = TMapClose
mach.index++
slab.release()
return true, nil
}
if mach.index > len(mach.keys) {
return true, fmt.Errorf("invalid state: value already consumed")
}
if mach.value {
val_rv := mach.target_rv.MapIndex(mach.keys[mach.index].rv)
mach.value = false
mach.index++
return false, driver.Recurse(tok, val_rv, mach.value_rt, mach.valueMach)
}
tok.Type = TString
tok.Str = mach.keys[mach.index].s
mach.value = true
return false, nil
}
// Holder for the reflect.Value and string form of a key.
// We need the reflect.Value for looking up the map value;
// and we need the string for sorting.
type wildcardMapStringyKey struct {
rv reflect.Value
s string
}
type wildcardMapStringyKey_byString []wildcardMapStringyKey
func (x wildcardMapStringyKey_byString) Len() int { return len(x) }
func (x wildcardMapStringyKey_byString) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x wildcardMapStringyKey_byString) Less(i, j int) bool { return x[i].s < x[j].s }
type wildcardMapStringyKey_RFC7049 []wildcardMapStringyKey
func (x wildcardMapStringyKey_RFC7049) Len() int { return len(x) }
func (x wildcardMapStringyKey_RFC7049) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x wildcardMapStringyKey_RFC7049) Less(i, j int) bool {
li, lj := len(x[i].s), len(x[j].s)
if li == lj {
return x[i].s < x[j].s
}
return li < lj
}

219
vendor/github.com/polydawn/refmt/obj/marshalSlab.go generated vendored Normal file
View File

@@ -0,0 +1,219 @@
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
/*
A lovely mechanism to stash marshalMachine objects pre-allocated and avoid mallocs.
Works together with the Atlas: the Atlas says what kind of machinery is needed;
the marshalSlab "allocates" it and returns it upon your request.
*/
type marshalSlab struct {
atlas atlas.Atlas
rows []marshalSlabRow
}
type marshalSlabRow struct {
ptrDerefDelegateMarshalMachine
marshalMachinePrimitive
marshalMachineWildcard
marshalMachineMapWildcard
marshalMachineSliceWildcard
marshalMachineStructAtlas
marshalMachineTransform
marshalMachineUnionKeyed
errThunkMarshalMachine
}
// A thunk value that can be used to trigger `isNil` paths.
// (Substituting an 'invalid' kind reflect.Value with this is an easy way
// to emit a null without needing any additional special cases or error handling.)
var nil_rv reflect.Value = reflect.Zero(reflect.PtrTo(reflect.TypeOf(0)))
/*
Return a reference to a machine from the slab.
*You must release() when done.*
Errors -- including "no info in Atlas for this type" -- are expressed by
returning a machine that is a constantly-erroring thunk.
*/
func (slab *marshalSlab) requisitionMachine(rt reflect.Type) MarshalMachine {
// Acquire a row.
off := len(slab.rows)
slab.grow()
row := &slab.rows[off]
// Yield machinery.
return _yieldMarshalMachinePtr(row, slab.atlas, rt)
}
/*
Like requisitionMachine, but does *not* grow the slab; assumes the current
tip row is usable.
Thus, you must grow() before using, and release correspondingly.
*/
func (slab *marshalSlab) yieldMachine(rt reflect.Type) MarshalMachine {
// Grab the last row.
row := &slab.rows[len(slab.rows)-1]
// Yield machinery.
return _yieldMarshalMachinePtr(row, slab.atlas, rt)
}
func _yieldMarshalMachinePtr(row *marshalSlabRow, atl atlas.Atlas, rt reflect.Type) MarshalMachine {
// Indirect pointers as necessary.
// Keep count of how many times we do this; we'll use this again at the end.
peelCount := 0
for rt.Kind() == reflect.Ptr {
rt = rt.Elem()
peelCount++
}
// Figure out what machinery to use at heart.
mach := _yieldBareMarshalMachinePtr(row, atl, rt)
// If nil answer, we had no match: yield an error thunk.
if mach == nil {
mach := &row.errThunkMarshalMachine
mach.err = fmt.Errorf("no machine found")
return mach
}
// If no indirection steps, return;
// otherwise wrap it in the ptrDeref machine and return that.
if peelCount == 0 {
return mach
}
row.ptrDerefDelegateMarshalMachine.MarshalMachine = mach
row.ptrDerefDelegateMarshalMachine.peelCount = peelCount
row.ptrDerefDelegateMarshalMachine.isNil = false
return &row.ptrDerefDelegateMarshalMachine
}
// Like _yieldMarshalMachinePtr, but assumes the ptr unwrapping has already been done.
func _yieldBareMarshalMachinePtr(row *marshalSlabRow, atl atlas.Atlas, rt reflect.Type) MarshalMachine {
rtid := reflect.ValueOf(rt).Pointer()
// Check primitives first; cheapest (and unoverridable).
switch rtid {
case rtid_bool,
rtid_string,
rtid_int, rtid_int8, rtid_int16, rtid_int32, rtid_int64,
rtid_uint, rtid_uint8, rtid_uint16, rtid_uint32, rtid_uint64, rtid_uintptr,
rtid_float32, rtid_float64,
rtid_bytes:
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
}
// Consult atlas second.
if entry, ok := atl.Get(rtid); ok {
return _yieldMarshalMachinePtrForAtlasEntry(row, entry, atl)
}
// If no specific behavior found, use default behavior based on kind.
switch rt.Kind() {
case reflect.Bool,
reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64:
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
case reflect.Slice:
// un-typedef'd byte slices were handled already, but a typedef'd one still gets gets treated like a special kind:
if rt.Elem().Kind() == reflect.Uint8 {
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
}
return &row.marshalMachineSliceWildcard
case reflect.Array:
// arrays of bytes have a similar special case to slices for when they're typedefed.
if rt.Elem().Kind() == reflect.Uint8 {
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
}
return &row.marshalMachineSliceWildcard.marshalMachineArrayWildcard
case reflect.Map:
row.marshalMachineMapWildcard.morphism = atl.GetDefaultMapMorphism()
return &row.marshalMachineMapWildcard
case reflect.Struct:
// TODO here we could also invoke automatic atlas autogen, if configured to be permitted
mach := &row.errThunkMarshalMachine
mach.err = fmt.Errorf("missing an atlas entry describing how to marshal type %v (and auto-atlasing for structs is not enabled)", rt)
return mach
case reflect.Interface:
return &row.marshalMachineWildcard
case reflect.Func:
panic(fmt.Errorf("functions cannot be marshalled!"))
case reflect.Ptr:
panic(fmt.Errorf("unreachable: ptrs must already be resolved"))
default:
panic(fmt.Errorf("excursion %s", rt.Kind()))
}
}
// given that we already have an atlasEntry in mind, yield a configured machine for it.
// it seems odd that this might still require a whole atlas, but tis so;
// some things (e.g. transform funcs) need to get additional machinery for delegation.
func _yieldMarshalMachinePtrForAtlasEntry(row *marshalSlabRow, entry *atlas.AtlasEntry, atl atlas.Atlas) MarshalMachine {
// Switch across which of the union of configurations is applicable.
switch {
case entry.MarshalTransformFunc != nil:
// Return a machine that calls the func(s), then later a real machine.
// The entry.MarshalTransformTargetType is used to do a recursive lookup.
// We can't just call the func here because we're still working off typeinfo
// and don't have a real value to transform until later.
row.marshalMachineTransform.trFunc = entry.MarshalTransformFunc
// Pick delegate without growing stack. (This currently means recursive transform won't fly.)
row.marshalMachineTransform.delegate = _yieldMarshalMachinePtr(row, atl, entry.MarshalTransformTargetType)
// If tags are in play: have the transformer machine glue that on.
row.marshalMachineTransform.tagged = entry.Tagged
row.marshalMachineTransform.tag = entry.Tag
return &row.marshalMachineTransform
case entry.StructMap != nil:
row.marshalMachineStructAtlas.cfg = entry
return &row.marshalMachineStructAtlas
case entry.UnionKeyedMorphism != nil:
row.marshalMachineUnionKeyed.cfg = entry
return &row.marshalMachineUnionKeyed
case entry.MapMorphism != nil:
row.marshalMachineMapWildcard.morphism = entry.MapMorphism
return &row.marshalMachineMapWildcard
default:
panic("invalid atlas entry")
}
}
// Returns the top row of the slab. Useful for machines that need to delegate
// to another type that's definitely not their own. Be careful with that
// caveat; if the delegation can be to another system that uses in-row delegation,
// this is not trivially safe to compose and you should grow the slab instead.
func (s *marshalSlab) tip() *marshalSlabRow {
return &s.rows[len(s.rows)-1]
}
func (s *marshalSlab) grow() {
s.rows = append(s.rows, marshalSlabRow{})
}
func (s *marshalSlab) release() {
s.rows = s.rows[0 : len(s.rows)-1]
}
type errThunkMarshalMachine struct {
err error
}
func (m *errThunkMarshalMachine) Reset(_ *marshalSlab, _ reflect.Value, _ reflect.Type) error {
return m.err
}
func (m *errThunkMarshalMachine) Step(d *Marshaller, s *marshalSlab, tok *Token) (done bool, err error) {
return true, m.err
}

View File

@@ -0,0 +1,63 @@
package obj
import (
"fmt"
"reflect"
. "github.com/polydawn/refmt/tok"
)
// Encodes a slice.
// This machine just wraps the array machine, checking to make sure the value isn't nil.
type marshalMachineSliceWildcard struct {
marshalMachineArrayWildcard
}
func (mach *marshalMachineSliceWildcard) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
if mach.index < 0 {
if mach.target_rv.IsNil() {
tok.Type = TNull
return true, nil
}
}
return mach.marshalMachineArrayWildcard.Step(driver, slab, tok)
}
type marshalMachineArrayWildcard struct {
target_rv reflect.Value
value_rt reflect.Type
valueMach MarshalMachine
index int
length int
}
func (mach *marshalMachineArrayWildcard) Reset(slab *marshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
mach.value_rt = rt.Elem()
mach.valueMach = slab.requisitionMachine(mach.value_rt)
mach.index = -1
mach.length = mach.target_rv.Len()
return nil
}
func (mach *marshalMachineArrayWildcard) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
if mach.index < 0 {
tok.Type = TArrOpen
tok.Length = mach.target_rv.Len()
mach.index++
return false, nil
}
if mach.index == mach.length {
tok.Type = TArrClose
mach.index++
slab.release()
return true, nil
}
if mach.index > mach.length {
return true, fmt.Errorf("invalid state: value already consumed")
}
rv := mach.target_rv.Index(mach.index)
driver.Recurse(tok, rv, mach.value_rt, mach.valueMach)
mach.index++
return false, nil
}

108
vendor/github.com/polydawn/refmt/obj/marshalStruct.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type marshalMachineStructAtlas struct {
cfg *atlas.AtlasEntry // set on initialization
target_rv reflect.Value
index int // Progress marker
value_rv reflect.Value // Next value (or nil if next step is key).
}
func (mach *marshalMachineStructAtlas) Reset(slab *marshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.target_rv = rv
mach.index = -1
mach.value_rv = reflect.Value{}
slab.grow() // we'll reuse the same row for all fields
return nil
}
func (mach *marshalMachineStructAtlas) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
//fmt.Printf("--step on %#v: i=%d/%d v=%v\n", mach.target_rv, mach.index, len(mach.cfg.Fields), mach.value)
// Check boundaries and do the special steps or either start or end.
nEntries := len(mach.cfg.StructMap.Fields)
if mach.index < 0 {
tok.Type = TMapOpen
tok.Length = countEmittableStructFields(mach.cfg, mach.target_rv)
tok.Tagged = mach.cfg.Tagged
tok.Tag = mach.cfg.Tag
mach.index++
return false, nil
}
if mach.index == nEntries {
tok.Type = TMapClose
mach.index++
slab.release()
return true, nil
}
if mach.index > nEntries {
return true, fmt.Errorf("invalid state: entire struct (%d fields) already consumed", nEntries)
}
// If value loaded from last step, recurse into handling that.
fieldEntry := mach.cfg.StructMap.Fields[mach.index]
if mach.value_rv != (reflect.Value{}) {
child_rv := mach.value_rv
mach.index++
mach.value_rv = reflect.Value{}
return false, driver.Recurse(
tok,
child_rv,
fieldEntry.Type,
slab.yieldMachine(fieldEntry.Type),
)
}
// If value was nil, that indicates we're supposed to pick the value and yield a key.
// We have to look ahead to the value because if it's zero and tagged as
// omitEmpty, then we have to skip emitting the key as well.
for fieldEntry.Ignore {
mach.index++
if mach.index == nEntries {
tok.Type = TMapClose
mach.index++
slab.release()
return true, nil
}
fieldEntry = mach.cfg.StructMap.Fields[mach.index]
}
mach.value_rv = fieldEntry.ReflectRoute.TraverseToValue(mach.target_rv)
if fieldEntry.OmitEmpty && isEmptyValue(mach.value_rv) {
mach.value_rv = reflect.Value{}
mach.index++
return mach.Step(driver, slab, tok)
}
tok.Type = TString
tok.Str = fieldEntry.SerialName
return false, nil
}
// Count how many fields in a struct should actually be marshalled.
// Fields that are tagged omitEmpty and are isEmptyValue are not counted, and
// StructMapEntry used to flag ignored fields unmarshalling never count, so
// this number may be less than the number of fields in the AtlasEntry.StructMap.
func countEmittableStructFields(cfg *atlas.AtlasEntry, target_rv reflect.Value) int {
total := 0
for _, fieldEntry := range cfg.StructMap.Fields {
if fieldEntry.Ignore {
continue
}
if !fieldEntry.OmitEmpty {
total++
continue
}
if !isEmptyValue(fieldEntry.ReflectRoute.TraverseToValue(target_rv)) {
total++
continue
}
}
return total
}

View File

@@ -0,0 +1,35 @@
package obj
import (
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type marshalMachineTransform struct {
trFunc atlas.MarshalTransformFunc
delegate MarshalMachine
tagged bool // Used to apply tag to first step (without forcing delegate to know).
tag int
first bool // This resets; 'tagged' persists (because it's type info).
}
func (mach *marshalMachineTransform) Reset(slab *marshalSlab, rv reflect.Value, _ reflect.Type) error {
tr_rv, err := mach.trFunc(rv)
if err != nil {
return err
}
mach.first = true
return mach.delegate.Reset(slab, tr_rv, tr_rv.Type())
}
func (mach *marshalMachineTransform) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
done, err = mach.delegate.Step(driver, slab, tok)
if mach.first && mach.tagged {
tok.Tagged = true
tok.Tag = mach.tag
mach.first = false
}
return
}

View File

@@ -0,0 +1,71 @@
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type marshalMachineUnionKeyed struct {
cfg *atlas.AtlasEntry // set on initialization
target_rv reflect.Value // the element (interface already unwrapped).
elementName string // the serial name for this union member type.
step marshalMachineStep
delegate MarshalMachine // actual machine, picked based on content of the interface.
}
func (mach *marshalMachineUnionKeyed) Reset(slab *marshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv.Elem()
if mach.target_rv.Kind() == reflect.Invalid {
return fmt.Errorf("nil is not a valid member for the union for interface %q", mach.cfg.Type.Name())
}
element_rt := mach.target_rv.Type()
mach.elementName = mach.cfg.UnionKeyedMorphism.Mappings[reflect.ValueOf(element_rt).Pointer()]
if mach.elementName == "" {
return fmt.Errorf("type %q is not one of the known members of the union for interface %q", element_rt.Name(), mach.cfg.Type.Name())
}
delegateAtlasEnt := mach.cfg.UnionKeyedMorphism.Elements[mach.elementName]
mach.delegate = _yieldMarshalMachinePtrForAtlasEntry(slab.tip(), delegateAtlasEnt, slab.atlas)
if err := mach.delegate.Reset(slab, mach.target_rv, delegateAtlasEnt.Type); err != nil {
return err
}
mach.step = mach.step_emitMapOpen
return nil
}
func (mach *marshalMachineUnionKeyed) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
return mach.step(driver, slab, tok)
}
func (mach *marshalMachineUnionKeyed) step_emitMapOpen(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
tok.Type = TMapOpen
tok.Length = 1
mach.step = mach.step_emitKey
return false, nil
}
func (mach *marshalMachineUnionKeyed) step_emitKey(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
tok.Type = TString
tok.Str = mach.elementName
mach.step = mach.step_delegate
return false, nil
}
func (mach *marshalMachineUnionKeyed) step_delegate(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
done, err = mach.delegate.Step(driver, slab, tok)
if done && err == nil {
mach.step = mach.step_emitMapClose
return false, nil
}
return
}
func (mach *marshalMachineUnionKeyed) step_emitMapClose(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
tok.Type = TMapClose
mach.step = nil
return true, nil
}

View File

@@ -0,0 +1,37 @@
package obj
import (
"reflect"
. "github.com/polydawn/refmt/tok"
)
/*
A MarshalMachine that unwraps an `interface{}` value,
selects the correct machinery for handling its content,
and delegates immediately to that machine.
*/
type marshalMachineWildcard struct {
delegate MarshalMachine
}
func (mach *marshalMachineWildcard) Reset(slab *marshalSlab, rv reflect.Value, rt reflect.Type) error {
// If the interface contains nil, go no further; we'll simply yield that single token.
if rv.IsNil() {
mach.delegate = nil
return nil
}
// Pick, reset, and retain a delegate machine for the interior type.
unwrap_rv := rv.Elem() // unwrap iface
unwrap_rt := unwrap_rv.Type()
mach.delegate = slab.requisitionMachine(unwrap_rt)
return mach.delegate.Reset(slab, unwrap_rv, unwrap_rt)
}
func (mach marshalMachineWildcard) Step(driver *Marshaller, slab *marshalSlab, tok *Token) (done bool, err error) {
if mach.delegate == nil {
tok.Type = TNull
return true, nil
}
return mach.delegate.Step(driver, slab, tok)
}

106
vendor/github.com/polydawn/refmt/obj/unmarshal.go generated vendored Normal file
View File

@@ -0,0 +1,106 @@
package obj
import (
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
/*
Allocates the machinery for treating an in-memory object like a `TokenSink`.
This machinery will walk over values, using received tokens to fill in
fields as it visits them.
Initialization must be finished by calling `Bind` to set the value to visit;
after this, the `Step` function is ready to be pumped.
Subsequent calls to `Bind` do a full reset, leaving `Step` ready to call
again and making all of the machinery reusable without re-allocating.
*/
func NewUnmarshaller(atl atlas.Atlas) *Unmarshaller {
d := &Unmarshaller{
unmarshalSlab: unmarshalSlab{
atlas: atl,
rows: make([]unmarshalSlabRow, 0, 10),
},
stack: make([]UnmarshalMachine, 0, 10),
}
return d
}
func (d *Unmarshaller) Bind(v interface{}) error {
d.stack = d.stack[0:0]
d.unmarshalSlab.rows = d.unmarshalSlab.rows[0:0]
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
err := ErrInvalidUnmarshalTarget{reflect.TypeOf(v)}
d.step = &errThunkUnmarshalMachine{err}
return err
}
rv = rv.Elem() // Let's just always be addressible, shall we?
rt := rv.Type()
d.step = d.unmarshalSlab.requisitionMachine(rt)
return d.step.Reset(&d.unmarshalSlab, rv, rt)
}
type Unmarshaller struct {
unmarshalSlab unmarshalSlab
stack []UnmarshalMachine
step UnmarshalMachine
}
type UnmarshalMachine interface {
Reset(*unmarshalSlab, reflect.Value, reflect.Type) error
Step(*Unmarshaller, *unmarshalSlab, *Token) (done bool, err error)
}
func (d *Unmarshaller) Step(tok *Token) (bool, error) {
done, err := d.step.Step(d, &d.unmarshalSlab, tok)
// If the step errored: out, entirely.
if err != nil {
return true, err
}
// If the step wasn't done, return same status.
if !done {
return false, nil
}
// If it WAS done, pop next, or if stack empty, we're entirely done.
nSteps := len(d.stack) - 1
if nSteps == -1 {
return true, nil // that's all folks
}
d.step = d.stack[nSteps]
d.stack = d.stack[0:nSteps]
return false, nil
}
/*
Starts the process of recursing unmarshalling over value `rv`.
Caller provides the machine to use (this is an optimization for maps and slices,
which already know the machine and keep reusing it for all their entries).
This method pushes the first step with `tok` (the upstream tends to have peeked at
it in order to decide what to do, but if recursing, it belongs to the next obj),
then saves this new machine onto the driver's stack: future calls to step
the driver will then continuing stepping the new machine it returns a done status,
at which point we'll finally "return" by popping back to the last machine on the stack
(which is presumably the same one that just called this Recurse method).
In other words, your UnmarshalMachine calls this when it wants to deal
with an object, and by the time we call back to your machine again,
that object will be traversed and the stream ready for you to continue.
*/
func (d *Unmarshaller) Recurse(tok *Token, rv reflect.Value, rt reflect.Type, nextMach UnmarshalMachine) (err error) {
// fmt.Printf(">>> pushing into recursion with %#v\n", nextMach)
// Push the current machine onto the stack (we'll resume it when the new one is done),
d.stack = append(d.stack, d.step)
// Initialize the machine for this new target value.
err = nextMach.Reset(&d.unmarshalSlab, rv, rt)
if err != nil {
return
}
d.step = nextMach
// Immediately make a step (we're still the delegate in charge of someone else's step).
_, err = d.Step(tok)
return
}

View File

@@ -0,0 +1,93 @@
package obj
import (
"reflect"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineArrayWildcard struct {
target_rv reflect.Value
value_rt reflect.Type
valueMach UnmarshalMachine
phase unmarshalMachineArrayWildcardPhase
index int
maxLen int
}
type unmarshalMachineArrayWildcardPhase uint8
const (
unmarshalMachineArrayWildcardPhase_initial unmarshalMachineArrayWildcardPhase = iota
unmarshalMachineArrayWildcardPhase_acceptValueOrClose
)
func (mach *unmarshalMachineArrayWildcard) Reset(slab *unmarshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
mach.value_rt = rt.Elem()
mach.valueMach = slab.requisitionMachine(mach.value_rt)
mach.phase = unmarshalMachineArrayWildcardPhase_initial
mach.index = 0
mach.maxLen = rt.Len()
return nil
}
func (mach *unmarshalMachineArrayWildcard) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch mach.phase {
case unmarshalMachineArrayWildcardPhase_initial:
return mach.step_Initial(driver, slab, tok)
case unmarshalMachineArrayWildcardPhase_acceptValueOrClose:
return mach.step_AcceptValueOrClose(driver, slab, tok)
}
panic("unreachable")
}
func (mach *unmarshalMachineArrayWildcard) step_Initial(_ *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// If it's a special state, start an object.
// (Or, blow up if its a special state that's silly).
switch tok.Type {
case TMapOpen:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
case TArrOpen:
// Great. Consumed.
mach.phase = unmarshalMachineArrayWildcardPhase_acceptValueOrClose
// Initialize the array. Its length is encoded in its type.
mach.target_rv.Set(reflect.Zero(mach.target_rv.Type()))
return false, nil
case TMapClose:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
case TArrClose:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
case TNull:
mach.target_rv.Set(reflect.Zero(mach.target_rv.Type()))
return true, nil
default:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
}
}
func (mach *unmarshalMachineArrayWildcard) step_AcceptValueOrClose(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// Either form of open token are valid, but
// - an arrClose is ours
// - and a mapClose is clearly invalid.
switch tok.Type {
case TMapClose:
// no special checks for ends of wildcard slice; no such thing as incomplete.
return true, ErrMalformedTokenStream{tok.Type, "start of value or end of array"}
case TArrClose:
// release the slab row we requisitioned for our value machine.
slab.release()
return true, nil
}
// Return an error if we're about to exceed our length limit.
if mach.index >= mach.maxLen {
return true, ErrMalformedTokenStream{tok.Type, "end of array (out of space)"}
}
// Recurse on a handle to the next index.
rv := mach.target_rv.Index(mach.index)
mach.index++
return false, driver.Recurse(tok, rv, mach.value_rt, mach.valueMach)
// Step simply remains `step_AcceptValueOrClose` -- arrays don't have much state machine.
}

View File

@@ -0,0 +1,176 @@
package obj
import (
"fmt"
"reflect"
. "github.com/polydawn/refmt/tok"
)
type ptrDerefDelegateUnmarshalMachine struct {
UnmarshalMachine // a delegate machine, already set for us by the slab
peelCount int // how deep the pointers go, already discoverd by the slab
ptr_rv reflect.Value // the top ptr, which we will use if setting to nil, or if we have to recursively make ptrs for `**X` types.
firstStep bool
}
func (mach *ptrDerefDelegateUnmarshalMachine) Reset(slab *unmarshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.ptr_rv = rv
mach.firstStep = true
// we defer reseting the delegate machine until later, in case we get a nil, which can save a lot of time.
return nil
}
func (mach *ptrDerefDelegateUnmarshalMachine) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// If first step: we have to do initializations.
if mach.firstStep {
mach.firstStep = false
// If nil: easy road. Nil the ptr.
if tok.Type == TNull {
mach.ptr_rv.Set(reflect.Zero(mach.ptr_rv.Type()))
return true, nil
}
// Walk the pointers: if some already exist, we accept them unmodified;
// if any are nil, make a new one, and recursively.
rv := mach.ptr_rv
for i := 0; i < mach.peelCount; i++ {
if rv.IsNil() {
rv.Set(reflect.New(rv.Type().Elem()))
rv = rv.Elem()
} else {
rv = rv.Elem()
}
}
if err := mach.UnmarshalMachine.Reset(slab, rv, rv.Type()); err != nil {
return true, err
}
}
// The remainder of the time: it's just delegation.
return mach.UnmarshalMachine.Step(driver, slab, tok)
}
type unmarshalMachinePrimitive struct {
kind reflect.Kind
rv reflect.Value
}
func (mach *unmarshalMachinePrimitive) Reset(_ *unmarshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.rv = rv
return nil
}
func (mach *unmarshalMachinePrimitive) Step(_ *Unmarshaller, _ *unmarshalSlab, tok *Token) (done bool, err error) {
switch mach.kind {
case reflect.Bool:
switch tok.Type {
case TBool:
mach.rv.SetBool(tok.Bool)
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.String:
switch tok.Type {
case TString:
mach.rv.SetString(tok.Str)
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch tok.Type {
case TInt:
mach.rv.SetInt(tok.Int)
return true, nil
case TUint:
mach.rv.SetInt(int64(tok.Uint)) // todo: overflow check
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch tok.Type {
case TInt:
if tok.Int >= 0 {
mach.rv.SetUint(uint64(tok.Int))
return true, nil
}
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
case TUint:
mach.rv.SetUint(tok.Uint)
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.Float32, reflect.Float64:
switch tok.Type {
case TFloat64:
mach.rv.SetFloat(tok.Float64)
return true, nil
case TInt:
mach.rv.SetFloat(float64(tok.Int))
return true, nil
case TUint:
mach.rv.SetFloat(float64(tok.Uint))
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.Slice: // implicitly bytes; no other slices are "primitive"
switch tok.Type {
case TBytes:
mach.rv.SetBytes(tok.Bytes)
return true, nil
case TNull:
mach.rv.SetBytes(nil)
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.Array: // implicitly bytes; no other arrays are "primitive"
switch tok.Type {
case TBytes:
// Unfortunately, there does not seem to be any efficient way to bulk set the contents of a byte array via reflect.
// There are similar complaints regarding slices on the marshalling side: apparently, we have no choice but to loop.
n := mach.rv.Len()
// We match aggressively on length. If the provided input is too short, we reject that too: we assume you asked for a fixed-length array for a reason.
if len(tok.Bytes) != n {
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, n}
}
for i := 0; i < n; i++ {
mach.rv.Index(i).SetUint(uint64(tok.Bytes[i]))
}
return true, nil
case TNull:
if mach.rv.Len() != 0 {
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
mach.rv.SetBytes(nil)
return true, nil
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.rv, 0}
}
case reflect.Interface:
switch tok.Type {
case TString:
mach.rv.Set(reflect.ValueOf(tok.Str))
case TBytes:
mach.rv.Set(reflect.ValueOf(tok.Bytes))
case TBool:
mach.rv.Set(reflect.ValueOf(tok.Bool))
case TInt:
mach.rv.Set(reflect.ValueOf(int(tok.Int))) // Unmarshalling with no particular type info should default to using plain 'int' whenever viable.
case TUint:
mach.rv.Set(reflect.ValueOf(int(tok.Uint))) // Unmarshalling with no particular type info should default to using plain 'int' whenever viable.
case TFloat64:
mach.rv.Set(reflect.ValueOf(tok.Float64))
case TNull:
mach.rv.Set(reflect.ValueOf(nil))
default: // any of the other token types should not have been routed here to begin with.
panic(fmt.Errorf("unhandled: %v", mach.kind))
}
return true, nil
default:
panic(fmt.Errorf("unhandled: %v", mach.kind))
}
}

View File

@@ -0,0 +1,151 @@
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineMapStringWildcard struct {
target_rv reflect.Value // Handle to the map. Can set to zero, or set k=v pairs into, etc.
value_rt reflect.Type // Type info for map values (cached for convenience in recurse calls).
valueMach UnmarshalMachine // Machine for map values.
valueZero_rv reflect.Value // Cached instance of the zero value of the value type, for re-zeroing tmp_rv.
key_rv reflect.Value // Addressable handle to a slot for keys to unmarshal into.
keyDestringer atlas.UnmarshalTransformFunc // Transform str->foo, to be used if keys are not plain strings.
tmp_rv reflect.Value // Addressable handle to a slot for values to unmarshal into.
phase unmarshalMachineMapStringWildcardPhase
}
type unmarshalMachineMapStringWildcardPhase uint8
const (
unmarshalMachineMapStringWildcardPhase_initial unmarshalMachineMapStringWildcardPhase = iota
unmarshalMachineMapStringWildcardPhase_acceptKeyOrClose // doesn't commit prev value
unmarshalMachineMapStringWildcardPhase_acceptValue
unmarshalMachineMapStringWildcardPhase_acceptAnotherKeyOrClose
)
func (mach *unmarshalMachineMapStringWildcard) Reset(slab *unmarshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
mach.value_rt = rt.Elem()
mach.valueMach = slab.requisitionMachine(mach.value_rt)
mach.valueZero_rv = reflect.Zero(mach.value_rt)
key_rt := rt.Key()
mach.key_rv = reflect.New(key_rt).Elem()
if mach.key_rv.Kind() != reflect.String {
rtid := reflect.ValueOf(key_rt).Pointer()
atlEnt, ok := slab.atlas.Get(rtid)
if !ok || atlEnt.UnmarshalTransformTargetType == nil || atlEnt.UnmarshalTransformTargetType.Kind() != reflect.String {
return fmt.Errorf("unsupported map key type %q (if you want to use struct keys, your atlas needs a transform from string)", key_rt.Name())
}
mach.keyDestringer = atlEnt.UnmarshalTransformFunc
}
mach.tmp_rv = reflect.New(mach.value_rt).Elem()
mach.phase = unmarshalMachineMapStringWildcardPhase_initial
return nil
}
func (mach *unmarshalMachineMapStringWildcard) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch mach.phase {
case unmarshalMachineMapStringWildcardPhase_initial:
return mach.step_Initial(driver, slab, tok)
case unmarshalMachineMapStringWildcardPhase_acceptKeyOrClose:
return mach.step_AcceptKeyOrClose(driver, slab, tok)
case unmarshalMachineMapStringWildcardPhase_acceptValue:
return mach.step_AcceptValue(driver, slab, tok)
case unmarshalMachineMapStringWildcardPhase_acceptAnotherKeyOrClose:
return mach.step_AcceptAnotherKeyOrClose(driver, slab, tok)
}
panic("unreachable")
}
func (mach *unmarshalMachineMapStringWildcard) step_Initial(_ *Unmarshaller, _ *unmarshalSlab, tok *Token) (done bool, err error) {
// If it's a special state, start an object.
// (Or, blow up if its a special state that's silly).
switch tok.Type {
case TNull:
mach.target_rv.Set(reflect.Zero(mach.target_rv.Type()))
return true, nil
case TMapOpen:
// Great. Consumed.
mach.phase = unmarshalMachineMapStringWildcardPhase_acceptKeyOrClose
// Initialize the map if it's nil.
if mach.target_rv.IsNil() {
mach.target_rv.Set(reflect.MakeMap(mach.target_rv.Type()))
}
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of map")
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of map")
case TArrOpen:
fallthrough
default:
return true, ErrUnmarshalTypeCantFit{*tok, mach.target_rv, 0}
}
}
func (mach *unmarshalMachineMapStringWildcard) step_AcceptKeyOrClose(_ *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// Switch on tokens.
switch tok.Type {
case TMapOpen:
return true, fmt.Errorf("unexpected mapOpen; expected map key")
case TArrOpen:
return true, fmt.Errorf("unexpected arrOpen; expected map key")
case TMapClose:
// no special checks for ends of wildcard map; no such thing as incomplete.
// release the slab row we requisitioned for our value machine.
slab.release()
return true, nil
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected map key")
case TString:
if mach.keyDestringer != nil {
key_rv, err := mach.keyDestringer(reflect.ValueOf(tok.Str))
if err != nil {
return true, fmt.Errorf("unsupported map key type %q: errors in stringifying: %s", mach.key_rv.Type().Name(), err)
}
mach.key_rv.Set(key_rv)
} else {
mach.key_rv.SetString(tok.Str)
}
if err = mach.mustAcceptKey(mach.key_rv); err != nil {
return true, err
}
mach.phase = unmarshalMachineMapStringWildcardPhase_acceptValue
return false, nil
default:
return true, fmt.Errorf("unexpected token %s; expected key string or end of map", tok)
}
}
func (mach *unmarshalMachineMapStringWildcard) mustAcceptKey(key_rv reflect.Value) error {
if exists := mach.target_rv.MapIndex(key_rv).IsValid(); exists {
return fmt.Errorf("repeated key %q", key_rv)
}
return nil
}
func (mach *unmarshalMachineMapStringWildcard) step_AcceptValue(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
mach.phase = unmarshalMachineMapStringWildcardPhase_acceptAnotherKeyOrClose
mach.tmp_rv.Set(mach.valueZero_rv)
return false, driver.Recurse(
tok,
mach.tmp_rv,
mach.value_rt,
mach.valueMach,
)
}
func (mach *unmarshalMachineMapStringWildcard) step_AcceptAnotherKeyOrClose(_ *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// First, save any refs from the last value.
// (This is fiddly: the delay comes mostly from the handling of slices, which may end up re-allocating
// themselves during their decoding.)
mach.target_rv.SetMapIndex(mach.key_rv, mach.tmp_rv)
// The rest is the same as the very first acceptKeyOrClose (and has the same future state transitions).
return mach.step_AcceptKeyOrClose(nil, slab, tok)
}

187
vendor/github.com/polydawn/refmt/obj/unmarshalSlab.go generated vendored Normal file
View File

@@ -0,0 +1,187 @@
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
/*
A lovely mechanism to stash unmarshalMachine objects pre-allocated and avoid mallocs.
Works together with the Atlas: the Atlas says what kind of machinery is needed;
the unmarshalSlab "allocates" it and returns it upon your request.
*/
type unmarshalSlab struct {
atlas atlas.Atlas
rows []unmarshalSlabRow
}
type unmarshalSlabRow struct {
ptrDerefDelegateUnmarshalMachine
unmarshalMachinePrimitive
unmarshalMachineWildcard
unmarshalMachineMapStringWildcard
unmarshalMachineSliceWildcard
unmarshalMachineArrayWildcard
unmarshalMachineStructAtlas
unmarshalMachineTransform
unmarshalMachineUnionKeyed
errThunkUnmarshalMachine
}
/*
Return a reference to a machine from the slab.
*You must release() when done.*
Errors -- including "no info in Atlas for this type" -- are expressed by
returning a machine that is a constantly-erroring thunk.
*/
func (slab *unmarshalSlab) requisitionMachine(rt reflect.Type) UnmarshalMachine {
// Acquire a row.
off := len(slab.rows)
slab.grow()
row := &slab.rows[off]
// Indirect pointers as necessary.
// Keep count of how many times we do this; we'll use this again at the end.
peelCount := 0
for rt.Kind() == reflect.Ptr {
rt = rt.Elem()
peelCount++
}
// Figure out what machinery to use at heart.
mach := _yieldUnmarshalMachinePtr(row, slab.atlas, rt)
// If nil answer, we had no match: yield an error thunk.
if mach == nil {
mach := &row.errThunkUnmarshalMachine
mach.err = fmt.Errorf("no machine found")
return mach
}
// If no indirection steps, return;
// otherwise wrap it in the ptrDeref machine and return that.
if peelCount == 0 {
return mach
}
row.ptrDerefDelegateUnmarshalMachine.UnmarshalMachine = mach
row.ptrDerefDelegateUnmarshalMachine.peelCount = peelCount
return &row.ptrDerefDelegateUnmarshalMachine
}
func _yieldUnmarshalMachinePtr(row *unmarshalSlabRow, atl atlas.Atlas, rt reflect.Type) UnmarshalMachine {
rtid := reflect.ValueOf(rt).Pointer()
// Check primitives first; cheapest (and unoverridable).
switch rtid {
case rtid_bool,
rtid_string,
rtid_int, rtid_int8, rtid_int16, rtid_int32, rtid_int64,
rtid_uint, rtid_uint8, rtid_uint16, rtid_uint32, rtid_uint64, rtid_uintptr,
rtid_float32, rtid_float64,
rtid_bytes:
row.unmarshalMachinePrimitive.kind = rt.Kind()
return &row.unmarshalMachinePrimitive
}
// Consult atlas second.
if entry, ok := atl.Get(rtid); ok {
return _yieldUnmarshalMachinePtrForAtlasEntry(row, entry, atl)
}
// If no specific behavior found, use default behavior based on kind.
switch rt.Kind() {
case reflect.Bool,
reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64:
row.unmarshalMachinePrimitive.kind = rt.Kind()
return &row.unmarshalMachinePrimitive
case reflect.Slice:
// un-typedef'd byte slices were handled already, but a typedef'd one still gets gets treated like a special kind:
if rt.Elem().Kind() == reflect.Uint8 {
row.unmarshalMachinePrimitive.kind = rt.Kind()
return &row.unmarshalMachinePrimitive
}
return &row.unmarshalMachineSliceWildcard
case reflect.Array:
// arrays of bytes have a similar special case to slices for when they're typedefed.
if rt.Elem().Kind() == reflect.Uint8 {
row.unmarshalMachinePrimitive.kind = rt.Kind()
return &row.unmarshalMachinePrimitive
}
return &row.unmarshalMachineArrayWildcard
case reflect.Map:
return &row.unmarshalMachineMapStringWildcard
case reflect.Struct:
// TODO here we could also invoke automatic atlas autogen, if configured to be permitted
mach := &row.errThunkUnmarshalMachine
mach.err = fmt.Errorf("missing an atlas entry describing how to unmarshal type %v (and auto-atlasing for structs is not enabled)", rt)
return mach
case reflect.Interface:
return &row.unmarshalMachineWildcard
case reflect.Func:
panic(fmt.Errorf("functions cannot be unmarshalled!"))
case reflect.Ptr:
panic(fmt.Errorf("unreachable: ptrs must already be resolved"))
default:
panic(fmt.Errorf("excursion %s", rt.Kind()))
}
}
// given that we already have an atlasEntry in mind, yield a configured machine for it.
// it seems odd that this might still require a whole atlas, but tis so;
// some things (e.g. transform funcs) need to get additional machinery for delegation.
func _yieldUnmarshalMachinePtrForAtlasEntry(row *unmarshalSlabRow, entry *atlas.AtlasEntry, atl atlas.Atlas) UnmarshalMachine {
// Switch across which of the union of configurations is applicable.
switch {
case entry.UnmarshalTransformFunc != nil:
// Return a machine that calls the func(s), then later a real machine.
// The entry.UnmarshalTransformTargetType is used to do a recursive lookup.
// We can't just call the func here because we're still working off typeinfo
// and don't have a real value to transform until later.
row.unmarshalMachineTransform.trFunc = entry.UnmarshalTransformFunc
row.unmarshalMachineTransform.recv_rt = entry.UnmarshalTransformTargetType
// Pick delegate without growing stack. (This currently means recursive transform won't fly.)
row.unmarshalMachineTransform.delegate = _yieldUnmarshalMachinePtr(row, atl, entry.UnmarshalTransformTargetType)
return &row.unmarshalMachineTransform
case entry.StructMap != nil:
row.unmarshalMachineStructAtlas.cfg = entry
return &row.unmarshalMachineStructAtlas
case entry.UnionKeyedMorphism != nil:
row.unmarshalMachineUnionKeyed.cfg = entry.UnionKeyedMorphism
return &row.unmarshalMachineUnionKeyed
default:
panic("invalid atlas entry")
}
}
// Returns the top row of the slab. Useful for machines that need to delegate
// to another type that's definitely not their own (comes up for the wildcard delegators).
func (s *unmarshalSlab) tip() *unmarshalSlabRow {
return &s.rows[len(s.rows)-1]
}
func (s *unmarshalSlab) grow() {
s.rows = append(s.rows, unmarshalSlabRow{})
}
func (s *unmarshalSlab) release() {
s.rows = s.rows[0 : len(s.rows)-1]
}
type errThunkUnmarshalMachine struct {
err error
}
func (m *errThunkUnmarshalMachine) Reset(_ *unmarshalSlab, _ reflect.Value, _ reflect.Type) error {
return m.err
}
func (m *errThunkUnmarshalMachine) Step(d *Unmarshaller, s *unmarshalSlab, tok *Token) (done bool, err error) {
return true, m.err
}

View File

@@ -0,0 +1,87 @@
package obj
import (
"reflect"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineSliceWildcard struct {
target_rv reflect.Value // target slice handle
working_rv reflect.Value // working slice (target is set to this at end)
value_rt reflect.Type
valueZero_rv reflect.Value // a zero of the slice's value type (for kicking append)
valueMach UnmarshalMachine
phase unmarshalMachineArrayWildcardPhase
index int
}
func (mach *unmarshalMachineSliceWildcard) Reset(slab *unmarshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
mach.working_rv = rv
mach.value_rt = rt.Elem()
mach.valueZero_rv = reflect.Zero(mach.value_rt)
mach.valueMach = slab.requisitionMachine(mach.value_rt)
mach.phase = unmarshalMachineArrayWildcardPhase_initial
mach.index = 0
return nil
}
func (mach *unmarshalMachineSliceWildcard) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch mach.phase {
case unmarshalMachineArrayWildcardPhase_initial:
return mach.step_Initial(driver, slab, tok)
case unmarshalMachineArrayWildcardPhase_acceptValueOrClose:
return mach.step_AcceptValueOrClose(driver, slab, tok)
}
panic("unreachable")
}
func (mach *unmarshalMachineSliceWildcard) step_Initial(_ *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// If it's a special state, start an object.
// (Or, blow up if its a special state that's silly).
switch tok.Type {
case TMapOpen:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
case TArrOpen:
// Great. Consumed.
mach.phase = unmarshalMachineArrayWildcardPhase_acceptValueOrClose
// Initialize the slice.
mach.target_rv.Set(reflect.MakeSlice(mach.target_rv.Type(), 0, 0))
return false, nil
case TMapClose:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
case TArrClose:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
case TNull:
mach.target_rv.Set(reflect.Zero(mach.target_rv.Type()))
return true, nil
default:
return true, ErrMalformedTokenStream{tok.Type, "start of array"}
}
}
func (mach *unmarshalMachineSliceWildcard) step_AcceptValueOrClose(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// Either form of open token are valid, but
// - an arrClose is ours
// - and a mapClose is clearly invalid.
switch tok.Type {
case TMapClose:
// no special checks for ends of wildcard slice; no such thing as incomplete.
return true, ErrMalformedTokenStream{tok.Type, "start of value or end of array"}
case TArrClose:
mach.target_rv.Set(mach.working_rv)
// release the slab row we requisitioned for our value machine.
slab.release()
return true, nil
}
// Grow the slice if necessary.
mach.working_rv = reflect.Append(mach.working_rv, mach.valueZero_rv)
// Recurse on a handle to the next index.
rv := mach.working_rv.Index(mach.index)
mach.index++
return false, driver.Recurse(tok, rv, mach.value_rt, mach.valueMach)
// Step simply remains `step_AcceptValueOrClose` -- arrays don't have much state machine.
}

109
vendor/github.com/polydawn/refmt/obj/unmarshalStruct.go generated vendored Normal file
View File

@@ -0,0 +1,109 @@
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineStructAtlas struct {
cfg *atlas.AtlasEntry // set on initialization
rv reflect.Value
expectLen int // Length header from mapOpen token. If it was set, we validate it.
index int // Progress marker: our distance into the stream of pairs.
value bool // Progress marker: whether the next token is a value.
fieldEntry atlas.StructMapEntry // Which field we expect next: set when consuming a key.
}
func (mach *unmarshalMachineStructAtlas) Reset(_ *unmarshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.rv = rv
// not necessary to reset expectLen because MapOpen tokens also consistently use the -1 convention.
mach.index = -1
mach.value = false
return nil
}
func (mach *unmarshalMachineStructAtlas) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// Starter state.
if mach.index < 0 {
switch tok.Type {
case TMapOpen:
// Great. Consumed.
mach.expectLen = tok.Length
mach.index++
return false, nil
case TMapClose:
return true, ErrMalformedTokenStream{tok.Type, "start of map"}
case TArrOpen:
return true, ErrMalformedTokenStream{tok.Type, "start of map"}
case TArrClose:
return true, ErrMalformedTokenStream{tok.Type, "start of map"}
case TNull:
mach.rv.Set(reflect.Zero(mach.rv.Type()))
return true, nil
default:
return true, ErrMalformedTokenStream{tok.Type, "start of map"}
}
}
// Accept value:
if mach.value {
var child_rv reflect.Value
var child_rt reflect.Type
if mach.fieldEntry.Ignore {
// Use a dummy slot to slurp up the value. This could be more efficient.
child_rt = reflect.TypeOf((*interface{})(nil)).Elem()
child_rv = reflect.New(child_rt).Elem()
} else {
child_rt = mach.fieldEntry.Type
child_rv = mach.fieldEntry.ReflectRoute.TraverseToValue(mach.rv)
}
mach.index++
mach.value = false
return false, driver.Recurse(
tok,
child_rv,
child_rt,
slab.requisitionMachine(child_rt),
)
}
// Accept key or end:
if mach.index > 0 {
slab.release()
}
switch tok.Type {
case TMapClose:
// If we got length header, validate that; error if mismatch.
if mach.expectLen >= 0 {
if mach.expectLen != mach.index {
return true, fmt.Errorf("malformed map token stream: declared length %d, actually got %d entries", mach.expectLen, mach.index)
}
}
// Future: this would be a reasonable place to check that all required fields have been filled in, if we add such a feature.
return true, nil
case TString:
for n := 0; n < len(mach.cfg.StructMap.Fields); n++ {
fieldEntry := mach.cfg.StructMap.Fields[n]
if fieldEntry.SerialName != tok.Str {
continue
}
mach.fieldEntry = fieldEntry
mach.value = true
break
}
if mach.value == false {
// FUTURE: it should be configurable per atlas.StructMap whether this is considered an error or to be tolerated.
// Currently we're being extremely strict about it, which is a divergence from the stdlib json behavior.
return true, ErrNoSuchField{tok.Str, mach.cfg.Type.String()}
}
default:
return true, ErrMalformedTokenStream{tok.Type, "map key"}
}
return false, nil
}

View File

@@ -0,0 +1,38 @@
package obj
import (
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineTransform struct {
trFunc atlas.UnmarshalTransformFunc
recv_rt reflect.Type
delegate UnmarshalMachine // machine for handling the recv type, stepped to completion before transform applied.
target_rv reflect.Value // given on Reset, retained until last step, and set into after using trFunc
recv_rv reflect.Value // if set, handle to slot where slice is stored; content must be placed into target at end.
}
func (mach *unmarshalMachineTransform) Reset(slab *unmarshalSlab, rv reflect.Value, _ reflect.Type) error {
mach.target_rv = rv
mach.recv_rv = reflect.New(mach.recv_rt).Elem() // REVIEW: this behavior with ptr vs not for in_rt. the star-star case is prob not what want.
return mach.delegate.Reset(slab, mach.recv_rv, mach.recv_rt)
}
func (mach *unmarshalMachineTransform) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
done, err = mach.delegate.Step(driver, slab, tok)
if err != nil {
return
}
if !done {
return
}
// on the last step, use transform, and finally set in real target.
tr_rv, err := mach.trFunc(mach.recv_rv)
// do attempt the set even if error. user may appreciate partial progress.
mach.target_rv.Set(tr_rv)
return true, err
}

View File

@@ -0,0 +1,109 @@
package obj
import (
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineUnionKeyed struct {
cfg *atlas.UnionKeyedMorphism // set on initialization
target_rv reflect.Value
target_rt reflect.Type
phase unmarshalMachineUnionKeyedPhase
tmp_rv reflect.Value
delegate UnmarshalMachine // actual machine, once we've demuxed with the second token (the key).
}
type unmarshalMachineUnionKeyedPhase uint8
const (
unmarshalMachineUnionKeyedPhase_acceptMapOpen unmarshalMachineUnionKeyedPhase = iota
unmarshalMachineUnionKeyedPhase_acceptKey
unmarshalMachineUnionKeyedPhase_delegate
unmarshalMachineUnionKeyedPhase_acceptMapClose
)
func (mach *unmarshalMachineUnionKeyed) Reset(_ *unmarshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
mach.target_rt = rt
mach.phase = unmarshalMachineUnionKeyedPhase_acceptMapOpen
return nil
}
func (mach *unmarshalMachineUnionKeyed) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch mach.phase {
case unmarshalMachineUnionKeyedPhase_acceptMapOpen:
return mach.step_acceptMapOpen(driver, slab, tok)
case unmarshalMachineUnionKeyedPhase_acceptKey:
return mach.step_acceptKey(driver, slab, tok)
case unmarshalMachineUnionKeyedPhase_delegate:
return mach.step_delegate(driver, slab, tok)
case unmarshalMachineUnionKeyedPhase_acceptMapClose:
return mach.step_acceptMapClose(driver, slab, tok)
}
panic("unreachable")
}
func (mach *unmarshalMachineUnionKeyed) step_acceptMapOpen(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch tok.Type {
case TMapOpen:
switch tok.Length {
case -1: // pass
case 1: // correct
default:
return true, ErrMalformedTokenStream{tok.Type, "unions in keyed format must be maps with exactly one entry"} // FIXME not malformed per se
}
mach.phase = unmarshalMachineUnionKeyedPhase_acceptKey
return false, nil
// REVIEW: is case TNull perhaps conditionally acceptable?
default:
return true, ErrMalformedTokenStream{tok.Type, "start of union value"} // FIXME not malformed per se
}
}
func (mach *unmarshalMachineUnionKeyed) step_acceptKey(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch tok.Type {
case TString:
// Look up the configuration for this key.
delegateAtlasEnt, ok := mach.cfg.Elements[tok.Str]
if !ok {
return true, ErrNoSuchUnionMember{tok.Str, mach.target_rt, mach.cfg.KnownMembers}
}
// Allocate a new concrete value, and hang on to that rv handle.
// Assigning into the interface must be done at the end if it's a non-pointer.
mach.tmp_rv = reflect.New(delegateAtlasEnt.Type).Elem()
// Get and configure a machine for the delegation.
delegate := _yieldUnmarshalMachinePtrForAtlasEntry(slab.tip(), delegateAtlasEnt, slab.atlas)
if err := delegate.Reset(slab, mach.tmp_rv, delegateAtlasEnt.Type); err != nil {
return true, err
}
mach.delegate = delegate
mach.phase = unmarshalMachineUnionKeyedPhase_delegate
return false, nil
default:
return true, ErrMalformedTokenStream{tok.Type, "map key"}
}
}
func (mach *unmarshalMachineUnionKeyed) step_delegate(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
done, err = mach.delegate.Step(driver, slab, tok)
if done && err == nil {
mach.phase = unmarshalMachineUnionKeyedPhase_acceptMapClose
return false, nil
}
return
}
func (mach *unmarshalMachineUnionKeyed) step_acceptMapClose(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
switch tok.Type {
case TMapClose:
mach.target_rv.Set(mach.tmp_rv)
return true, nil
default:
return true, ErrMalformedTokenStream{tok.Type, "map close at end of union value"}
}
}

View File

@@ -0,0 +1,112 @@
package obj
import (
"fmt"
"reflect"
. "github.com/polydawn/refmt/tok"
)
type unmarshalMachineWildcard struct {
target_rv reflect.Value
target_rt reflect.Type
delegate UnmarshalMachine // actual machine, once we've demuxed with the first token.
holder_rv reflect.Value // if set, handle to slot where slice is stored; content must be placed into target at end.
}
func (mach *unmarshalMachineWildcard) Reset(_ *unmarshalSlab, rv reflect.Value, rt reflect.Type) error {
mach.target_rv = rv
mach.target_rt = rt
mach.delegate = nil
mach.holder_rv = reflect.Value{}
return nil
}
func (mach *unmarshalMachineWildcard) Step(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
if mach.delegate == nil {
done, err = mach.prepareDemux(driver, slab, tok)
if done {
return
}
}
done, err = mach.delegate.Step(driver, slab, tok)
if !done {
return
}
if mach.holder_rv.IsValid() {
mach.target_rv.Set(mach.holder_rv)
}
return
}
func (mach *unmarshalMachineWildcard) prepareDemux(driver *Unmarshaller, slab *unmarshalSlab, tok *Token) (done bool, err error) {
// If a "tag" is set in the token, we try to follow that as a hint for
// any specifically customized behaviors for how this should be unmarshalled.
if tok.Tagged == true {
atlasEntry, exists := slab.atlas.GetEntryByTag(tok.Tag)
if !exists {
return true, fmt.Errorf("missing an unmarshaller for tag %v", tok.Tag)
}
value_rt := atlasEntry.Type
mach.holder_rv = reflect.New(value_rt).Elem()
mach.delegate = _yieldUnmarshalMachinePtr(slab.tip(), slab.atlas, value_rt)
if err := mach.delegate.Reset(slab, mach.holder_rv, value_rt); err != nil {
return true, err
}
return false, nil
}
// Switch on token type: we may be able to delegate to a primitive machine,
// but we may also need to initialize a container type and then hand off.
switch tok.Type {
case TMapOpen:
child := make(map[string]interface{})
child_rv := reflect.ValueOf(child)
mach.target_rv.Set(child_rv)
mach.delegate = &slab.tip().unmarshalMachineMapStringWildcard
if err := mach.delegate.Reset(slab, child_rv, child_rv.Type()); err != nil {
return true, err
}
return false, nil
case TArrOpen:
// Stdlib has very interesting branch here: 'case reflect.Interface: if v.NumMethod() == 0 {'
// If that matches, it goes on a *totally different* branch that leaves the reflective path entirely forever.
// (Which is kind of interesting, because it also means it will never reuse memory there. If you wanted that.)
// This definitely went through a few discovery steps...
// - https://play.golang.org/p/Qbtpxwh68e
// - https://play.golang.org/p/l5RQujLnDN
// - https://play.golang.org/p/Z2ilpPk0vk
// - https://play.golang.org/p/jV9VFDht6F -- finally getting somewhere good
holder := make([]interface{}, 0)
mach.holder_rv = reflect.ValueOf(&holder).Elem()
mach.delegate = &slab.tip().unmarshalMachineSliceWildcard
if err := mach.delegate.Reset(slab, mach.holder_rv, mach.holder_rv.Type()); err != nil {
return true, err
}
return false, nil
case TMapClose:
return true, ErrMalformedTokenStream{tok.Type, "start of value"}
case TArrClose:
return true, ErrMalformedTokenStream{tok.Type, "start of value"}
case TNull:
mach.target_rv.Set(reflect.Zero(mach.target_rt))
return true, nil
default:
// If it wasn't the start of composite, shell out to the machine for literals.
// Don't bother to replace our internal step func because literal machines are never multi-call,
// and this lets us avoid grabbing a pointer and it shuffling around.
delegateMach := slab.tip().unmarshalMachinePrimitive
delegateMach.kind = reflect.Interface
if err := delegateMach.Reset(slab, mach.target_rv, nil); err != nil {
return true, err
}
return delegateMach.Step(driver, slab, tok)
}
}