View Javadoc
1   //******************************************************************************
2   //
3   // File:    ByteSequence.java
4   // Package: edu.rit.util
5   // Unit:    Class edu.rit.util.ByteSequence
6   //
7   // This Java source file is copyright (C) 2006 by Alan Kaminsky. All rights
8   // reserved. For further information, contact the author, Alan Kaminsky, at
9   // ark@cs.rit.edu.
10  //
11  // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12  // software; you can redistribute it and/or modify it under the terms of the GNU
13  // General Public License as published by the Free Software Foundation; either
14  // version 3 of the License, or (at your option) any later version.
15  //
16  // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17  // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19  //
20  // Linking this library statically or dynamically with other modules is making a
21  // combined work based on this library. Thus, the terms and conditions of the GNU
22  // General Public License cover the whole combination.
23  //
24  // As a special exception, the copyright holders of this library give you
25  // permission to link this library with independent modules to produce an
26  // executable, regardless of the license terms of these independent modules, and
27  // to copy and distribute the resulting executable under terms of your choice,
28  // provided that you also meet, for each linked independent module, the terms
29  // and conditions of the license of that module. An independent module is a module
30  // which is not derived from or based on this library. If you modify this library,
31  // you may extend this exception to your version of the library, but you are not
32  // obligated to do so. If you do not wish to do so, delete this exception
33  // statement from your version.
34  //
35  // A copy of the GNU General Public License is provided in the file gpl.txt. You
36  // may also obtain a copy of the GNU General Public License on the World Wide
37  // Web at http://www.gnu.org/licenses/gpl.html.
38  //
39  //******************************************************************************
40  package edu.rit.util;
41  
42  import java.io.DataOutput;
43  import java.io.IOException;
44  import java.io.InputStream;
45  import java.io.OutputStream;
46  import java.util.Iterator;
47  import java.util.LinkedList;
48  
49  /**
50   * Class ByteSequence provides an abstraction for a sequence of bytes. The
51   * contents of the byte sequence are specified at construction time; the
52   * contents may come from a byte array, an input stream, or another byte
53   * sequence. You can obtain the byte sequence's contents as a byte array or
54   * write the byte sequence's contents to an output stream.
55   *
56   * @author Alan Kaminsky
57   * @version 02-Nov-2006
58   */
59  public class ByteSequence {
60  
61  // Hidden data members.
62      private final LinkedList<byte[]> myChunkList = new LinkedList<>();
63      private final LinkedList<Integer> myLengthList = new LinkedList<>();
64      private int myTotalLength;
65  
66  // Exported constructors.
67      /**
68       * Construct a new byte sequence whose contents are a copy of the given byte
69       * array.
70       *
71       * @param buf Byte array to copy.
72       * @exception NullPointerException Thrown if <code>buf</code> is null.
73       */
74      public ByteSequence(byte[] buf) {
75          this(buf, 0, buf.length);
76      }
77  
78      /**
79       * Construct a new byte sequence whose contents are a copy of a portion of
80       * the given byte array.
81       *
82       * @param buf Byte array to copy.
83       * @param off Index of first byte to copy.
84       * @param len Number of bytes to copy.
85       * @exception NullPointerException Thrown if <code>buf</code> is null.
86       * @exception IndexOutOfBoundsException Thrown if <code>off</code> &lt; 0,
87       * <code>len</code> &lt; 0, or
88       * <code>off+len</code> &gt; <code>buf.length</code>.
89       */
90      public ByteSequence(byte[] buf,
91              int off,
92              int len) {
93          if (off < 0 || len < 0 || off + len > buf.length) {
94              throw new IndexOutOfBoundsException();
95          }
96          byte[] chunk = new byte[len];
97          System.arraycopy(buf, off, chunk, 0, len);
98          myChunkList.add(chunk);
99          myLengthList.add(len);
100         myTotalLength = len;
101     }
102 
103     /**
104      * Construct a new byte sequence whose contents come from the given input
105      * stream. Bytes are read from <code>theInputStream</code> into the byte
106      * sequence until the end-of-stream is encountered, then
107      * <code>theInputStream</code> is closed. If <code>theInputStream</code> is null,
108      * the byte sequence's length is 0.
109      *
110      * @param theInputStream Input stream, or null.
111      * @exception IOException Thrown if an I/O error occurred.
112      * @throws java.io.IOException if any.
113      */
114     public ByteSequence(InputStream theInputStream)
115             throws IOException {
116         if (theInputStream != null) {
117             for (;;) {
118                 byte[] chunk = new byte[4096];
119                 int length = theInputStream.read(chunk);
120                 if (length == -1) {
121                     break;
122                 }
123                 myChunkList.add(chunk);
124                 myLengthList.add(length);
125                 myTotalLength += length;
126                 if (myTotalLength < 0) {
127                     throw new IOException("ByteSequence(): Input stream too long");
128                 }
129             }
130             theInputStream.close();
131         }
132     }
133 
134     /**
135      * Construct a new byte sequence whose contents are a copy of the given byte
136      * sequence.
137      *
138      * @param theByteSequence Byte sequence to copy.
139      * @exception NullPointerException Thrown if <code>theByteSequence</code> is
140      * null.
141      */
142     public ByteSequence(ByteSequence theByteSequence) {
143         byte[] chunk = theByteSequence.toByteArray();
144         myChunkList.add(chunk);
145         myLengthList.add(chunk.length);
146         myTotalLength = chunk.length;
147     }
148 
149 // Exported operations.
150     /**
151      * Obtain the length of this byte sequence.
152      *
153      * @return Number of bytes.
154      */
155     public int length() {
156         return myTotalLength;
157     }
158 
159     /**
160      * Obtain a byte array with a copy of this byte sequence's contents. A new
161      * byte array of the proper size is created and returned.
162      *
163      * @return Contents.
164      */
165     public byte[] toByteArray() {
166         byte[] result = new byte[myTotalLength];
167         copy(result);
168         return result;
169     }
170 
171     /**
172      * Copy this byte sequence's contents into the given byte array. Bytes are
173      * copied into <code>buf</code> starting at index 0. The number of bytes copied
174      * is <code>buf.length</code> or this byte sequence's length, whichever is
175      * smaller.
176      *
177      * @param buf Buffer to hold the copy.
178      * @return Actual number of bytes copied.
179      * @exception NullPointerException (unchecked exception) Thrown if
180      * <code>buf</code> is null.
181      */
182     public int copy(byte[] buf) {
183         return copy(buf, 0, buf.length);
184     }
185 
186     /**
187      * Copy this byte sequence's contents into a portion of the given byte
188      * array. Bytes are copied into <code>buf</code> starting at index <code>off</code>.
189      * The number of bytes copied is <code>len</code> or this byte sequence's
190      * length, whichever is smaller.
191      *
192      * @param buf Buffer to hold the copy.
193      * @param off Index in <code>buf</code> at which to start copying.
194      * @param len Maximum number of bytes to copy.
195      * @return Actual number of bytes copied.
196      * @exception NullPointerException (unchecked exception) Thrown if
197      * <code>buf</code> is null.
198      * @exception IndexOutOfBoundsException Thrown if <code>off</code> &lt; 0,
199      * <code>len</code> &lt; 0, or
200      * <code>off+len</code> &gt; <code>buf.length</code>.
201      */
202     public int copy(byte[] buf,
203             int off,
204             int len) {
205         if (off < 0 || len < 0 || off + len > buf.length) {
206             throw new IndexOutOfBoundsException();
207         }
208         int total = 0;
209         Iterator<byte[]> chunkiter = myChunkList.iterator();
210         Iterator<Integer> lengthiter = myLengthList.iterator();
211         while (len > 0 && chunkiter.hasNext()) {
212             byte[] chunk = chunkiter.next();
213             int length = lengthiter.next();
214             int n = Math.min(length, len);
215             System.arraycopy(chunk, 0, buf, off, n);
216             off += n;
217             len -= n;
218             total += n;
219         }
220         return total;
221     }
222 
223     /**
224      * Write this byte sequence's contents to the given output stream.
225      *
226      * @param theOutputStream Output stream.
227      * @exception IOException Thrown if an I/O error occurred.
228      * @throws java.io.IOException if any.
229      */
230     public void write(OutputStream theOutputStream)
231             throws IOException {
232         Iterator<byte[]> chunkiter = myChunkList.iterator();
233         Iterator<Integer> lengthiter = myLengthList.iterator();
234         while (chunkiter.hasNext()) {
235             byte[] chunk = chunkiter.next();
236             int length = lengthiter.next();
237             theOutputStream.write(chunk, 0, length);
238         }
239     }
240 
241     /**
242      * Write this byte sequence's contents to the given data output stream.
243      *
244      * @param theOutputStream Data output stream.
245      * @exception IOException Thrown if an I/O error occurred.
246      * @throws java.io.IOException if any.
247      */
248     public void write(DataOutput theOutputStream)
249             throws IOException {
250         Iterator<byte[]> chunkiter = myChunkList.iterator();
251         Iterator<Integer> lengthiter = myLengthList.iterator();
252         while (chunkiter.hasNext()) {
253             byte[] chunk = chunkiter.next();
254             int length = lengthiter.next();
255             theOutputStream.write(chunk, 0, length);
256         }
257     }
258 
259 }