github.com/scottcagno/storage@v1.8.0/pkg/mmap/segment/segment.go (about)

     1  package segment
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math"
     7  	"reflect"
     8  	"unsafe"
     9  )
    10  
    11  var (
    12  	ErrBadValue    = fmt.Errorf("segment: bad value")
    13  	ErrOutOfBounds = fmt.Errorf("segment: out of bounds")
    14  	Fault          = fmt.Errorf("segmentation fault")
    15  )
    16  
    17  // MaxUintptr is the maximum platform dependent unsigned integer
    18  // large enough to store the uninterpreted bits of a pointer value.
    19  const MaxUintptr = ^uintptr(0)
    20  
    21  // Sizes of types in bytes.
    22  const (
    23  	Int8Size       = 1
    24  	Int16Size      = 2
    25  	Int32Size      = 4
    26  	Int64Size      = 8
    27  	Uint8Size      = 1
    28  	Uint16Size     = 2
    29  	Uint32Size     = 4
    30  	Uint64Size     = 8
    31  	Float32Size    = 4
    32  	Float64Size    = 8
    33  	Complex64Size  = 8
    34  	Complex128Size = 16
    35  )
    36  
    37  // Segment is a data segment.
    38  // See https://golang.org/ref/spec#Numeric_types for details.
    39  type Segment struct {
    40  	// offset specifies the offset of this segment.
    41  	offset int64
    42  	// data specifies the descriptor of the raw byte data associated with this segment.
    43  	// TODO: Choose the correct type for this field and it's initialization mechanism.
    44  	data reflect.SliceHeader
    45  }
    46  
    47  // New returns a new data segment.
    48  func New(offset int64, data []byte) *Segment {
    49  	return &Segment{
    50  		offset: offset,
    51  		data:   *(*reflect.SliceHeader)(unsafe.Pointer(&data)),
    52  	}
    53  }
    54  
    55  // Pointer returns an untyped pointer to the value from this segment or panics at the access violation.
    56  func (seg *Segment) Pointer(offset int64, length uintptr) uintptr {
    57  	if offset < seg.offset || length > math.MaxInt64 {
    58  		panic(Fault)
    59  	}
    60  	offset -= seg.offset
    61  	if offset > math.MaxInt64-int64(length) || offset+int64(length) > int64(seg.data.Len) {
    62  		panic(Fault)
    63  	}
    64  	if uint64(offset) > uint64(MaxUintptr-seg.data.Data) {
    65  		panic(Fault)
    66  	}
    67  	return seg.data.Data + uintptr(offset)
    68  }
    69  
    70  // Int8 returns a pointer to the signed 8-bit integer from this segment or panics at the access violation.
    71  func (seg *Segment) Int8(offset int64) *int8 {
    72  	return (*int8)(unsafe.Pointer(seg.Pointer(offset, Int8Size)))
    73  }
    74  
    75  // Int16 returns a pointer to the signed 16-bit integer from this segment or panics at the access violation.
    76  func (seg *Segment) Int16(offset int64) *int16 {
    77  	return (*int16)(unsafe.Pointer(seg.Pointer(offset, Int16Size)))
    78  }
    79  
    80  // Int32 returns a pointer to the signed 32-bit integer from this segment or panics at the access violation.
    81  func (seg *Segment) Int32(offset int64) *int32 {
    82  	return (*int32)(unsafe.Pointer(seg.Pointer(offset, Int32Size)))
    83  }
    84  
    85  // Int64 returns a pointer to the signed 64-bit integer from this segment or panics at the access violation.
    86  func (seg *Segment) Int64(offset int64) *int64 {
    87  	return (*int64)(unsafe.Pointer(seg.Pointer(offset, Int64Size)))
    88  }
    89  
    90  // Uint8 returns a pointer to the unsigned 8-bit integer from this segment or panics at the access violation.
    91  func (seg *Segment) Uint8(offset int64) *uint8 {
    92  	return (*uint8)(unsafe.Pointer(seg.Pointer(offset, Uint8Size)))
    93  }
    94  
    95  // Uint16 returns a pointer to the unsigned 16-bit integer from this segment or panics at the access violation.
    96  func (seg *Segment) Uint16(offset int64) *uint16 {
    97  	return (*uint16)(unsafe.Pointer(seg.Pointer(offset, Uint16Size)))
    98  }
    99  
   100  // Uint32 returns a pointer to the unsigned 32-bit integer from this segment or panics at the access violation.
   101  func (seg *Segment) Uint32(offset int64) *uint32 {
   102  	return (*uint32)(unsafe.Pointer(seg.Pointer(offset, Uint32Size)))
   103  }
   104  
   105  // Uint16 returns a pointer to the unsigned 64-bit integer from this segment or panics at the access violation.
   106  func (seg *Segment) Uint64(offset int64) *uint64 {
   107  	return (*uint64)(unsafe.Pointer(seg.Pointer(offset, Uint64Size)))
   108  }
   109  
   110  // ScanUint sequentially reads the data into the unsigned integers pointed by v starting from the given offset.
   111  func (seg *Segment) ScanUint(offset int64, v ...interface{}) error {
   112  	data := *(*[]byte)(unsafe.Pointer(&seg.data))
   113  	if offset < seg.offset {
   114  		return ErrOutOfBounds
   115  	}
   116  	offset -= seg.offset
   117  	for _, val := range v {
   118  		switch value := val.(type) {
   119  		default:
   120  			return ErrBadValue
   121  		case *uint8:
   122  			if offset < 0 || offset > math.MaxInt64-Uint8Size || offset+Uint8Size > int64(len(data)) {
   123  				return ErrOutOfBounds
   124  			}
   125  			*value = data[offset:][0]
   126  			offset += Uint8Size
   127  		case *uint16:
   128  			if offset < 0 || offset > math.MaxInt64-Uint16Size || offset+Uint16Size > int64(len(data)) {
   129  				return ErrOutOfBounds
   130  			}
   131  			*value = binary.LittleEndian.Uint16(data[offset : offset+Uint16Size])
   132  			offset += Uint16Size
   133  		case *uint32:
   134  			if offset < 0 || offset > math.MaxInt64-Uint32Size || offset+Uint32Size > int64(len(data)) {
   135  				return ErrOutOfBounds
   136  			}
   137  			*value = binary.LittleEndian.Uint32(data[offset : offset+Uint32Size])
   138  			offset += Uint32Size
   139  		case *uint64:
   140  			if offset < 0 || offset > math.MaxInt64-Uint64Size || offset+Uint64Size > int64(len(data)) {
   141  				return ErrOutOfBounds
   142  			}
   143  			*value = binary.LittleEndian.Uint64(data[offset : offset+Uint64Size])
   144  			offset += Uint64Size
   145  		}
   146  	}
   147  	return nil
   148  }
   149  
   150  // Float32 returns a pointer to the IEEE-754 32-bit floating-point number from this segment
   151  // or panics at the access violation.
   152  func (seg *Segment) Float32(offset int64) *float32 {
   153  	return (*float32)(unsafe.Pointer(seg.Pointer(offset, Float32Size)))
   154  }
   155  
   156  // Float64 returns a pointer to the IEEE-754 64-bit floating-point number from this segment
   157  // or panics at the access violation.
   158  func (seg *Segment) Float64(offset int64) *float64 {
   159  	return (*float64)(unsafe.Pointer(seg.Pointer(offset, Float64Size)))
   160  }
   161  
   162  // Complex64 returns a pointer to the complex number with float32 real and imaginary parts from this segment
   163  // or panics at the access violation.
   164  func (seg *Segment) Complex64(offset int64) *complex64 {
   165  	return (*complex64)(unsafe.Pointer(seg.Pointer(offset, Complex64Size)))
   166  }
   167  
   168  // Complex128 returns a pointer to the complex number with float64 real and imaginary parts from this segment
   169  // or panics at the access violation.
   170  func (seg *Segment) Complex128(offset int64) *complex128 {
   171  	return (*complex128)(unsafe.Pointer(seg.Pointer(offset, Complex128Size)))
   172  }