forge (work in progress): copy ptr package from go-schema
This commit is contained in:
parent
fc5f140059
commit
1487629123
4 changed files with 339 additions and 0 deletions
38
common/ptr/ptr.go
Normal file
38
common/ptr/ptr.go
Normal 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
130
common/ptr/scalar.go
Normal 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
94
common/ptr/slice.go
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue