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  }