1 package org.djutils.serialization;
2
3 import java.io.UnsupportedEncodingException;
4 import java.nio.ByteOrder;
5
6 /**
7 * Method to help with Little Endian / Big Endian conversions for the Sim0MQ messages. All Sim0MQ messages are encoded Big
8 * Endian over the wire.
9 * <p>
10 * Copyright (c) 2016-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
11 * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>.
12 * </p>
13 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
14 * initial version Dec 15, 2016 <br>
15 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
16 */
17 public final class EndianUtil
18 {
19 /** Does this EndianUtil encode and decode messages in bigEndian? */
20 private final boolean bigEndian;
21
22 /** Is this platform bigEndian? */
23 private static final boolean PLATFORM_BIG_ENDIAN = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
24
25 /**
26 * Report whether this platform is bigEndian, or littleEndian.
27 * @return boolean; true if this platform is bigEndian; false if this platform is littleEndian
28 */
29 public static boolean isPlatformBigEndian()
30 {
31 return PLATFORM_BIG_ENDIAN;
32 }
33
34 /** Directly usable bigEndian EndianUtil. */
35 public static final EndianUtil BIG_ENDIAN = new EndianUtil(true);
36
37 /** Directly usable littleEndian EndianUtil. */
38 public static final EndianUtil LITTLE_ENDIAN = new EndianUtil(false);
39
40 /**
41 * Construct an EndianUtil object with user specified endianness.
42 * @param bigEndian boolean; if true encoding and decoding use big endian style; if false; encoding and decoding use little
43 * endian style
44 */
45 private EndianUtil(final boolean bigEndian)
46 {
47 this.bigEndian = bigEndian;
48 }
49
50 /**
51 * Construct an EndianUtil object that uses bigEndian encoding.
52 * @return EndianUtil that uses bigEndian encoding
53 */
54 public static EndianUtil bigEndian()
55 {
56 return BIG_ENDIAN;
57 }
58
59 /**
60 * Construct an EndianUtil object that uses littleEndian encoding.
61 * @return EndianUtil that uses littleEndian encoding
62 */
63 public static EndianUtil littleEndian()
64 {
65 return LITTLE_ENDIAN;
66 }
67
68 /**
69 * Report if this EndianUtil is bigEndian.
70 * @return bigEndian boolean; true if this EndianUtil is bigEndian; false if this EndianUtil is littleEndian
71 */
72 public boolean isBigEndian()
73 {
74 return this.bigEndian;
75 }
76
77 /**
78 * Decode a short.
79 * @param message the ZeroMQ byte array to decode
80 * @param pointer the first byte to consider
81 * @return the short value
82 */
83 public short decodeShort(final byte[] message, final int pointer)
84 {
85 if (this.bigEndian)
86 {
87 return (short) (((message[pointer] & 0xff) << 8) | ((message[pointer + 1] & 0xff)));
88 }
89 else
90 {
91 return (short) (((message[pointer + 1] & 0xff) << 8) | ((message[pointer] & 0xff)));
92 }
93 }
94
95 /**
96 * Decode a int.
97 * @param message the ZeroMQ byte array to decode
98 * @param pointer the first byte to consider
99 * @return the integer value
100 */
101 public int decodeInt(final byte[] message, final int pointer)
102 {
103 if (this.bigEndian)
104 {
105 return (((message[pointer] & 0xff) << 24) | ((message[pointer + 1] & 0xff) << 16)
106 | ((message[pointer + 2] & 0xff) << 8) | ((message[pointer + 3] & 0xff)));
107 }
108 else
109 {
110 return (((message[pointer + 3] & 0xff) << 24) | ((message[pointer + 2] & 0xff) << 16)
111 | ((message[pointer + 1] & 0xff) << 8) | ((message[pointer] & 0xff)));
112 }
113 }
114
115 /**
116 * Decode a long.
117 * @param message the ZeroMQ byte array to decode
118 * @param pointer the first byte to consider
119 * @return the long value
120 */
121 public long decodeLong(final byte[] message, final int pointer)
122 {
123 if (this.bigEndian)
124 {
125 return ((((long) message[pointer]) << 56) | (((long) message[pointer + 1] & 0xff) << 48)
126 | (((long) message[pointer + 2] & 0xff) << 40) | (((long) message[pointer + 3] & 0xff) << 32)
127 | (((long) message[pointer + 4] & 0xff) << 24) | (((long) message[pointer + 5] & 0xff) << 16)
128 | (((long) message[pointer + 6] & 0xff) << 8) | (((long) message[pointer + 7] & 0xff)));
129 }
130 else
131 {
132 return ((((long) message[pointer + 7]) << 56) | (((long) message[pointer + 6] & 0xff) << 48)
133 | (((long) message[pointer + 5] & 0xff) << 40) | (((long) message[pointer + 4] & 0xff) << 32)
134 | (((long) message[pointer + 3] & 0xff) << 24) | (((long) message[pointer + 2] & 0xff) << 16)
135 | (((long) message[pointer + 1] & 0xff) << 8) | (((long) message[pointer] & 0xff)));
136 }
137 }
138
139 /**
140 * Decode a float.
141 * @param message the ZeroMQ byte array to decode
142 * @param pointer the first byte to consider
143 * @return the float value
144 */
145 public float decodeFloat(final byte[] message, final int pointer)
146 {
147 int bits = decodeInt(message, pointer);
148 return Float.intBitsToFloat(bits);
149 }
150
151 /**
152 * Decode a double.
153 * @param message the ZeroMQ byte array to decode
154 * @param pointer the first byte to consider
155 * @return the double value
156 */
157 public double decodeDouble(final byte[] message, final int pointer)
158 {
159 long bits = decodeLong(message, pointer);
160 return Double.longBitsToDouble(bits);
161 }
162
163 /**
164 * Decode a char (16 bits).
165 * @param message the ZeroMQ byte array to decode
166 * @param pointer the first byte to consider
167 * @return the short value
168 */
169 public char decodeChar(final byte[] message, final int pointer)
170 {
171 return (char) decodeShort(message, pointer);
172 }
173
174 /**
175 * Decode a String including the length int from the message byte array.
176 * @param message the message byte array
177 * @param pointer the start position in the array
178 * @return the Java String at position pointer
179 * @throws SerializationException when the bytes cannot be parsed as UTF8
180 */
181 public String decodeUTF8String(final byte[] message, final int pointer) throws SerializationException
182 {
183 int len = decodeInt(message, pointer);
184 byte[] c = new byte[len];
185 for (int i = 0; i < len; i++)
186 {
187 c[i] = message[pointer + i + 4];
188 }
189 try
190 {
191 return new String(c, "UTF-8");
192 }
193 catch (UnsupportedEncodingException e)
194 {
195 throw new SerializationException(e);
196 }
197 }
198
199 /**
200 * Decode a String including the length int from the message byte array.
201 * @param message the message byte array
202 * @param pointer the start position in the array
203 * @return the Java String at position pointer
204 */
205 public String decodeUTF16String(final byte[] message, final int pointer)
206 {
207 int len = decodeInt(message, pointer);
208 char[] c = new char[len];
209 for (int i = 0; i < len; i++)
210 {
211 c[i] = decodeChar(message, pointer + 2 * i + 4);
212 }
213 return String.copyValueOf(c);
214 }
215
216 /**
217 * Encode a short into a message buffer.
218 * @param v the variable to encode
219 * @param message the message buffer to encode the variable into
220 * @param pointer the pointer to start writing
221 * @return the new pointer after writing
222 */
223 public int encodeShort(final short v, final byte[] message, final int pointer)
224 {
225 int p = pointer;
226 if (this.bigEndian)
227 {
228 message[p++] = (byte) (v >> 8);
229 message[p++] = (byte) (v);
230 }
231 else
232 {
233 message[p++] = (byte) (v);
234 message[p++] = (byte) (v >> 8);
235 }
236 return p;
237 }
238
239 /**
240 * Encode a char (16 bits) into a message buffer.
241 * @param v the variable to encode
242 * @param message the message buffer to encode the variable into
243 * @param pointer the pointer to start writing
244 * @return the new pointer after writing
245 */
246 public int encodeChar(final char v, final byte[] message, final int pointer)
247 {
248 return encodeShort((short) v, message, pointer);
249 }
250
251 /**
252 * Encode a int into a message buffer.
253 * @param v the variable to encode
254 * @param message the message buffer to encode the variable into
255 * @param pointer the pointer to start writing
256 */
257 public void encodeInt(final int v, final byte[] message, final int pointer)
258 {
259 int p = pointer;
260 if (this.bigEndian)
261 {
262 message[p++] = (byte) ((v >> 24) & 0xFF);
263 message[p++] = (byte) ((v >> 16) & 0xFF);
264 message[p++] = (byte) ((v >> 8) & 0xFF);
265 message[p++] = (byte) (v & 0xFF);
266 }
267 else
268 {
269 message[p++] = (byte) (v & 0xFF);
270 message[p++] = (byte) ((v >> 8) & 0xFF);
271 message[p++] = (byte) ((v >> 16) & 0xFF);
272 message[p++] = (byte) ((v >> 24) & 0xFF);
273 }
274 }
275
276 /**
277 * Encode a long into a message buffer.
278 * @param v the variable to encode
279 * @param message the message buffer to encode the variable into
280 * @param pointer the pointer to start writing
281 * @return the new pointer after writing
282 */
283 public int encodeLong(final long v, final byte[] message, final int pointer)
284 {
285 int p = pointer;
286 if (this.bigEndian)
287 {
288 message[p++] = (byte) ((v >> 56) & 0xFF);
289 message[p++] = (byte) ((v >> 48) & 0xFF);
290 message[p++] = (byte) ((v >> 40) & 0xFF);
291 message[p++] = (byte) ((v >> 32) & 0xFF);
292 message[p++] = (byte) ((v >> 24) & 0xFF);
293 message[p++] = (byte) ((v >> 16) & 0xFF);
294 message[p++] = (byte) ((v >> 8) & 0xFF);
295 message[p++] = (byte) (v & 0xFF);
296 }
297 else
298 {
299 message[p++] = (byte) (v & 0xFF);
300 message[p++] = (byte) ((v >> 8) & 0xFF);
301 message[p++] = (byte) ((v >> 16) & 0xFF);
302 message[p++] = (byte) ((v >> 24) & 0xFF);
303 message[p++] = (byte) ((v >> 32) & 0xFF);
304 message[p++] = (byte) ((v >> 40) & 0xFF);
305 message[p++] = (byte) ((v >> 48) & 0xFF);
306 message[p++] = (byte) ((v >> 56) & 0xFF);
307 }
308 return p;
309 }
310
311 /**
312 * Encode a float into a message buffer.
313 * @param v the variable to encode
314 * @param message the message buffer to encode the variable into
315 * @param pointer the pointer to start writing
316 */
317 public void encodeFloat(final float v, final byte[] message, final int pointer)
318 {
319 int vint = Float.floatToIntBits(v);
320 encodeInt(vint, message, pointer);
321 }
322
323 /**
324 * Encode a double into a message buffer.
325 * @param v the variable to encode
326 * @param message the message buffer to encode the variable into
327 * @param pointer the pointer to start writing
328 * @return the new pointer after writing
329 */
330 public int encodeDouble(final double v, final byte[] message, final int pointer)
331 {
332 long vlong = Double.doubleToLongBits(v);
333 return encodeLong(vlong, message, pointer);
334 }
335
336 @Override
337 public String toString()
338 {
339 return "EndianUtil [bigEndian=" + this.bigEndian + "]";
340 }
341
342 }