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 EndianUtil BIG_ENDIAN = new EndianUtil(true);
34  
35      /** Directly usable littleEndian EndianUtil. */
36      public static final 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 }