1 package nom.tam.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 import java.io.EOFException;
35 import java.io.IOException;
36 import java.util.Arrays;
37
38 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39
40
41
42
43
44
45
46
47 public class ByteArrayIO implements ReadWriteAccess {
48
49
50 private static final int BYTE_MASK = 0xFF;
51
52
53 private byte[] buf;
54
55
56 private boolean isGrowable;
57
58
59 private int pos;
60
61
62 private int end;
63
64
65
66
67
68
69
70 @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "by design this class provides an IO interface for an accessible array.")
71 public ByteArrayIO(byte[] buffer) {
72 buf = buffer;
73 end = 0;
74 isGrowable = false;
75 }
76
77
78
79
80
81
82
83
84 public ByteArrayIO(int initialCapacity) throws IllegalArgumentException {
85 if (initialCapacity <= 0) {
86 throw new IllegalArgumentException("Illegal buffer size:" + initialCapacity);
87 }
88
89 buf = new byte[initialCapacity];
90 end = 0;
91 isGrowable = true;
92 }
93
94
95
96
97
98
99 public synchronized ByteArrayIO copy() {
100 ByteArrayIO copy = new ByteArrayIO(Arrays.copyOf(buf, buf.length));
101 synchronized (copy) {
102 copy.isGrowable = isGrowable;
103 copy.pos = pos;
104 copy.end = end;
105 }
106 return copy;
107 }
108
109
110
111
112
113
114 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "by design this class provides an IO interface for an accessible array.")
115 public synchronized byte[] getBuffer() {
116 return buf;
117 }
118
119
120
121
122
123
124
125 public final synchronized int capacity() {
126 return buf.length;
127 }
128
129 @Override
130 public final synchronized long length() {
131 return end;
132 }
133
134
135
136
137
138
139 public final synchronized int getRemaining() {
140 if (pos >= end) {
141 return 0;
142 }
143 return end - pos;
144 }
145
146 @Override
147 public final synchronized long position() {
148 return pos;
149 }
150
151 @Override
152 public synchronized void position(long offset) throws IOException {
153 if (offset < 0) {
154 throw new EOFException("Negative buffer index: " + offset);
155 }
156
157 if (offset > buf.length) {
158 if (!isGrowable) {
159 throw new EOFException("Position " + offset + " beyond fixed buffer size " + buf.length);
160 }
161 }
162
163 pos = (int) offset;
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public synchronized void setLength(int length) throws IllegalArgumentException {
183 if (length < 0) {
184 throw new IllegalArgumentException("Buffer set to negative length: " + length);
185 }
186
187 if (length > capacity()) {
188 if (!isGrowable) {
189 throw new IllegalArgumentException(
190 "the new length " + length + " is larger than the fixed capacity " + capacity());
191 }
192 grow(length - capacity());
193 }
194 end = length;
195
196
197 if (pos > end) {
198 pos = end;
199 }
200 }
201
202
203
204
205
206
207
208 private synchronized void grow(int need) {
209 long size = capacity() + need;
210 long below = Long.highestOneBit(size);
211 if (below != size) {
212 size = below << 1;
213 }
214 byte[] newbuf = new byte[(int) Math.min(size, Integer.MAX_VALUE)];
215 System.arraycopy(buf, 0, newbuf, 0, buf.length);
216 buf = newbuf;
217 }
218
219 @Override
220 public final synchronized void write(int b) throws IOException {
221 if (pos + 1 > buf.length) {
222 if (!isGrowable) {
223 throw new EOFException("buffer is full (size=" + length() + ")");
224 }
225 grow(pos + 1 - buf.length);
226 }
227 buf[pos++] = (byte) b;
228 if (pos > end) {
229 end = pos;
230 }
231 }
232
233 @Override
234 public final synchronized void write(byte[] b, int from, int length) throws IOException {
235 if (length <= 0) {
236 return;
237 }
238
239 if (pos > buf.length || (isGrowable && pos + length > buf.length)) {
240
241 grow(buf.length + length - pos);
242 }
243
244 int l = Math.min(length, buf.length - pos);
245
246 System.arraycopy(b, from, buf, pos, l);
247 pos += l;
248 if (pos > end) {
249 end = pos;
250 }
251
252 if (l < length) {
253 throw new EOFException("Incomplete write of " + l + " of " + length + " bytes in buffer of size " + length());
254 }
255 }
256
257 @Override
258 public final synchronized int read() throws IOException {
259 if (getRemaining() <= 0) {
260 return -1;
261 }
262 return buf[pos++] & BYTE_MASK;
263 }
264
265 @Override
266 public final synchronized int read(byte[] b, int from, int length) {
267 if (length <= 0) {
268 return 0;
269 }
270
271 int remaining = getRemaining();
272
273 if (remaining <= 0) {
274 return -1;
275 }
276
277 int n = Math.min(remaining, length);
278 System.arraycopy(buf, pos, b, from, n);
279 pos += n;
280
281 return n;
282 }
283 }