package proto import ( "io" "reflect" "unsafe" . "github.com/segmentio/encoding/internal/runtime_reflect" ) type repeatedField struct { codec *codec fieldNumber fieldNumber wireType wireType embedded bool } func sliceCodecOf(t reflect.Type, f structField, seen map[reflect.Type]*codec) *codec { s := new(codec) seen[t] = s r := &repeatedField{ codec: f.codec, fieldNumber: f.fieldNumber(), wireType: f.wireType(), embedded: f.embedded(), } s.wire = f.codec.wire s.size = sliceSizeFuncOf(t, r) s.encode = sliceEncodeFuncOf(t, r) s.decode = sliceDecodeFuncOf(t, r) return s } func sliceSizeFuncOf(t reflect.Type, r *repeatedField) sizeFunc { elemSize := alignedSize(t.Elem()) tagSize := sizeOfTag(r.fieldNumber, r.wireType) return func(p unsafe.Pointer, _ flags) int { n := 0 if v := (*Slice)(p); v != nil { for i := 0; i < v.Len(); i++ { elem := v.Index(i, elemSize) size := r.codec.size(elem, wantzero) n += tagSize + size if r.embedded { n += sizeOfVarint(uint64(size)) } } } return n } } func sliceEncodeFuncOf(t reflect.Type, r *repeatedField) encodeFunc { elemSize := alignedSize(t.Elem()) tagSize := sizeOfTag(r.fieldNumber, r.wireType) tagData := make([]byte, tagSize) encodeTag(tagData, r.fieldNumber, r.wireType) return func(b []byte, p unsafe.Pointer, _ flags) (int, error) { offset := 0 if s := (*Slice)(p); s != nil { for i := 0; i < s.Len(); i++ { elem := s.Index(i, elemSize) size := r.codec.size(elem, wantzero) n := copy(b[offset:], tagData) offset += n if n < len(tagData) { return offset, io.ErrShortBuffer } if r.embedded { n, err := encodeVarint(b[offset:], uint64(size)) offset += n if err != nil { return offset, err } } if (len(b) - offset) < size { return len(b), io.ErrShortBuffer } n, err := r.codec.encode(b[offset:offset+size], elem, wantzero) offset += n if err != nil { return offset, err } } } return offset, nil } } func sliceDecodeFuncOf(t reflect.Type, r *repeatedField) decodeFunc { elemType := t.Elem() elemSize := alignedSize(elemType) return func(b []byte, p unsafe.Pointer, _ flags) (int, error) { s := (*Slice)(p) i := s.Len() if i == s.Cap() { *s = growSlice(elemType, s) } n, err := r.codec.decode(b, s.Index(i, elemSize), noflags) if err == nil { s.SetLen(i + 1) } return n, err } } func alignedSize(t reflect.Type) uintptr { a := t.Align() s := t.Size() return align(uintptr(a), uintptr(s)) } func align(align, size uintptr) uintptr { if align != 0 && (size%align) != 0 { size = ((size / align) + 1) * align } return size } func growSlice(t reflect.Type, s *Slice) Slice { cap := 2 * s.Cap() if cap == 0 { cap = 10 } p := pointer(t) d := MakeSlice(p, s.Len(), cap) CopySlice(p, d, *s) return d }