1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.commons.io.channels;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.nio.ByteBuffer;
26 import java.nio.channels.ClosedChannelException;
27 import java.nio.channels.SeekableByteChannel;
28 import java.util.Arrays;
29 import java.util.Objects;
30 import java.util.concurrent.locks.ReentrantLock;
31
32 import org.apache.commons.io.IOUtils;
33
34
35
36
37
38
39
40
41
42
43
44 public class ByteArraySeekableByteChannel implements SeekableByteChannel {
45
46 private static final int RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public static ByteArraySeekableByteChannel wrap(final byte[] bytes) {
65 Objects.requireNonNull(bytes, "bytes");
66 return new ByteArraySeekableByteChannel(bytes);
67 }
68
69 private byte[] data;
70 private volatile boolean closed;
71 private int position;
72 private int size;
73 private final ReentrantLock lock = new ReentrantLock();
74
75
76
77
78
79
80
81
82
83 public ByteArraySeekableByteChannel() {
84 this(IOUtils.DEFAULT_BUFFER_SIZE);
85 }
86
87 private ByteArraySeekableByteChannel(final byte[] data) {
88 this.data = data;
89 this.position = 0;
90 this.size = data.length;
91 }
92
93
94
95
96
97
98
99
100
101
102 public ByteArraySeekableByteChannel(final int size) {
103 if (size < 0) {
104 throw new IllegalArgumentException("Size must be non-negative");
105 }
106 this.data = new byte[size];
107 this.position = 0;
108 this.size = 0;
109 }
110
111
112
113
114
115
116
117
118
119 public byte[] array() {
120 return data;
121 }
122
123 private void checkOpen() throws ClosedChannelException {
124 if (!isOpen()) {
125 throw new ClosedChannelException();
126 }
127 }
128
129 private int checkRange(final long newSize, final String method) {
130 if (newSize < 0L || newSize > IOUtils.SOFT_MAX_ARRAY_LENGTH) {
131 throw new IllegalArgumentException(String.format("%s must be in range [0..%,d]: %,d", method, IOUtils.SOFT_MAX_ARRAY_LENGTH, newSize));
132 }
133 return (int) newSize;
134 }
135
136 @Override
137 public void close() {
138 closed = true;
139 }
140
141
142
143
144
145
146 public long getSize() {
147 return size;
148 }
149
150 @Override
151 public boolean isOpen() {
152 return !closed;
153 }
154
155 @Override
156 public long position() throws ClosedChannelException {
157 checkOpen();
158 lock.lock();
159 try {
160 return position;
161 } finally {
162 lock.unlock();
163 }
164 }
165
166 @Override
167 public SeekableByteChannel position(final long newPosition) throws IOException {
168 checkOpen();
169 final int intPos = checkRange(newPosition, "position()");
170 lock.lock();
171 try {
172 position = intPos;
173 } finally {
174 lock.unlock();
175 }
176 return this;
177 }
178
179 @Override
180 public int read(final ByteBuffer buf) throws IOException {
181 checkOpen();
182 lock.lock();
183 try {
184 int wanted = buf.remaining();
185 final int possible = size - position;
186 if (possible <= 0) {
187 return IOUtils.EOF;
188 }
189 if (wanted > possible) {
190 wanted = possible;
191 }
192 buf.put(data, position, wanted);
193 position += wanted;
194 return wanted;
195 } finally {
196 lock.unlock();
197 }
198 }
199
200 private void resize(final int newLength) {
201 int len = data.length;
202 if (len == 0) {
203 len = 1;
204 }
205 if (newLength < RESIZE_LIMIT) {
206 while (len < newLength) {
207 len <<= 1;
208 }
209 } else {
210 len = newLength;
211 }
212 data = Arrays.copyOf(data, len);
213 }
214
215 @Override
216 public long size() throws ClosedChannelException {
217 checkOpen();
218 lock.lock();
219 try {
220 return size;
221 } finally {
222 lock.unlock();
223 }
224 }
225
226
227
228
229
230
231
232
233
234 public byte[] toByteArray() {
235 return Arrays.copyOf(data, size);
236 }
237
238 @Override
239 public SeekableByteChannel truncate(final long newSize) throws ClosedChannelException {
240 checkOpen();
241 final int intSize = checkRange(newSize, "truncate()");
242 lock.lock();
243 try {
244 if (size > intSize) {
245 size = intSize;
246 }
247 if (position > intSize) {
248 position = intSize;
249 }
250 } finally {
251 lock.unlock();
252 }
253 return this;
254 }
255
256 @Override
257 public int write(final ByteBuffer b) throws IOException {
258 checkOpen();
259 lock.lock();
260 try {
261 final int wanted = b.remaining();
262 final int possibleWithoutResize = Math.max(0, size - position);
263 if (wanted > possibleWithoutResize) {
264 final int newSize = position + wanted;
265 if (newSize < 0 || newSize > IOUtils.SOFT_MAX_ARRAY_LENGTH) {
266 throw new OutOfMemoryError("required array size " + Integer.toUnsignedString(newSize) + " too large");
267 }
268 resize(newSize);
269 }
270 b.get(data, position, wanted);
271 position += wanted;
272 if (size < position) {
273 size = position;
274 }
275 return wanted;
276 } finally {
277 lock.unlock();
278 }
279 }
280 }