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="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 }