gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

marshalers.go (6490B)


      1 package ebpf
      2 
      3 import (
      4 	"bytes"
      5 	"encoding"
      6 	"encoding/binary"
      7 	"errors"
      8 	"fmt"
      9 	"reflect"
     10 	"runtime"
     11 	"sync"
     12 	"unsafe"
     13 
     14 	"github.com/cilium/ebpf/internal"
     15 	"github.com/cilium/ebpf/internal/sys"
     16 )
     17 
     18 // marshalPtr converts an arbitrary value into a pointer suitable
     19 // to be passed to the kernel.
     20 //
     21 // As an optimization, it returns the original value if it is an
     22 // unsafe.Pointer.
     23 func marshalPtr(data interface{}, length int) (sys.Pointer, error) {
     24 	if ptr, ok := data.(unsafe.Pointer); ok {
     25 		return sys.NewPointer(ptr), nil
     26 	}
     27 
     28 	buf, err := marshalBytes(data, length)
     29 	if err != nil {
     30 		return sys.Pointer{}, err
     31 	}
     32 
     33 	return sys.NewSlicePointer(buf), nil
     34 }
     35 
     36 // marshalBytes converts an arbitrary value into a byte buffer.
     37 //
     38 // Prefer using Map.marshalKey and Map.marshalValue if possible, since
     39 // those have special cases that allow more types to be encoded.
     40 //
     41 // Returns an error if the given value isn't representable in exactly
     42 // length bytes.
     43 func marshalBytes(data interface{}, length int) (buf []byte, err error) {
     44 	if data == nil {
     45 		return nil, errors.New("can't marshal a nil value")
     46 	}
     47 
     48 	switch value := data.(type) {
     49 	case encoding.BinaryMarshaler:
     50 		buf, err = value.MarshalBinary()
     51 	case string:
     52 		buf = []byte(value)
     53 	case []byte:
     54 		buf = value
     55 	case unsafe.Pointer:
     56 		err = errors.New("can't marshal from unsafe.Pointer")
     57 	case Map, *Map, Program, *Program:
     58 		err = fmt.Errorf("can't marshal %T", value)
     59 	default:
     60 		var wr bytes.Buffer
     61 		err = binary.Write(&wr, internal.NativeEndian, value)
     62 		if err != nil {
     63 			err = fmt.Errorf("encoding %T: %v", value, err)
     64 		}
     65 		buf = wr.Bytes()
     66 	}
     67 	if err != nil {
     68 		return nil, err
     69 	}
     70 
     71 	if len(buf) != length {
     72 		return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length)
     73 	}
     74 	return buf, nil
     75 }
     76 
     77 func makeBuffer(dst interface{}, length int) (sys.Pointer, []byte) {
     78 	if ptr, ok := dst.(unsafe.Pointer); ok {
     79 		return sys.NewPointer(ptr), nil
     80 	}
     81 
     82 	buf := make([]byte, length)
     83 	return sys.NewSlicePointer(buf), buf
     84 }
     85 
     86 var bytesReaderPool = sync.Pool{
     87 	New: func() interface{} {
     88 		return new(bytes.Reader)
     89 	},
     90 }
     91 
     92 // unmarshalBytes converts a byte buffer into an arbitrary value.
     93 //
     94 // Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since
     95 // those have special cases that allow more types to be encoded.
     96 //
     97 // The common int32 and int64 types are directly handled to avoid
     98 // unnecessary heap allocations as happening in the default case.
     99 func unmarshalBytes(data interface{}, buf []byte) error {
    100 	switch value := data.(type) {
    101 	case unsafe.Pointer:
    102 		dst := unsafe.Slice((*byte)(value), len(buf))
    103 		copy(dst, buf)
    104 		runtime.KeepAlive(value)
    105 		return nil
    106 	case Map, *Map, Program, *Program:
    107 		return fmt.Errorf("can't unmarshal into %T", value)
    108 	case encoding.BinaryUnmarshaler:
    109 		return value.UnmarshalBinary(buf)
    110 	case *string:
    111 		*value = string(buf)
    112 		return nil
    113 	case *[]byte:
    114 		*value = buf
    115 		return nil
    116 	case *int32:
    117 		if len(buf) < 4 {
    118 			return errors.New("int32 requires 4 bytes")
    119 		}
    120 		*value = int32(internal.NativeEndian.Uint32(buf))
    121 		return nil
    122 	case *uint32:
    123 		if len(buf) < 4 {
    124 			return errors.New("uint32 requires 4 bytes")
    125 		}
    126 		*value = internal.NativeEndian.Uint32(buf)
    127 		return nil
    128 	case *int64:
    129 		if len(buf) < 8 {
    130 			return errors.New("int64 requires 8 bytes")
    131 		}
    132 		*value = int64(internal.NativeEndian.Uint64(buf))
    133 		return nil
    134 	case *uint64:
    135 		if len(buf) < 8 {
    136 			return errors.New("uint64 requires 8 bytes")
    137 		}
    138 		*value = internal.NativeEndian.Uint64(buf)
    139 		return nil
    140 	case string:
    141 		return errors.New("require pointer to string")
    142 	case []byte:
    143 		return errors.New("require pointer to []byte")
    144 	default:
    145 		rd := bytesReaderPool.Get().(*bytes.Reader)
    146 		rd.Reset(buf)
    147 		defer bytesReaderPool.Put(rd)
    148 		if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
    149 			return fmt.Errorf("decoding %T: %v", value, err)
    150 		}
    151 		return nil
    152 	}
    153 }
    154 
    155 // marshalPerCPUValue encodes a slice containing one value per
    156 // possible CPU into a buffer of bytes.
    157 //
    158 // Values are initialized to zero if the slice has less elements than CPUs.
    159 //
    160 // slice must have a type like []elementType.
    161 func marshalPerCPUValue(slice interface{}, elemLength int) (sys.Pointer, error) {
    162 	sliceType := reflect.TypeOf(slice)
    163 	if sliceType.Kind() != reflect.Slice {
    164 		return sys.Pointer{}, errors.New("per-CPU value requires slice")
    165 	}
    166 
    167 	possibleCPUs, err := internal.PossibleCPUs()
    168 	if err != nil {
    169 		return sys.Pointer{}, err
    170 	}
    171 
    172 	sliceValue := reflect.ValueOf(slice)
    173 	sliceLen := sliceValue.Len()
    174 	if sliceLen > possibleCPUs {
    175 		return sys.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
    176 	}
    177 
    178 	alignedElemLength := internal.Align(elemLength, 8)
    179 	buf := make([]byte, alignedElemLength*possibleCPUs)
    180 
    181 	for i := 0; i < sliceLen; i++ {
    182 		elem := sliceValue.Index(i).Interface()
    183 		elemBytes, err := marshalBytes(elem, elemLength)
    184 		if err != nil {
    185 			return sys.Pointer{}, err
    186 		}
    187 
    188 		offset := i * alignedElemLength
    189 		copy(buf[offset:offset+elemLength], elemBytes)
    190 	}
    191 
    192 	return sys.NewSlicePointer(buf), nil
    193 }
    194 
    195 // unmarshalPerCPUValue decodes a buffer into a slice containing one value per
    196 // possible CPU.
    197 //
    198 // valueOut must have a type like *[]elementType
    199 func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
    200 	slicePtrType := reflect.TypeOf(slicePtr)
    201 	if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
    202 		return fmt.Errorf("per-cpu value requires pointer to slice")
    203 	}
    204 
    205 	possibleCPUs, err := internal.PossibleCPUs()
    206 	if err != nil {
    207 		return err
    208 	}
    209 
    210 	sliceType := slicePtrType.Elem()
    211 	slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)
    212 
    213 	sliceElemType := sliceType.Elem()
    214 	sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
    215 	if sliceElemIsPointer {
    216 		sliceElemType = sliceElemType.Elem()
    217 	}
    218 
    219 	step := len(buf) / possibleCPUs
    220 	if step < elemLength {
    221 		return fmt.Errorf("per-cpu element length is larger than available data")
    222 	}
    223 	for i := 0; i < possibleCPUs; i++ {
    224 		var elem interface{}
    225 		if sliceElemIsPointer {
    226 			newElem := reflect.New(sliceElemType)
    227 			slice.Index(i).Set(newElem)
    228 			elem = newElem.Interface()
    229 		} else {
    230 			elem = slice.Index(i).Addr().Interface()
    231 		}
    232 
    233 		// Make a copy, since unmarshal can hold on to itemBytes
    234 		elemBytes := make([]byte, elemLength)
    235 		copy(elemBytes, buf[:elemLength])
    236 
    237 		err := unmarshalBytes(elem, elemBytes)
    238 		if err != nil {
    239 			return fmt.Errorf("cpu %d: %w", i, err)
    240 		}
    241 
    242 		buf = buf[step:]
    243 	}
    244 
    245 	reflect.ValueOf(slicePtr).Elem().Set(slice)
    246 	return nil
    247 }