61802e80bc14fccaf5d1fe947e1ad799d76cfa31
[cppcodec.git] / cppcodec / data / access.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_DATA_ACCESS
25 #define CPPCODEC_DETAIL_DATA_ACCESS
26
27 #include <stdint.h> // for size_t
28
29 namespace cppcodec {
30 namespace data {
31
32 // This file contains a number of templated data accessors that can be
33 // implemented in the cppcodec::data namespace for types that don't fulfill
34 // the default type requirements:
35 // For result types: init(Result&, ResultState&, size_t capacity),
36 //     put(Result&, ResultState&, char), finish(Result&, State&)
37 // For const (read-only) types: char_data(const T&)
38 // For both const and result types: size(const T&)
39
40 template <typename T> inline size_t size(const T& t) { return t.size(); }
41 template <typename T, size_t N> inline constexpr size_t size(const T (&t)[N]) noexcept {
42     return N * sizeof(t[0]);
43 }
44
45 class general_t {};
46 class specific_t : public general_t {};
47
48 class empty_result_state {
49     template <typename Result> inline void size(const Result& result) { return size(result); }
50 };
51
52 // SFINAE: Generic fallback in case no specific state function applies.
53 template <typename Result>
54 inline empty_result_state create_state(Result&, general_t) { return empty_result_state(); }
55
56 //
57 // Generic templates for containers: Use these init()/put()/finish()
58 // implementations if no specialization was found.
59 //
60
61 template <typename Result>
62 inline void init(Result& result, empty_result_state&, size_t capacity)
63 {
64     result.resize(0);
65     result.reserve(capacity);
66 }
67
68 template <typename Result>
69 inline void finish(Result&, empty_result_state&)
70 {
71     // Default is to push_back(), which already increases the size.
72 }
73
74 // For the put() default implementation, we try calling push_back() with either uint8_t or char,
75 // whichever compiles. Scary-fancy template magic from http://stackoverflow.com/a/1386390.
76 namespace fallback {
77     struct flag { char c[2]; }; // sizeof > 1
78     flag put_uint8(...);
79
80     int operator,(flag, flag);
81     template <typename T> void operator,(flag, T&); // map everything else to void
82     char operator,(int, flag); // sizeof 1
83 }
84
85 template <typename Result> inline void put_uint8(Result& result, uint8_t c) { result.push_back(c); }
86
87 template <bool> struct put_impl;
88 template <> struct put_impl<true> { // put_uint8() available
89     template<typename Result> static void put(Result& result, uint8_t c) { put_uint8(result, c); }
90 };
91 template <> struct put_impl<false> { // put_uint8() not available
92     template<typename Result> static void put(Result& result, uint8_t c) {
93         result.push_back(static_cast<char>(c));
94     }
95 };
96
97 template <typename Result> inline void put(Result& result, empty_result_state&, uint8_t c)
98 {
99     using namespace fallback;
100     put_impl<sizeof(fallback::flag(), put_uint8(result, c), fallback::flag()) != 1>::put(result, c);
101 }
102
103 //
104 // Specialization for container types with direct mutable data access.
105 // The expected way to specialize is to subclass empty_result_state and
106 // return an instance of it from a create_state() template specialization.
107 // You can then create overloads for init(), put() and finish()
108 // that are more specific than the empty_result_state ones above.
109 // See the example below for direct access to a mutable data() method.
110 //
111 // If desired, a non-templated overload for both specific types
112 // (result & state) can be added to tailor it to that particular result type.
113 //
114
115 template <typename Result> class direct_data_access_result_state : empty_result_state
116 {
117 public:
118     using result_type = Result;
119
120     inline void init(Result& result, size_t capacity)
121     {
122         // resize(0) is not called here since we don't rely on it
123         result.reserve(capacity);
124     }
125     inline void put(Result& result, char c)
126     {
127         // This only compiles if decltype(data) == char*
128         result.data()[m_offset++] = static_cast<char>(c);
129     }
130     inline void finish(Result& result)
131     {
132         result.resize(m_offset);
133     }
134     inline size_t size(const Result&)
135     {
136         return m_offset;
137     }
138 private:
139     size_t m_offset = 0;
140 };
141
142 // SFINAE: Select a specific state based on the result type and possible result state type.
143 // Implement this if direct data access (`result.data()[0] = 'x') isn't already possible
144 // and you want to specialize it for your own result type.
145 template <typename Result, typename ResultState =
146         typename direct_data_access_result_state<Result>::result_type::value>
147 inline ResultState create_state(Result&, specific_t) { return ResultState(); }
148
149 template <typename Result>
150 inline void init(Result& result, direct_data_access_result_state<Result>& state, size_t capacity)
151 {
152     state.init(result);
153 }
154
155 // Specialized put function for direct_data_access_result_state.
156 template <typename Result>
157 inline void put(Result& result, direct_data_access_result_state<Result>& state, char c)
158 {
159     state.put(result, c);
160 }
161
162 // char_data() is only used to read, not for result buffers.
163 template <typename T> inline const char* char_data(const T& t)
164 {
165     return reinterpret_cast<const char*>(t.data());
166 }
167 template <typename T, size_t N> inline const char* char_data(const T (&t)[N]) noexcept
168 {
169     return reinterpret_cast<const char*>(&(t[0]));
170 }
171
172 template <typename T> inline const uint8_t* uchar_data(const T& t)
173 {
174     return reinterpret_cast<const uint8_t*>(char_data(t));
175 }
176
177 } // namespace data
178 } // namespace cppcodec
179
180 #endif