View Javadoc

1   /*
2    Copyright 2007 Ramon Servadei
3   
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7   
8    http://www.apache.org/licenses/LICENSE-2.0
9   
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15   */
16  package fulmine.protocol.specification;
17  
18  import static fulmine.protocol.specification.ByteConstants.bitShiftForByteOrdinal;
19  import static fulmine.util.Utils.COLON;
20  import static fulmine.util.Utils.SPACE;
21  
22  import java.io.UnsupportedEncodingException;
23  import java.util.Arrays;
24  
25  /**
26   * Utility methods for reading primitives from a byte[] using the fulmine delta
27   * (FD) protocol. Integrals can be compacted using the FD protocol and the logic
28   * to correctly reconstruct them is executed in the appropriate integral read
29   * method.
30   * <p>
31   * Please refer to the "Fulmine Delta Transmission Protocol" specification for a
32   * complete description of the fulmine serialisation technique.
33   * 
34   * @see ByteWriter
35   * @author Ramon Servadei
36   * 
37   */
38  public final class ByteReader
39  {
40  
41      /**
42       * Reconstruct a String from its byte form in the byte[].
43       * 
44       * @param bytes
45       *            the byte[] holding the data in byte form
46       * @param start
47       *            the position in bytes where the data starts
48       * @param numberOfBytes
49       *            the number of bytes that form the data to read
50       * @return a String
51       */
52      public static String readString(final byte[] bytes, final int start,
53          final int numberOfBytes)
54      {
55          if(numberOfBytes < 0)
56          {
57              throw new NegativeArraySizeException("size=" + numberOfBytes + ", start=" + start);
58          }
59          final byte[] dataBytes = new byte[numberOfBytes];
60          System.arraycopy(bytes, start, dataBytes, 0, numberOfBytes);
61          try
62          {
63              if (numberOfBytes == 0)
64              {
65                  return null;
66              }
67              return new String(dataBytes, ByteConstants.ENCODING);
68          }
69          catch (UnsupportedEncodingException e)
70          {
71              throw new RuntimeException("Could not construct "
72                  + ByteConstants.ENCODING + " string from bytes");
73          }
74      }
75  
76      /**
77       * Reconstruct a boolean from its byte form in the byte[].
78       * 
79       * @param bytes
80       *            the byte[] holding the data in byte form
81       * @param start
82       *            the position in bytes where the data starts
83       * @param numberOfBytes
84       *            the number of bytes that form the data to read
85       * @return a boolean
86       */
87      public static boolean readBoolean(final byte[] bytes, final int start,
88          final int numberOfBytes)
89      {
90          return (byte) readInteger(bytes, start, numberOfBytes) == 1;
91      }
92  
93      /**
94       * Reconstruct a float from its byte form in the byte[].
95       * 
96       * @param bytes
97       *            the byte[] holding the data in byte form
98       * @param start
99       *            the position in bytes where the data starts
100      * @param numberOfBytes
101      *            the number of bytes that form the data to read
102      * @return a float
103      */
104     public static float readFloat(final byte[] bytes, final int start,
105         final int numberOfBytes)
106     {
107         return Float.intBitsToFloat(readInteger(bytes, start, numberOfBytes));
108     }
109 
110     /**
111      * Reconstruct a double from its byte form in the byte[].
112      * 
113      * @param bytes
114      *            the byte[] holding the data in byte form
115      * @param start
116      *            the position in bytes where the data starts
117      * @param numberOfBytes
118      *            the number of bytes that form the data to read
119      * @return a double
120      */
121     public static double readDouble(final byte[] bytes, final int start,
122         final int numberOfBytes)
123     {
124         return Double.longBitsToDouble(readLong(bytes, start, numberOfBytes));
125     }
126 
127     /**
128      * Reconstruct an integer from its byte form in the byte[].
129      * 
130      * @param bytes
131      *            the byte[] holding the data in byte form
132      * @param start
133      *            the position in bytes where the data starts
134      * @param numberOfBytes
135      *            the number of bytes that form the data to read
136      * @return an integer
137      */
138     public static int readInteger(final byte[] bytes, final int start,
139         final int numberOfBytes)
140     {
141         return (int) readLong(bytes, start, numberOfBytes);
142     }
143 
144     /**
145      * Reconstruct a long from its byte form in the byte[].
146      * 
147      * @param bytes
148      *            the byte[] holding the data in byte form
149      * @param start
150      *            the position in bytes where the data starts
151      * @param numberOfBytes
152      *            the number of bytes that form the data to read
153      * @return a long
154      */
155     public static long readLong(final byte[] bytes, final int start,
156         int numberOfBytes)
157     {
158         long output = 0;
159         final int end = numberOfBytes + start;
160         // identify if this is a -ve number, has ramifications for building up
161         // the output
162         boolean negative = false;
163         if ((bytes[start] & 0x80) == 0x80)
164         {
165             output = -1;
166             negative = true;
167         }
168         for (int i = start; i < end; i++)
169         {
170             // cast to a char as it is unsigned, and also AND with 00FF to
171             // correctly promote whilst keeping the byte unsigned
172             final char dataByte = (char) (bytes[i] & 0x00FF);
173             if (negative)
174             {
175                 // clear out this byte position with 0, prior to bitwise ORing
176                 // the byte
177                 output ^=
178                     ((long) 0xff << bitShiftForByteOrdinal[numberOfBytes]);
179             }
180             // bitwise OR the byte after shifting it by the correct number of
181             // bits
182             output |=
183                 ((long) dataByte << bitShiftForByteOrdinal[numberOfBytes--]);
184         }
185         return output;
186     }
187 
188     /**
189      * Get a string from its byte representation in
190      * {@link ByteConstants#ENCODING}. <br>
191      * <b>WARNING: System.exit(2) if encoding fails.</b>
192      * 
193      * @param bytes
194      *            the bytes of the string, encoded in
195      *            {@link ByteConstants#ENCODING}
196      * @return the string, in {@link ByteConstants#ENCODING}
197      */
198     public static String fromBytes(byte[] bytes)
199     {
200         try
201         {
202             return new String(bytes, ByteConstants.ENCODING);
203         }
204         catch (UnsupportedEncodingException e)
205         {
206             System.err.println("Could not properly encode "
207                 + Arrays.toString(bytes) + " to " + ByteConstants.ENCODING
208                 + COLON + SPACE + e);
209             e.printStackTrace();
210             System.exit(2);
211         }
212         return null;
213     }
214 
215 }