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 }