/* Copyright (c) Marshall Clow 2011. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ /* General problem - turn a sequence of integral types into a sequence of hexadecimal characters. - and back. TO DO: 1. these should really only work on integral types. (see the >> and << operations) 2. The 'value_type_or_char' struct is really a hack. */ #include // for std::iterator_traits #include #include #include namespace detail { template OutputIterator encode_one ( T val, OutputIterator out ) { char res [ 2 * sizeof ( T ) ]; char *p = res + sizeof ( res ); for ( std::size_t i = 0; i < sizeof ( res ); ++i, val >>= 4 ) *--p = "0123456789ABCDEF" [ val & 0x0F ]; return std::copy ( res, res + sizeof ( res ), out ); } unsigned hex_char_to_int ( char c ) { if ( c >= '0' && c <= '9' ) return c - '0'; if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10; if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10; throw std::runtime_error ( "Non-hex char"); return 0; // keep dumb compilers happy } // REVISIT: // Iterators created with std::back_inserter have a value type of 'void'. Kinda sucks. // In that case, we assume that you want to output chars. // If you don't, pass in an iterator with a real value_type. template ::value> struct value_type_or_char; template struct value_type_or_char { typedef char value_type; }; template struct value_type_or_char { typedef T value_type; }; // What can we assume here about the inputs? // is std::iterator_traits::value_type always 'char' ? // Could it be wchar_t, say? Does it matter? // We are assuming ASCII for the values - but what about the storage? template OutputIterator decode_one ( InputIterator &first, InputIterator last, OutputIterator out ) { typedef typename value_type_or_char::value_type>::value_type T; T res = 0; // Need to make sure that we get can read that many chars here. for ( std::size_t i = 0; i < 2 * sizeof ( T ); ++i ) { if ( first == last ) throw std::runtime_error ( "Not enough input" ); res <<= 4; res += hex_char_to_int ( *first++ ); } *out++ = res; return out; } } template OutputIterator hex ( InputIterator first, InputIterator last, OutputIterator out ) { for ( ; first != last; ++first ) out = detail::encode_one ( *first, out ); return out; } template OutputIterator hex ( const T *ptr, OutputIterator out ) { while ( *ptr ) out = detail::encode_one ( *ptr++, out ); return out; } template OutputIterator hex ( const Range &r, OutputIterator out ) { return hex (boost::begin(r), boost::end(r), out); } template OutputIterator unhex ( InputIterator first, InputIterator last, OutputIterator out ) { while ( first != last ) out = detail::decode_one ( first, last, out ); return out; } // See comments on decode_one - what can we assume about T? template OutputIterator unhex ( const T *ptr, OutputIterator out ) { // FIXME: if we run into the terminator while decoding, we will throw a // malformed input exception. It would be nicer to throw a 'Not enough input' // exception - but how much extra work would that require? // For now, I just make up an "end iterator" which we will never get to. while ( *ptr ) out = detail::decode_one ( ptr, ptr + sizeof(T), out ); return out; } template OutputIterator unhex ( const Range &r, OutputIterator out ) { return unhex (boost::begin(r), boost::end(r), out); } // --- examples & tests: #include #include std::string hexS ( const std::string &input ) { std::string result; result.reserve ( input.size () * 2 ); // Harry Harrison hex ( input, std::back_inserter ( result )); return result; } std::string unhexS ( const std::string &input ) { std::string result; result.reserve ( input.size () / 2 ); // Harry Harrison unhex ( input, std::back_inserter ( result )); return result; } std::string hexS ( const char *p ) { std::string result; hex ( p, std::back_inserter ( result )); return result; } std::string unhexS ( const char *p ) { std::string result; unhex ( p, std::back_inserter ( result )); return result; } int main ( int argc, char *argv [] ) { for ( int i = 1; i < argc; ++i ) { std::string arg, res1, res2; if ( argv[i][0] == '-' ) { arg.assign ( argv[i] + 1 ); // skip the '-' try { res1 = unhexS ( arg ); } catch ( const std::runtime_error &ex ) { std::cerr << "# Caught: " << ex.what () << std::endl; } res2 = hexS ( res1 ); } else { arg.assign ( argv[i] ); res1 = hexS ( arg ); // std::cout << res1 << std::endl; try { res2 = unhexS ( res1 ); } catch ( const std::runtime_error &ex ) { std::cerr << "# Caught: " << ex.what () << std::endl; } } if ( arg != res2 ) std::cerr << "# Round trip fail on '" << argv[i] << "'" << std::endl; std::cout << arg << '\t' << res1 << '\t' << res2 << std::endl; } }