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