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