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 * @param pointer int; the first byte to consider
79 * @return the short value
80 */
81 public short decodeShort(final byte[] message, final int pointer)
82 {
83 if (this.bigEndian)
84 {
85 return (short) (((message[pointer] & 0xff) << 8) | ((message[pointer + 1] & 0xff)));
86 }
87 else
88 {
89 return (short) (((message[pointer + 1] & 0xff) << 8) | ((message[pointer] & 0xff)));
90 }
91 }
92
93 /**
94 * Decode a int.
95 * @param message byte[]; the ZeroMQ byte array to decode
96 * @param pointer int; the first byte to consider
97 * @return the integer value
98 */
99 public int decodeInt(final byte[] message, final int pointer)
100 {
101 if (this.bigEndian)
102 {
103 return (((message[pointer] & 0xff) << 24) | ((message[pointer + 1] & 0xff) << 16)
104 | ((message[pointer + 2] & 0xff) << 8) | ((message[pointer + 3] & 0xff)));
105 }
106 else
107 {
108 return (((message[pointer + 3] & 0xff) << 24) | ((message[pointer + 2] & 0xff) << 16)
109 | ((message[pointer + 1] & 0xff) << 8) | ((message[pointer] & 0xff)));
110 }
111 }
112
113 /**
114 * Decode a long.
115 * @param message byte[]; the ZeroMQ byte array to decode
116 * @param pointer int; the first byte to consider
117 * @return the long value
118 */
119 public long decodeLong(final byte[] message, final int pointer)
120 {
121 if (this.bigEndian)
122 {
123 return ((((long) message[pointer]) << 56) | (((long) message[pointer + 1] & 0xff) << 48)
124 | (((long) message[pointer + 2] & 0xff) << 40) | (((long) message[pointer + 3] & 0xff) << 32)
125 | (((long) message[pointer + 4] & 0xff) << 24) | (((long) message[pointer + 5] & 0xff) << 16)
126 | (((long) message[pointer + 6] & 0xff) << 8) | (((long) message[pointer + 7] & 0xff)));
127 }
128 else
129 {
130 return ((((long) message[pointer + 7]) << 56) | (((long) message[pointer + 6] & 0xff) << 48)
131 | (((long) message[pointer + 5] & 0xff) << 40) | (((long) message[pointer + 4] & 0xff) << 32)
132 | (((long) message[pointer + 3] & 0xff) << 24) | (((long) message[pointer + 2] & 0xff) << 16)
133 | (((long) message[pointer + 1] & 0xff) << 8) | (((long) message[pointer] & 0xff)));
134 }
135 }
136
137 /**
138 * Decode a float.
139 * @param message byte[]; the ZeroMQ byte array to decode
140 * @param pointer int; the first byte to consider
141 * @return the float value
142 */
143 public float decodeFloat(final byte[] message, final int pointer)
144 {
145 int bits = decodeInt(message, pointer);
146 return Float.intBitsToFloat(bits);
147 }
148
149 /**
150 * Decode a double.
151 * @param message byte[]; the ZeroMQ byte array to decode
152 * @param pointer int; the first byte to consider
153 * @return the double value
154 */
155 public double decodeDouble(final byte[] message, final int pointer)
156 {
157 long bits = decodeLong(message, pointer);
158 return Double.longBitsToDouble(bits);
159 }
160
161 /**
162 * Decode a char (16 bits).
163 * @param message byte[]; the ZeroMQ byte array to decode
164 * @param pointer int; the first byte to consider
165 * @return the short value
166 */
167 public char decodeChar(final byte[] message, final int pointer)
168 {
169 return (char) decodeShort(message, pointer);
170 }
171
172 /**
173 * Decode a String including the length int from the message byte array.
174 * @param message byte[]; the message byte array
175 * @param pointer int; the start position in the array
176 * @return the Java String at position pointer
177 * @throws SerializationException when the bytes cannot be parsed as UTF8
178 */
179 public String decodeUTF8String(final byte[] message, final int pointer) throws SerializationException
180 {
181 int len = decodeInt(message, pointer);
182 byte[] c = new byte[len];
183 for (int i = 0; i < len; i++)
184 {
185 c[i] = message[pointer + i + 4];
186 }
187 try
188 {
189 return new String(c, "UTF-8");
190 }
191 catch (UnsupportedEncodingException e)
192 {
193 throw new SerializationException(e);
194 }
195 }
196
197 /**
198 * Decode a String including the length int from the message byte array.
199 * @param message byte[]; the message byte array
200 * @param pointer int; the start position in the array
201 * @return the Java String at position pointer
202 */
203 public String decodeUTF16String(final byte[] message, final int pointer)
204 {
205 int len = decodeInt(message, pointer);
206 char[] c = new char[len];
207 for (int i = 0; i < len; i++)
208 {
209 c[i] = decodeChar(message, pointer + 2 * i + 4);
210 }
211 return String.copyValueOf(c);
212 }
213
214 /**
215 * Encode a short into a message buffer.
216 * @param v short; the variable to encode
217 * @param message byte[]; the message buffer to encode the variable into
218 * @param pointer int; the pointer to start writing
219 * @return the new pointer after writing
220 */
221 public int encodeShort(final short v, final byte[] message, final int pointer)
222 {
223 int p = pointer;
224 if (this.bigEndian)
225 {
226 message[p++] = (byte) (v >> 8);
227 message[p++] = (byte) (v);
228 }
229 else
230 {
231 message[p++] = (byte) (v);
232 message[p++] = (byte) (v >> 8);
233 }
234 return p;
235 }
236
237 /**
238 * Encode a char (16 bits) into a message buffer.
239 * @param v char; the variable to encode
240 * @param message byte[]; the message buffer to encode the variable into
241 * @param pointer int; the pointer to start writing
242 * @return the new pointer after writing
243 */
244 public int encodeChar(final char v, final byte[] message, final int pointer)
245 {
246 return encodeShort((short) v, message, pointer);
247 }
248
249 /**
250 * Encode a int into a message buffer.
251 * @param v int; the variable to encode
252 * @param message byte[]; the message buffer to encode the variable into
253 * @param pointer int; the pointer to start writing
254 */
255 public void encodeInt(final int v, final byte[] message, final int pointer)
256 {
257 int p = pointer;
258 if (this.bigEndian)
259 {
260 message[p++] = (byte) ((v >> 24) & 0xFF);
261 message[p++] = (byte) ((v >> 16) & 0xFF);
262 message[p++] = (byte) ((v >> 8) & 0xFF);
263 message[p++] = (byte) (v & 0xFF);
264 }
265 else
266 {
267 message[p++] = (byte) (v & 0xFF);
268 message[p++] = (byte) ((v >> 8) & 0xFF);
269 message[p++] = (byte) ((v >> 16) & 0xFF);
270 message[p++] = (byte) ((v >> 24) & 0xFF);
271 }
272 }
273
274 /**
275 * Encode a long into a message buffer.
276 * @param v long; the variable to encode
277 * @param message byte[]; the message buffer to encode the variable into
278 * @param pointer int; the pointer to start writing
279 * @return the new pointer after writing
280 */
281 public int encodeLong(final long v, final byte[] message, final int pointer)
282 {
283 int p = pointer;
284 if (this.bigEndian)
285 {
286 message[p++] = (byte) ((v >> 56) & 0xFF);
287 message[p++] = (byte) ((v >> 48) & 0xFF);
288 message[p++] = (byte) ((v >> 40) & 0xFF);
289 message[p++] = (byte) ((v >> 32) & 0xFF);
290 message[p++] = (byte) ((v >> 24) & 0xFF);
291 message[p++] = (byte) ((v >> 16) & 0xFF);
292 message[p++] = (byte) ((v >> 8) & 0xFF);
293 message[p++] = (byte) (v & 0xFF);
294 }
295 else
296 {
297 message[p++] = (byte) (v & 0xFF);
298 message[p++] = (byte) ((v >> 8) & 0xFF);
299 message[p++] = (byte) ((v >> 16) & 0xFF);
300 message[p++] = (byte) ((v >> 24) & 0xFF);
301 message[p++] = (byte) ((v >> 32) & 0xFF);
302 message[p++] = (byte) ((v >> 40) & 0xFF);
303 message[p++] = (byte) ((v >> 48) & 0xFF);
304 message[p++] = (byte) ((v >> 56) & 0xFF);
305 }
306 return p;
307 }
308
309 /**
310 * Encode a float into a message buffer.
311 * @param v float; the variable to encode
312 * @param message byte[]; the message buffer to encode the variable into
313 * @param pointer int; the pointer to start writing
314 */
315 public void encodeFloat(final float v, final byte[] message, final int pointer)
316 {
317 int vint = Float.floatToIntBits(v);
318 encodeInt(vint, message, pointer);
319 }
320
321 /**
322 * Encode a double into a message buffer.
323 * @param v double; the variable to encode
324 * @param message byte[]; the message buffer to encode the variable into
325 * @param pointer int; the pointer to start writing
326 * @return the new pointer after writing
327 */
328 public int encodeDouble(final double v, final byte[] message, final int pointer)
329 {
330 long vlong = Double.doubleToLongBits(v);
331 return encodeLong(vlong, message, pointer);
332 }
333
334 @Override
335 public String toString()
336 {
337 return "EndianUtil [bigEndian=" + this.bigEndian + "]";
338 }
339
340 }