Skip to content

Commit

Permalink
more updates
Browse files Browse the repository at this point in the history
  • Loading branch information
smrz2001 committed Sep 26, 2022
1 parent db28eaa commit 79b03dd
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 48 deletions.
4 changes: 0 additions & 4 deletions datamodel/amender.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@ type NodeAmender interface {
// Transform will do an in-place transformation of the node at the specified path and return its previous value.
// If `createParents = true`, any missing parents will be created, otherwise this function will return an error.
Transform(path Path, transform func(Node) (Node, error), createParents bool) (Node, error)

// Amend returns a `Node` representing the "effective" view of the base `Node` (if specified) along with accumulated
// update (if any).
Amend() Node
}
28 changes: 26 additions & 2 deletions node/basicnode/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (

var (
//_ datamodel.Node = &anyNode{}
_ datamodel.NodePrototype = Prototype__Any{}
_ datamodel.NodeBuilder = &anyBuilder{}
_ datamodel.NodePrototype = Prototype__Any{}
_ datamodel.NodeBuilder = &anyBuilder{}
_ datamodel.NodePrototypeSupportingAmend = Prototype__Any{}
//_ datamodel.NodeAssembler = &anyAssembler{}
)

Expand Down Expand Up @@ -38,6 +39,19 @@ func (Prototype__Any) NewBuilder() datamodel.NodeBuilder {
return &anyBuilder{}
}

func (p Prototype__Any) AmendingBuilder(base datamodel.Node) datamodel.NodeAmender {
nb := &anyBuilder{}
if base != nil {
switch base.Kind() {
case datamodel.Kind_Map, datamodel.Kind_List:
nb.AssignNull()
default:
nb.AssignNode(base)
}
}
return nb
}

// -- NodeBuilder -->

// anyBuilder is a builder for any kind of node.
Expand Down Expand Up @@ -70,6 +84,16 @@ type anyBuilder struct {
scalarNode datamodel.Node
}

func (nb *anyBuilder) Get(path datamodel.Path) (datamodel.Node, error) {
//TODO implement me
panic("implement me")
}

func (nb *anyBuilder) Transform(path datamodel.Path, transform func(datamodel.Node) (datamodel.Node, error), createParents bool) (datamodel.Node, error) {
//TODO implement me
panic("implement me")
}

func (nb *anyBuilder) Reset() {
*nb = anyBuilder{}
}
Expand Down
92 changes: 50 additions & 42 deletions node/basicnode/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
var (
_ datamodel.Node = &plainMap{}
_ datamodel.NodePrototype = Prototype__Map{}
_ datamodel.NodePrototypeSupportingAmend = &Prototype__Map{}
_ datamodel.NodePrototypeSupportingAmend = Prototype__Map{}
_ datamodel.NodeAmender = &plainMap__Builder{}
_ datamodel.NodeAssembler = &plainMap__Assembler{}
)
Expand All @@ -23,7 +23,7 @@ var (
// plainMap is also embedded in the 'any' struct and usable from there.
type plainMap struct {
// Parent node (can be any recursive type)
//p datamodel.Node
p datamodel.NodeAmender
// Map contents
m linkedhashmap.Map
// The following fields are needed to present an accurate "effective" view of the base node and all accumulated
Expand All @@ -35,16 +35,14 @@ type plainMap struct {
// This is the count of children *present in the base node* that are removed. Knowing this count allows accurate
// traversal of the "effective" node view.
r int
// This is the count of new children. If an added node is removed, this count should be decremented instead of
// `r`.
// This is the count of new children. If an added node is removed, this count should be decremented instead of `r`.
a int
}

type plainMap__Entry struct {
k plainString // address of this used when we return keys as nodes, such as in iterators. Need in one place to amortize shifts to heap when ptr'ing for iface.
v datamodel.Node // identical to map values. keeping them here simplifies iteration. (in codegen'd maps, this position is also part of amortization, but in this implementation, that's less useful.)
// note on alternate implementations: 'v' could also use the 'any' type, and thus amortize value allocations. the memory size trade would be large however, so we don't, here.
c bool // whether this node was "added" to the map, or wraps an existing base node child node.
k plainString // address of this used when we return keys as nodes, such as in iterators. Need in one place to amortize shifts to heap when ptr'ing for iface.
v datamodel.NodeAmender // store an amender instead of the node so that we can access both the node and the API to update it.
a bool // whether this node was "added" to the map, or wraps an existing base node child node.
}

// -- Node interface methods -->
Expand All @@ -53,17 +51,17 @@ func (plainMap) Kind() datamodel.Kind {
return datamodel.Kind_Map
}
func (n *plainMap) LookupByString(key string) (datamodel.Node, error) {
if e, err := n.lookupEntryByString(key); err != nil {
if entry, err := n.lookupEntryByString(key); err != nil {
return nil, err
} else {
return e.v, err
return entry.v.Build(), err
}
}
func (n *plainMap) lookupEntryByString(key string) (*plainMap__Entry, error) {
// Look at local state first
if entry, exists := n.m.Get(key); exists {
e := entry.(*plainMap__Entry)
if e.v.IsNull() {
if e.v == nil {
// Node was removed
return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
}
Expand Down Expand Up @@ -167,11 +165,12 @@ func (itr *plainMap_MapIterator) Next() (k datamodel.Node, v datamodel.Node, _ e
return nil, nil, err
}
if entry, exists := itr.n.m.Get(ks); exists {
v = entry.(*plainMap__Entry).v
e := entry.(*plainMap__Entry)
// Skip removed nodes
if v.IsNull() {
if e.v == nil {
continue
}
v = e.v.Build()
// Fall-through and return wrapped nodes
}
// We found a "real" node to return, increment the counter.
Expand All @@ -182,16 +181,16 @@ func (itr *plainMap_MapIterator) Next() (k datamodel.Node, v datamodel.Node, _ e
if itr.m != nil {
// Iterate over mods, skipping removed nodes.
for itr.m.Next() {
e := itr.m.Value().(*plainMap__Entry)
k = &e.k
v = e.v
entry := itr.m.Value().(*plainMap__Entry)
// Skip removed nodes
if v.IsNull() {
if entry.v == nil {
continue
}
k = &entry.k
v = entry.v.Build()
// Skip "wrapper" nodes that represent existing sub-nodes in the hierarchy corresponding to an added leaf
// node.
if !e.c {
if !entry.a {
continue
}
// We found a "real" node to return, increment the counter.
Expand All @@ -209,14 +208,23 @@ func (itr *plainMap_MapIterator) Done() bool {

type Prototype__Map struct{}

func (Prototype__Map) NewBuilder() datamodel.NodeBuilder {
return &plainMap__Builder{plainMap__Assembler{w: &plainMap{}}}
func (p Prototype__Map) NewBuilder() datamodel.NodeBuilder {
return p.AmendingBuilder(nil)
}

// -- NodePrototypeSupportingAmend -->

func (p Prototype__Map) AmendingBuilder(base datamodel.Node) datamodel.NodeAmender {
return p.NewBuilder().(*plainMap__Builder)
func (Prototype__Map) AmendingBuilder(base datamodel.Node) datamodel.NodeAmender {
var b *plainMap = nil
if base != nil {
// If `base` is specified, it MUST be another `plainMap`.
if baseMap, castOk := base.(*plainMap); !castOk {
panic("misuse")
} else {
b = baseMap
}
}
return &plainMap__Builder{plainMap__Assembler{w: &plainMap{b: b}}}
}

// -- NodeBuilder -->
Expand All @@ -226,14 +234,15 @@ type plainMap__Builder struct {
}

func (nb *plainMap__Builder) Build() datamodel.Node {
if nb.state != maState_finished {
panic("invalid state: assembler must be 'finished' before Build can be called!")
if (nb.state != maState_initial) && (nb.state != maState_finished) {
panic("invalid state: assembly in progress must be 'finished' before Build can be called!")
}
return nb.w
}
func (nb *plainMap__Builder) Reset() {
base := nb.w.b
*nb = plainMap__Builder{}
nb.w = &plainMap{}
nb.w = &plainMap{b: base}
}

// -- NodeAmender -->
Expand All @@ -244,13 +253,15 @@ func (nb *plainMap__Builder) Get(path datamodel.Path) (datamodel.Node, error) {
return nb.w, nil
}
childSeg, remainingPath := path.Shift()
childVal, err := nb.w.LookupBySegment(childSeg)
childKey := childSeg.String()
childEntry, err := nb.w.lookupEntryByString(childKey)
// Since we're explicitly looking for a node, look for the child node in the current amender state and throw an
// error if it does not exist.
if err != nil {
return nil, err
}
return nb.storeChildEntry(childSeg, childVal, childVal.Kind(), false).Get(remainingPath)
childVal := childEntry.v.Build()
return nb.storeChildEntry(childKey, childVal, childVal.Kind(), false).Get(remainingPath)
}

func (nb *plainMap__Builder) Transform(path datamodel.Path, transform func(datamodel.Node) (datamodel.Node, error), createParents bool) (datamodel.Node, error) {
Expand Down Expand Up @@ -281,19 +292,19 @@ func (nb *plainMap__Builder) Transform(path datamodel.Path, transform func(datam
}
}
if atLeaf {
if newChildVal, err := transform(childEntry.v); err != nil {
if newChildVal, err := transform(childEntry.v.Build()); err != nil {
return nil, err
} else if newChildVal == nil {
// Use the "Null" node to indicate a removed child.
nb.w.m.Put(childKey, &plainMap__Entry{plainString(childKey), datamodel.Null, false})
nb.w.m.Put(childKey, &plainMap__Entry{plainString(childKey), nil, false})
// If the child being removed didn't already exist, we could error out but we don't have to because the
// state will remain consistent. This operation is equivalent to adding a child then removing it, in which
// case we would have incremented then decremented `adds`, leaving it the same.
if childEntry != nil {
// If the child node being removed is a new node previously added to the node hierarchy, decrement
// `adds`, otherwise increment `rems`. This allows us to retain knowledge about the "history" of the
// base hierarchy.
if childEntry.c {
if childEntry.a {
nb.w.a--
} else {
nb.w.r++
Expand All @@ -307,9 +318,9 @@ func (nb *plainMap__Builder) Transform(path datamodel.Path, transform func(datam
nb.w.a++
created = true
}
nb.storeChildEntry(childSeg, newChildVal, newChildVal.Kind(), created)
nb.storeChildEntry(childKey, newChildVal, newChildVal.Kind(), created)
}
return childEntry.v, nil
return childEntry.v.Build(), nil
}
// While building the nested amender tree, only count nodes as "added" when they didn't exist and had to be created
// to fill out the hierarchy.
Expand All @@ -330,20 +341,16 @@ func (nb *plainMap__Builder) Transform(path datamodel.Path, transform func(datam
childKind = datamodel.Kind_Map
}
} else {
childKind = childEntry.v.Kind()
childKind = childEntry.v.Build().Kind()
}
return nb.storeChildEntry(childKey, childEntry.v, created).Transform(remainingPath, transform, createParents)
return nb.storeChildEntry(childKey, childEntry.v.Build(), childKind, created).Transform(remainingPath, transform, createParents)
}

func (nb *plainMap__Builder) storeChildEntry(k string, v datamodel.Node, created bool) datamodel.NodeAmender {
nb.w.m.Put(k, &plainMap__Entry{plainString(k), v, created})
func (nb *plainMap__Builder) storeChildEntry(k string, v datamodel.Node, kind datamodel.Kind, created bool) datamodel.NodeAmender {
nb.w.m.Put(k, &plainMap__Entry{plainString(k), Prototype.Any.AmendingBuilder(v), created})
return nb
}

func (nb *plainMap__Builder) Amend() datamodel.Node {
return nb.w
}

// -- NodeAssembler -->

type plainMap__Assembler struct {
Expand Down Expand Up @@ -613,8 +620,9 @@ func (mva *plainMap__ValueAssembler) AssignNode(v datamodel.Node) error {
itr := mva.ma.w.m.Iterator()
itr.Last()
val := itr.Value().(*plainMap__Entry)
val.v = v
val.c = true
nb := Prototype.Any.AmendingBuilder(v)
val.v = nb
val.a = true
mva.ma.w.a++
mva.ma.state = maState_initial
mva.ma = nil // invalidate self to prevent further incorrect use.
Expand Down

0 comments on commit 79b03dd

Please sign in to comment.