forge (work in progress): copy ptr package from go-schema

This commit is contained in:
Helmut Merz 2023-07-21 12:58:49 +02:00
parent fc5f140059
commit 1487629123
4 changed files with 339 additions and 0 deletions

38
common/ptr/ptr.go Normal file
View file

@ -0,0 +1,38 @@
// Package ptr provides a generic interface for accessing an ordered
// collection of values sequentially, updating it and appending to it
// using a pointer (or cursor) type that keeps track of the current
// position. There are also implementations based on single
// scalar values, object attributes, or accessors.
package ptr
type Ptr[V any] interface {
Append(V) Ptr[V]
Insert(V, int) Ptr[V]
Next() Ptr[V]
Value() V
Set(V) Ptr[V]
Size() int
Started() bool
Seek(int) Ptr[V]
Reset() Ptr[V]
Clone() Ptr[V]
New() Ptr[V]
Copy() Ptr[V]
}
// generic functions
func New[V any](p Ptr[V]) Ptr[V] {
np := p.Clone()
np.Reset()
return np
}
func Copy[V any](p, np Ptr[V]) Ptr[V] {
op := p.New()
for op.Next() != nil {
np.Append(op.Value())
}
np.Reset()
return np
}

130
common/ptr/scalar.go Normal file
View file

@ -0,0 +1,130 @@
package ptr
// scb: scalar base / common stuff
type scb[V any] struct {
started bool
}
func (p *scb[V]) Size() int {
return 1
}
func (p *scb[V]) Started() bool {
return p.started
}
func (p *scb[V]) Append(v V) Ptr[V] {
p.started = true
return p.Set(v)
}
func (p *scb[V]) Insert(v V, pos int) Ptr[V] {
return p.Append(v)
}
func (p *scb[V]) Next() Ptr[V] {
if p.started {
return nil
}
p.started = true
return p
}
func (p *scb[V]) Value() V {
var v V
return v
}
func (p *scb[V]) Set(v V) Ptr[V] {
return p
}
func (p *scb[V]) Seek(i int) Ptr[V] {
p.started = true
return p
}
func (p *scb[V]) Reset() Ptr[V] {
p.started = false
return p
}
func (p *scb[V]) Clone() Ptr[V] {
return &scb[V]{p.started}
}
func (p *scb[V]) New() Ptr[V] {
return New[V](p)
}
func (p *scb[V]) Copy() Ptr[V] {
return Copy[V](p, &scb[V]{false})
}
// scalar implementation using a value pointer
func NewScalar[V any]() Ptr[V] {
var v V
return NewVP(&v)
}
func NewVP[V any](vp *V) Ptr[V] {
get := func() V {
return *vp
}
set := func(v V) {
*vp = v
}
return NewAcc(get, set)
}
// scalar implementation using accessor functions
type acc[V any] struct {
*scb[V]
getter func() V
setter func(v V)
}
func NewAcc[V any](g func() V, s func(v V)) *acc[V] {
return &acc[V]{
getter: g,
setter: s,
scb: &scb[V]{false},
}
}
func (p *acc[V]) Append(v V) Ptr[V] {
p.started = true
return p.Set(v)
}
func (p *acc[V]) Insert(v V, pos int) Ptr[V] {
return p.Append(v)
}
func (p *acc[V]) Value() V {
return p.getter()
}
func (p *acc[V]) Set(v V) Ptr[V] {
p.setter(v)
return p
}
func (p *acc[V]) Clone() Ptr[V] {
return &acc[V]{
getter: p.getter,
setter: p.setter,
scb: &scb[V]{p.started},
}
}
func (p *acc[V]) New() Ptr[V] {
return New[V](p)
}
func (p *acc[V]) Copy() Ptr[V] {
return Copy[V](p, NewScalar[V]())
}

94
common/ptr/slice.go Normal file
View file

@ -0,0 +1,94 @@
package ptr
// slice-based implementation
type sliceseq[V any] []V
type sptr[V any] struct {
seq *sliceseq[V]
offset int
}
func newSliceSeq[V any]() *sliceseq[V] {
return &sliceseq[V]{}
}
func NewSlice[V any]() *sptr[V] {
return &sptr[V]{
seq: newSliceSeq[V](),
offset: -1,
}
}
// Ptr methods
func (p *sptr[V]) Append(v V) Ptr[V] {
*p.seq = append(*p.seq, v)
p.offset = len(*p.seq) - 1
return p
}
func (p *sptr[V]) Insert(v V, pos int) Ptr[V] {
lidx := len(*p.seq) - 1
if lidx < pos {
return p.Append(v)
}
*p.seq = append(*p.seq, (*p.seq)[lidx])
for i := lidx; i > pos; i -= 1 {
(*p.seq)[i] = (*p.seq)[i-1]
}
(*p.seq)[pos] = v
p.offset = pos
return p
}
func (p *sptr[V]) Next() Ptr[V] {
if p.offset < len(*p.seq)-1 {
p.offset += 1
return p
}
return nil
}
func (p *sptr[V]) Value() V {
return (*p.seq)[p.offset]
}
func (p *sptr[V]) Set(v V) Ptr[V] {
(*p.seq)[p.offset] = v
return p
}
func (p *sptr[V]) Size() int {
return len(*p.seq)
}
func (p *sptr[V]) Started() bool {
return p.offset >= 0
}
func (p *sptr[V]) Seek(i int) Ptr[V] {
p.offset = i
return p
}
func (p *sptr[V]) Reset() Ptr[V] {
p.offset = -1
return p
}
func (p *sptr[V]) Clone() Ptr[V] {
return &sptr[V]{
seq: p.seq,
offset: p.offset,
}
}
func (p *sptr[V]) New() Ptr[V] {
return New[V](p)
}
func (p *sptr[V]) Copy() Ptr[V] {
np := NewSlice[V]()
return Copy[V](p, np)
}

