/* * Author: Sven Gothel * Copyright (c) 2021 Gothel Software e.K. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef JAU_STRING_UTIL_HPP_ #define JAU_STRING_UTIL_HPP_ #include #include #include #include #include #include #include #include #include #include namespace jau { /** * Returns a C++ String taken from buffer with maximum length of min(max_len, max_len). *

* The maximum length only delimits the string length and does not contain the EOS null byte. * An EOS null byte will will be added. *

*

* The source string within buffer is not required to contain an EOS null byte; *

*/ std::string get_string(const uint8_t *buffer, nsize_t const buffer_len, nsize_t const max_len) noexcept; /** trim in place */ void trimInPlace(std::string &s) noexcept; /** trim copy */ std::string trimCopy(const std::string &s) noexcept; /** // ************************************************* // ************************************************* // ************************************************* */ /** * Converts a given hexadecimal string representation into a byte vector. * * In case a non valid hexadecimal digit appears in the given string, * conversion ends and fills the byte vector up until the violation. * * @param out the byte vector sink * @param hexstr the hexadecimal string representation * @param lsbFirst low significant byte first * @param checkLeading0x if true, checks for a leading `0x` and removes it, otherwise not. * @return the length of the matching byte vector */ nsize_t hexStringBytes(std::vector& out, const std::string& hexstr, const bool lsbFirst, const bool checkLeading0x) noexcept; /** * Produce a hexadecimal string representation of the given byte values. *

* If lsbFirst is true, orders LSB left -> MSB right, usual for byte streams. Result will not have a leading `0x`.
* Otherwise orders MSB left -> LSB right, usual for readable integer values. Result will have a leading `0x`. *

* @param bytes pointer to the first byte to print, less offset * @param offset offset to bytes pointer to the first byte to print * @param length number of bytes to print * @param lsbFirst true having the least significant byte printed first (lowest addressed byte to highest), * otherwise have the most significant byte printed first (highest addressed byte to lowest). * A leading `0x` will be prepended if `lsbFirst == false`. * @param lowerCase true to use lower case hex-chars, otherwise capital letters are being used. * @return the hex-string representation of the data */ std::string bytesHexString(const uint8_t * bytes, const nsize_t offset, const nsize_t length, const bool lsbFirst, const bool lowerCase=true) noexcept; /** * Produce a hexadecimal string representation of the given byte value. * @param dest the std::string reference destination to append * @param value the byte value to represent * @param lowerCase true to use lower case hex-chars, otherwise capital letters are being used. * @return the given std::string reference for chaining */ std::string& byteHexString(std::string& dest, const uint8_t value, const bool lowerCase) noexcept; /** * Produce a lower-case hexadecimal string representation of the given pointer. * @tparam value_type a pointer type * @param v the pointer of given pointer type * @return the hex-string representation of the value * @see bytesHexString() */ template< class value_type, std::enable_if_t, bool> = true> inline std::string to_hexstring(value_type const & v) noexcept { const uint64_t v2 = reinterpret_cast(v); return bytesHexString(pointer_cast(&v2), 0, sizeof(v), false /* lsbFirst */); } /** * Produce a lower-case hexadecimal string representation of the given value with standard layout. * @tparam value_type a standard layout value type * @param v the value of given standard layout type * @return the hex-string representation of the value * @see bytesHexString() */ template< class value_type, std::enable_if_t && std::is_standard_layout_v, bool> = true> inline std::string to_hexstring(value_type const & v) noexcept { return bytesHexString(pointer_cast(&v), 0, sizeof(v), false /* lsbFirst */); } /** // ************************************************* // ************************************************* // ************************************************* */ /** * Produce a decimal string representation of an integral integer value. * @tparam T an integral integer type * @param v the integral integer value * @param separator if not 0, use as separation character, otherwise no separation characters are being used * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. * @return the string representation of the integral integer value */ template< class value_type, std::enable_if_t< std::is_integral_v, bool> = true> std::string to_decstring(const value_type& v, const char separator=',', const nsize_t width=0) noexcept { const snsize_t v_sign = jau::sign(v); const nsize_t digit10_count1 = jau::digits10(v, v_sign, true /* sign_is_digit */); const nsize_t digit10_count2 = v_sign < 0 ? digit10_count1 - 1 : digit10_count1; // less sign const nsize_t comma_count = 0 == separator ? 0 : ( digit10_count1 - 1 ) / 3; const nsize_t net_chars = digit10_count1 + comma_count; const nsize_t total_chars = std::max(width, net_chars); std::string res(total_chars, ' '); value_type n = v; nsize_t char_iter = 0; for(nsize_t digit10_iter = 0; digit10_iter < digit10_count2 /* && char_iter < total_chars */; digit10_iter++ ) { const int digit = v_sign < 0 ? invert_sign( n % 10 ) : n % 10; n /= 10; if( 0 < digit10_iter && 0 == digit10_iter % 3 ) { res[total_chars-1-(char_iter++)] = separator; } res[total_chars-1-(char_iter++)] = '0' + digit; } if( v_sign < 0 /* && char_iter < total_chars */ ) { res[total_chars-1-(char_iter++)] = '-'; } return res; } /** // ************************************************* // ************************************************* // ************************************************* */ template< class value_type, std::enable_if_t< std::is_integral_v || std::is_floating_point_v, bool> = true> inline std::string to_string(const value_type & ref) { return std::to_string(ref); } template< class value_type, std::enable_if_t && !std::is_floating_point_v && std::is_pointer_v, bool> = true> inline std::string to_string(const value_type & ref) { return to_hexstring((void*)ref); } template< class value_type, std::enable_if_t && !std::is_floating_point_v && !std::is_pointer_v && jau::has_toString_v, bool> = true> inline std::string to_string(const value_type & ref) { return ref.toString(); } template< class value_type, std::enable_if_t && !std::is_floating_point_v && !std::is_pointer_v && !jau::has_toString_v && jau::has_to_string_v, bool> = true> inline std::string to_string(const value_type & ref) { return ref.to_string(); } template< class value_type, std::enable_if_t && !std::is_floating_point_v && !std::is_pointer_v && !jau::has_toString_v && !jau::has_to_string_v && jau::has_member_of_pointer_v, bool> = true> inline std::string to_string(const value_type & ref) { return to_hexstring((void*)ref.operator->()); } template< class value_type, std::enable_if_t && !std::is_floating_point_v && !std::is_pointer_v && !jau::has_toString_v && !jau::has_to_string_v && !jau::has_member_of_pointer_v, bool> = true> inline std::string to_string(const value_type & ref) { (void)ref; return "jau::to_string not available for "+type_cue::print("unknown", TypeTraitGroup::ALL); } } // namespace jau /** \example test_intdecstring01.cpp * This C++ unit test validates the jau::to_decstring implementation */ #endif /* JAU_STRING_UTIL_HPP_ */