1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.compress.utils;
19
20 import java.io.IOException;
21 import java.nio.ByteBuffer;
22 import java.nio.channels.ClosedChannelException;
23 import java.nio.channels.SeekableByteChannel;
24 import java.util.Arrays;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27
28
29
30
31
32
33
34
35
36
37
38 public class SeekableInMemoryByteChannel implements SeekableByteChannel {
39
40 private static final int NAIVE_RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
41
42 private byte[] data;
43 private final AtomicBoolean closed = new AtomicBoolean();
44 private int position, size;
45
46
47
48
49 public SeekableInMemoryByteChannel() {
50 this(ByteUtils.EMPTY_BYTE_ARRAY);
51 }
52
53
54
55
56
57
58
59
60
61 public SeekableInMemoryByteChannel(final byte[] data) {
62 this.data = data;
63 this.size = data.length;
64 }
65
66
67
68
69
70
71
72
73
74 public SeekableInMemoryByteChannel(final int size) {
75 this(new byte[size]);
76 }
77
78
79
80
81
82
83
84
85
86 public byte[] array() {
87 return data;
88 }
89
90 @Override
91 public void close() {
92 closed.set(true);
93 }
94
95 private void ensureOpen() throws ClosedChannelException {
96 if (!isOpen()) {
97 throw new ClosedChannelException();
98 }
99 }
100
101 @Override
102 public boolean isOpen() {
103 return !closed.get();
104 }
105
106
107
108
109
110
111
112
113 @Override
114 public long position() {
115 return position;
116 }
117
118 @Override
119 public SeekableByteChannel position(final long newPosition) throws IOException {
120 ensureOpen();
121 if (newPosition < 0L || newPosition > Integer.MAX_VALUE) {
122 throw new IOException("Position has to be in range 0.. " + Integer.MAX_VALUE);
123 }
124 position = (int) newPosition;
125 return this;
126 }
127
128 @Override
129 public int read(final ByteBuffer buf) throws IOException {
130 ensureOpen();
131 int wanted = buf.remaining();
132 final int possible = size - position;
133 if (possible <= 0) {
134 return -1;
135 }
136 if (wanted > possible) {
137 wanted = possible;
138 }
139 buf.put(data, position, wanted);
140 position += wanted;
141 return wanted;
142 }
143
144 private void resize(final int newLength) {
145 int len = data.length;
146 if (len <= 0) {
147 len = 1;
148 }
149 if (newLength < NAIVE_RESIZE_LIMIT) {
150 while (len < newLength) {
151 len <<= 1;
152 }
153 } else {
154 len = newLength;
155 }
156 data = Arrays.copyOf(data, len);
157 }
158
159
160
161
162
163
164
165
166 @Override
167 public long size() {
168 return size;
169 }
170
171
172
173
174
175
176
177
178
179 @Override
180 public SeekableByteChannel truncate(final long newSize) {
181 if (newSize < 0L || newSize > Integer.MAX_VALUE) {
182 throw new IllegalArgumentException("Size has to be in range 0.. " + Integer.MAX_VALUE);
183 }
184 if (size > newSize) {
185 size = (int) newSize;
186 }
187 if (position > newSize) {
188 position = (int) newSize;
189 }
190 return this;
191 }
192
193 @Override
194 public int write(final ByteBuffer b) throws IOException {
195 ensureOpen();
196 int wanted = b.remaining();
197 final int possibleWithoutResize = size - position;
198 if (wanted > possibleWithoutResize) {
199 final int newSize = position + wanted;
200 if (newSize < 0) {
201 resize(Integer.MAX_VALUE);
202 wanted = Integer.MAX_VALUE - position;
203 } else {
204 resize(newSize);
205 }
206 }
207 b.get(data, position, wanted);
208 position += wanted;
209 if (size < position) {
210 size = position;
211 }
212 return wanted;
213 }
214
215 }