Skip to content

Commit

Permalink
experimental: Add TextField
Browse files Browse the repository at this point in the history
The TextField is a reference to a specific text field inside a struct.
It records both the pointer and value locations inside a struct, which
may be used to fetch or update the underlying value.
  • Loading branch information
matheusd committed May 6, 2024
1 parent bde3071 commit e7d8d6f
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 0 deletions.
5 changes: 5 additions & 0 deletions internal/aircraftlib/experimental.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ func (s BenchmarkA) FlatSetName(v string) error {
func (s BenchmarkA) UpdateName(v string) error {
return capnp.Struct(s).UpdateText(0, v)
}

// Experimental: return the name as a field that can be mutated.
func (s BenchmarkA) NameField() (capnp.TextField, error) {
return capnp.Struct(s).TextField(0)
}
62 changes: 62 additions & 0 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,68 @@ func (p Struct) UpdateText(i uint16, v string) error {
return nil
}

type TextField struct {
// Pointer location
pSeg *Segment
pAddr address

// Current value location
vSeg *Segment
vAddr address
vLen int
}

// EXPERIMENTAL: return ith pointer as a text field.
func (p Struct) TextField(i uint16) (TextField, error) {
ptrStart, _ := p.off.addSize(p.size.DataSize)
offInsideP, _ := ptrStart.element(int32(i), wordSize)

// ptr, err := p.seg.readPtr(offInsideP, p.depthLimit)
s, base, val, err := p.seg.resolveFarPointer(offInsideP)
if err != nil {
return TextField{}, exc.WrapError("read pointer", err)
}

tf := TextField{pSeg: p.seg, pAddr: offInsideP}

if val == 0 {
return tf, nil
}

addr, ok := val.offset().resolve(base)
if !ok {
return TextField{}, errors.New("list pointer: invalid address")
}

tf.vSeg = s
tf.vLen = int(val.numListElements())
tf.vAddr = addr

return tf, nil
}

// UpdateText updates the value of the text field.
func (tf *TextField) Set(v string) error {
if tf.vLen < len(v)+1 || tf.vSeg == nil {
// TODO: handle this case. Needs to alloc and set pointer.
// Needs to set tf.vSeg, tf.vLen and tf.vAddr.
panic("we can work it out")
}

// Existing buffer location has space for new text. Copy text over it.
dst := tf.vSeg.slice(tf.vAddr, Size(tf.vLen))
n := copy(dst, []byte(v))

// Pad with zeros (clear leftover). Last byte is already zero.
//
// TODO: replace with clear(dst[n:length-1]) after go1.21.
for i := n; i < int(tf.vLen-1); i++ {
dst[i] = 0
}

return nil
}

// SetNewText sets the i'th pointer to a newly allocated text.
func (p Struct) SetNewText(i uint16, v string) error {
t, err := NewText(p.seg, v)
Expand Down
38 changes: 38 additions & 0 deletions struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,41 @@ func BenchmarkSetTextUpdate(b *testing.B) {
}
}
}

func BenchmarkSetTextAsField(b *testing.B) {
var msg capnp.Message
var arena = &capnp.SimpleSingleSegmentArena{}
arena.ReplaceBuffer(make([]byte, 0, 1024))

// NOTE: Needs to be ResetForRead() because Reset() allocates
// the root pointer. This is part of API madness.
msg.ResetForRead(arena)

a, err := aircraftlib.AllocateNewRootBenchmark(&msg)
if err != nil {
b.Fatal(err)
}

err = a.SetName("my name")
if err != nil {
b.Fatal(err)
}

// WHY?!?!?!?
msg.ResetReadLimit(1 << 31)

nameField, err := a.NameField()
if err != nil {
b.Fatal(err)
}

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
err := nameField.Set("my name")
if err != nil {
b.Fatal(err)
}
}
}

0 comments on commit e7d8d6f

Please sign in to comment.