agent/native/ext/TextOutputStream.cpp (91 lines of code) (raw):

/* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "TextOutputStream.h" TextOutputStream makeTextOutputStream( char* bufferBegin, size_t bufferSize ) { ELASTIC_APM_ASSERT_VALID_PTR( bufferBegin ); ELASTIC_APM_ASSERT_GE_UINT64( bufferSize, ELASTIC_APM_TEXT_OUTPUT_STREAM_MIN_BUFFER_SIZE ); TextOutputStream txtOutStream = { .bufferBegin = bufferBegin, .bufferSize = bufferSize, .freeSpaceBegin = bufferBegin, .isOverflowed = false, .autoTermZero = true, .shouldEncloseUserString = true }; ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( &txtOutStream ); // We cannot work with a buffer that doesn't have reserved space if ( bufferSize < ELASTIC_APM_TEXT_OUTPUT_STREAM_MIN_BUFFER_SIZE ) { txtOutStream.isOverflowed = true; // If buffer is not zero sized then mark it as empty if ( bufferSize != 0 ) *bufferBegin = '\0'; ELASTIC_APM_ASSERT( textOutputStreamIsOverflowed( &txtOutStream ), "" ); } ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( &txtOutStream ); return txtOutStream; } bool textOutputStreamStartEntry( TextOutputStream* txtOutStream, TextOutputStreamState* txtOutStreamState ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); ELASTIC_APM_ASSERT_VALID_PTR( txtOutStreamState ); if ( textOutputStreamIsOverflowed( txtOutStream ) ) return false; txtOutStreamState->autoTermZero = txtOutStream->autoTermZero; txtOutStreamState->freeSpaceBegin = txtOutStream->freeSpaceBegin; txtOutStream->autoTermZero = false; return true; } String streamStringView( StringView value, TextOutputStream* txtOutStream ) { TextOutputStreamState txtOutStreamStateOnEntryStart; if ( ! textOutputStreamStartEntry( txtOutStream, &txtOutStreamStateOnEntryStart ) ) return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; const size_t freeSizeBeforeWrite = textOutputStreamGetFreeSpaceSize( txtOutStream ); const size_t numberOfCharsToCopy = std::min( value.length, freeSizeBeforeWrite ); if ( numberOfCharsToCopy != 0 ) memcpy( txtOutStreamStateOnEntryStart.freeSpaceBegin, value.begin, numberOfCharsToCopy * sizeof( char ) ); textOutputStreamSkipNChars( txtOutStream, numberOfCharsToCopy ); if ( numberOfCharsToCopy < value.length ) return textOutputStreamEndEntryAsOverflowed( &txtOutStreamStateOnEntryStart, txtOutStream ); return textOutputStreamEndEntry( &txtOutStreamStateOnEntryStart, txtOutStream ); } String streamVPrintf( TextOutputStream* txtOutStream, String printfFmt, va_list printfFmtArgs ) { TextOutputStreamState txtOutStreamStateOnEntryStart; if ( ! textOutputStreamStartEntry( txtOutStream, &txtOutStreamStateOnEntryStart ) ) return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; const size_t freeSpaceSize = textOutputStreamGetFreeSpaceSize( txtOutStream ); // We can take one more char from reserved space for printf's terminating '\0' int snprintfRetVal = vsnprintf( txtOutStreamStateOnEntryStart.freeSpaceBegin, freeSpaceSize + 1, printfFmt, printfFmtArgs ); // snprintf: If an encoding error occurs, a negative number is returned // so we don't change advance the buffer tracker if ( snprintfRetVal < 0 ) return "<vsnprintf returned error>"; // snprintf: when returned value is non-negative and less than buffer-size // then the string has been completely written // otherwise it means buffer overflowed const bool isOverflowed = ( static_cast<size_t>(snprintfRetVal) > freeSpaceSize ); // snprintf always writes terminating '\0' // but return value is number of chars written not counting the terminating '\0' textOutputStreamSkipNChars( txtOutStream, isOverflowed ? freeSpaceSize : snprintfRetVal ); return ( isOverflowed ) ? textOutputStreamEndEntryAsOverflowed( &txtOutStreamStateOnEntryStart, txtOutStream ) : textOutputStreamEndEntry( &txtOutStreamStateOnEntryStart, txtOutStream ); } size_t textOutputStreamGetFreeSpaceSize( const TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); const size_t reservedAndUsedSpaceSize = ELASTIC_APM_TEXT_OUTPUT_STREAM_RESERVED_SPACE_SIZE + ( txtOutStream->freeSpaceBegin - txtOutStream->bufferBegin ); if ( txtOutStream->bufferSize < reservedAndUsedSpaceSize ) return 0; return txtOutStream->bufferSize - reservedAndUsedSpaceSize; } String textOutputStreamEndEntryAsOverflowed( const TextOutputStreamState* txtOutStreamStateOnEntryStart, TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); const char* const contentEnd = txtOutStream->freeSpaceBegin; if ( ! textOutputStreamIsOverflowed( txtOutStream ) ) { ELASTIC_APM_ASSERT( textOutputStreamHasReservedSpace( txtOutStream ), "" ); strcpy( txtOutStream->freeSpaceBegin, ELASTIC_APM_TEXT_OUTPUT_STREAM_OVERFLOWED_MARKER ); txtOutStream->freeSpaceBegin += ELASTIC_APM_TEXT_OUTPUT_STREAM_OVERFLOWED_MARKER_LENGTH; txtOutStream->isOverflowed = true; ELASTIC_APM_ASSERT( textOutputStreamIsOverflowed( txtOutStream ), "" ); } return textOutputStreamEndEntryEx( contentEnd, txtOutStreamStateOnEntryStart,txtOutStream ); } String textOutputStreamEndEntry( const TextOutputStreamState* txtOutStreamStateOnEntryStart, TextOutputStream* txtOutStream ) { // If we need to append terminating '\0' but there's no space for it // then we consider the stream as overflowed if ( txtOutStreamStateOnEntryStart->autoTermZero && ( textOutputStreamGetFreeSpaceSize( txtOutStream ) == 0 ) ) return textOutputStreamEndEntryAsOverflowed( txtOutStreamStateOnEntryStart, txtOutStream ); return textOutputStreamEndEntryEx( /* contentEnd: */ txtOutStream->freeSpaceBegin, txtOutStreamStateOnEntryStart, txtOutStream ); }