View file

@ -3,6 +3,7 @@ package scopes_test
import (
tbase "testing"
"git.sr.ht/~cco/go-scopes/common/ptr"
"git.sr.ht/~cco/go-scopes/common/stack"
"git.sr.ht/~cco/go-scopes/common/testing"
"git.sr.ht/~cco/go-scopes/common/voc"
@ -12,6 +13,9 @@ func TestCommon(tb *tbase.T) {
t := testing.SetUp(tb)
t.Run("stack", StackTest)
t.Run("voc", VocTest)
t.Run("ptr-value", PtrValuePointerTest)
t.Run("ptr-scalar", PtrScalarTest)
t.Run("ptr-slice", PtrSliceTest)
}
func StackTest(t *testing.T) {
@ -42,3 +46,76 @@ func VocTest(t *testing.T) {
m = v1.Lookup("two")
t.AssertEqual(m.IsNothing(), true)
}
func PtrValuePointerTest(t *testing.T) {
var v int
var sp1 ptr.Ptr[int] = ptr.NewVP[int](&v)
sp1.Append(11)
t.AssertEqual(sp1.Value(), 11)
t.AssertEqual(sp1.Next(), nil)
sp2 := sp1.Copy()
t.AssertEqual(sp2.Value(), 11)
sp2.Set(42)
t.AssertEqual(sp2.Value(), 42)
t.AssertEqual(sp1.Value(), 11)
sp3 := sp1.Clone()
t.AssertEqual(sp3.Value(), 11)
sp3.Set(46)
t.AssertEqual(sp3.Value(), 46)
t.AssertEqual(sp1.Value(), 46)
}
func PtrScalarTest(t *testing.T) {
var sp1 ptr.Ptr[int] = ptr.NewScalar[int]()
sp1.Append(11)
t.AssertEqual(sp1.Value(), 11)
t.AssertEqual(sp1.Next(), nil)
sp2 := sp1.Copy()
t.AssertEqual(sp2.Value(), 11)
sp2.Set(42)
t.AssertEqual(sp2.Value(), 42)
t.AssertEqual(sp1.Value(), 11)
sp3 := sp1.Clone()
t.AssertEqual(sp3.Value(), 11)
sp3.Set(46)
t.AssertEqual(sp3.Value(), 46)
t.AssertEqual(sp1.Value(), 46)
}
func PtrSliceTest(t *testing.T) {
var sp1 ptr.Ptr[int] = ptr.NewSlice[int]()
sp1.Append(11)
t.AssertEqual(sp1.Value(), 11)
t.AssertEqual(sp1.Next(), nil)
sp1.Append(12)
t.AssertEqual(sp1.Size(), 2)
sp1.Reset()
t.AssertEqual(sp1.Next().Value(), 11)
t.AssertEqual(sp1.Value(), 11)
sp1.Append(13)
t.AssertEqual(sp1.Value(), 13)
sp2 := sp1.Clone()
t.AssertEqual(sp2.Value(), 13)
sp2.Set(31)
t.AssertEqual(sp2.Value(), 31)
t.AssertEqual(sp1.Value(), 31)
sp1.Append(14)
t.AssertEqual(sp1.Size(), 4)
t.AssertEqual(sp2.Size(), 4)
t.AssertEqual(sp1.Value(), 14)
t.AssertEqual(sp2.Value(), 31)
sp3 := sp2.New()
sp3.Next()
t.AssertEqual(sp3.Value(), 11)
sp3.Append(15)
t.AssertEqual(sp1.Size(), 5)
sp1.Next()
t.AssertEqual(sp1.Value(), 15)
t.AssertEqual(sp1.Size(), 5)
sp1.Insert(99, 1)
t.AssertEqual(sp1.Value(), 99)
sp1.Next()
t.AssertEqual(sp1.Value(), 12)
sp1.Reset()
t.AssertEqual(sp1.Next().Value(), 11)
}