agent/native/ext/util.h (400 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 <stdbool.h> #include <stddef.h> #include <string.h> #include <stdio.h> #include <ctype.h> #include "constants.h" #include "basic_types.h" #include "StringView.h" #include "basic_macros.h" #include "basic_util.h" #include "ResultCode.h" #include "elastic_apm_assert.h" #include "elastic_apm_clock.h" static inline bool isEmtpyString( String str ) { return str[ 0 ] == '\0'; } static inline bool isNullOrEmtpyString( String str ) { return str == NULL || isEmtpyString( str ); } static inline void replaceCharInString( MutableString str, char originalChar, char replacementChar ) { ELASTIC_APM_ASSERT_VALID_PTR( str ); for ( size_t i = 0 ; str[ i ] != '\0' ; ++i ) if ( str[ i ] == originalChar ) str[ i ] = replacementChar; } static inline void replaceCharInStringView( StringView strView, char originalChar, char replacementChar ) { ELASTIC_APM_ASSERT_VALID_STRING_VIEW( strView ); for ( size_t i = 0 ; i != strView.length ; ++i ) if ( strView.begin[ i ] == originalChar ) ((char*)(strView.begin))[ i ] = replacementChar; } static inline StringView stringToView( String str ) { ELASTIC_APM_ASSERT_VALID_STRING( str ); return (StringView) { .begin = str, .length = strlen( str ) }; } static inline StringView stringViewSkipFirstNChars( StringView strView, size_t numberOfCharsToSkip ) { ELASTIC_APM_ASSERT_VALID_STRING_VIEW( strView ); return (StringView) { .begin = strView.begin + numberOfCharsToSkip, .length = strView.length - numberOfCharsToSkip }; } static inline char charToUpperCase( char c ) { if ( ELASTIC_APM_IS_IN_INCLUSIVE_RANGE( 'a', c, 'z' ) ) { return (char) ( (char) ( c - 'a' ) + 'A' ); } return c; } static inline char charToLowerCase( char c ) { if ( ELASTIC_APM_IS_IN_INCLUSIVE_RANGE( 'A', c, 'Z' ) ) { return (char) ( (char) ( c - 'A' ) + 'a' ); } return c; } static inline void copyStringAsUpperCase( String src, /* out */ MutableString dst ) { size_t i = 0; for ( i = 0 ; src[ i ] != '\0' ; ++i ) dst[ i ] = charToUpperCase( src[ i ] ); dst[ i ] = '\0'; } static inline bool areCharsEqualIgnoringCase( char c1, char c2 ) { return charToUpperCase( c1 ) == charToUpperCase( c2 ); } static inline bool areCharsEqual( char c1, char c2, bool shouldIgnoreCase ) { return shouldIgnoreCase ? areCharsEqualIgnoringCase( c1, c2 ) : ( c1 == c2 ); } static inline bool isStringViewPrefix( StringView str, StringView prefix, bool shouldIgnoreCase ) { ELASTIC_APM_ASSERT_VALID_STRING_VIEW( str ); ELASTIC_APM_ASSERT_VALID_STRING_VIEW( prefix ); if ( prefix.length > str.length ) { return false; } ELASTIC_APM_FOR_EACH_INDEX( i, prefix.length ) { if ( ! areCharsEqual( str.begin[ i ], prefix.begin[ i ], shouldIgnoreCase ) ) { return false; } } return true; } static inline bool isStringViewPrefixIgnoringCase( StringView str, StringView prefix ) { return isStringViewPrefix( str, prefix, /* shouldIgnoreCase */ true); } static inline bool isStringViewSuffix( StringView str, StringView suffix ) { ELASTIC_APM_ASSERT_VALID_STRING_VIEW( str ); ELASTIC_APM_ASSERT_VALID_STRING_VIEW( suffix ); if ( suffix.length > str.length ) { return false; } size_t strBeginIndex = str.length - suffix.length; ELASTIC_APM_FOR_EACH_INDEX( i, suffix.length ) { if ( str.begin[ strBeginIndex + i ] != suffix.begin[ i ] ) { return false; } } return true; } static inline bool areStringsEqualIgnoringCase( String str1, String str2 ) { ELASTIC_APM_ASSERT_VALID_STRING( str1 ); ELASTIC_APM_ASSERT_VALID_STRING( str2 ); for ( const char *p1 = str1, *p2 = str2; areCharsEqualIgnoringCase( *p1, *p2 ) ; ++p1, ++p2 ) { if ( *p1 == '\0' ) { return true; } } return false; } static inline bool areStringViewsEqualIgnoringCase( StringView strVw1, StringView strVw2 ) { ELASTIC_APM_ASSERT_VALID_STRING_VIEW( strVw1 ); ELASTIC_APM_ASSERT_VALID_STRING_VIEW( strVw2 ); if ( strVw1.length != strVw2.length ) { return false; } ELASTIC_APM_FOR_EACH_INDEX( i, strVw1.length ) { if ( ! areCharsEqualIgnoringCase( strVw1.begin[ i ], strVw2.begin[ i ] ) ) { return false; } } return true; } static inline bool areStringViewsEqual( StringView strVw1, StringView strVw2 ) { ELASTIC_APM_ASSERT_VALID_STRING_VIEW( strVw1 ); ELASTIC_APM_ASSERT_VALID_STRING_VIEW( strVw2 ); if ( strVw1.length != strVw2.length ) { return false; } ELASTIC_APM_FOR_EACH_INDEX( i, strVw1.length ) { if ( strVw1.begin[ i ] != strVw2.begin[ i ] ) { return false; } } return true; } static inline bool areEqualNullableStrings( String str1, String str2 ) { if ( ( str1 == NULL ) && ( str2 == NULL ) ) return true; if ( ( str1 == NULL ) || ( str2 == NULL ) ) return false; return ( strcmp( str1, str2 ) == 0 ); } enum { escapeNonPrintableCharBufferSize = 10 }; static inline String spacialInvisibleCharToSymbol( char c ) { switch ( c ) { case '\0': return "\\0"; case '\a': return "\\a"; case '\b': return "\\b"; case '\f': return "\\f"; case '\n': return "\\n"; case '\r': return "\\r"; case '\t': return "\\t"; case '\v': return "\\v"; default: return NULL; } } static inline String escapeNonPrintableChar( char c, char buffer[ escapeNonPrintableCharBufferSize ] ) { // According to https://en.wikipedia.org/wiki/ASCII#Printable_characters // Codes 20 (hex) to 7E (hex), known as the printable characters if ( ELASTIC_APM_IS_IN_INCLUSIVE_RANGE( '\x20', c, '\x7E' ) ) { buffer[ 0 ] = c; buffer[ 1 ] = '\0'; } else { String asSymbol = spacialInvisibleCharToSymbol( c ); if ( asSymbol != NULL ) { return asSymbol; } snprintf( buffer, escapeNonPrintableCharBufferSize, "\\x%X", (UInt)((unsigned char)c) ); } return buffer; } static inline bool isLocalOsFilePathSeparator( char c ) { return ( c == '/' ) #ifdef PHP_WIN32 || ( c == '\\' ) #endif ; } static inline int findLastPathSeparatorPosition( StringView filePath ) { ELASTIC_APM_FOR_EACH_BACKWARDS( i, filePath.length ) if ( isLocalOsFilePathSeparator( filePath.begin[ i ] ) ) return (int)i; return -1; } static inline StringView extractLastPartOfFilePathStringView( StringView filePath ) { // some/file/path/Logger.c // some\file\path\Logger.c // ^^^^^^^^^^^^^^^ ELASTIC_APM_ASSERT_VALID_STRING_VIEW( filePath ); int lastPathSeparatorPosition = findLastPathSeparatorPosition( filePath ); if ( lastPathSeparatorPosition == -1 ) return filePath; return stringViewSkipFirstNChars( filePath, (size_t)( lastPathSeparatorPosition + 1 ) ); } static inline String extractLastPartOfFilePathString( String filePath ) { return extractLastPartOfFilePathStringView( makeStringViewFromString( filePath ) ).begin; } static inline size_t calcAlignedSize( size_t size, size_t alignment ) { const size_t remainder = size % alignment; return ( remainder == 0 ) ? size : ( size - remainder + alignment ); } static inline bool isWhiteSpace( char c ) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } StringView trimStringView( StringView src ); StringView findEndOfLineSequence( StringView text ); static inline bool isDecimalDigit( char c ) { return isdigit( c ) != 0; } typedef bool (* CharPredicate)( char c ); bool findCharByPredicate( StringView src, CharPredicate predicate, size_t* foundPosition ); static inline bool isDecimalDigitOrSign( char c ) { return ( c == '-' ) || isDecimalDigit( c ); } static inline bool isNotDecimalDigitOrSign( char c ) { return ! isDecimalDigitOrSign( c ); } ResultCode parseDecimalInteger( StringView inputString, /* out */ Int64* result ); ResultCode parseDecimalIntegerWithUnits( StringView inputString, StringView unitNames[], size_t numberOfUnits, /* out */ Int64* valueInUnits, /* out */ size_t* unitsIndex ); static inline bool ifThen( bool ifCond, bool thenCond ) { return ( ! ifCond ) || thenCond; } enum SizeUnits { sizeUnits_byte, sizeUnits_kibibyte, sizeUnits_mebibyte, sizeUnits_gibibyte, numberOfSizeUnits }; typedef enum SizeUnits SizeUnits; extern StringView sizeUnitsNames[ numberOfSizeUnits ]; struct Size { Int64 valueInUnits; SizeUnits units; }; typedef struct Size Size; static inline bool isValidSizeUnits( SizeUnits units ) { return ( sizeUnits_byte <= units ) && ( units < numberOfSizeUnits ); } #define ELASTIC_APM_UNKNOWN_SIZE_UNITS_AS_STRING "<UNKNOWN SizeUnits>" static inline String sizeUnitsToString( SizeUnits sizeUnits ) { if ( isValidSizeUnits( sizeUnits ) ) { return sizeUnitsNames[ sizeUnits ].begin; } return ELASTIC_APM_UNKNOWN_SIZE_UNITS_AS_STRING; } static inline Size makeSize( Int64 valueInUnits, SizeUnits units ) { return (Size){ .valueInUnits = valueInUnits, .units = units }; } ResultCode parseSize( StringView inputString, SizeUnits defaultUnits, /* out */ Size* result ); String streamSize( Size size, TextOutputStream* txtOutStream ); Int64 sizeToBytes( Size size ); static inline String stringIfNotNullElse( String str, String elseStr ) { return str == NULL ? elseStr: str; } static inline ResultCode safeStringCopy( StringView src, char* dstBuf, size_t dstBufCapacity ) { ResultCode resultCode; if ( src.length == 0 ) { ELASTIC_APM_SET_RESULT_CODE_TO_SUCCESS_AND_GOTO_FINALLY(); } // +1 for terminating '\0' if ( src.length + 1 > dstBufCapacity ) { ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE_EX( resultBufferIsTooSmall ); } #ifdef PHP_WIN32 // errno_t strncpy_s( char *restrict dest, size_t destsz, const char *restrict src, size_t count ); // returns zero on success, returns non-zero on error. Also, on error, writes zero to dest[0] // (unless dest is a null pointer or destsz is zero or greater than RSIZE_MAX) // and may clobber the rest of the destination array with unspecified values. errno_t strncpy_s_ret_val = strncpy_s( /* dest */ dstBufCapacity, /* destsz */ dstBufCapacity, /* src */ src.begin, /* count */ src.length ); if ( strncpy_s_ret_val != 0 ) { ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE(); } #else // #ifdef PHP_WIN32 // char *strncpy( char *dest, const char *src, size_t count ); // Copies at most count characters of the character array pointed to by src (including the terminating null character, // but not any of the characters that follow the null character) to character array pointed to by dest. // If count is reached before the entire array src was copied, the resulting character array is not null-terminated. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy( /* dest */ dstBuf, /* src */ src.begin, /* count */ src.length ); #pragma GCC diagnostic pop dstBuf[ src.length ] = '\0'; #endif // #ifdef PHP_WIN32 resultCode = resultSuccess; finally: return resultCode; failure: goto finally; } static inline ResultCode appendToString( StringView suffixToAppend, size_t bufCapacity, /* in */ char* bufBegin, /* in,out */ size_t* bufContentLen ) { ResultCode resultCode; if ( suffixToAppend.length == 0 ) { ELASTIC_APM_SET_RESULT_CODE_TO_SUCCESS_AND_GOTO_FINALLY(); } ELASTIC_APM_ASSERT_VALID_PTR( bufBegin ); ELASTIC_APM_ASSERT_VALID_PTR( bufContentLen ); ELASTIC_APM_ASSERT( *bufContentLen < bufCapacity, "*bufContentLen: %" PRIu64 ", bufCapacity: %" PRIu64, (UInt64)(*bufContentLen), (UInt64)bufCapacity ); ELASTIC_APM_CALL_IF_FAILED_GOTO( safeStringCopy( /* src */ suffixToAppend, /* dstBuf */ bufBegin + *bufContentLen, bufCapacity - *bufContentLen ) ); *bufContentLen += suffixToAppend.length; resultCode = resultSuccess; finally: return resultCode; failure: goto finally; } struct StringBuffer { char* begin; size_t size; }; typedef struct StringBuffer StringBuffer; #define ELASTIC_APM_MAKE_STRING_BUFFER( beginArg, sizeArg ) ((StringBuffer){ .begin = (beginArg), .size = (sizeArg) }) #define ELASTIC_APM_EMPTY_STRING_BUFFER ( ELASTIC_APM_MAKE_STRING_BUFFER( NULL, 0 ) ) static inline StringView stringBufferToView( StringBuffer strBuf ) { // -1 since terminating '\0' is counted in buffer's size but not in string's length return (StringView) { .begin = strBuf.begin, .length = (strBuf.begin == NULL) ? 0 : (strBuf.size - 1) }; } static inline ResultCode appendToStringBuffer( StringView suffixToAppend, StringBuffer buf, /* in,out */ size_t* bufContentLen ) { return appendToString( suffixToAppend, buf.size, buf.begin, /* in,out */ bufContentLen ); }