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 }