github.com/philpearl/plenc@v0.0.15/plenccodec/wrapper.go (about)

     1  package plenccodec
     2  
     3  import (
     4  	"fmt"
     5  	"unsafe"
     6  
     7  	"github.com/philpearl/plenc/plenccore"
     8  )
     9  
    10  // PointerWrapper wraps a codec so it can be used for a pointer to the type
    11  type PointerWrapper struct {
    12  	Underlying Codec
    13  }
    14  
    15  func (p PointerWrapper) Omit(ptr unsafe.Pointer) bool {
    16  	t := *(*unsafe.Pointer)(ptr)
    17  	return t == nil
    18  }
    19  
    20  func (p PointerWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) {
    21  	t := (*unsafe.Pointer)(ptr)
    22  	if *t == nil {
    23  		*t = p.Underlying.New()
    24  	}
    25  
    26  	return p.Underlying.Read(data, *t, wt)
    27  }
    28  
    29  func (p PointerWrapper) New() unsafe.Pointer {
    30  	v := p.Underlying.New()
    31  	return unsafe.Pointer(&v)
    32  }
    33  
    34  func (p PointerWrapper) WireType() plenccore.WireType {
    35  	return p.Underlying.WireType()
    36  }
    37  
    38  func (p PointerWrapper) Descriptor() Descriptor {
    39  	d := p.Underlying.Descriptor()
    40  	d.ExplicitPresence = true
    41  	return d
    42  }
    43  
    44  func (p PointerWrapper) Size(ptr unsafe.Pointer, tag []byte) int {
    45  	// This should never be called if Omit returns true
    46  	t := *(*unsafe.Pointer)(ptr)
    47  	if t == nil {
    48  		return 0
    49  	}
    50  	return p.Underlying.Size(t, tag)
    51  }
    52  
    53  func (p PointerWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte {
    54  	// This should never be called if Omit returns true
    55  	t := *(*unsafe.Pointer)(ptr)
    56  	if t == nil {
    57  		return data
    58  	}
    59  	return p.Underlying.Append(data, t, tag)
    60  }
    61  
    62  type BaseSliceWrapper struct {
    63  	Underlying Codec
    64  	EltSize    uintptr
    65  	EltType    unsafe.Pointer
    66  }
    67  
    68  func (c BaseSliceWrapper) Omit(ptr unsafe.Pointer) bool {
    69  	h := *(*sliceHeader)(ptr)
    70  	return h.Len == 0
    71  }
    72  
    73  func (c BaseSliceWrapper) New() unsafe.Pointer {
    74  	return unsafe.Pointer(&sliceHeader{})
    75  }
    76  
    77  func (c BaseSliceWrapper) WireType() plenccore.WireType {
    78  	return plenccore.WTLength
    79  }
    80  
    81  func (c BaseSliceWrapper) Descriptor() Descriptor {
    82  	return Descriptor{
    83  		Type: FieldTypeSlice,
    84  		Elements: []Descriptor{
    85  			c.Underlying.Descriptor(),
    86  		},
    87  	}
    88  }
    89  
    90  // WTLengthSliceWrapper is a codec for a slice of a type that's encoded using
    91  // the WTLength wire type. It uses the WTSlice wire type for the slice itself.
    92  type WTLengthSliceWrapper struct {
    93  	BaseSliceWrapper
    94  }
    95  
    96  func (c WTLengthSliceWrapper) size(ptr unsafe.Pointer) int {
    97  	h := *(*sliceHeader)(ptr)
    98  	size := plenccore.SizeVarUint(uint64(h.Len))
    99  	for i := 0; i < h.Len; i++ {
   100  		s := c.Underlying.Size(unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil)
   101  		size += s + plenccore.SizeVarUint(uint64(s))
   102  	}
   103  	return size
   104  }
   105  
   106  // append encodes the slice, and appends the encoded version to data
   107  func (c WTLengthSliceWrapper) append(data []byte, ptr unsafe.Pointer) []byte {
   108  	h := *(*sliceHeader)(ptr)
   109  
   110  	// Append the count of items in the slice
   111  	data = plenccore.AppendVarUint(data, uint64(h.Len))
   112  	// Append each of the items. They're all prefixed by their length
   113  	for i := 0; i < h.Len; i++ {
   114  		ptr := unsafe.Pointer(uintptr(h.Data) + uintptr(i)*c.EltSize)
   115  		data = plenccore.AppendVarUint(data, uint64(c.Underlying.Size(ptr, nil)))
   116  		data = c.Underlying.Append(data, ptr, nil)
   117  	}
   118  	return data
   119  }
   120  
   121  // Read decodes a slice. It assumes the WTLength tag has already been decoded
   122  // and that the data slice is the corect size for the slice
   123  func (c WTLengthSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) {
   124  	if wt == plenccore.WTLength {
   125  		return c.readAsWTLength(data, ptr)
   126  	}
   127  
   128  	// First we read the number of items in the slice
   129  	count, n := plenccore.ReadVarUint(data)
   130  	if n < 0 {
   131  		return 0, fmt.Errorf("corrupt data looking for WTSlice count")
   132  	}
   133  
   134  	// Now make sure we have enough capacity in the slice
   135  	h := (*sliceHeader)(ptr)
   136  	if h.Cap < int(count) {
   137  		// Ensure the GC knows the type of this slice.
   138  		h.Data = unsafe_NewArray(c.EltType, int(count))
   139  		h.Cap = int(count)
   140  	} else {
   141  		// We're going to re-use the backing array. It's going to be surprising
   142  		// if we don't start from zeros so we zero everything.
   143  		for i := 0; i < int(count); i++ {
   144  			// We'll only write to fields if the data is present, so start by zeroing
   145  			// the target
   146  			ptr := unsafe.Add(h.Data, i*int(c.EltSize))
   147  			typedmemclr(unpackEFace(c.EltType).data, ptr)
   148  		}
   149  	}
   150  	h.Len = int(count)
   151  
   152  	offset := n
   153  	for i := 0; i < h.Len; i++ {
   154  		s, n := plenccore.ReadVarUint(data[offset:])
   155  		if n <= 0 {
   156  			return 0, fmt.Errorf("invalid varint for slice entry %d", i)
   157  		}
   158  		offset += n
   159  
   160  		ptr := unsafe.Add(h.Data, i*int(c.EltSize))
   161  		n, err := c.Underlying.Read(data[offset:offset+int(s)], ptr, plenccore.WTLength)
   162  		if err != nil {
   163  			return 0, err
   164  		}
   165  		offset += n
   166  	}
   167  
   168  	return offset, nil
   169  }
   170  
   171  // readAsWTLength is here for protobuf compatibility. protobuf writes certain
   172  // array types by simply repeating the encoding for an individual field. So here
   173  // we just read one underlying value and append it to the slice
   174  func (c WTLengthSliceWrapper) readAsWTLength(data []byte, ptr unsafe.Pointer) (n int, err error) {
   175  	h := (*sliceHeader)(ptr)
   176  	if h.Cap == h.Len {
   177  		// Need to make room
   178  		cap := h.Cap * 2
   179  		if cap == 0 {
   180  			cap = 8
   181  		}
   182  		nh := sliceHeader{
   183  			Data: unsafe_NewArray(c.EltType, int(cap)),
   184  			Len:  h.Len,
   185  			Cap:  cap,
   186  		}
   187  		if h.Len != 0 {
   188  			// copy over the old data
   189  			typedslicecopy(c.EltType, nh, *h)
   190  		}
   191  		nh.Len = h.Len
   192  		nh.Cap = cap
   193  
   194  		*h = nh
   195  	}
   196  
   197  	dptr := unsafe.Add(h.Data, h.Len*int(c.EltSize))
   198  	typedmemclr(unpackEFace(c.EltType).data, dptr)
   199  	n, err = c.Underlying.Read(data, dptr, plenccore.WTLength)
   200  	if err != nil {
   201  		return 0, err
   202  	}
   203  	h.Len++
   204  	return n, nil
   205  }
   206  
   207  func (c WTLengthSliceWrapper) WireType() plenccore.WireType {
   208  	return plenccore.WTSlice
   209  }
   210  
   211  func (c WTLengthSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int {
   212  	return c.size(ptr) + len(tag)
   213  }
   214  
   215  func (c WTLengthSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte {
   216  	data = append(data, tag...)
   217  	return c.append(data, ptr)
   218  }
   219  
   220  // WTFixedSliceWrapper is a codec for a type that's encoded as a fixed 32 or 64
   221  // byte value (i.e. float32 or float64)
   222  type WTFixedSliceWrapper struct {
   223  	BaseSliceWrapper
   224  }
   225  
   226  // append encodes the slice without the tag
   227  func (c WTFixedSliceWrapper) append(data []byte, ptr unsafe.Pointer) []byte {
   228  	h := *(*sliceHeader)(ptr)
   229  	for i := 0; i < h.Len; i++ {
   230  		data = c.Underlying.Append(data, unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil)
   231  	}
   232  	return data
   233  }
   234  
   235  // Read decodes a slice. It assumes the WTLength tag has already been decoded
   236  // and that the data slice is the corect size for the slice
   237  func (c WTFixedSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) {
   238  	count := len(data) / c.Underlying.Size(nil, nil)
   239  
   240  	// Now make sure we have enough data in the slice
   241  	h := (*sliceHeader)(ptr)
   242  	if h.Cap < count {
   243  		// Ensure the GC knows the type of this slice.
   244  		h.Data = unsafe_NewArray(c.EltType, int(count))
   245  		h.Cap = int(count)
   246  	}
   247  	h.Len = count
   248  
   249  	var offset int
   250  	for i := 0; i < h.Len; i++ {
   251  		n, err := c.Underlying.Read(data[offset:], unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), c.Underlying.WireType())
   252  		if err != nil {
   253  			return 0, err
   254  		}
   255  		offset += n
   256  	}
   257  
   258  	return offset, nil
   259  }
   260  
   261  func (p WTFixedSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int {
   262  	h := *(*sliceHeader)(ptr)
   263  	l := p.Underlying.Size(nil, nil) * h.Len
   264  	if len(tag) > 0 {
   265  		l += len(tag) + plenccore.SizeVarUint(uint64(l))
   266  	}
   267  	return l
   268  }
   269  
   270  func (p WTFixedSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte {
   271  	if len(tag) > 0 {
   272  		data = append(data, tag...)
   273  		data = plenccore.AppendVarUint(data, uint64(p.Size(ptr, nil)))
   274  	}
   275  	return p.append(data, ptr)
   276  }
   277  
   278  // WTVarIntSliceWrapper is a codec for a type encoded using the WTVarInt wire
   279  // type.
   280  type WTVarIntSliceWrapper struct {
   281  	BaseSliceWrapper
   282  }
   283  
   284  func (c WTVarIntSliceWrapper) size(ptr unsafe.Pointer) int {
   285  	h := *(*sliceHeader)(ptr)
   286  	size := 0
   287  	for i := 0; i < h.Len; i++ {
   288  		size += c.Underlying.Size(unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil)
   289  	}
   290  	return size
   291  }
   292  
   293  // append encodes the slice without the tag
   294  func (c WTVarIntSliceWrapper) append(data []byte, ptr unsafe.Pointer) []byte {
   295  	h := *(*sliceHeader)(ptr)
   296  	for i := 0; i < h.Len; i++ {
   297  		data = c.Underlying.Append(data, unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil)
   298  	}
   299  	return data
   300  }
   301  
   302  // Read decodes a slice. It assumes the WTLength tag has already been decoded
   303  // and that the data slice is the correct size for the slice
   304  func (c WTVarIntSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) {
   305  	// We step forward through out data to count how many things are in the slice
   306  	var offset, count int
   307  	for offset < len(data) {
   308  		_, n := plenccore.ReadVarUint(data[offset:])
   309  		if n < 0 {
   310  			return 0, fmt.Errorf("corrupt data")
   311  		}
   312  		offset += n
   313  		count++
   314  	}
   315  
   316  	// Now make sure we have enough data in the slice
   317  	h := (*sliceHeader)(ptr)
   318  	if h.Cap < count {
   319  		// Ensure the GC knows the type of this slice.
   320  		h.Data = unsafe_NewArray(c.EltType, int(count))
   321  		h.Cap = int(count)
   322  	}
   323  	h.Len = count
   324  
   325  	offset = 0
   326  	for i := 0; i < h.Len; i++ {
   327  		n, err := c.Underlying.Read(data[offset:], unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), plenccore.WTVarInt)
   328  		if err != nil {
   329  			return 0, err
   330  		}
   331  		offset += n
   332  	}
   333  
   334  	return offset, nil
   335  }
   336  
   337  func (p WTVarIntSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int {
   338  	l := p.size(ptr)
   339  	if len(tag) > 0 {
   340  		l += len(tag) + plenccore.SizeVarUint(uint64(l))
   341  	}
   342  	return l
   343  }
   344  
   345  func (p WTVarIntSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte {
   346  	if len(tag) > 0 {
   347  		data = append(data, tag...)
   348  		data = plenccore.AppendVarUint(data, uint64(p.size(ptr)))
   349  	}
   350  	return p.append(data, ptr)
   351  }
   352  
   353  // ProtoSliceWrapper is for encoding slices of WTLength encoded objects how
   354  // protobuf does it. When writing the elements of the slice are simply repeated.
   355  // When reading each element is treated separately and appended to the slice.
   356  //
   357  // Note this does not work outside of a struct as we don't add tags and
   358  // therefore the length of each element is not known
   359  type ProtoSliceWrapper struct {
   360  	BaseSliceWrapper
   361  }
   362  
   363  // Size calculates the amount of data needed for this slice, including
   364  // repeated tags and lengths
   365  func (c ProtoSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int {
   366  	h := *(*sliceHeader)(ptr)
   367  	var l int
   368  	for i := 0; i < h.Len; i++ {
   369  		l += c.Underlying.Size(unsafe.Add(h.Data, uintptr(i)*c.EltSize), tag)
   370  	}
   371  	return l
   372  }
   373  
   374  // Append appends the data for this slice, including repeated tags and
   375  // lengths for each element
   376  func (c ProtoSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte {
   377  	h := *(*sliceHeader)(ptr)
   378  	for i := 0; i < h.Len; i++ {
   379  		data = c.Underlying.Append(data, unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), tag)
   380  	}
   381  	return data
   382  }
   383  
   384  func (c ProtoSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) {
   385  	h := (*sliceHeader)(ptr)
   386  	if h.Cap == h.Len {
   387  		// Need to make room
   388  		cap := h.Cap * 2
   389  		if cap == 0 {
   390  			cap = 8
   391  		}
   392  		nh := sliceHeader{
   393  			Data: unsafe_NewArray(c.EltType, int(cap)),
   394  			Len:  h.Len,
   395  			Cap:  cap,
   396  		}
   397  		if h.Len != 0 {
   398  			// copy over the old data
   399  			typedslicecopy(c.EltType, nh, *h)
   400  		}
   401  		nh.Len = h.Len
   402  		nh.Cap = cap
   403  
   404  		*h = nh
   405  	}
   406  
   407  	dptr := unsafe.Add(h.Data, h.Len*int(c.EltSize))
   408  	typedmemclr(unpackEFace(c.EltType).data, dptr)
   409  	n, err = c.Underlying.Read(data, dptr, plenccore.WTLength)
   410  	if err != nil {
   411  		return 0, err
   412  	}
   413  	h.Len++
   414  	return n, nil
   415  }