View Javadoc

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 }