2 * Copyright (C) 2015 Topology LP
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 #ifndef CPPCODEC_DETAIL_HEX
25 #define CPPCODEC_DETAIL_HEX
29 #include "../data/access.hpp"
30 #include "../parse_error.hpp"
31 #include "stream_codec.hpp"
39 static inline constexpr uint8_t index_of(char c)
41 // Hex decoding is always case-insensitive (even in RFC 4648),
42 // it's only a question of what we want to encode ourselves.
43 return (c >= '0' && c <= '9') ? (c - '0')
44 : (c >= 'A' && c <= 'F') ? (c - 'A' + 10)
45 : (c >= 'a' && c <= 'f') ? (c - 'a' + 10)
46 : (c == '\0') ? 255 // stop at end of string
47 : throw symbol_error(c);
50 // RFC4648 does not specify any whitespace being allowed in base64 encodings.
51 static inline constexpr bool should_ignore(uint8_t /*index*/) { return false; }
52 static inline constexpr bool is_special_character(uint8_t index) { return index > 64; }
53 static inline constexpr bool is_eof(uint8_t index) { return index == 255; }
55 static inline constexpr bool generates_padding() { return false; }
56 // FIXME: doesn't require padding, but requires a multiple of the encoded block size (2)
57 static inline constexpr bool requires_padding() { return false; }
58 static inline constexpr bool is_padding_symbol(uint8_t /*index*/) { return false; }
61 template <typename CodecVariant>
62 class hex : public CodecVariant::template codec_impl<hex<CodecVariant>>
65 static inline constexpr uint8_t binary_block_size() { return 1; }
66 static inline constexpr uint8_t encoded_block_size() { return 2; }
68 template <typename Result, typename ResultState>
69 static void encode_block(Result& encoded, ResultState&, const uint8_t* src);
71 template <typename Result, typename ResultState>
72 static void encode_tail(Result& encoded, ResultState&, const uint8_t* src, size_t src_len);
74 template <typename Result, typename ResultState>
75 static void pad(Result&, ResultState&, size_t) { }
77 template <typename Result, typename ResultState>
78 static void decode_block(Result& decoded, ResultState&, const uint8_t* idx);
80 template <typename Result, typename ResultState>
81 static void decode_tail(Result& decoded, ResultState&, const uint8_t* idx, size_t idx_len);
85 template <typename CodecVariant>
86 template <typename Result, typename ResultState>
87 inline void hex<CodecVariant>::encode_block(Result& encoded, ResultState& state, const uint8_t* src)
89 using V = CodecVariant;
90 data::put(encoded, state, V::symbol(src[0] >> 4)); // first 4 bits
91 data::put(encoded, state, V::symbol(src[0] & 0xF)); // last 4 bits
94 template <typename CodecVariant>
95 template <typename Result, typename ResultState>
96 inline void hex<CodecVariant>::encode_tail(Result&, ResultState&, const uint8_t*, size_t)
98 // Octet-streaming hex always expands to two symbols per input byte.
99 // In order to decode odd-length hex numbers such as 0xF, 0x1a5, etc.,
100 // we'll want a place-based single number codec rather than a stream codec.
101 // That's a different beast and requires an encode_head(), rather than encode_tail().
105 template <typename CodecVariant>
106 template <typename Result, typename ResultState>
107 inline void hex<CodecVariant>::decode_block(Result& decoded, ResultState& state, const uint8_t* idx)
109 data::put(decoded, state, (uint8_t)((idx[0] << 4) | idx[1]));
112 template <typename CodecVariant>
113 template <typename Result, typename ResultState>
114 inline void hex<CodecVariant>::decode_tail(Result&, ResultState&, const uint8_t*, size_t)
116 throw invalid_input_length(
117 "odd-length hex input is not supported by the streaming octet decoder, "
118 "use a place-based number decoder instead");
121 } // namespace detail
122 } // namespace cppcodec
124 #endif // CPPCODEC_DETAIL_HEX