1 /*
2 * Copyright (C) 2003 - David W. Durham
3 *
4 * This file is not part of any particular application.
5 *
6 * endian_util.h is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * istring is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #ifndef __endian_util_H__
22 #define __endian_util_H__
23
24 #include "../../config/common.h"
25
26 /*
27 * This header files given functionality to convenient change endian-ness of values
28 * and through the use of very few function names regardless of the type.
29 *
30 * To use this header file, WORDS_BIGENDIAN must be #defined if the platform is a
31 * big-endian platform and NOT defined if it's a little-endian platform.
32 *
33 * Though there is only one function name with the use of C++ templates the compiler
34 * automatically chooses the implementation depending on the size of the type of the
35 * value given. It should even be free to call the functions on say a 1 byte value
36 * that doesn't need conversion just to be complete in the code.
37 *
38 * Having one function name for all types makes mantenence easier since for instance:
39 * changing the type size of a variable won't break the endian conversion when someone
40 * forgets to look further down the code at the specific uses of that variable.
41 *
42 * As a word of advice, whenever writing data to stable storage or to a network, use
43 * the types defined in the now standard header file, stdint.h It defines fixed
44 * sized typenames so that you don't have to assume the sizes of int, long, short, etc
45 *
46 * float and doubles should be implemented as the same size on any ANSI C compiler that
47 * has them as types. They are supposed to follow IEEE standards.
48 *
49 *
50 *
51 * 4 macros are defined to do endian conversion
52 *
53 * lethe - given a little-endian value returns the same value in the host endian
54 * lethe - given a pointer to a little-endian value modifies the value to be the host endian
55 *
56 * hetle - reverse of lethe
57 *
58 * bethe - given a big-endian value returns the same value in the host endian
59 * bethe - given a pointer to a big-endian value modifies the value to be the host endian
60 *
61 * hetbe - reverse of bethe
62 *
63 * These macros both call on swap_endian() which does the work. This function will swap the
64 * endian of the given value, or swap the endian of the value at the given pointer. Again,
65 * the parameter type determines the correct implmenation.
66 *
67 * There are also xxx_many() version of these macros which should accept a pointer to many
68 * values and secondly a count of how many values the pointer points to. Each value will
69 * have the endian swapped if necessary.
70 *
71 */
72
73
74 /* 'lethe' means little-endian to host-endian */
75 /* 'hetle' means host-endian to little-endian */
76 #ifdef WORDS_BIGENDIAN
77 #define lethe(value) swap_endian(value)
78 #define hetle(value) swap_endian(value)
79
80 #define lethe_many(value,count) swap_endian_many((value),(count))
81 #define hetle_many(value,count) swap_endian_many((value),(count))
82 #else // assuming now a little-endian platform
83 #define lethe(value) (value)
84 #define hetle(value) (value)
85
86 #define lethe_many(value,count) (value)
87 #define hetle_many(value,count) (value)
88 #endif
89
90 /* 'bethe' means big-endian to host-endian */
91 /* 'hetbe' means host-endian to big-endian */
92 #ifdef WORDS_BIGENDIAN
93 #define bethe(value) (value)
94 #define hetbe(value) (value)
95
96 #define bethe_many(value,count) (value)
97 #define hetbe_many(value,count) (value)
98
99 #else // assuming now a little-endian platform
100 #define bethe(value) swap_endian(value)
101 #define hetbe(value) swap_endian(value)
102
103 #define bethe_many(value,count) swap_endian_many((value),(count))
104 #define hetbe_many(value,count) swap_endian_many((value),(count))
105 #endif
106
107 #ifndef __cplusplus
108 #error this header file requires a C++ compiler
109 #endif
110
111
112
113
114
115 #include <stdint.h>
116 namespace endian_util
117 {
118 /*
119 * Implementations that swap the endian of a value at a memory location
120 */
121
122 // --- generic implementation
really_swap_endian_ptr(void * _value,const unsigned size)123 template<unsigned Size> inline static void really_swap_endian_ptr(void *_value,const unsigned size)
124 {
125 uint8_t * const value=(uint8_t *)_value;
126 register int k=(size+1)/2;
127 for(register int t=(size/2)-1;t>=0;t--,k++)
128 {
129 const uint8_t temp=value[t];
130 value[k]=value[t];
131 value[k]=temp;
132 }
133 }
134
135 // --- implementation for 1 byte quantities (nothing)
136 template<> inline STATIC_TPL void really_swap_endian_ptr<1>(void *value,const unsigned size)
137 {
138 // nothing to do
139 }
140
141 // --- implementation for 2 byte quantities
142 template<> inline STATIC_TPL void really_swap_endian_ptr<2>(void *value,const unsigned size)
143 {
144 const register uint16_t v=((uint16_t *)value)[0];
145 ((uint16_t *)value)[0]=
146 // of 2, swap upper and lower 8 octets
147 ((v>>8)&0x00ff) | ((v<<8)&0xff00);
148
149 }
150
151 // --- implementation for 4 byte quantities
152 template<> inline STATIC_TPL void really_swap_endian_ptr<4>(void *value,const unsigned size)
153 {
154 const register uint32_t v=((uint32_t *)value)[0];
155 ((uint32_t *)value)[0]=
156 // of 4, swap upper most and lower most octets then swap two middle octets
157 ((v>>24)&0x000000ff) | ((v<<24)&0xff000000) |
158 ((v>> 8)&0x0000ff00) | ((v<< 8)&0x00ff0000);
159 }
160
161 // --- implementation for 8 byte quantities
162 template<> inline STATIC_TPL void really_swap_endian_ptr<8>(void *value,const unsigned size)
163 {
164 const register uint64_t v=((uint64_t *)value)[0];
165 // of 8, swap upper most and lower most octets then the next two inward, and so on ..
166 ((uint64_t *)value)[0]=
167 ((v>>56)&0x00000000000000ffLL) | ((v<<56)&0xff00000000000000LL) |
168 ((v>>40)&0x000000000000ff00LL) | ((v<<40)&0x00ff000000000000LL) |
169 ((v>>24)&0x0000000000ff0000LL) | ((v<<24)&0x0000ff0000000000LL) |
170 ((v>> 8)&0x00000000ff000000LL) | ((v<< 8)&0x000000ff00000000LL);
171 }
172
173 }; // namespace endian_util
174
175
176 /*
177 * These two are implemented so that if you pass it a pointer, it modifies the data
178 * you're pointing to but if you pass it a value, then it returns the modified value
179 */
180
swap_endian(Type * value)181 template<typename Type> inline static void swap_endian(Type *value)
182 {
183 endian_util::really_swap_endian_ptr<sizeof(Type)>(value,sizeof(Type));
184 }
185
186 #include <stdint.h>
swap_endian(uint8_t value)187 template<typename Type> inline static const Type swap_endian(uint8_t value)
188 {
189 return value;
190 }
swap_endian(int8_t value)191 template<typename Type> inline static const Type swap_endian(int8_t value) { return swap_endian<uint8_t>((uint8_t)value); }
192
swap_endian(uint16_t value)193 template<typename Type> inline static const Type swap_endian(uint16_t value)
194 {
195 return
196 ((value>>8)&0x00ff) | ((value<<8)&0xff00);
197 }
swap_endian(int16_t value)198 template<typename Type> inline static const Type swap_endian(int16_t value) { return swap_endian<uint16_t>((uint16_t)value); }
199
swap_endian(uint32_t value)200 template<typename Type> inline static const Type swap_endian(uint32_t value)
201 {
202 return
203 // of 4, swap upper most and lower most octets then swap two middle octets
204 ((value>>24)&0x000000ff) | ((value<<24)&0xff000000) |
205 ((value>> 8)&0x0000ff00) | ((value<< 8)&0x00ff0000);
206 }
swap_endian(int32_t value)207 template<typename Type> inline static const Type swap_endian(int32_t value) { return swap_endian<uint32_t>((uint32_t)value); }
swap_endian(float value)208 template<typename Type> inline static const Type swap_endian(float value) { return swap_endian<uint32_t>(*reinterpret_cast<uint32_t *>(&value)); }
209
swap_endian(uint64_t value)210 template<typename Type> inline static const Type swap_endian(uint64_t value)
211 {
212 return
213 ((value>>56)&0x00000000000000ffLL) | ((value<<56)&0xff00000000000000LL) |
214 ((value>>40)&0x000000000000ff00LL) | ((value<<40)&0x00ff000000000000LL) |
215 ((value>>24)&0x0000000000ff0000LL) | ((value<<24)&0x0000ff0000000000LL) |
216 ((value>> 8)&0x00000000ff000000LL) | ((value<< 8)&0x000000ff00000000LL);
217 }
swap_endian(int64_t value)218 template<typename Type> inline static const Type swap_endian(int64_t value) { return swap_endian<uint64_t>((uint64_t)value); }
swap_endian(double value)219 template<typename Type> inline static const Type swap_endian(double value) { return swap_endian<uint64_t>(*reinterpret_cast<uint64_t *>(&value)); }
220
221
222 /* generic value implementation which just uses the ptr version (which is slower) */
swap_endian(Type value)223 template<typename Type> inline static const Type swap_endian(Type value)
224 {
225 endian_util::really_swap_endian_ptr<sizeof(Type)>(&value,sizeof(Type));
226 return value;
227 }
228
229
230 /* this implementation takes a pointer to an array of values, the size is also given as a parameter */
swap_endian_many(Type * values,const size_t count)231 template<typename Type> inline static void swap_endian_many(Type *values,const size_t count)
232 {
233 for(size_t t=0;t<count;t++)
234 endian_util::really_swap_endian_ptr<sizeof(Type)>(values+t,sizeof(Type));
235 }
236
237
238 #endif
239