Skip to content

Commit

Permalink
global names and other metadata in the 0-2 object
Browse files Browse the repository at this point in the history
  • Loading branch information
gritzko committed Apr 16, 2024
1 parent c50a4bb commit 010016b
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 56 deletions.
2 changes: 1 addition & 1 deletion chotki_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestChotki_Sync(t *testing.T) {

bvv, err := b.VersionVector()
assert.Nil(t, err)
assert.Equal(t, "1,a-0-1,b-0-1", bvv.String())
assert.Equal(t, "0-2-3,a-0-1,b-0-1", bvv.String())

b.DumpAll()

Expand Down
35 changes: 34 additions & 1 deletion log0.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,44 @@ import (
"github.com/learn-decentralized-systems/toytlv"
)

const id1 = rdx.ID0 + rdx.ProInc
const ID2 = id1 + rdx.ProInc
const NamesID = ID2 + 1
const NodesID = ID2 + 2
const NodeInfoID = ID2 + 3

// FORMAT: replica creation packet
var Log0 = toyqueue.Records{
toytlv.Record('Y',
toytlv.Record('I', rdx.ID0.ZipBytes()), // identifier, `src-0`
toytlv.Record('R', rdx.ID0.ZipBytes()), // reference, `0-0`
toytlv.Record('S', rdx.Stlv("Sname")), // replica Name (string)
toytlv.Record('T', rdx.Ttlv("Name")), // replica Name (string)
toytlv.Record('T', rdx.Ttlv("S")),
),
toytlv.Record('C',
toytlv.Record('I', id1.ZipBytes()), // identifier, `src-0`
toytlv.Record('R', rdx.ID0.ZipBytes()), // reference, `0-0`
toytlv.Record('T', rdx.Ttlv("Names")), // global-scope names
toytlv.Record('T', rdx.Ttlv("M")),
toytlv.Record('T', rdx.Ttlv("Nodes")), // replica addresses
toytlv.Record('T', rdx.Ttlv("M")),
toytlv.Record('T', rdx.Ttlv("NodeInfo")), // replica addresses
toytlv.Record('T', rdx.Ttlv("M")),
),
toytlv.Record('O',
toytlv.Record('I', ID2.ZipBytes()),
toytlv.Record('R', id1.ZipBytes()),
toytlv.Record('M',
toytlv.Record('T', rdx.Ttlv("0")),
toytlv.Record('R', rdx.Rtlv(rdx.ID0)),
toytlv.Record('T', rdx.Ttlv("Global")),
toytlv.Record('R', rdx.Rtlv(ID2)),
toytlv.Record('T', rdx.Ttlv("Names")),
toytlv.Record('R', rdx.Rtlv(ID2+1)),
toytlv.Record('T', rdx.Ttlv("Nodes")),
toytlv.Record('R', rdx.Rtlv(ID2+2)),
),
toytlv.Record('M'),
toytlv.Record('M'),
),
}
22 changes: 21 additions & 1 deletion objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func (cho *Chotki) NewObject(tid rdx.ID, fields ...string) (id rdx.ID, err error
}
var packet toyqueue.Records
for i := 0; i < len(fields); i++ {
rdt := byte(form[i+1].RdxType)
rdt := form[i+1].RdxType
tlv := rdx.Xparse(rdt, fields[i])
if tlv == nil {
return rdx.BadId, ErrBadValueForAType
Expand Down Expand Up @@ -337,3 +337,23 @@ func (cho *Chotki) IncNField(fid rdx.ID) (id rdx.ID, err error) {
id, err = cho.CommitPacket('E', fid.ZeroOff(), tlvs)
return
}

func (cho *Chotki) ObjectFieldMapTermId(fid rdx.ID) (themap map[string]rdx.ID, err error) {
rdt, tlv, e := cho.ObjectFieldTLV(fid)
if e != nil {
return nil, e
}
if rdt != rdx.Mapping {
return nil, ErrWrongFieldType
}
themap = rdx.MnativeTR(tlv)
return
}

func (cho *Chotki) EditFieldTLV(fid rdx.ID, delta []byte) (id rdx.ID, err error) {
tlvs := toyqueue.Records{}
tlvs = append(tlvs, toytlv.TinyRecord('F', rdx.ZipUint64(fid.Off())))
tlvs = append(tlvs, delta)
id, err = cho.CommitPacket('E', fid.ZeroOff(), tlvs)
return
}
145 changes: 132 additions & 13 deletions rdx/ELM.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"github.com/learn-decentralized-systems/toytlv"
"slices"
"sort"
)

Expand Down Expand Up @@ -36,32 +37,32 @@ func TimeFromZipBytes(zip []byte) (t Time) {
return
}

var ErrBadISFR = errors.New("bad ISFR record")
var ErrBadFIRST = errors.New("bad FIRST record")

func MelAppend(to []byte, lit byte, t Time, body []byte) []byte {
tb := toytlv.TinyRecord('T', t.ZipBytes())
return toytlv.Append(to, lit, tb, body)
}

func MelReSource(isfr []byte, src uint64) (ret []byte, err error) {
func MelReSource(first []byte, src uint64) (ret []byte, err error) {
var lit byte
var time Time
var body []byte
rest := isfr
rest := first
for len(rest) > 0 {
at := len(isfr) - len(rest)
at := len(first) - len(rest)
lit, time, body, rest, err = ParseEnvelopedFIRST(rest)
if err != nil {
return
}
if time.src != src {
ret = make([]byte, at, len(isfr)*2)
copy(ret, isfr[:at])
ret = make([]byte, at, len(first)*2)
copy(ret, first[:at])
break
}
}
if ret == nil && err == nil {
return isfr, nil
return first, nil
}
for err == nil {
time.src = src
Expand Down Expand Up @@ -219,15 +220,33 @@ func Mstring(tlv []byte) (txt string) {
return string(ret)
}

type kv struct {
k, v []byte
}

// parse a text form into a TLV value
func Mparse(txt string) (tlv []byte) {
rdx, err := ParseRDX([]byte(txt))
if err != nil || rdx == nil || rdx.RdxType != Mapping {
return nil
}
for i := 0; i < len(rdx.Nested); i++ {
n := &rdx.Nested[i]
tlv = appendParsedFirstTlv(tlv, n)
kvs := []kv{}
pairs := rdx.Nested
for i := 0; i+1 < len(pairs); i += 2 {
next := kv{
FIRSTparsee(pairs[i].RdxType, string(pairs[i].Text)),
FIRSTparsee(pairs[i+1].RdxType, string(pairs[i+1].Text)),
}
if next.k != nil && next.v != nil {
kvs = append(kvs, next)
}
}
slices.SortFunc(kvs, func(i, j kv) int {
return FIRSTcompare(i.k, j.k)
})
for _, x := range kvs {
tlv = append(tlv, x.k...)
tlv = append(tlv, x.v...)
}
return
}
Expand All @@ -244,9 +263,84 @@ func Mmerge(tlvs [][]byte) (merged []byte) {
return
}

func MnativeTR(tlv []byte) MapTR {
ret := make(map[string]ID)
it := FIRSTIterator{TLV: tlv}
for it.Next() {
keyrdt, _, key := it.ParsedValue()
if !it.Next() {
break
}
valrdt, _, val := it.ParsedValue()
if keyrdt != Term || valrdt != Reference {
continue
}
id := IDFromZipBytes(val)
ret[string(key)] = id
}
return ret
}

func MparseTR(arg *RDX) MapTR {
if arg == nil || arg.RdxType != Mapping {
return nil
}
ret := make(MapTR)
kvs := arg.Nested
for i := 0; i+1 < len(kvs); i += 2 {
if kvs[i].RdxType != Term || kvs[i+1].RdxType != Reference {
continue
}
ret[string(kvs[i].Text)] = IDFromText(kvs[i+1].Text)
}
return ret
}

type MapTR map[string]ID

func (m MapTR) String() string {
var keys []string
for key := range m {
keys = append(keys, key)
}
var ret []byte
ret = append(ret, '{', '\n')
for _, key := range keys {
ret = append(ret, '\t')
ret = append(ret, key...)
ret = append(ret, ':', '\t')
ret = append(ret, m[key].String()...)
ret = append(ret, ',', '\n')
}
ret = append(ret, '}')
return string(ret)
}

// produce an op that turns the old value into the new one
func Mdelta(tlv []byte, new_val int64) (tlv_delta []byte) {
return nil // todo
func MdeltaTR(tlv []byte, changes MapTR) (tlv_delta []byte) {
it := MIterator{it: FIRSTIterator{TLV: tlv}}
for it.Next() {
if it.lit != Term {
continue
}
change, ok := changes[string(it.val)]
if !ok {
continue
}
new_rev := ZagZigUint64(it.revz)
if new_rev < 0 {
new_rev = -new_rev
}
new_rev++
tlv_delta = append(tlv_delta, toytlv.Record(Term, FIRSTtlv(new_rev, 0, it.val))...)
tlv_delta = append(tlv_delta, toytlv.Record(Reference, FIRSTtlv(new_rev, 0, change.ZipBytes()))...)
delete(changes, string(it.val))
}
for key, val := range changes {
tlv_delta = append(tlv_delta, toytlv.Record(Term, Ttlv(key))...)
tlv_delta = append(tlv_delta, toytlv.Record(Reference, Rtlv(val))...)
}
return
}

// checks a TLV value for validity (format violations)
Expand All @@ -260,6 +354,7 @@ func Mdiff(tlv []byte, vvdiff VV) []byte {

type MIterator struct {
it FIRSTIterator
keye []byte
val []byte
src uint64
revz uint64
Expand All @@ -270,6 +365,7 @@ type MIterator struct {
func (a *MIterator) Next() (got bool) {
tlv := a.it.TLV
got = a.it.Next() // skip value
a.keye = a.it.one
a.val = a.it.val
a.src = a.it.src
a.revz = a.it.revz
Expand All @@ -283,7 +379,7 @@ func (a *MIterator) Next() (got bool) {

func (a *MIterator) Merge(b SortedIterator) int {
bb := b.(*MIterator)
cmp := bytes.Compare(a.val, bb.val)
cmp := FIRSTcompare(a.keye, bb.keye)
if cmp < 0 {
return MergeAB
} else if cmp > 0 {
Expand Down Expand Up @@ -486,3 +582,26 @@ func Mrdx2tlv(a *RDX) (tlv []byte) {
func ELMdefault() (tlv []byte) {
return []byte{}
}

func ELMstring(tlv []byte) string {
ret := []byte{}
it := FIRSTIterator{TLV: tlv}
for it.Next() {
switch it.lit {
case 'F':
ret = append(ret, Fstring(it.bare)...)
case 'I':
ret = append(ret, Istring(it.bare)...)
case 'R':
ret = append(ret, Rstring(it.bare)...)
case 'S':
ret = append(ret, Sstring(it.bare)...)
case 'T':
ret = append(ret, Tstring(it.bare)...)
default:
ret = append(ret, '?')
}
ret = append(ret, ',')
}
return string(ret)
}
4 changes: 2 additions & 2 deletions rdx/ELM_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ func TestEmerge(t *testing.T) {
}

func TestMmerge(t *testing.T) {
tlv1 := Mparse("{1: 2, 3: 4, 5:6}")
tlv1 := Mparse("{1: 2, 5:6, 3: 4}")
assert.Equal(t, "{1:2,3:4,5:6}", Mstring(tlv1))
tlv2 := Mparse("{3:4, 5:6, 7:8}")
tlv2 := Mparse("{ 7:8, 3:4, 5:6}")
tlv12 := Mmerge(toyqueue.Records{tlv1, tlv2})
str12 := Mstring(tlv12)
assert.Equal(t, "{1:2,3:4,5:6,7:8}", str12)
Expand Down
19 changes: 15 additions & 4 deletions rdx/FIRST.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@ func FIRSTparsez(bulk []byte) (zrev uint64, src uint64, value []byte) {
return
}

// Parses an enveloped ISFR record
// Parses an enveloped FIRST record
func ParseEnvelopedFIRST(data []byte) (lit byte, t Time, value, rest []byte, err error) {
var hlen, blen int
lit, hlen, blen = toytlv.ProbeHeader(data)
if lit == 0 || hlen+blen > len(data) {
err = toytlv.ErrIncomplete
return
}
rec := data[:hlen+blen]
rec := data[hlen : hlen+blen]
rest = data[hlen+blen:]
tlit, thlen, tblen := toytlv.ProbeHeader(data)
tlit, thlen, tblen := toytlv.ProbeHeader(rec)
tlen := thlen + tblen
if (tlit != 'T' && tlit != '0') || (tlen > len(rec)) {
err = ErrBadISFR
err = ErrBadFIRST
return
}
tsb := rec[thlen:tlen]
Expand Down Expand Up @@ -210,6 +210,17 @@ func FIRSTrdxs2tlvs(a []RDX) (tlv toyqueue.Records) {
return
}

func FIRSTcompare(a, b []byte) int {
alit, ahlen, ablen := toytlv.ProbeHeader(a)
blit, bhlen, bblen := toytlv.ProbeHeader(b)
if alit != blit {
return int(alit) - int(blit)
}
_, _, av := FIRSTparsez(a[ahlen : ahlen+ablen])
_, _, bv := FIRSTparsez(b[bhlen : bhlen+bblen])
return bytes.Compare(av, bv)
}

// I is a last-write-wins int64

func Idefault() []byte { return FIRSTdefault('I') }
Expand Down
Loading

0 comments on commit 010016b

Please sign in to comment.