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 }