agent/native/ext/TextOutputStream.h (195 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. */ #pragma once #include "TextOutputStream_forward_decl.h" #include <stdbool.h> #include <stdio.h> #include <string.h> #include <stdarg.h> #include <inttypes.h> #include "elastic_apm_assert.h" #include "basic_types.h" #include "StringView.h" #include "basic_macros.h" #include "basic_util.h" struct TextOutputStream { char* bufferBegin; size_t bufferSize; char* freeSpaceBegin; bool isOverflowed; // Append terminating '\0' automatically in streamInt(), streamString(), etc. bool autoTermZero; // If shouldEncloseUserString is true then user provided strings are written as `text' (enclosed in quotes) // otherwise user provided strings are written as text (i.e., no quotes) bool shouldEncloseUserString; }; struct TextOutputStreamState { char* freeSpaceBegin; // Append terminating '\0' automatically in streamInt(), streamString(), etc. bool autoTermZero; }; typedef struct TextOutputStreamState TextOutputStreamState; #define ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER "<NOT ENOUGH SPACE in TextOutputStream>" #define ELASTIC_APM_TEXT_OUTPUT_STREAM_OVERFLOWED_MARKER "..." ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER #define ELASTIC_APM_TEXT_OUTPUT_STREAM_OVERFLOWED_MARKER_LENGTH ( sizeof( ELASTIC_APM_TEXT_OUTPUT_STREAM_OVERFLOWED_MARKER ) - 1 ) #define ELASTIC_APM_TEXT_OUTPUT_STREAM_RESERVED_SPACE_SIZE ( ELASTIC_APM_TEXT_OUTPUT_STREAM_OVERFLOWED_MARKER_LENGTH + 1 ) // 1+ for terminating '\0' // We need at least one char in addition to terminating '\0' // because some functions need additional space to detect overflow ELASTIC_APM_STATIC_ASSERT( ELASTIC_APM_TEXT_OUTPUT_STREAM_RESERVED_SPACE_SIZE >= 2 ); // 1 for at least one char of content in case of overflow #define ELASTIC_APM_TEXT_OUTPUT_STREAM_MIN_BUFFER_SIZE ( 1 + ELASTIC_APM_TEXT_OUTPUT_STREAM_RESERVED_SPACE_SIZE ) #define ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ( 1024 ) ELASTIC_APM_STATIC_ASSERT( ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE >= ELASTIC_APM_TEXT_OUTPUT_STREAM_MIN_BUFFER_SIZE ); static inline void assertValidEndPtrIntoTextOutputStream( const char* ptr, const TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR( ptr ); ELASTIC_APM_ASSERT_LE_PTR( txtOutStream->bufferBegin, ptr ); ELASTIC_APM_ASSERT_LE_PTR( ptr, txtOutStream->bufferBegin + txtOutStream->bufferSize ); } static inline void assertValidBeginPtrIntoTextOutputStream( const char* ptr, const TextOutputStream* txtOutStream ) { assertValidEndPtrIntoTextOutputStream( ptr, txtOutStream ); ELASTIC_APM_ASSERT_LT_PTR( ptr, txtOutStream->bufferBegin + txtOutStream->bufferSize ); } static inline void assertValidTextOutputStream( const TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR( txtOutStream ); ELASTIC_APM_ASSERT_VALID_PTR( txtOutStream->bufferBegin ); ELASTIC_APM_ASSERT_GE_UINT64( txtOutStream->bufferSize, ELASTIC_APM_TEXT_OUTPUT_STREAM_MIN_BUFFER_SIZE ); if ( txtOutStream->isOverflowed ) { assertValidEndPtrIntoTextOutputStream( txtOutStream->freeSpaceBegin, txtOutStream ); } else { assertValidBeginPtrIntoTextOutputStream( txtOutStream->freeSpaceBegin, txtOutStream ); } } #define ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ) \ ELASTIC_APM_ASSERT_VALID_OBJ( assertValidTextOutputStream( txtOutStream ) ) \ /**/ static inline bool textOutputStreamIsOverflowed( TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); return txtOutStream->isOverflowed; } TextOutputStream makeTextOutputStream( char* bufferBegin, size_t bufferSize ); #define ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( staticBuffer ) \ ( makeTextOutputStream( (staticBuffer), ELASTIC_APM_STATIC_ARRAY_SIZE( (staticBuffer) ) ) ) static inline char* textOutputStreamGetFreeSpaceBegin( const TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); return txtOutStream->freeSpaceBegin; } static inline bool textOutputStreamHasReservedSpace( TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); return ( txtOutStream->freeSpaceBegin - txtOutStream->bufferBegin ) + ELASTIC_APM_TEXT_OUTPUT_STREAM_RESERVED_SPACE_SIZE <= txtOutStream->bufferSize; } size_t textOutputStreamGetFreeSpaceSize( const TextOutputStream* txtOutStream ); bool textOutputStreamStartEntry( TextOutputStream* txtOutStream, TextOutputStreamState* txtOutStreamState ); static inline String textOutputStreamEndEntryEx( const char* const contentEnd, const TextOutputStreamState* txtOutStreamStateOnEntryStart, TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); ELASTIC_APM_ASSERT_VALID_PTR( txtOutStreamStateOnEntryStart ); ELASTIC_APM_ASSERT_VALID_OBJ( assertValidEndPtrIntoTextOutputStream( contentEnd, txtOutStream ) ); txtOutStream->autoTermZero = txtOutStreamStateOnEntryStart->autoTermZero; if ( textOutputStreamIsOverflowed( txtOutStream ) ) { // If we didn't write any of this entry's content then we just return the marker if ( contentEnd == txtOutStreamStateOnEntryStart->freeSpaceBegin ) return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; // otherwise we return partial content with the overflowed marker appended return txtOutStreamStateOnEntryStart->freeSpaceBegin; } ELASTIC_APM_ASSERT( textOutputStreamHasReservedSpace( txtOutStream ), "" ); if ( txtOutStream->autoTermZero ) *( txtOutStream->freeSpaceBegin++ ) = '\0'; return txtOutStreamStateOnEntryStart->freeSpaceBegin; } String textOutputStreamEndEntryAsOverflowed( const TextOutputStreamState* txtOutStreamStateOnEntryStart, TextOutputStream* txtOutStream ); String textOutputStreamEndEntry( const TextOutputStreamState* txtOutStreamStateOnEntryStart, TextOutputStream* txtOutStream ); static inline StringView textOutputStreamContentAsStringView( TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); return makeStringView( txtOutStream->bufferBegin, txtOutStream->freeSpaceBegin - txtOutStream->bufferBegin ); } static inline void textOutputStreamRewind( TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); txtOutStream->freeSpaceBegin = txtOutStream->bufferBegin; txtOutStream->isOverflowed = false; } static inline void textOutputStreamSkipNChars( TextOutputStream* txtOutStream, size_t numberCharsToSkip ) { ELASTIC_APM_ASSERT_GE_UINT64( textOutputStreamGetFreeSpaceSize( txtOutStream ), numberCharsToSkip ); txtOutStream->freeSpaceBegin += numberCharsToSkip; } static inline void textOutputStreamGoBack( TextOutputStream* txtOutStream, size_t numberCharsToGoBack ) { ELASTIC_APM_ASSERT_GE_PTR( txtOutStream->freeSpaceBegin, txtOutStream->bufferBegin + numberCharsToGoBack ); txtOutStream->freeSpaceBegin -= numberCharsToGoBack; } static inline StringView textOutputStreamViewFrom( TextOutputStream* txtOutStream, const char* stringViewBegin ) { ELASTIC_APM_ASSERT_VALID_PTR_TEXT_OUTPUT_STREAM( txtOutStream ); ELASTIC_APM_ASSERT_LE_PTR( txtOutStream->bufferBegin, stringViewBegin ); ELASTIC_APM_ASSERT_LE_PTR( stringViewBegin, txtOutStream->freeSpaceBegin ); return makeStringView( stringViewBegin, (size_t)( txtOutStream->freeSpaceBegin - stringViewBegin ) ); } String streamStringView( StringView value, TextOutputStream* txtOutStream ); String streamVPrintf( TextOutputStream* txtOutStream, String printfFmt, va_list printfFmtArgs ); // It seems that it's a compilation error to put __attribute__ at function definition // so we add a seemingly redundant declaration just for __attribute__ ( ( format ( printf, ?, ? ) ) ) static inline String streamPrintf( TextOutputStream* txtOutStream, String printfFmt, /* printfFmtArgs: */ ... ) ELASTIC_APM_PRINTF_ATTRIBUTE( /* fmtPos: */ 2, /* fmtArgsPos: */ 3 ); static inline String streamPrintf( TextOutputStream* txtOutStream, String printfFmt, ... ) { String retVal = NULL; va_list printfFmtArgs; va_start( printfFmtArgs, printfFmt ); retVal = streamVPrintf( txtOutStream, printfFmt, printfFmtArgs ); va_end( printfFmtArgs ); return retVal; } static inline String streamInt( int value, TextOutputStream* txtOutStream ) { return streamPrintf( txtOutStream, "%d", value ); } static inline String streamString( String value, TextOutputStream* txtOutStream ) { return streamPrintf( txtOutStream, "%s", value ); } static inline String streamBool( bool value, TextOutputStream* txtOutStream ) { return streamPrintf( txtOutStream, "%s", boolToString( value ) ); } static inline String streamChar( char value, TextOutputStream* txtOutStream ) { return streamStringView( makeStringView( &value, 1 ), txtOutStream ); } static inline String streamUserString( String value, TextOutputStream* txtOutStream ) { return ( value == NULL ) ? streamStringView( ELASTIC_APM_STRING_LITERAL_TO_VIEW( "NULL" ), txtOutStream ) : streamPrintf( txtOutStream, "`%s'", value ); } static inline String streamIndent( unsigned int nestingDepth, TextOutputStream* txtOutStream ) { TextOutputStreamState txtOutStreamStateOnEntryStart; if ( ! textOutputStreamStartEntry( txtOutStream, &txtOutStreamStateOnEntryStart ) ) return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; StringView indent = ELASTIC_APM_STRING_LITERAL_TO_VIEW( " " ); ELASTIC_APM_REPEAT_N_TIMES( nestingDepth ) streamStringView( indent, txtOutStream ); return textOutputStreamEndEntry( &txtOutStreamStateOnEntryStart, txtOutStream ); }