-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
hexarray & hexdump #12241
Merged
Merged
hexarray & hexdump #12241
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
19f551a
add rsutils::string::hexdump
maloel a443fa7
add hexarray
maloel a1ae6bb
add test-hexarray
maloel 5073852
revise 'hwm' with hexarray
maloel 94bfb7d
clarify big-endian handling, especially with {0#} notation
maloel 697e82a
clarify single-value hexdump to be more usable
maloel b9e6036
sample usage in d500
maloel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// License: Apache 2.0. See LICENSE file in root directory. | ||
// Copyright(c) 2023 Intel Corporation. All Rights Reserved. | ||
|
||
#pragma once | ||
|
||
#include <nlohmann/json_fwd.hpp> | ||
|
||
#include <vector> | ||
#include <string> | ||
#include <cstdint> | ||
#include <iosfwd> | ||
|
||
|
||
namespace rsutils { | ||
namespace string { | ||
|
||
|
||
class slice; | ||
|
||
|
||
// If a 'bytearray' is an array of bytes, then an array of hex representations is a 'hexarray'. | ||
// | ||
// An object wrapping a vector of bytes, for writing to/reading from either ostream or json. With ostream, usage is | ||
// standard via operator<<. For json, the type should be implicitly compatible with the various json::get<> variants | ||
// through the to_json() and from_json() functions provided below. | ||
// | ||
// The representation is like a hexdump, but here we go in both directions: either to-hex or from-hex. | ||
// | ||
// Since we represent data that needs to be returned, we cannot simply refer to an existing set of bytes (like hexdump, | ||
// for example) and instead must own it. Construction is always through moves to avoid implicit data copies. Static | ||
// functions for to_string() and to_stream() are provided to deal with such data that cannot be moved. | ||
// | ||
class hexarray | ||
{ | ||
typedef std::vector< uint8_t > bytes; | ||
|
||
private: | ||
bytes _bytes; | ||
|
||
friend void from_json( nlohmann::json const &, hexarray & ); | ||
|
||
public: | ||
hexarray() = default; | ||
hexarray( hexarray && hexa ) = default; | ||
hexarray( bytes && bytearray ) | ||
: _bytes( std::move( bytearray ) ) | ||
{ | ||
} | ||
|
||
// Only lower-case hex is understood: any non-lower-case hex characters will throw | ||
static hexarray from_string( slice const & ); | ||
|
||
hexarray & operator=( hexarray && ) = default; | ||
|
||
bytes detach() { return std::move( _bytes ); } | ||
bytes const & get_bytes() const { return _bytes; } | ||
|
||
bool empty() const { return _bytes.empty(); } | ||
void clear() { _bytes.clear(); } | ||
|
||
std::string to_string() const { return to_string( _bytes ); } | ||
static std::string to_string( bytes const & ); | ||
|
||
static std::ostream & to_stream( std::ostream &, bytes const & ); | ||
}; | ||
|
||
|
||
inline std::ostream & operator<<( std::ostream & os, hexarray const & hexa ) | ||
{ | ||
return hexa.to_stream( os, hexa.get_bytes() ); | ||
} | ||
|
||
|
||
// Allow j["key"] = hexarray( bytes ); | ||
void to_json( nlohmann::json &, const hexarray & ); | ||
// Allow j.get< hexarray >(); | ||
void from_json( nlohmann::json const &, hexarray & ); | ||
// See https://github.com/nlohmann/json#arbitrary-types-conversions | ||
|
||
|
||
} // namespace string | ||
} // namespace rsutils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// License: Apache 2.0. See LICENSE file in root directory. | ||
// Copyright(c) 2023 Intel Corporation. All Rights Reserved. | ||
|
||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <iosfwd> | ||
#include <type_traits> | ||
|
||
|
||
namespace rsutils { | ||
namespace string { | ||
|
||
|
||
// Allow easy dumping of memory contents into a stream, so easily stringified | ||
// | ||
// E.g.: | ||
// std::ostringstream ss; | ||
// ss << hexdump( this ); // output pointer to this object as a hex value | ||
// | ||
struct hexdump | ||
{ | ||
uint8_t const * const _data; | ||
size_t const _cb; | ||
|
||
size_t _max_bytes = 0; // no more than this number of bytes (extra will print "..."; 0=no max) | ||
size_t _gap = 0; // pad with spaces every <gap> bytes (0=no gap) | ||
char _gap_character = ' '; | ||
bool _big_endian = false; | ||
|
||
// Manual ctor for custom memory layout | ||
hexdump( uint8_t const * data, size_t len ) | ||
: _data( data ) | ||
, _cb( len ) | ||
{ | ||
} | ||
|
||
// Simplify usage with any type buffer, to avoid having to cast on the outside | ||
template< class T > | ||
hexdump( T const * pt, size_t len ) | ||
: hexdump( reinterpret_cast< uint8_t const * >( pt ), sizeof( T ) ) | ||
{ | ||
} | ||
|
||
|
||
// Non-buffer usage would take the form of: | ||
// hexdump( t ) // no length | ||
// These are easy to translate into a buffer. | ||
// However, if 't' is some human-legible type (integral, pointer), we make it so the values show big-endian: | ||
// if i=0x01020304 | ||
// hexdump( &i, sizeof( i )) would give little-endian 04030201 <- not readable | ||
// so hexdump( i ) should give 01020304 | ||
|
||
template< typename T, typename = void > | ||
struct show_in_big_endian | ||
{ | ||
static const bool value = false; | ||
}; | ||
template< typename T > | ||
struct show_in_big_endian< T, std::enable_if_t< std::is_integral< T >::value || std::is_pointer< T >::value > > | ||
{ | ||
static const bool value = true; | ||
}; | ||
|
||
// Single-value dump: BIG-endian if integral or pointer; otherwise little-endian | ||
template< class T > | ||
hexdump( T const & t ) | ||
: hexdump( &t, sizeof( T ) ) | ||
{ | ||
_big_endian = show_in_big_endian< T >::value; | ||
} | ||
|
||
|
||
// Allow no more than this number of bytes out. If 0 (default), there is no maximum. | ||
// Anything past the max bytes will cause a '...' to be appended. | ||
// | ||
// E.g.: | ||
// ss << hexdump( buffer, 100 ).max_bytes( 2 ); | ||
// Will stream: | ||
// 0001... | ||
// | ||
hexdump & max_bytes( size_t cb ) | ||
{ | ||
_max_bytes = cb; | ||
return *this; | ||
} | ||
|
||
// Automatically insert a gap character (default ' ') every <gap> bytes, or none if 0. | ||
// | ||
// E.g.: | ||
// ss << hexdump( int32_var ).gap( 2 ); | ||
// Will stream: | ||
// 0001 0203 | ||
// | ||
hexdump & gap( size_t cb, char character = ' ' ) | ||
{ | ||
_gap = cb; | ||
_gap_character = character; | ||
return *this; | ||
} | ||
|
||
struct _format | ||
{ | ||
hexdump & _h; | ||
char const * _fmt; | ||
}; | ||
|
||
// Allow custom formatting of the memory buffer. | ||
// | ||
// E.g.: | ||
// ss << hexdump( vec.data(), vec.size() ).format( "two: {2} four: {4} two: {2}" ); | ||
// Will stream the first 6 bytes: | ||
// two: 0102 four: 03040506 two: 0708 | ||
// | ||
// Syntax: | ||
// {#} Output # bytes consecutively | ||
// {0#} Output # bytes, but with leading 0's removed (implies big-endian) | ||
// {-[0]#} Output # bytes, but in reverse (big-endian) order | ||
// {#i} Interpret # bytes as a signed integral value, and output decimal value | ||
// {#u} Interpret # bytes as an unsigned integral value, and output decimal value | ||
// {#f} Interpret # bytes as a floating point value (4f=float; 8f=double) | ||
// {+#} Skip # bytes | ||
// \{ Output '{' (or any character following the '\') | ||
// {repeat:#} Start a repeat sequence that repeats # times (default is 0: as many times as buffer allows) | ||
// {:} End the current sequence and repeat it as necessary | ||
// {:?} Same, but add '...' if there are still more bytes | ||
// {:?<text>} Same, but add <text> if there are still more bytes | ||
// E.g., a simple implementation of a gap: | ||
// ss << hexdump(...).format( "data[{2}{repeat:} {2}{:?}]" ); | ||
// | ||
inline _format format( char const * fmt ) | ||
{ | ||
return _format{ *this, fmt }; | ||
} | ||
}; | ||
|
||
std::ostream & operator<<( std::ostream & os, hexdump const & ); | ||
std::ostream & operator<<( std::ostream & os, hexdump::_format const & ); | ||
|
||
|
||
} // namespace string | ||
} // namespace rsutils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// License: Apache 2.0. See LICENSE file in root directory. | ||
// Copyright(c) 2023 Intel Corporation. All Rights Reserved. | ||
|
||
#include <rsutils/string/hexarray.h> | ||
#include <rsutils/string/from.h> | ||
#include <rsutils/string/slice.h> | ||
#include <rsutils/string/hexdump.h> | ||
|
||
#include <nlohmann/json.hpp> | ||
|
||
|
||
namespace rsutils { | ||
namespace string { | ||
|
||
|
||
/*static*/ std::string hexarray::to_string( bytes const & bytearray ) | ||
{ | ||
return rsutils::string::from() << hexdump( bytearray.data(), bytearray.size() ); | ||
} | ||
|
||
|
||
/*static*/ std::ostream & hexarray::to_stream( std::ostream & os, bytes const & bytearray ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
{ | ||
return os << hexdump( bytearray.data(), bytearray.size() ); | ||
} | ||
|
||
|
||
void to_json( nlohmann::json & j, const hexarray & hexa ) | ||
{ | ||
j = hexa.to_string(); | ||
} | ||
|
||
|
||
void from_json( nlohmann::json const & j, hexarray & hexa ) | ||
{ | ||
if( j.is_array() ) | ||
{ | ||
hexa._bytes.resize( j.size() ); | ||
std::transform( j.begin(), j.end(), std::begin( hexa._bytes ), | ||
[]( nlohmann::json const & elem ) | ||
{ | ||
if( ! elem.is_number_unsigned() ) | ||
throw nlohmann::json::type_error::create( 302, "array value not an unsigned integer", &elem ); | ||
auto v = elem.template get< uint64_t >(); | ||
if( v > 255 ) | ||
throw nlohmann::json::out_of_range::create( 401, "array value out of range", &elem ); | ||
return uint8_t( v ); | ||
} ); | ||
} | ||
else if( j.is_string() ) | ||
hexa = hexarray::from_string( j.get< std::string >() ); | ||
else | ||
throw nlohmann::json::type_error::create( 317, "hexarray should be a string", &j ); | ||
} | ||
|
||
|
||
hexarray hexarray::from_string( slice const & s ) | ||
{ | ||
if( s.length() % 2 != 0 ) | ||
throw std::runtime_error( "odd length" ); | ||
char const * pch = s.begin(); | ||
hexarray hexa; | ||
hexa._bytes.resize( s.length() / 2 ); | ||
uint8_t * pb = hexa._bytes.data(); | ||
while( pch < s.end() ) | ||
{ | ||
if( *pch >= '0' && *pch <= '9' ) | ||
*pb = ( *pch - '0' ) << 4; | ||
else if( *pch >= 'a' && *pch <= 'f' ) | ||
*pb = ( *pch - 'a' + 10 ) << 4; | ||
else | ||
throw std::runtime_error( "invalid character" ); | ||
|
||
++pch; | ||
if( *pch >= '0' && *pch <= '9' ) | ||
*pb += ( *pch - '0' ); | ||
else if( *pch >= 'a' && *pch <= 'f' ) | ||
*pb += ( *pch - 'a' + 10 ); | ||
else | ||
throw std::runtime_error( "invalid character" ); | ||
++pch; | ||
|
||
++pb; | ||
} | ||
return hexa; | ||
} | ||
|
||
|
||
} // namespace string | ||
} // namespace rsutils |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about this "static" ? should be commented out or not ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put these there on purpose, since it's not always clear that you're looking at a static function member body. I find it helps me understand where I am.