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