Different take on pad() SFINAE, with overloads and only one enable_if<>.
[cppcodec.git] / cppcodec / detail / hex.hpp
1 /**
2  *  Copyright (C) 2015 Topology LP
3  *  All rights reserved.
4  *
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:
11  *
12  *  The above copyright notice and this permission notice shall be included in
13  *  all copies or substantial portions of the Software.
14  *
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
21  *  IN THE SOFTWARE.
22  */
23
24 #ifndef CPPCODEC_DETAIL_HEX
25 #define CPPCODEC_DETAIL_HEX
26
27 #include <stdint.h>
28
29 #include "../data/access.hpp"
30 #include "../parse_error.hpp"
31 #include "stream_codec.hpp"
32
33 namespace cppcodec {
34 namespace detail {
35
36 class hex_base
37 {
38 public:
39     static inline constexpr uint8_t index_of(char c)
40     {
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);
48     }
49
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; }
54
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; }
59 };
60
61 template <typename CodecVariant>
62 class hex : public CodecVariant::template codec_impl<hex<CodecVariant>>
63 {
64 public:
65     static inline constexpr uint8_t binary_block_size() { return 1; }
66     static inline constexpr uint8_t encoded_block_size() { return 2; }
67
68     template <typename Result, typename ResultState>
69     static void encode_block(Result& encoded, ResultState&, const uint8_t* src);
70
71     template <typename Result, typename ResultState>
72     static void encode_tail(Result& encoded, ResultState&, const uint8_t* src, size_t src_len);
73
74     template <typename Result, typename ResultState>
75     static void pad(Result&, ResultState&, size_t) { }
76
77     template <typename Result, typename ResultState>
78     static void decode_block(Result& decoded, ResultState&, const uint8_t* idx);
79
80     template <typename Result, typename ResultState>
81     static void decode_tail(Result& decoded, ResultState&, const uint8_t* idx, size_t idx_len);
82 };
83
84
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)
88 {
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
92 }
93
94 template <typename CodecVariant>
95 template <typename Result, typename ResultState>
96 inline void hex<CodecVariant>::encode_tail(Result&, ResultState&, const uint8_t*, size_t)
97 {
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().
102     abort();
103 }
104
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)
108 {
109     data::put(decoded, state, (uint8_t)((idx[0] << 4) | idx[1]));
110 }
111
112 template <typename CodecVariant>
113 template <typename Result, typename ResultState>
114 inline void hex<CodecVariant>::decode_tail(Result&, ResultState&, const uint8_t*, size_t)
115 {
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");
119 }
120
121 } // namespace detail
122 } // namespace cppcodec
123
124 #endif // CPPCODEC_DETAIL_HEX