From 148762912312aff3f5cf169fea2ea1a12b301eeb Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 21 Jul 2023 12:58:49 +0200 Subject: [PATCH] forge (work in progress): copy ptr package from go-schema --- common/ptr/ptr.go | 38 +++++++++++++ common/ptr/scalar.go | 130 +++++++++++++++++++++++++++++++++++++++++++ common/ptr/slice.go | 94 +++++++++++++++++++++++++++++++ tests/common_test.go | 77 +++++++++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 common/ptr/ptr.go create mode 100644 common/ptr/scalar.go create mode 100644 common/ptr/slice.go diff --git a/common/ptr/ptr.go b/common/ptr/ptr.go new file mode 100644 index 0000000..30a8efe --- /dev/null +++ b/common/ptr/ptr.go @@ -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 +} diff --git a/common/ptr/scalar.go b/common/ptr/scalar.go new file mode 100644 index 0000000..b444764 --- /dev/null +++ b/common/ptr/scalar.go @@ -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]()) +} diff --git a/common/ptr/slice.go b/common/ptr/slice.go new file mode 100644 index 0000000..1b2ff25 --- /dev/null +++ b/common/ptr/slice.go @@ -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) +} diff --git a/tests/common_test.go b/tests/common_test.go index 069ab89..f0182a2 100644 --- a/tests/common_test.go +++ b/tests/common_test.go @@ -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) +}