Skip to content

Commit

Permalink
Array rework (#119)
Browse files Browse the repository at this point in the history
This PR reworks our handling of arrays, to avoid `reflect.ArrayOf` and
the risk of unbounded memory usage due to caching of reflect types. Each
time an array (or slice/string with a backing array) is encountered, the
serialization layer will create a `reflect.Type` to represent this
array, and each `(elemType,length)` pair requires a new type.

This fixes #61.
  • Loading branch information
chriso authored Nov 24, 2023
2 parents 0fe43fc + f21a835 commit 0f55583
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 124 deletions.
51 changes: 33 additions & 18 deletions gen/proto/go/coroutine/v1/coroutine.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 28 additions & 1 deletion gen/proto/go/coroutine/v1/coroutine_vtproto.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions proto/coroutine/v1/coroutine.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ message Build {

// Region is an encoded region of memory.
message Region {
// Type is the type of the region.
// Type is the type of the region, shifted left by one.
//
// The least significant bit indicates that this region represents
// an array, and that the type is the array element type rather
// than the object that's encoded in this region.
uint32 type = 1;

// Array length, for regions that are arrays.
uint32 array_length = 2;

// Data is the encoded contents of the memory region.
bytes data = 2;
bytes data = 3;
}
49 changes: 39 additions & 10 deletions types/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ type Type struct {
index int
}

// Index is the index of the type in the serialized state.
// Index is the index of the type in the serialized state, or -1
// if the type is derived from a serialized type.
func (t *Type) Index() int {
return t.index
}
Expand Down Expand Up @@ -597,7 +598,27 @@ func (t *Region) Index() int {

// Type is the type of the region.
func (r *Region) Type() *Type {
return r.state.Type(int(r.region.Type - 1))
t := r.state.Type(int((r.region.Type >> 1) - 1))
if r.region.Type&1 == 1 {
t = newArrayType(r.state, int64(r.region.ArrayLength), t)
}
return t
}

func newArrayType(state *State, length int64, t *Type) *Type {
idx := t.Index()
if idx < 0 {
panic("BUG")
}
return &Type{
state: state,
typ: &coroutinev1.Type{
Kind: coroutinev1.Kind_KIND_ARRAY,
Length: int64(length),
Elem: uint32(idx + 1),
},
index: -1, // aka. a derived type
}
}

// Size is the size of the region in bytes.
Expand Down Expand Up @@ -727,7 +748,10 @@ func (s *Scanner) Next() bool {

case scancustom:
if uint64(s.pos) < last.customtil {
return s.readTypedAny(len(s.stack))
if !s.readType() {
return false
}
return s.readAny(s.typ, len(s.stack))
}
if uint64(s.pos) > last.customtil {
s.err = fmt.Errorf("invalid custom object size")
Expand Down Expand Up @@ -951,13 +975,22 @@ func (s *Scanner) readAny(t *Type, depth int) (ok bool) {
}
}

func (s *Scanner) readTypedAny(depth int) (ok bool) {
func (s *Scanner) readType() (ok bool) {
id, ok := s.getVarint()
if !ok {
return false
}
t := s.state.Type(int(id - 1))
return s.readAny(t, depth)

len, ok := s.getVarint()
if !ok {
return false
}
if len >= 0 {
t = newArrayType(s.state, len, t)
}
s.typ = t
return true
}

func (s *Scanner) readUint8() (ok bool) {
Expand Down Expand Up @@ -1110,13 +1143,9 @@ func (s *Scanner) readInterface() (ok bool) {
s.nil = true
return true
}

typeid, ok := s.getVarint()
if !ok {
if !s.readType() {
return false
}
s.typ = s.state.Type(int(typeid - 1))

return s.readRegionPointer()
}

Expand Down
Loading

0 comments on commit 0f55583

Please sign in to comment.