github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/atomic/buffer.go (about)

     1  /*
     2  Copyright 2016 Stanislav Liberman
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package atomic
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"log"
    23  	"reflect"
    24  	"sync/atomic"
    25  	"unsafe"
    26  
    27  	"github.com/lirm/aeron-go/aeron/util"
    28  )
    29  
    30  // Buffer is the equivalent of AtomicBuffer used by Aeron Java and C++ implementations. It provides
    31  // atomic operations on a raw byte buffer wrapped by the structure.
    32  type Buffer struct {
    33  	bufferPtr unsafe.Pointer
    34  	length    int32
    35  }
    36  
    37  // MakeBuffer takes a variety of argument options and returns a new atomic.Buffer to the best of its ability
    38  //	Options for calling
    39  //		MakeAtomicBuffer(Pointer)
    40  //		MakeAtomicBuffer([]byte)
    41  //		MakeAtomicBuffer(Pointer, len)
    42  //		MakeAtomicBuffer([]byte, len)
    43  func MakeBuffer(args ...interface{}) *Buffer {
    44  	var bufPtr unsafe.Pointer
    45  	var bufLen int32
    46  
    47  	switch len(args) {
    48  	case 1:
    49  		// just wrap
    50  		switch reflect.TypeOf(args[0]) {
    51  		case reflect.TypeOf(unsafe.Pointer(nil)):
    52  			bufPtr = unsafe.Pointer(args[0].(unsafe.Pointer))
    53  
    54  		case reflect.TypeOf(([]uint8)(nil)):
    55  			arr := ([]byte)(args[0].([]uint8))
    56  			bufPtr = unsafe.Pointer(&arr[0])
    57  			bufLen = int32(len(arr))
    58  		}
    59  	case 2:
    60  		// wrap with length
    61  		if reflect.TypeOf(args[1]).ConvertibleTo(reflect.TypeOf(int32(0))) {
    62  			v := reflect.ValueOf(args[1])
    63  			t := reflect.TypeOf(int32(0))
    64  			bufLen = int32(v.Convert(t).Int())
    65  		}
    66  
    67  		// pointer
    68  		switch reflect.TypeOf(args[0]) {
    69  		case reflect.TypeOf(uintptr(0)):
    70  			bufPtr = unsafe.Pointer(args[0].(uintptr))
    71  
    72  		case reflect.TypeOf(unsafe.Pointer(nil)):
    73  			bufPtr = unsafe.Pointer(args[0].(unsafe.Pointer))
    74  
    75  		case reflect.TypeOf(([]uint8)(nil)):
    76  			arr := ([]byte)(args[0].([]uint8))
    77  			bufPtr = unsafe.Pointer(&arr[0])
    78  		}
    79  	case 3:
    80  		// wrap with offset and length
    81  	}
    82  
    83  	buf := new(Buffer)
    84  	return buf.Wrap(bufPtr, bufLen)
    85  }
    86  
    87  // Wrap raw memory with this buffer instance
    88  //go:norace
    89  func (buf *Buffer) Wrap(buffer unsafe.Pointer, length int32) *Buffer {
    90  	buf.bufferPtr = buffer
    91  	buf.length = length
    92  	return buf
    93  }
    94  
    95  // Ptr will return the raw memory pointer for the underlying buffer
    96  //go:norace
    97  func (buf *Buffer) Ptr() unsafe.Pointer {
    98  	return buf.bufferPtr
    99  }
   100  
   101  // Capacity of the buffer, which is used for bound checking
   102  //go:norace
   103  func (buf *Buffer) Capacity() int32 {
   104  	return buf.length
   105  }
   106  
   107  // Fill the buffer with the value of the argument byte. Generally used for initialization,
   108  // since it's somewhat expensive.
   109  //go:norace
   110  func (buf *Buffer) Fill(b uint8) {
   111  	if buf.length == 0 {
   112  		return
   113  	}
   114  	for ix := 0; ix < int(buf.length); ix++ {
   115  		uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(ix))
   116  		*(*uint8)(uptr) = b
   117  	}
   118  }
   119  
   120  //go:norace
   121  func (buf *Buffer) GetUInt8(offset int32) uint8 {
   122  	BoundsCheck(offset, 1, buf.length)
   123  
   124  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   125  
   126  	return *(*uint8)(uptr)
   127  }
   128  
   129  //go:norace
   130  func (buf *Buffer) GetUInt16(offset int32) uint16 {
   131  	BoundsCheck(offset, 2, buf.length)
   132  
   133  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   134  
   135  	return *(*uint16)(uptr)
   136  }
   137  
   138  //go:norace
   139  func (buf *Buffer) GetInt32(offset int32) int32 {
   140  	BoundsCheck(offset, 4, buf.length)
   141  
   142  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   143  
   144  	return *(*int32)(uptr)
   145  }
   146  
   147  //go:norace
   148  func (buf *Buffer) GetInt64(offset int32) int64 {
   149  	BoundsCheck(offset, 8, buf.length)
   150  
   151  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   152  
   153  	return *(*int64)(uptr)
   154  }
   155  
   156  //go:norace
   157  func (buf *Buffer) PutUInt8(offset int32, value uint8) {
   158  	BoundsCheck(offset, 1, buf.length)
   159  
   160  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   161  
   162  	*(*uint8)(uptr) = value
   163  }
   164  
   165  //go:norace
   166  func (buf *Buffer) PutInt8(offset int32, value int8) {
   167  	BoundsCheck(offset, 1, buf.length)
   168  
   169  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   170  
   171  	*(*int8)(uptr) = value
   172  }
   173  
   174  //go:norace
   175  func (buf *Buffer) PutUInt16(offset int32, value uint16) {
   176  	BoundsCheck(offset, 2, buf.length)
   177  
   178  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   179  
   180  	*(*uint16)(uptr) = value
   181  }
   182  
   183  //go:norace
   184  func (buf *Buffer) PutInt32(offset int32, value int32) {
   185  	BoundsCheck(offset, 4, buf.length)
   186  
   187  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   188  
   189  	*(*int32)(uptr) = value
   190  }
   191  
   192  //go:norace
   193  func (buf *Buffer) PutInt64(offset int32, value int64) {
   194  	BoundsCheck(offset, 8, buf.length)
   195  
   196  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   197  
   198  	*(*int64)(uptr) = value
   199  }
   200  
   201  //go:norace
   202  func (buf *Buffer) GetAndAddInt64(offset int32, delta int64) int64 {
   203  	BoundsCheck(offset, 8, buf.length)
   204  
   205  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   206  	newVal := atomic.AddUint64((*uint64)(uptr), uint64(delta))
   207  
   208  	return int64(newVal) - delta
   209  }
   210  
   211  //go:norace
   212  func (buf *Buffer) GetInt32Volatile(offset int32) int32 {
   213  	BoundsCheck(offset, 4, buf.length)
   214  
   215  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   216  	cur := atomic.LoadUint32((*uint32)(uptr))
   217  
   218  	return int32(cur)
   219  }
   220  
   221  //go:norace
   222  func (buf *Buffer) GetInt64Volatile(offset int32) int64 {
   223  	BoundsCheck(offset, 8, buf.length)
   224  
   225  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   226  	cur := atomic.LoadUint64((*uint64)(uptr))
   227  
   228  	return int64(cur)
   229  }
   230  
   231  //go:norace
   232  func (buf *Buffer) PutInt64Ordered(offset int32, value int64) {
   233  	BoundsCheck(offset, 8, buf.length)
   234  
   235  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   236  	atomic.StoreInt64((*int64)(uptr), value)
   237  }
   238  
   239  //go:norace
   240  func (buf *Buffer) PutInt32Ordered(offset int32, value int32) {
   241  	BoundsCheck(offset, 4, buf.length)
   242  
   243  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   244  	atomic.StoreInt32((*int32)(uptr), value)
   245  }
   246  
   247  //go:norace
   248  func (buf *Buffer) PutIntOrdered(offset int32, value int) {
   249  	BoundsCheck(offset, 4, buf.length)
   250  
   251  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   252  	atomic.StoreInt32((*int32)(uptr), int32(value))
   253  }
   254  
   255  //go:norace
   256  func (buf *Buffer) CompareAndSetInt64(offset int32, expectedValue, updateValue int64) bool {
   257  	BoundsCheck(offset, 8, buf.length)
   258  
   259  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   260  	return atomic.CompareAndSwapInt64((*int64)(uptr), expectedValue, updateValue)
   261  }
   262  
   263  //go:norace
   264  func (buf *Buffer) CompareAndSetInt32(offset int32, expectedValue, updateValue int32) bool {
   265  	BoundsCheck(offset, 4, buf.length)
   266  
   267  	uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset))
   268  	return atomic.CompareAndSwapInt32((*int32)(uptr), expectedValue, updateValue)
   269  }
   270  
   271  //go:norace
   272  func (buf *Buffer) PutBytes(index int32, srcBuffer *Buffer, srcint32 int32, length int32) {
   273  	BoundsCheck(index, length, buf.length)
   274  	BoundsCheck(srcint32, length, srcBuffer.length)
   275  
   276  	util.Memcpy(uintptr(buf.bufferPtr)+uintptr(index), uintptr(srcBuffer.bufferPtr)+uintptr(srcint32), length)
   277  }
   278  
   279  //go:norace
   280  func (buf *Buffer) GetBytesArray(offset int32, length int32) []byte {
   281  	BoundsCheck(offset, length, buf.length)
   282  
   283  	bArr := make([]byte, length)
   284  	for ix := 0; ix < int(length); ix++ {
   285  		uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset) + uintptr(ix))
   286  		bArr[ix] = *(*uint8)(uptr)
   287  	}
   288  
   289  	return bArr
   290  }
   291  
   292  //go:norace
   293  func (buf *Buffer) GetBytes(offset int32, b []byte) {
   294  	length := len(b)
   295  	BoundsCheck(offset, int32(length), buf.length)
   296  
   297  	for ix := 0; ix < length; ix++ {
   298  		uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset) + uintptr(ix))
   299  		b[ix] = *(*uint8)(uptr)
   300  	}
   301  }
   302  
   303  // WriteBytes writes data from offset and length to the given dest buffer. This will
   304  // grow the buffer as needed.
   305  //go:norace
   306  func (buf *Buffer) WriteBytes(dest *bytes.Buffer, offset int32, length int32) {
   307  	BoundsCheck(offset, length, buf.length)
   308  	// grow the buffer all at once to prevent additional allocations.
   309  	dest.Grow(int(length))
   310  	for ix := 0; ix < int(length); ix++ {
   311  		uptr := unsafe.Pointer(uintptr(buf.bufferPtr) + uintptr(offset) + uintptr(ix))
   312  		dest.WriteByte(*(*uint8)(uptr))
   313  	}
   314  }
   315  
   316  //go:norace
   317  func (buf *Buffer) PutBytesArray(index int32, arr *[]byte, srcint32 int32, length int32) {
   318  	BoundsCheck(index, length, buf.length)
   319  	BoundsCheck(srcint32, length, int32(len(*arr)))
   320  
   321  	bArr := *arr
   322  
   323  	util.Memcpy(uintptr(buf.bufferPtr)+uintptr(index), uintptr(unsafe.Pointer(&bArr[0]))+uintptr(srcint32), length)
   324  }
   325  
   326  // BoundsCheck is helper function to make sure buffer writes and reads to
   327  // not go out of bounds on stated buffer capacity
   328  //go:norace
   329  func BoundsCheck(index int32, length int32, myLength int32) {
   330  	if (index + length) > myLength {
   331  		log.Fatal(fmt.Sprintf("Out of Bounds. int32: %d + %d Capacity: %d", index, length, myLength))
   332  	}
   333  }