Skip to content

Commit 1af2569

Browse files
committed
Add hex/unhex algorithms suggested by Olaf
[SVN r77138]
1 parent 49668cf commit 1af2569

File tree

6 files changed

+722
-0
lines changed

6 files changed

+722
-0
lines changed

include/boost/algorithm/hex.hpp

+278
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
Copyright (c) Marshall Clow 2011-2012.
3+
4+
Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
7+
Thanks to Nevin for his comments/help.
8+
*/
9+
10+
/*
11+
General problem - turn a sequence of integral types into a sequence of hexadecimal characters.
12+
- and back.
13+
14+
TO DO:
15+
1. these should really only work on integral types. (see the >> and << operations)
16+
-- this is done, I think.
17+
2. The 'value_type_or_char' struct is really a hack.
18+
-- but it's a better hack now that it works with back_insert_iterators
19+
*/
20+
21+
/// \file hex.hpp
22+
/// \brief Convert sequence of integral types into a sequence of hexadecimal
23+
/// characters and back. Based on the MySQL functions HEX and UNHEX
24+
/// \author Marshall Clow
25+
26+
#ifndef BOOST_ALGORITHM_HEXHPP
27+
#define BOOST_ALGORITHM_HEXHPP
28+
29+
#include <iterator> // for std::iterator_traits
30+
#include <stdexcept>
31+
32+
#include <boost/range/begin.hpp>
33+
#include <boost/range/end.hpp>
34+
#include <boost/exception/all.hpp>
35+
36+
#include <boost/utility/enable_if.hpp>
37+
#include <boost/type_traits/is_integral.hpp>
38+
39+
40+
namespace boost { namespace algorithm {
41+
42+
/*!
43+
\struct hex_decode_error
44+
\brief Base exception class for all hex decoding errors
45+
46+
\struct non_hex_input
47+
\brief Thrown when a non-hex value (0-9, A-F) encountered when decoding.
48+
Contains the offending character
49+
50+
\struct not_enough_input
51+
\brief Thrown when the input sequence unexpectedly ends
52+
53+
*/
54+
struct hex_decode_error: virtual boost::exception, virtual std::exception {};
55+
struct not_enough_input : public hex_decode_error {};
56+
struct non_hex_input : public hex_decode_error {
57+
non_hex_input ( char ch ) : bad_char ( ch ) {}
58+
char bad_char;
59+
private:
60+
non_hex_input (); // don't allow creation w/o a char
61+
};
62+
63+
namespace detail {
64+
/// \cond DOXYGEN_HIDE
65+
66+
template <typename T, typename OutputIterator>
67+
OutputIterator encode_one ( T val, OutputIterator out ) {
68+
const std::size_t num_hex_digits = 2 * sizeof ( T );
69+
char res [ num_hex_digits ];
70+
char *p = res + num_hex_digits;
71+
for ( std::size_t i = 0; i < num_hex_digits; ++i, val >>= 4 )
72+
*--p = "0123456789ABCDEF" [ val & 0x0F ];
73+
return std::copy ( res, res + num_hex_digits, out );
74+
}
75+
76+
unsigned hex_char_to_int ( char c ) {
77+
if ( c >= '0' && c <= '9' ) return c - '0';
78+
if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10;
79+
if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
80+
BOOST_THROW_EXCEPTION (non_hex_input (c));
81+
return 0; // keep dumb compilers happy
82+
}
83+
84+
85+
// My own iterator_traits class.
86+
// It is here so that I can "reach inside" some kinds of output iterators
87+
// and get the type to write.
88+
template <typename Iterator>
89+
struct hex_iterator_traits {
90+
typedef typename std::iterator_traits<Iterator>::value_type value_type;
91+
};
92+
93+
template<typename Container>
94+
struct hex_iterator_traits< std::back_insert_iterator<Container> > {
95+
typedef typename Container::value_type value_type;
96+
};
97+
98+
template<typename Container>
99+
struct hex_iterator_traits< std::front_insert_iterator<Container> > {
100+
typedef typename Container::value_type value_type;
101+
};
102+
103+
template<typename Container>
104+
struct hex_iterator_traits< std::insert_iterator<Container> > {
105+
typedef typename Container::value_type value_type;
106+
};
107+
108+
// ostream_iterators have three template parameters.
109+
// The first one is the output type, the second one is the character type of
110+
// the underlying stream, the third is the character traits.
111+
// We only care about the first one.
112+
template<typename T, typename charType, typename traits>
113+
struct hex_iterator_traits< std::ostream_iterator<T, charType, traits> > {
114+
typedef T value_type;
115+
};
116+
117+
// Output Iterators have a value type of 'void'. Kinda sucks.
118+
// We special case some output iterators, but we can't enumerate them all.
119+
// If we can't figure it out, we assume that you want to output chars.
120+
// If you don't, pass in an iterator with a real value_type.
121+
template <typename T> struct value_type_or_char { typedef T value_type; };
122+
template <> struct value_type_or_char<void> { typedef char value_type; };
123+
124+
// All in one step
125+
template <typename Iterator>
126+
struct iterator_value_type {
127+
// typedef typename value_type_or_char<typename hex_iterator_traits<Iterator>::value_type>::value_type value_type;
128+
typedef typename hex_iterator_traits<Iterator>::value_type value_type;
129+
};
130+
131+
// What can we assume here about the inputs?
132+
// is std::iterator_traits<InputIterator>::value_type always 'char' ?
133+
// Could it be wchar_t, say? Does it matter?
134+
// We are assuming ASCII for the values - but what about the storage?
135+
template <typename InputIterator, typename OutputIterator>
136+
typename boost::enable_if<boost::is_integral<typename iterator_value_type<OutputIterator>::value_type>, OutputIterator>::type
137+
decode_one ( InputIterator &first, InputIterator last, OutputIterator out ) {
138+
typedef typename iterator_value_type<OutputIterator>::value_type T;
139+
T res (0);
140+
141+
// Need to make sure that we get can read that many chars here.
142+
for ( std::size_t i = 0; i < 2 * sizeof ( T ); ++i, ++first ) {
143+
if ( first == last )
144+
BOOST_THROW_EXCEPTION (not_enough_input ());
145+
res = ( 16 * res ) + hex_char_to_int (static_cast<char> (*first));
146+
}
147+
148+
*out = res;
149+
return ++out;
150+
}
151+
/// \endcond
152+
}
153+
154+
155+
/// \fn hex ( InputIterator first, InputIterator last, OutputIterator out )
156+
/// \brief Converts a sequence of integral types into a hexadecimal sequence of characters.
157+
///
158+
/// \param first The start of the input sequence
159+
/// \param last One past the end of the input sequence
160+
/// \param out An output iterator to the results into
161+
/// \note Based on the MySQL function of the same name
162+
template <typename InputIterator, typename OutputIterator>
163+
typename boost::enable_if<boost::is_integral<typename detail::hex_iterator_traits<InputIterator>::value_type>, OutputIterator>::type
164+
hex ( InputIterator first, InputIterator last, OutputIterator out ) {
165+
for ( ; first != last; ++first )
166+
out = detail::encode_one ( *first, out );
167+
return out;
168+
}
169+
170+
171+
/// \fn hex ( const T *ptr, OutputIterator out )
172+
/// \brief Converts a sequence of integral types into a hexadecimal sequence of characters.
173+
///
174+
/// \param ptr A pointer to a 0-terminated sequence of data.
175+
/// \param out An output iterator to the results into
176+
/// \return The updated output iterator
177+
/// \note Based on the MySQL function of the same name
178+
template <typename T, typename OutputIterator>
179+
typename boost::enable_if<boost::is_integral<T>, OutputIterator>::type
180+
hex ( const T *ptr, OutputIterator out ) {
181+
while ( *ptr )
182+
out = detail::encode_one ( *ptr++, out );
183+
return out;
184+
}
185+
186+
/// \fn hex ( const Range &r, OutputIterator out )
187+
/// \brief Converts a sequence of integral types into a hexadecimal sequence of characters.
188+
///
189+
/// \param r The input range
190+
/// \param out An output iterator to the results into
191+
/// \return The updated output iterator
192+
/// \note Based on the MySQL function of the same name
193+
template <typename Range, typename OutputIterator>
194+
typename boost::enable_if<boost::is_integral<typename detail::hex_iterator_traits<typename Range::iterator>::value_type>, OutputIterator>::type
195+
hex ( const Range &r, OutputIterator out ) {
196+
return hex (boost::begin(r), boost::end(r), out);
197+
}
198+
199+
200+
/// \fn unhex ( InputIterator first, InputIterator last, OutputIterator out )
201+
/// \brief Converts a sequence of hexadecimal characters into a sequence of integers.
202+
///
203+
/// \param first The start of the input sequence
204+
/// \param last One past the end of the input sequence
205+
/// \param out An output iterator to the results into
206+
/// \return The updated output iterator
207+
/// \note Based on the MySQL function of the same name
208+
template <typename InputIterator, typename OutputIterator>
209+
OutputIterator unhex ( InputIterator first, InputIterator last, OutputIterator out ) {
210+
while ( first != last )
211+
out = detail::decode_one ( first, last, out );
212+
return out;
213+
}
214+
215+
216+
/// \fn unhex ( const T *ptr, OutputIterator out )
217+
/// \brief Converts a sequence of hexadecimal characters into a sequence of integers.
218+
///
219+
/// \param ptr A pointer to a null-terminated input sequence.
220+
/// \param out An output iterator to the results into
221+
/// \return The updated output iterator
222+
/// \note Based on the MySQL function of the same name
223+
template <typename T, typename OutputIterator>
224+
OutputIterator unhex ( const T *ptr, OutputIterator out ) {
225+
typedef typename detail::iterator_value_type<OutputIterator>::value_type OutputType;
226+
// If we run into the terminator while decoding, we will throw a
227+
// malformed input exception. It would be nicer to throw a 'Not enough input'
228+
// exception - but how much extra work would that require?
229+
// I just make up an "end iterator" which we will never get to -
230+
// two Ts per byte of the output type.
231+
while ( *ptr )
232+
out = detail::decode_one ( ptr, ptr + 2 * sizeof(OutputType), out );
233+
return out;
234+
}
235+
236+
237+
/// \fn unhex ( const Range &r, OutputIterator out )
238+
/// \brief Converts a sequence of hexadecimal characters into a sequence of integers.
239+
///
240+
/// \param r The input range
241+
/// \param out An output iterator to the results into
242+
/// \return The updated output iterator
243+
/// \note Based on the MySQL function of the same name
244+
template <typename Range, typename OutputIterator>
245+
OutputIterator unhex ( const Range &r, OutputIterator out ) {
246+
return unhex (boost::begin(r), boost::end(r), out);
247+
}
248+
249+
250+
/// \fn hex ( const String &input )
251+
/// \brief Converts a sequence of integral types into a hexadecimal sequence of characters.
252+
///
253+
/// \param input A container to be converted
254+
/// \return A container with the encoded text
255+
template<typename String>
256+
String hex ( const String &input ) {
257+
String output;
258+
output.reserve (input.size () * (2 * sizeof (typename String::value_type)));
259+
(void) hex (input, std::back_inserter (output));
260+
return output;
261+
}
262+
263+
/// \fn unhex ( const String &input )
264+
/// \brief Converts a sequence of hexadecimal characters into a sequence of integers.
265+
///
266+
/// \param input A container to be converted
267+
/// \return A container with the decoded text
268+
template<typename String>
269+
String unhex ( const String &input ) {
270+
String output;
271+
output.reserve (input.size () / (2 * sizeof (typename String::value_type)));
272+
(void) unhex (input, std::back_inserter (output));
273+
return output;
274+
}
275+
276+
}}
277+
278+
#endif // BOOST_ALGORITHM_HEXHPP

test/Jamfile.v2

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ import testing ;
3939
[ run is_partitioned_test1.cpp : : : : is_partitioned_test1 ]
4040
[ run partition_copy_test1.cpp : : : : partition_copy_test1 ]
4141

42+
# Hex tests
43+
[ run hex_test1.cpp : : : : hex_test1 ]
44+
[ run hex_test2.cpp : : : : hex_test2 ]
45+
[ run hex_test3.cpp : : : : hex_test3 ]
46+
[ compile-fail hex_fail1.cpp ]
4247
;
4348
}
4449

test/hex_fail1.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright (c) Marshall Clow 2011-2012.
3+
4+
Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
7+
For more information, see http://www.boost.org
8+
*/
9+
10+
#include <boost/config.hpp>
11+
#include <boost/algorithm/hex.hpp>
12+
#include <boost/test/included/test_exec_monitor.hpp>
13+
14+
#include <string>
15+
#include <iostream>
16+
#include <vector>
17+
18+
// should not compile: vector is not an integral type
19+
int test_main( int , char* [] )
20+
{
21+
std::vector<float> v;
22+
std::string out;
23+
boost::algorithm::unhex ( out, std::back_inserter(v));
24+
return 0;
25+
}

0 commit comments

Comments
 (0)