github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/logbuffer/term/appender.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 term 18 19 import ( 20 "github.com/lirm/aeron-go/aeron/atomic" 21 "github.com/lirm/aeron-go/aeron/flyweight" 22 "github.com/lirm/aeron-go/aeron/logbuffer" 23 "github.com/lirm/aeron-go/aeron/util" 24 ) 25 26 const ( 27 // AppenderTripped is returned when the end of the term has been reached and buffer roll was done 28 AppenderTripped int64 = -1 29 30 // AppenderFailed is returned when appending is not possible due to position being outside of the term. ?? 31 AppenderFailed int64 = -2 32 33 beginFrag uint8 = 0x80 34 endFrag uint8 = 0x40 35 unfragmented uint8 = 0x80 | 0x40 36 ) 37 38 // DefaultReservedValueSupplier is the default reserved value provider 39 var DefaultReservedValueSupplier ReservedValueSupplier = func(termBuffer *atomic.Buffer, termOffset int32, length int32) int64 { 40 return 0 41 } 42 43 // ReservedValueSupplier is the type definition for a provider of user supplied header data 44 type ReservedValueSupplier func(termBuffer *atomic.Buffer, termOffset int32, length int32) int64 45 46 // HeaderWriter is a helper class for writing frame header to the term 47 type headerWriter struct { 48 sessionID int32 49 streamID int32 50 } 51 52 func (header *headerWriter) fill(defaultHdr *atomic.Buffer) { 53 header.sessionID = defaultHdr.GetInt32(logbuffer.DataFrameHeader.SessionIDFieldOffset) 54 header.streamID = defaultHdr.GetInt32(logbuffer.DataFrameHeader.StreamIDFieldOffset) 55 } 56 57 func (header *headerWriter) write(termBuffer *atomic.Buffer, offset, length, termID int32) { 58 termBuffer.PutInt32Ordered(offset, -length) 59 60 termBuffer.PutInt8(offset+logbuffer.DataFrameHeader.VersionFieldOffset, logbuffer.DataFrameHeader.CurrentVersion) 61 termBuffer.PutUInt8(offset+logbuffer.DataFrameHeader.FlagsFieldOffset, unfragmented) 62 termBuffer.PutUInt16(offset+logbuffer.DataFrameHeader.TypeFieldOffset, logbuffer.DataFrameHeader.TypeData) 63 termBuffer.PutInt32(offset+logbuffer.DataFrameHeader.TermOffsetFieldOffset, offset) 64 termBuffer.PutInt32(offset+logbuffer.DataFrameHeader.SessionIDFieldOffset, header.sessionID) 65 termBuffer.PutInt32(offset+logbuffer.DataFrameHeader.StreamIDFieldOffset, header.streamID) 66 termBuffer.PutInt32(offset+logbuffer.DataFrameHeader.TermIDFieldOffset, termID) 67 } 68 69 // Appender type is the term writer 70 type Appender struct { 71 termBuffer *atomic.Buffer 72 tailCounter flyweight.Int64Field 73 headerWriter headerWriter 74 } 75 76 // MakeAppender is the factory function for term Appenders 77 func MakeAppender(logBuffers *logbuffer.LogBuffers, partitionIndex int) *Appender { 78 79 appender := new(Appender) 80 appender.termBuffer = logBuffers.Buffer(partitionIndex) 81 appender.tailCounter = logBuffers.Meta().TailCounter[partitionIndex] 82 83 header := logBuffers.Meta().DefaultFrameHeader.Get() 84 appender.headerWriter.fill(header) 85 86 return appender 87 } 88 89 // RawTail is the accessor to the raw value of the tail offset used by Publication 90 func (appender *Appender) RawTail() int64 { 91 return appender.tailCounter.Get() 92 } 93 94 // SetRawTail sets the raw value of the tail. It should not be used outside of testing 95 func (appender *Appender) SetRawTail(v int64) { 96 appender.tailCounter.Set(v) 97 } 98 99 func (appender *Appender) getAndAddRawTail(alignedLength int32) int64 { 100 return appender.tailCounter.GetAndAddInt64(int64(alignedLength)) 101 } 102 103 // Claim is the interface for using Buffer Claims for zero copy sends 104 func (appender *Appender) Claim(length int32, claim *logbuffer.Claim) (resultingOffset int64, termID int32) { 105 106 frameLength := length + logbuffer.DataFrameHeader.Length 107 alignedLength := util.AlignInt32(frameLength, logbuffer.FrameAlignment) 108 rawTail := appender.getAndAddRawTail(alignedLength) 109 termLength := appender.termBuffer.Capacity() 110 111 termID = logbuffer.TermID(rawTail) 112 termOffset := rawTail & 0xFFFFFFFF 113 resultingOffset = termOffset + int64(alignedLength) 114 if resultingOffset > int64(termLength) { 115 resultingOffset = handleEndOfLogCondition(termID, appender.termBuffer, int32(termOffset), 116 &appender.headerWriter, termLength) 117 } else { 118 offset := int32(termOffset) 119 appender.headerWriter.write(appender.termBuffer, offset, frameLength, termID) 120 claim.Wrap(appender.termBuffer, offset, frameLength) 121 } 122 123 return resultingOffset, termID 124 } 125 126 // AppendUnfragmentedMessage appends an unfragmented message in a single frame to the term 127 func (appender *Appender) AppendUnfragmentedMessage(srcBuffer *atomic.Buffer, srcOffset int32, length int32, 128 reservedValueSupplier ReservedValueSupplier) (resultingOffset int64, termID int32) { 129 130 frameLength := length + logbuffer.DataFrameHeader.Length 131 alignedLength := util.AlignInt32(frameLength, logbuffer.FrameAlignment) 132 rawTail := appender.getAndAddRawTail(alignedLength) 133 termLength := appender.termBuffer.Capacity() 134 135 termID = logbuffer.TermID(rawTail) 136 termOffset := rawTail & 0xFFFFFFFF 137 resultingOffset = termOffset + int64(alignedLength) 138 if resultingOffset > int64(termLength) { 139 resultingOffset = handleEndOfLogCondition(termID, appender.termBuffer, int32(termOffset), 140 &appender.headerWriter, termLength) 141 } else { 142 offset := int32(termOffset) 143 appender.headerWriter.write(appender.termBuffer, offset, frameLength, termID) 144 appender.termBuffer.PutBytes(offset+logbuffer.DataFrameHeader.Length, srcBuffer, srcOffset, length) 145 146 if nil != reservedValueSupplier { 147 reservedValue := reservedValueSupplier(appender.termBuffer, offset, frameLength) 148 appender.termBuffer.PutInt64(offset+logbuffer.DataFrameHeader.ReservedValueFieldOffset, reservedValue) 149 } 150 151 logbuffer.SetFrameLength(appender.termBuffer, offset, frameLength) 152 } 153 154 return resultingOffset, termID 155 } 156 157 // AppendUnfragmentedMessage2 appends the given pair of buffers as an unfragmented message in a single frame to the term 158 func (appender *Appender) AppendUnfragmentedMessage2( 159 srcBufferOne *atomic.Buffer, srcOffsetOne int32, lengthOne int32, 160 srcBufferTwo *atomic.Buffer, srcOffsetTwo int32, lengthTwo int32, 161 reservedValueSupplier ReservedValueSupplier, 162 ) (resultingOffset int64, termID int32) { 163 164 frameLength := lengthOne + lengthTwo + logbuffer.DataFrameHeader.Length 165 alignedLength := util.AlignInt32(frameLength, logbuffer.FrameAlignment) 166 rawTail := appender.getAndAddRawTail(alignedLength) 167 termLength := appender.termBuffer.Capacity() 168 169 termID = logbuffer.TermID(rawTail) 170 termOffset := rawTail & 0xFFFFFFFF 171 resultingOffset = termOffset + int64(alignedLength) 172 if resultingOffset > int64(termLength) { 173 resultingOffset = handleEndOfLogCondition(termID, appender.termBuffer, int32(termOffset), 174 &appender.headerWriter, termLength) 175 } else { 176 offset := int32(termOffset) 177 dataOffset := offset + logbuffer.DataFrameHeader.Length 178 appender.headerWriter.write(appender.termBuffer, offset, frameLength, logbuffer.TermID(rawTail)) 179 appender.termBuffer.PutBytes(dataOffset, srcBufferOne, srcOffsetOne, lengthOne) 180 appender.termBuffer.PutBytes(dataOffset+lengthOne, srcBufferTwo, srcOffsetTwo, lengthTwo) 181 182 if nil != reservedValueSupplier { 183 reservedValue := reservedValueSupplier(appender.termBuffer, offset, frameLength) 184 appender.termBuffer.PutInt64(offset+logbuffer.DataFrameHeader.ReservedValueFieldOffset, reservedValue) 185 } 186 187 logbuffer.SetFrameLength(appender.termBuffer, offset, frameLength) 188 } 189 190 return resultingOffset, termID 191 } 192 193 // AppendFragmentedMessage appends a message greater than frame length as a batch of fragments 194 func (appender *Appender) AppendFragmentedMessage(srcBuffer *atomic.Buffer, srcOffset int32, length int32, 195 maxPayloadLength int32, reservedValueSupplier ReservedValueSupplier) (resultingOffset int64, termID int32) { 196 197 numMaxPayloads := length / maxPayloadLength 198 remainingPayload := length % maxPayloadLength 199 var lastFrameLength int32 200 if remainingPayload > 0 { 201 lastFrameLength = util.AlignInt32(remainingPayload+logbuffer.DataFrameHeader.Length, logbuffer.FrameAlignment) 202 } 203 requiredLength := (numMaxPayloads * (maxPayloadLength + logbuffer.DataFrameHeader.Length)) + lastFrameLength 204 rawTail := appender.getAndAddRawTail(requiredLength) 205 206 termLength := appender.termBuffer.Capacity() 207 208 termID = logbuffer.TermID(rawTail) 209 termOffset := rawTail & 0xFFFFFFFF 210 resultingOffset = termOffset + int64(requiredLength) 211 if resultingOffset > int64(termLength) { 212 resultingOffset = handleEndOfLogCondition(termID, appender.termBuffer, int32(termOffset), 213 &appender.headerWriter, termLength) 214 } else { 215 flags := beginFrag 216 remaining := length 217 offset := int32(termOffset) 218 219 for remaining > 0 { 220 bytesToWrite := minInt32(remaining, maxPayloadLength) 221 frameLength := bytesToWrite + logbuffer.DataFrameHeader.Length 222 alignedLength := util.AlignInt32(frameLength, logbuffer.FrameAlignment) 223 224 appender.headerWriter.write(appender.termBuffer, offset, frameLength, termID) 225 appender.termBuffer.PutBytes( 226 offset+logbuffer.DataFrameHeader.Length, srcBuffer, srcOffset+(length-remaining), bytesToWrite) 227 228 if remaining <= maxPayloadLength { 229 flags |= endFrag 230 } 231 232 logbuffer.FrameFlags(appender.termBuffer, offset, flags) 233 234 reservedValue := reservedValueSupplier(appender.termBuffer, offset, frameLength) 235 appender.termBuffer.PutInt64(offset+logbuffer.DataFrameHeader.ReservedValueFieldOffset, reservedValue) 236 237 logbuffer.SetFrameLength(appender.termBuffer, offset, frameLength) 238 239 flags = 0 240 offset += alignedLength 241 remaining -= bytesToWrite 242 } 243 } 244 245 return resultingOffset, termID 246 } 247 248 // AppendFragmentedMessage2 appends the given pair of buffers (with combined length greater than max frame length) 249 // as a batch of fragments 250 func (appender *Appender) AppendFragmentedMessage2( 251 srcBufferOne *atomic.Buffer, srcOffsetOne int32, lengthOne int32, 252 srcBufferTwo *atomic.Buffer, srcOffsetTwo int32, lengthTwo int32, 253 maxPayloadLength int32, reservedValueSupplier ReservedValueSupplier, 254 ) (resultingOffset int64, termID int32) { 255 length := lengthOne + lengthTwo 256 numMaxPayloads := length / maxPayloadLength 257 remainingPayload := length % maxPayloadLength 258 var lastFrameLength int32 259 if remainingPayload > 0 { 260 lastFrameLength = util.AlignInt32(remainingPayload+logbuffer.DataFrameHeader.Length, logbuffer.FrameAlignment) 261 } 262 requiredLength := (numMaxPayloads * (maxPayloadLength + logbuffer.DataFrameHeader.Length)) + lastFrameLength 263 rawTail := appender.getAndAddRawTail(requiredLength) 264 265 termLength := appender.termBuffer.Capacity() 266 267 termID = logbuffer.TermID(rawTail) 268 termOffset := rawTail & 0xFFFFFFFF 269 resultingOffset = termOffset + int64(requiredLength) 270 if resultingOffset > int64(termLength) { 271 resultingOffset = handleEndOfLogCondition(termID, appender.termBuffer, int32(termOffset), 272 &appender.headerWriter, termLength) 273 } else { 274 flags := beginFrag 275 remaining := length 276 frameOffset := int32(termOffset) 277 var posOne, posTwo int32 278 279 for remaining > 0 { 280 bytesToWrite := minInt32(remaining, maxPayloadLength) 281 frameLength := bytesToWrite + logbuffer.DataFrameHeader.Length 282 alignedLength := util.AlignInt32(frameLength, logbuffer.FrameAlignment) 283 284 appender.headerWriter.write(appender.termBuffer, frameOffset, frameLength, termID) 285 286 var bytesWritten int32 287 payloadOffset := frameOffset + logbuffer.DataFrameHeader.Length 288 for bytesWritten < bytesToWrite { 289 remainingOne := lengthOne - posOne 290 if remainingOne > 0 { 291 numBytes := minInt32(bytesToWrite-bytesWritten, remainingOne) 292 appender.termBuffer.PutBytes(payloadOffset, srcBufferOne, srcOffsetOne+posOne, numBytes) 293 bytesWritten += numBytes 294 payloadOffset += numBytes 295 posOne += numBytes 296 } else { 297 numBytes := minInt32(bytesToWrite-bytesWritten, lengthTwo-posTwo) 298 appender.termBuffer.PutBytes(payloadOffset, srcBufferTwo, srcOffsetTwo+posTwo, numBytes) 299 bytesWritten += numBytes 300 payloadOffset += numBytes 301 posTwo += numBytes 302 } 303 } 304 305 if remaining <= maxPayloadLength { 306 flags |= endFrag 307 } 308 logbuffer.FrameFlags(appender.termBuffer, frameOffset, flags) 309 310 reservedValue := reservedValueSupplier(appender.termBuffer, frameOffset, frameLength) 311 appender.termBuffer.PutInt64(frameOffset+logbuffer.DataFrameHeader.ReservedValueFieldOffset, reservedValue) 312 313 logbuffer.SetFrameLength(appender.termBuffer, frameOffset, frameLength) 314 315 flags = 0 316 frameOffset += alignedLength 317 remaining -= bytesToWrite 318 } 319 } 320 321 return resultingOffset, termID 322 } 323 324 func handleEndOfLogCondition(termID int32, termBuffer *atomic.Buffer, termOffset int32, header *headerWriter, 325 termLength int32) int64 { 326 newOffset := AppenderFailed 327 328 if termOffset <= termLength { 329 newOffset = AppenderTripped 330 331 if termOffset < termLength { 332 paddingLength := termLength - termOffset 333 header.write(termBuffer, termOffset, paddingLength, termID) 334 logbuffer.SetFrameType(termBuffer, termOffset, logbuffer.DataFrameHeader.TypePad) 335 logbuffer.SetFrameLength(termBuffer, termOffset, paddingLength) 336 } 337 } 338 339 return newOffset 340 } 341 342 func (appender *Appender) SetTailTermID(termID int32) { 343 appender.tailCounter.Set(int64(termID) << 32) 344 } 345 346 func minInt32(v1, v2 int32) int32 { 347 if v1 < v2 { 348 return v1 349 } else { 350 return v2 351 } 352 }