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 }