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