github.com/tidwall/pair@v0.0.0-20170418221434-1140497fc57a/pair.go (about)

     1  package pair
     2  
     3  import (
     4  	"encoding/binary"
     5  	"reflect"
     6  	"unsafe"
     7  )
     8  
     9  const maxInt = int(^uint(0) >> 1)
    10  
    11  // Pair is a tightly packed key/value pair
    12  type Pair struct {
    13  	ptr unsafe.Pointer
    14  }
    15  
    16  // New returns a Pair
    17  func New(key, value []byte) Pair {
    18  	slice := makenz(8 + len(value) + len(key))
    19  	binary.LittleEndian.PutUint32(slice, uint32(len(value)))
    20  	binary.LittleEndian.PutUint32(slice[4:], uint32(len(key)))
    21  	copy(slice[8:], value)
    22  	copy(slice[8+len(value):], key)
    23  	return Pair{unsafe.Pointer(&slice[0])}
    24  }
    25  func (pair Pair) getSlice() []byte {
    26  	if uintptr(pair.ptr) == 0 {
    27  		return nil
    28  	}
    29  	return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
    30  		Data: uintptr(pair.ptr),
    31  		Len:  maxInt,
    32  		Cap:  maxInt,
    33  	}))
    34  }
    35  
    36  // Value returns the value
    37  func (pair Pair) Value() []byte {
    38  	slice := pair.getSlice()
    39  	if slice == nil {
    40  		return nil
    41  	}
    42  	valuesz := binary.LittleEndian.Uint32(slice)
    43  	return slice[8 : 8+valuesz : 8+valuesz]
    44  }
    45  
    46  // Key returns the key portion of the key
    47  func (pair Pair) Key() []byte {
    48  	slice := pair.getSlice()
    49  	if slice == nil {
    50  		return nil
    51  	}
    52  	valuesz := binary.LittleEndian.Uint32(slice)
    53  	keysz := binary.LittleEndian.Uint32(slice[4:])
    54  	return slice[8+valuesz : 8+valuesz+keysz : 8+valuesz+keysz]
    55  }
    56  
    57  // Size returns the size of the allocation
    58  func (pair Pair) Size() int {
    59  	slice := pair.getSlice()
    60  	if slice == nil {
    61  		return 0
    62  	}
    63  	valuesz := binary.LittleEndian.Uint32(slice)
    64  	keysz := binary.LittleEndian.Uint32(slice[4:])
    65  	return int(8 + valuesz + keysz)
    66  }
    67  
    68  // Zero return true if the pair is unallocated
    69  func (pair Pair) Zero() bool {
    70  	return uintptr(pair.ptr) == 0
    71  }
    72  
    73  //go:linkname mallocgc runtime.mallocgc
    74  func mallocgc(size, typ uintptr, needzero bool) uintptr
    75  
    76  // makenz returns a byte slice that is not zero filled. This can provide a big
    77  // performance boost for large pairs.
    78  func makenz(count int) []byte {
    79  	return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
    80  		Data: mallocgc(uintptr(count), 0, false),
    81  		Len:  count,
    82  		Cap:  count,
    83  	}))
    84  }
    85  
    86  // Pointer returns the underlying pointer
    87  func (pair Pair) Pointer() unsafe.Pointer {
    88  	return pair.ptr
    89  }
    90  
    91  // FromPointer returns a pair that uses the memory at the pointer.
    92  func FromPointer(ptr unsafe.Pointer) Pair {
    93  	return Pair{ptr}
    94  }