1 // Code taken from Boost.Geometry
2 //
3 // Copyright Mateusz Loskot <mateusz@loskot.net> 2009
4 // Use, modification and distribution is subject to the Boost Software License,
5 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Load/Store values from/to stream of bytes across different endianness.
9 //
10 // Original design of unrolled_byte_loops templates based on
11 // endian utility library from Boost C++ Libraries,
12 // source: boost/spirit/home/support/detail/integer/endian.hpp
13 // Copyright Darin Adler 2000
14 // Copyright Beman Dawes 2006, 2009
15 // Distributed under the Boost Software License, Version 1.0.
16
17 #ifndef LIBLAS_DETAIL_BINARY_HPP_INCLUDED
18 #define LIBLAS_DETAIL_BINARY_HPP_INCLUDED
19
20 #include <cassert>
21 #include <climits>
22 #include <cstring>
23 #include <cstddef>
24 #include <iterator>
25
26 #include <boost/config.hpp>
27 #include <boost/static_assert.hpp>
28 #include <boost/detail/endian.hpp>
29 #include <boost/type_traits/is_signed.hpp>
30
31 #if CHAR_BIT != 8
32 #error Platforms with CHAR_BIT != 8 are not supported
33 #endif
34
35 // TODO: mloskot - add static asserts to validate compile-time pre-conditions
36
37 namespace liblas {
38
39 namespace detail { namespace binary {
40
41 // Endianness tag used to indicate load/store directoin
42
43 struct big_endian_tag {};
44 struct little_endian_tag {};
45
46 #ifdef BOOST_BIG_ENDIAN
47 typedef big_endian_tag native_endian_tag;
48 #else
49 typedef little_endian_tag native_endian_tag;
50 #endif
51
52 // Unrolled loops for loading and storing streams of bytes.
53
54 template <typename T, std::size_t N, bool Sign = boost::is_signed<T>::value>
55 struct unrolled_byte_loops
56 {
57 typedef unrolled_byte_loops<T, N - 1, Sign> next;
58
59 template <typename Iterator>
load_forwardliblas::detail::binary::unrolled_byte_loops60 static T load_forward(Iterator& bytes)
61 {
62 T const value = *bytes;
63 ++bytes;
64 return value | (next::load_forward(bytes) << 8);
65 }
66
67 template <typename Iterator>
load_backwardliblas::detail::binary::unrolled_byte_loops68 static T load_backward(Iterator& bytes)
69 {
70 T const value = *(bytes - 1);
71 --bytes;
72 return value | (next::load_backward(bytes) << 8);
73 }
74
75 template <typename Iterator>
store_forwardliblas::detail::binary::unrolled_byte_loops76 static void store_forward(Iterator& bytes, T value)
77 {
78 *bytes = static_cast<char>(value);
79 next::store_forward(++bytes, value >> 8);
80 }
81
82 template <typename Iterator>
store_backwardliblas::detail::binary::unrolled_byte_loops83 static void store_backward(Iterator& bytes, T value)
84 {
85 *(bytes - 1) = static_cast<char>(value);
86 next::store_backward(--bytes, value >> 8);
87 }
88 };
89
90 template <typename T>
91 struct unrolled_byte_loops<T, 1, false>
92 {
93 template <typename Iterator>
load_forwardliblas::detail::binary::unrolled_byte_loops94 static T load_forward(Iterator& bytes)
95 {
96 return *bytes;
97 }
98
99 template <typename Iterator>
load_backwardliblas::detail::binary::unrolled_byte_loops100 static T load_backward(Iterator& bytes)
101 {
102 return *(bytes - 1);
103 }
104
105 template <typename Iterator>
store_forwardliblas::detail::binary::unrolled_byte_loops106 static void store_forward(Iterator& bytes, T value)
107 {
108 // typename Iterator::value_type
109 *bytes = static_cast<char>(value);
110 }
111
112 template <typename Iterator>
store_backwardliblas::detail::binary::unrolled_byte_loops113 static void store_backward(Iterator& bytes, T value)
114 {
115 *(bytes - 1) = static_cast<char>(value);
116 }
117 };
118
119 template <typename T>
120 struct unrolled_byte_loops<T, 1, true>
121 {
122 template <typename Iterator>
load_forwardliblas::detail::binary::unrolled_byte_loops123 static T load_forward(Iterator& bytes)
124 {
125 return *reinterpret_cast<const signed char*>(&*bytes);
126 }
127
128 template <typename Iterator>
load_backwardliblas::detail::binary::unrolled_byte_loops129 static T load_backward(Iterator& bytes)
130 {
131 return *reinterpret_cast<const signed char*>(&*(bytes - 1));
132 }
133
134 template <typename Iterator>
store_forwardliblas::detail::binary::unrolled_byte_loops135 static void store_forward(Iterator& bytes, T value)
136 {
137 *bytes = static_cast<char>(value);
138 }
139
140 template <typename Iterator>
store_backwardliblas::detail::binary::unrolled_byte_loops141 static void store_backward(Iterator& bytes, T value)
142 {
143 *(bytes - 1) = static_cast<char>(value);
144 }
145 };
146
147 // load/store operation dispatch
148 // E, E - source and target endianness is the same
149 // E1, E2 - source and target endianness is different (big-endian <-> little-endian)
150
151 template <typename T, std::size_t N, typename Iterator, typename E>
152 T load_dispatch(Iterator& bytes, E, E)
153 {
154 return unrolled_byte_loops<T, N>::load_forward(bytes);
155 }
156
157 template <typename T, std::size_t N, typename Iterator, typename E1, typename E2>
158 T load_dispatch(Iterator& bytes, E1, E2)
159 {
160 std::advance(bytes, N);
161 return unrolled_byte_loops<T, N>::load_backward(bytes);
162 }
163
164 template <typename T, std::size_t N, typename Iterator, typename E>
store_dispatch(Iterator & bytes,T value,E,E)165 void store_dispatch(Iterator& bytes, T value, E, E)
166 {
167 return unrolled_byte_loops<T, N>::store_forward(bytes, value);
168 }
169
170 template <typename T, std::size_t N, typename Iterator, typename E1, typename E2>
store_dispatch(Iterator & bytes,T value,E1,E2)171 void store_dispatch(Iterator& bytes, T value, E1, E2)
172 {
173 std::advance(bytes, N);
174 return unrolled_byte_loops<T, N>::store_backward(bytes, value);
175 }
176
177 // numeric value holder for load/store operation
178
179 template <typename T>
180 struct endian_value_base
181 {
182 typedef T value_type;
183 typedef native_endian_tag endian_type;
184
endian_value_baseliblas::detail::binary::endian_value_base185 endian_value_base() : value(T()) {}
endian_value_baseliblas::detail::binary::endian_value_base186 explicit endian_value_base(T value) : value(value) {}
187
operator Tliblas::detail::binary::endian_value_base188 operator T() const
189 {
190 return value;
191 }
192
193 protected:
194 T value;
195 };
196
197 template <typename T, std::size_t N = sizeof(T)>
198 struct endian_value : public endian_value_base<T>
199 {
200 typedef endian_value_base<T> base;
201
endian_valueliblas::detail::binary::endian_value202 endian_value() {}
endian_valueliblas::detail::binary::endian_value203 explicit endian_value(T value) : base(value) {}
204
205 template <typename E, typename Iterator>
loadliblas::detail::binary::endian_value206 void load(Iterator bytes)
207 {
208 base::value = load_dispatch<T, N>(bytes, typename base::endian_type(), E());
209 }
210
211 template <typename E, typename Iterator>
storeliblas::detail::binary::endian_value212 void store(Iterator bytes)
213 {
214 store_dispatch<T, N>(bytes, base::value, typename base::endian_type(), E());
215 }
216 };
217
218 template <>
219 struct endian_value<double, 8> : public endian_value_base<double>
220 {
221 typedef endian_value_base<double> base;
222
endian_valueliblas::detail::binary::endian_value223 endian_value() {}
endian_valueliblas::detail::binary::endian_value224 explicit endian_value(double value) : base(value) {}
225
226 template <typename E, typename Iterator>
loadliblas::detail::binary::endian_value227 void load(Iterator bytes)
228 {
229 endian_value<uint64_t, 8> raw;
230 raw.load<E>(bytes);
231
232 double& target_value = base::value;
233 std::memcpy(&target_value, &raw, sizeof(double));
234 }
235
236 template <typename E, typename Iterator>
storeliblas::detail::binary::endian_value237 void store(Iterator bytes)
238 {
239 uint64_t raw;
240 double const& source_value = base::value;
241 std::memcpy(&raw, &source_value, sizeof(uint64_t));
242
243 store_dispatch
244 <
245 uint64_t,
246 sizeof(uint64_t)
247 >(bytes, raw, typename base::endian_type(), E());
248 }
249 };
250
251 }} // namespace detail::binary
252 } // namespace liblas
253
254 #endif // LIBLAS_DETAIL_BINARY_HPP_INCLUDED
255