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 | |
15 | /// \file hex.hpp |
16 | /// \brief Convert sequence of integral types into a sequence of hexadecimal |
17 | /// characters and back. Based on the MySQL functions HEX and UNHEX |
18 | /// \author Marshall Clow |
19 | |
20 | #ifndef BOOST_ALGORITHM_HEXHPP |
21 | #define BOOST_ALGORITHM_HEXHPP |
22 | |
23 | #include <iterator> // for std::iterator_traits |
24 | #include <stdexcept> |
25 | |
26 | #include <boost/range/begin.hpp> |
27 | #include <boost/range/end.hpp> |
28 | #include <boost/exception/all.hpp> |
29 | |
30 | #include <boost/utility/enable_if.hpp> |
31 | #include <boost/type_traits/is_integral.hpp> |
32 | |
33 | |
34 | namespace boost { namespace algorithm { |
35 | |
36 | /*! |
37 | \struct hex_decode_error |
38 | \brief Base exception class for all hex decoding errors |
39 | */ /*! |
40 | \struct non_hex_input |
41 | \brief Thrown when a non-hex value (0-9, A-F) encountered when decoding. |
42 | Contains the offending character |
43 | */ /*! |
44 | \struct not_enough_input |
45 | \brief Thrown when the input sequence unexpectedly ends |
46 | |
47 | */ |
48 | struct hex_decode_error : virtual boost::exception, virtual std::exception {}; |
49 | struct not_enough_input : virtual hex_decode_error {}; |
50 | struct non_hex_input : virtual hex_decode_error {}; |
51 | typedef boost::error_info<struct bad_char_,char> bad_char; |
52 | |
53 | namespace detail { |
54 | /// \cond DOXYGEN_HIDE |
55 | |
56 | template <typename T, typename OutputIterator> |
57 | OutputIterator encode_one ( T val, OutputIterator out ) { |
58 | const std::size_t num_hex_digits = 2 * sizeof ( T ); |
59 | char res [ num_hex_digits ]; |
60 | char *p = res + num_hex_digits; |
61 | for ( std::size_t i = 0; i < num_hex_digits; ++i, val >>= 4 ) |
62 | *--p = "0123456789ABCDEF" [ val & 0x0F ]; |
63 | return std::copy ( res, res + num_hex_digits, out ); |
64 | } |
65 | |
66 | template <typename T> |
67 | unsigned char hex_char_to_int ( T val ) { |
68 | char c = static_cast<char> ( val ); |
69 | unsigned retval = 0; |
70 | if ( c >= '0' && c <= '9' ) retval = c - '0'; |
71 | else if ( c >= 'A' && c <= 'F' ) retval = c - 'A' + 10; |
72 | else if ( c >= 'a' && c <= 'f' ) retval = c - 'a' + 10; |
73 | else BOOST_THROW_EXCEPTION (non_hex_input() << bad_char (c)); |
74 | return retval; |
75 | } |
76 | |
77 | // My own iterator_traits class. |
78 | // It is here so that I can "reach inside" some kinds of output iterators |
79 | // and get the type to write. |
80 | template <typename Iterator> |
81 | struct hex_iterator_traits { |
82 | typedef typename std::iterator_traits<Iterator>::value_type value_type; |
83 | }; |
84 | |
85 | template<typename Container> |
86 | struct hex_iterator_traits< std::back_insert_iterator<Container> > { |
87 | typedef typename Container::value_type value_type; |
88 | }; |
89 | |
90 | template<typename Container> |
91 | struct hex_iterator_traits< std::front_insert_iterator<Container> > { |
92 | typedef typename Container::value_type value_type; |
93 | }; |
94 | |
95 | template<typename Container> |
96 | struct hex_iterator_traits< std::insert_iterator<Container> > { |
97 | typedef typename Container::value_type value_type; |
98 | }; |
99 | |
100 | // ostream_iterators have three template parameters. |
101 | // The first one is the output type, the second one is the character type of |
102 | // the underlying stream, the third is the character traits. |
103 | // We only care about the first one. |
104 | template<typename T, typename charType, typename traits> |
105 | struct hex_iterator_traits< std::ostream_iterator<T, charType, traits> > { |
106 | typedef T value_type; |
107 | }; |
108 | |
109 | template <typename Iterator> |
110 | bool iter_end ( Iterator current, Iterator last ) { return current == last; } |
111 | |
112 | template <typename T> |
113 | bool ptr_end ( const T* ptr, const T* /*end*/ ) { return *ptr == '\0'; } |
114 | |
115 | // What can we assume here about the inputs? |
116 | // is std::iterator_traits<InputIterator>::value_type always 'char' ? |
117 | // Could it be wchar_t, say? Does it matter? |
118 | // We are assuming ASCII for the values - but what about the storage? |
119 | template <typename InputIterator, typename OutputIterator, typename EndPred> |
120 | typename boost::enable_if<boost::is_integral<typename hex_iterator_traits<OutputIterator>::value_type>, OutputIterator>::type |
121 | decode_one ( InputIterator &first, InputIterator last, OutputIterator out, EndPred pred ) { |
122 | typedef typename hex_iterator_traits<OutputIterator>::value_type T; |
123 | T res (0); |
124 | |
125 | // Need to make sure that we get can read that many chars here. |
126 | for ( std::size_t i = 0; i < 2 * sizeof ( T ); ++i, ++first ) { |
127 | if ( pred ( first, last )) |
128 | BOOST_THROW_EXCEPTION (not_enough_input ()); |
129 | res = ( 16 * res ) + hex_char_to_int (*first); |
130 | } |
131 | |
132 | *out = res; |
133 | return ++out; |
134 | } |
135 | /// \endcond |
136 | } |
137 | |
138 | |
139 | /// \fn hex ( InputIterator first, InputIterator last, OutputIterator out ) |
140 | /// \brief Converts a sequence of integral types into a hexadecimal sequence of characters. |
141 | /// |
142 | /// \param first The start of the input sequence |
143 | /// \param last One past the end of the input sequence |
144 | /// \param out An output iterator to the results into |
145 | /// \return The updated output iterator |
146 | /// \note Based on the MySQL function of the same name |
147 | template <typename InputIterator, typename OutputIterator> |
148 | typename boost::enable_if<boost::is_integral<typename detail::hex_iterator_traits<InputIterator>::value_type>, OutputIterator>::type |
149 | hex ( InputIterator first, InputIterator last, OutputIterator out ) { |
150 | for ( ; first != last; ++first ) |
151 | out = detail::encode_one ( *first, out ); |
152 | return out; |
153 | } |
154 | |
155 | |
156 | /// \fn hex ( const T *ptr, OutputIterator out ) |
157 | /// \brief Converts a sequence of integral types into a hexadecimal sequence of characters. |
158 | /// |
159 | /// \param ptr A pointer to a 0-terminated sequence of data. |
160 | /// \param out An output iterator to the results into |
161 | /// \return The updated output iterator |
162 | /// \note Based on the MySQL function of the same name |
163 | template <typename T, typename OutputIterator> |
164 | typename boost::enable_if<boost::is_integral<T>, OutputIterator>::type |
165 | hex ( const T *ptr, OutputIterator out ) { |
166 | while ( *ptr ) |
167 | out = detail::encode_one ( *ptr++, out ); |
168 | return out; |
169 | } |
170 | |
171 | /// \fn hex ( const Range &r, OutputIterator out ) |
172 | /// \brief Converts a sequence of integral types into a hexadecimal sequence of characters. |
173 | /// |
174 | /// \param r The input range |
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 Range, typename OutputIterator> |
179 | typename boost::enable_if<boost::is_integral<typename detail::hex_iterator_traits<typename Range::iterator>::value_type>, OutputIterator>::type |
180 | hex ( const Range &r, OutputIterator out ) { |
181 | return hex (boost::begin(r), boost::end(r), out); |
182 | } |
183 | |
184 | |
185 | /// \fn unhex ( InputIterator first, InputIterator last, OutputIterator out ) |
186 | /// \brief Converts a sequence of hexadecimal characters into a sequence of integers. |
187 | /// |
188 | /// \param first The start of the input sequence |
189 | /// \param last One past the end of the input sequence |
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 InputIterator, typename OutputIterator> |
194 | OutputIterator unhex ( InputIterator first, InputIterator last, OutputIterator out ) { |
195 | while ( first != last ) |
196 | out = detail::decode_one ( first, last, out, detail::iter_end<InputIterator> ); |
197 | return out; |
198 | } |
199 | |
200 | |
201 | /// \fn unhex ( const T *ptr, OutputIterator out ) |
202 | /// \brief Converts a sequence of hexadecimal characters into a sequence of integers. |
203 | /// |
204 | /// \param ptr A pointer to a null-terminated 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 T, typename OutputIterator> |
209 | OutputIterator unhex ( const T *ptr, OutputIterator out ) { |
210 | // If we run into the terminator while decoding, we will throw a |
211 | // malformed input exception. It would be nicer to throw a 'Not enough input' |
212 | // exception - but how much extra work would that require? |
213 | while ( *ptr ) |
214 | out = detail::decode_one ( ptr, (const T *) NULL, out, detail::ptr_end<T> ); |
215 | return out; |
216 | } |
217 | |
218 | |
219 | /// \fn OutputIterator unhex ( const Range &r, OutputIterator out ) |
220 | /// \brief Converts a sequence of hexadecimal characters into a sequence of integers. |
221 | /// |
222 | /// \param r The input range |
223 | /// \param out An output iterator to the results into |
224 | /// \return The updated output iterator |
225 | /// \note Based on the MySQL function of the same name |
226 | template <typename Range, typename OutputIterator> |
227 | OutputIterator unhex ( const Range &r, OutputIterator out ) { |
228 | return unhex (boost::begin(r), boost::end(r), out); |
229 | } |
230 | |
231 | |
232 | /// \fn String hex ( const String &input ) |
233 | /// \brief Converts a sequence of integral types into a hexadecimal sequence of characters. |
234 | /// |
235 | /// \param input A container to be converted |
236 | /// \return A container with the encoded text |
237 | template<typename String> |
238 | String hex ( const String &input ) { |
239 | String output; |
240 | output.reserve (input.size () * (2 * sizeof (typename String::value_type))); |
241 | (void) hex (input, std::back_inserter (output)); |
242 | return output; |
243 | } |
244 | |
245 | /// \fn String unhex ( const String &input ) |
246 | /// \brief Converts a sequence of hexadecimal characters into a sequence of characters. |
247 | /// |
248 | /// \param input A container to be converted |
249 | /// \return A container with the decoded text |
250 | template<typename String> |
251 | String unhex ( const String &input ) { |
252 | String output; |
253 | output.reserve (input.size () / (2 * sizeof (typename String::value_type))); |
254 | (void) unhex (input, std::back_inserter (output)); |
255 | return output; |
256 | } |
257 | |
258 | }} |
259 | |
260 | #endif // BOOST_ALGORITHM_HEXHPP |
261 | |