1 /*** 2 * Copyright 2006 Joseph M. Ferner 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 com.fernsroth.easyio; 17 18 import java.io.FilterInputStream; 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.lang.reflect.Field; 22 import java.math.BigInteger; 23 import java.util.LinkedList; 24 import java.util.List; 25 import java.util.Queue; 26 27 import com.fernsroth.easyio.exception.EasyIOException; 28 import com.fernsroth.easyio.util.BeanUtils; 29 import com.fernsroth.easyio.util.EasyIOUtils; 30 31 /*** 32 * input stream for reading and writing binary data. 33 * @author Joseph M. Ferner (Near Infinity Corporation) 34 */ 35 public class EasyIOInputStream extends FilterInputStream implements 36 IInputStream { 37 38 /*** 39 * the currect working byte. 40 */ 41 private int currentByte; 42 43 /*** 44 * number of bits left in {@link currentByte}. 45 */ 46 private int bitsLeft; 47 48 /*** 49 * the field handler registry. 50 */ 51 private FieldHandlerRegistry fieldHandlerRegistry = DefaultFieldHandlerRegistry 52 .getInstance(); 53 54 /*** 55 * constructor. 56 * @param in the input stream to wrap. 57 */ 58 public EasyIOInputStream(InputStream in) { 59 super(in); 60 this.currentByte = 0; 61 this.bitsLeft = 0; 62 } 63 64 /*** 65 * constructor. 66 * @param in the input stream. 67 */ 68 public EasyIOInputStream(IInputStream in) { 69 this(new InputStreamAdapter(in)); 70 } 71 72 /*** 73 * reads in a object. 74 * @param <T> the object type. 75 * @param obj the object to read data into. 76 * @return the read in object. 77 * @throws EasyIOException 78 * @throws IOException 79 */ 80 public <T> T read(T obj) throws EasyIOException, IOException { 81 List<Field> fields = BeanUtils.getAllFields(obj.getClass()); 82 Queue<Field> fieldsQueue = new LinkedList<Field>(fields); 83 84 while (!fieldsQueue.isEmpty()) { 85 Field field = fieldsQueue.peek(); 86 FieldHandler handler = this.fieldHandlerRegistry.lookup(obj, field); 87 if (handler != null) { 88 handler.read(this, obj, fieldsQueue); 89 } else { 90 fieldsQueue.remove(); 91 } 92 } 93 94 return obj; 95 } 96 97 /*** 98 * reads a little-endien int64. 99 * @return the int. 100 * @throws IOException 101 */ 102 public long readINT64() throws IOException { 103 byte[] data = new byte[8]; 104 if (read(data) != data.length) { 105 throw new IOException("read past end of stream"); 106 } 107 long l1 = (((long) data[0] & 0xff) << 0) 108 | (((long) data[1] & 0xff) << 8) 109 | (((long) data[2] & 0xff) << 16) 110 | (((long) data[3] & 0xff) << 24); 111 long l2 = (((long) data[4] & 0xff) << 0) 112 | (((long) data[5] & 0xff) << 8) 113 | (((long) data[6] & 0xff) << 16) 114 | (((long) data[7] & 0xff) << 24); 115 return (l1 << 0) | (l2 << 32); 116 } 117 118 /*** 119 * reads a little-endien uint64. 120 * @return the int. 121 * @throws IOException 122 * @throws IOException 123 */ 124 public BigInteger readUINT64() throws IOException { 125 byte[] data = new byte[8]; 126 if (read(data) != data.length) { 127 throw new IOException("read past end of stream"); 128 } 129 StringBuffer sb = new StringBuffer(); 130 for (int i = 0; i < data.length; i++) { 131 sb.append(EasyIOUtils.toHexString(data[i], 2)); 132 } 133 return new BigInteger(sb.toString(), 16); 134 } 135 136 /*** 137 * reads a little-endien uint32. 138 * @return the int. 139 * @throws IOException 140 */ 141 public long readUINT32() throws IOException { 142 byte[] data = new byte[4]; 143 if (read(data) != data.length) { 144 throw new IOException("read past end of stream"); 145 } 146 return (((long) data[0] & 0xff) << 0) | (((long) data[1] & 0xff) << 8) 147 | (((long) data[2] & 0xff) << 16) 148 | (((long) data[3] & 0xff) << 24); 149 } 150 151 /*** 152 * reads a little-endien uint16. 153 * @return the int. 154 * @throws IOException 155 */ 156 public int readUINT16() throws IOException { 157 byte[] data = new byte[2]; 158 if (read(data) != data.length) { 159 throw new IOException("read past end of stream"); 160 } 161 return (((int) data[0] & 0xff) << 0) | (((int) data[1] & 0xff) << 8); 162 } 163 164 /*** 165 * reads a uint8. 166 * @return the int. 167 * @throws IOException 168 */ 169 public int readUINT8() throws IOException { 170 byte[] data = new byte[1]; 171 if (read(data) != data.length) { 172 throw new IOException("read past end of stream"); 173 } 174 return (int) data[0] & 0xff; 175 } 176 177 /*** 178 * read the specified number of bits from the input stream. 179 * @param buffer the data array to read it into. 180 * @param bits the number of bits to read. 181 * @return the number of bits read. 182 * @throws IOException 183 */ 184 public int readBits(int bits, byte[] buffer) throws IOException { 185 return readBits(bits, buffer, 0, buffer.length); 186 } 187 188 /*** 189 * read the specified number of bits from the input stream 190 * (the bits will be left aligned within the buffer). 191 * @param buffer the data array to read it into. 192 * @param bits the number of bits to read. 193 * @param offset offset into buffer. 194 * @param length length of buffer. 195 * @return the number of bits read. 196 * @throws IOException 197 */ 198 public int readBits(int bits, byte[] buffer, int offset, int length) 199 throws IOException { 200 int bitsLeftToRead = bits; 201 int bufferIdx = offset; 202 int bitsRead = 0; 203 while (bitsLeftToRead > 0) { 204 int bitsToRead = Math.min(8, bitsLeftToRead); 205 int data = readBits(bitsToRead); 206 bitsRead += bitsToRead; 207 buffer[bufferIdx++] = (byte) data; 208 bitsLeftToRead -= bitsToRead; 209 } 210 return bitsRead; 211 } 212 213 /*** 214 * bit masks for {@link EasyIOInputStream#readBits(int)}. 215 */ 216 private static final int[] BIT_MASKS = new int[] { 0x01, 0x03, 0x07, 0x0f, 217 0x1f, 0x3f, 0x7f, 0xff }; 218 219 /*** 220 * @param bits number of bits to read (<=8). 221 * @return the bits read. 222 * @throws IOException 223 */ 224 public int readBits(int bits) throws IOException { 225 int ret = 0; 226 int bitsToRead = bits; 227 228 if (bits == 0) { 229 return 0; 230 } 231 if (bits > 8) { 232 throw new IOException("bits requested but be <= 8"); 233 } 234 235 while (bitsToRead > 0) { 236 if (this.bitsLeft == 0) { 237 this.currentByte = super.read(); 238 if (this.currentByte == -1) { 239 return -1; 240 } 241 this.bitsLeft = 8; 242 } 243 244 int bitsRead = Math.min(bitsToRead, this.bitsLeft); 245 ret |= this.currentByte >> (this.bitsLeft - bitsRead); 246 bitsToRead -= bitsRead; 247 this.bitsLeft -= bitsRead; 248 ret <<= bitsToRead; 249 } 250 251 return ret & BIT_MASKS[bits - 1]; 252 } 253 254 }