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> < 0,
87 * <code>len</code> < 0, or
88 * <code>off+len</code> > <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> < 0,
199 * <code>len</code> < 0, or
200 * <code>off+len</code> > <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 }