github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/internal/sysenc/buffer.go (about)

     1  package sysenc
     2  
     3  import (
     4  	"unsafe"
     5  
     6  	"github.com/cilium/ebpf/internal/sys"
     7  )
     8  
     9  type Buffer struct {
    10  	ptr unsafe.Pointer
    11  	// Size of the buffer. syscallPointerOnly if created from UnsafeBuffer or when using
    12  	// zero-copy unmarshaling.
    13  	size int
    14  }
    15  
    16  const syscallPointerOnly = -1
    17  
    18  func newBuffer(buf []byte) Buffer {
    19  	if len(buf) == 0 {
    20  		return Buffer{}
    21  	}
    22  	return Buffer{unsafe.Pointer(&buf[0]), len(buf)}
    23  }
    24  
    25  // UnsafeBuffer constructs a Buffer for zero-copy unmarshaling.
    26  //
    27  // [Pointer] is the only valid method to call on such a Buffer.
    28  // Use [SyscallBuffer] instead if possible.
    29  func UnsafeBuffer(ptr unsafe.Pointer) Buffer {
    30  	return Buffer{ptr, syscallPointerOnly}
    31  }
    32  
    33  // SyscallOutput prepares a Buffer for a syscall to write into.
    34  //
    35  // size is the length of the desired buffer in bytes.
    36  // The buffer may point at the underlying memory of dst, in which case [Unmarshal]
    37  // becomes a no-op.
    38  //
    39  // The contents of the buffer are undefined and may be non-zero.
    40  func SyscallOutput(dst any, size int) Buffer {
    41  	if dstBuf := unsafeBackingMemory(dst); len(dstBuf) == size {
    42  		buf := newBuffer(dstBuf)
    43  		buf.size = syscallPointerOnly
    44  		return buf
    45  	}
    46  
    47  	return newBuffer(make([]byte, size))
    48  }
    49  
    50  // CopyTo copies the buffer into dst.
    51  //
    52  // Returns the number of copied bytes.
    53  func (b Buffer) CopyTo(dst []byte) int {
    54  	return copy(dst, b.unsafeBytes())
    55  }
    56  
    57  // AppendTo appends the buffer onto dst.
    58  func (b Buffer) AppendTo(dst []byte) []byte {
    59  	return append(dst, b.unsafeBytes()...)
    60  }
    61  
    62  // Pointer returns the location where a syscall should write.
    63  func (b Buffer) Pointer() sys.Pointer {
    64  	// NB: This deliberately ignores b.length to support zero-copy
    65  	// marshaling / unmarshaling using unsafe.Pointer.
    66  	return sys.NewPointer(b.ptr)
    67  }
    68  
    69  // Unmarshal the buffer into the provided value.
    70  func (b Buffer) Unmarshal(data any) error {
    71  	if b.size == syscallPointerOnly {
    72  		return nil
    73  	}
    74  
    75  	return Unmarshal(data, b.unsafeBytes())
    76  }
    77  
    78  func (b Buffer) unsafeBytes() []byte {
    79  	if b.size == syscallPointerOnly {
    80  		return nil
    81  	}
    82  	return unsafe.Slice((*byte)(b.ptr), b.size)
    83  }