View Javadoc
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 }