001 /*
002 * Copyright (C) 2008 Trustin Heuiseung Lee
003 *
004 * This library is free software; you can redistribute it and/or
005 * modify it under the terms of the GNU Lesser General Public
006 * License as published by the Free Software Foundation; either
007 * version 2.1 of the License, or (at your option) any later version.
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this library; if not, write to the Free Software
016 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA
017 */
018 package net.gleamynode.netty.buffer;
019
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.io.OutputStream;
023 import java.nio.ByteBuffer;
024 import java.nio.ByteOrder;
025 import java.nio.channels.GatheringByteChannel;
026 import java.nio.channels.ScatteringByteChannel;
027 import java.util.ArrayList;
028 import java.util.List;
029
030
031 /**
032 * @author The Netty Project (netty@googlegroups.com)
033 * @author Trustin Lee (trustin@gmail.com)
034 *
035 * @version $Rev$, $Date$
036 *
037 */
038 public class CompositeChannelBuffer extends AbstractChannelBuffer {
039
040 private final ChannelBuffer[] slices;
041 private final int[] indices;
042 private int lastSliceId;
043
044 public CompositeChannelBuffer(ChannelBuffer... buffers) {
045 if (buffers.length == 0) {
046 throw new IllegalArgumentException("buffers should not be empty.");
047 }
048
049 ByteOrder expectedEndianness = buffers[0].order();
050 slices = new ChannelBuffer[buffers.length];
051 for (int i = 0; i < buffers.length; i ++) {
052 if (buffers[i].order() != expectedEndianness) {
053 throw new IllegalArgumentException(
054 "All buffers must have the same endianness.");
055 }
056 slices[i] = buffers[i].slice();
057 }
058 indices = new int[buffers.length + 1];
059 for (int i = 1; i <= buffers.length; i ++) {
060 indices[i] = indices[i - 1] + slices[i - 1].capacity();
061 }
062 writerIndex(capacity());
063 }
064
065 private CompositeChannelBuffer(CompositeChannelBuffer buffer) {
066 slices = buffer.slices.clone();
067 indices = buffer.indices.clone();
068 setIndex(buffer.readerIndex(), buffer.writerIndex());
069 }
070
071 public ByteOrder order() {
072 return slices[0].order();
073 }
074
075 public int capacity() {
076 return indices[slices.length];
077 }
078
079 public byte getByte(int index) {
080 int sliceId = sliceId(index);
081 return slices[sliceId].getByte(index - indices[sliceId]);
082 }
083
084 public short getShort(int index) {
085 int sliceId = sliceId(index);
086 if (index + 2 <= indices[sliceId + 1]) {
087 return slices[sliceId].getShort(index - indices[sliceId]);
088 } else if (order() == ByteOrder.BIG_ENDIAN) {
089 return (short) ((getByte(index) & 0xff) << 8 | getByte(index + 1) & 0xff);
090 } else {
091 return (short) (getByte(index) & 0xff | (getByte(index + 1) & 0xff) << 8);
092 }
093 }
094
095 public int getMedium(int index) {
096 int sliceId = sliceId(index);
097 if (index + 3 <= indices[sliceId + 1]) {
098 return slices[sliceId].getMedium(index - indices[sliceId]);
099 } else if (order() == ByteOrder.BIG_ENDIAN) {
100 return (getShort(index) & 0xffff) << 8 | getByte(index + 2) & 0xff;
101 } else {
102 return getShort(index) & 0xFFFF | (getByte(index + 2) & 0xFF) << 16;
103 }
104 }
105
106 public int getInt(int index) {
107 int sliceId = sliceId(index);
108 if (index + 4 <= indices[sliceId + 1]) {
109 return slices[sliceId].getInt(index - indices[sliceId]);
110 } else if (order() == ByteOrder.BIG_ENDIAN) {
111 return (getShort(index) & 0xffff) << 16 | getShort(index + 2) & 0xffff;
112 } else {
113 return getShort(index) & 0xFFFF | (getShort(index + 2) & 0xFFFF) << 16;
114 }
115 }
116
117 public long getLong(int index) {
118 int sliceId = sliceId(index);
119 if (index + 8 <= indices[sliceId + 1]) {
120 return slices[sliceId].getLong(index - indices[sliceId]);
121 } else if (order() == ByteOrder.BIG_ENDIAN) {
122 return (getInt(index) & 0xffffffffL) << 32 | getInt(index + 4) & 0xffffffffL;
123 } else {
124 return getInt(index) & 0xFFFFFFFFL | (getInt(index + 4) & 0xFFFFFFFFL) << 32;
125 }
126 }
127
128 public void getBytes(int index, byte[] dst, int dstIndex, int length) {
129 int sliceId = sliceId(index);
130 if (index + length >= capacity()) {
131 throw new IndexOutOfBoundsException();
132 }
133
134 int i = sliceId;
135 while (length > 0) {
136 ChannelBuffer s = slices[i];
137 int adjustment = indices[i];
138 int localLength = Math.min(length, s.capacity() - (index - adjustment));
139 s.getBytes(index - adjustment, dst, dstIndex, localLength);
140 index += localLength;
141 dstIndex += localLength;
142 length -= localLength;
143 i ++;
144 }
145 }
146
147 public void getBytes(int index, ByteBuffer dst) {
148 int sliceId = sliceId(index);
149 int limit = dst.limit();
150 int length = dst.remaining();
151 if (index + length >= capacity()) {
152 throw new IndexOutOfBoundsException();
153 }
154
155 int i = sliceId;
156 try {
157 while (length > 0) {
158 ChannelBuffer s = slices[i];
159 int adjustment = indices[i];
160 int localLength = Math.min(length, s.capacity() - (index - adjustment));
161 dst.limit(dst.position() + localLength);
162 s.getBytes(index - adjustment, dst);
163 index += localLength;
164 length -= localLength;
165 i ++;
166 }
167 } finally {
168 dst.limit(limit);
169 }
170 }
171
172 public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) {
173 int sliceId = sliceId(index);
174 if (index + length >= capacity()) {
175 throw new IndexOutOfBoundsException();
176 }
177
178 int i = sliceId;
179 while (length > 0) {
180 ChannelBuffer s = slices[i];
181 int adjustment = indices[i];
182 int localLength = Math.min(length, s.capacity() - (index - adjustment));
183 s.getBytes(index - adjustment, dst, dstIndex, localLength);
184 index += localLength;
185 dstIndex += localLength;
186 length -= localLength;
187 i ++;
188 }
189 }
190
191 public int getBytes(int index, GatheringByteChannel out, int length)
192 throws IOException {
193 // XXX Gathering write is not supported because of a known issue.
194 // See http://bugs.sun.com/view_bug.do?bug_id=6210541
195 return out.write(toByteBuffer());
196 }
197
198 public void getBytes(int index, OutputStream out, int length)
199 throws IOException {
200 int sliceId = sliceId(index);
201 if (index + length >= capacity()) {
202 throw new IndexOutOfBoundsException();
203 }
204
205 int i = sliceId;
206 while (length > 0) {
207 ChannelBuffer s = slices[i];
208 int adjustment = indices[i];
209 int localLength = Math.min(length, s.capacity() - (index - adjustment));
210 s.getBytes(index - adjustment, out, localLength);
211 index += localLength;
212 length -= localLength;
213 i ++;
214 }
215 }
216
217 public void setByte(int index, byte value) {
218 int sliceId = sliceId(index);
219 slices[sliceId].setByte(index - indices[sliceId], value);
220 }
221
222 public void setShort(int index, short value) {
223 int sliceId = sliceId(index);
224 if (index + 2 <= indices[sliceId + 1]) {
225 slices[sliceId].setShort(index - indices[sliceId], value);
226 } else if (order() == ByteOrder.BIG_ENDIAN) {
227 setByte(index, (byte) (value >>> 8));
228 setByte(index + 1, (byte) value);
229 } else {
230 setByte(index , (byte) value);
231 setByte(index + 1, (byte) (value >>> 8));
232 }
233 }
234
235 public void setMedium(int index, int value) {
236 int sliceId = sliceId(index);
237 if (index + 3 <= indices[sliceId + 1]) {
238 slices[sliceId].setMedium(index - indices[sliceId], value);
239 } else if (order() == ByteOrder.BIG_ENDIAN) {
240 setShort(index, (short) (value >>> 8));
241 setByte(index + 2, (byte) value);
242 } else {
243 setShort(index , (short) value);
244 setByte (index + 2, (byte) (value >>> 16));
245 }
246 }
247
248 public void setInt(int index, int value) {
249 int sliceId = sliceId(index);
250 if (index + 4 <= indices[sliceId + 1]) {
251 slices[sliceId].setInt(index - indices[sliceId], value);
252 } else if (order() == ByteOrder.BIG_ENDIAN) {
253 setShort(index, (short) (value >>> 16));
254 setShort(index + 2, (short) value);
255 } else {
256 setShort(index , (short) value);
257 setShort(index + 2, (short) (value >>> 16));
258 }
259 }
260
261 public void setLong(int index, long value) {
262 int sliceId = sliceId(index);
263 if (index + 8 <= indices[sliceId + 1]) {
264 slices[sliceId].setLong(index - indices[sliceId], value);
265 } else if (order() == ByteOrder.BIG_ENDIAN) {
266 setInt(index, (int) (value >>> 32));
267 setInt(index + 4, (int) value);
268 } else {
269 setInt(index , (int) value);
270 setInt(index + 4, (int) (value >>> 32));
271 }
272 }
273
274 public void setBytes(int index, byte[] src, int srcIndex, int length) {
275 int sliceId = sliceId(index);
276 if (index + length >= capacity()) {
277 throw new IndexOutOfBoundsException();
278 }
279
280 int i = sliceId;
281 while (length > 0) {
282 ChannelBuffer s = slices[i];
283 int adjustment = indices[i];
284 int localLength = Math.min(length, s.capacity() - (index - adjustment));
285 s.setBytes(index - adjustment, src, srcIndex, localLength);
286 index += localLength;
287 srcIndex += localLength;
288 length -= localLength;
289 i ++;
290 }
291 }
292
293 public void setBytes(int index, ByteBuffer src) {
294 int sliceId = sliceId(index);
295 int limit = src.limit();
296 int length = src.remaining();
297 if (index + length >= capacity()) {
298 throw new IndexOutOfBoundsException();
299 }
300
301 int i = sliceId;
302 try {
303 while (length > 0) {
304 ChannelBuffer s = slices[i];
305 int adjustment = indices[i];
306 int localLength = Math.min(length, s.capacity() - (index - adjustment));
307 src.limit(src.position() + localLength);
308 s.setBytes(index - adjustment, src);
309 index += localLength;
310 length -= localLength;
311 i ++;
312 }
313 } finally {
314 src.limit(limit);
315 }
316 }
317
318 public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) {
319 int sliceId = sliceId(index);
320 if (index + length >= capacity()) {
321 throw new IndexOutOfBoundsException();
322 }
323
324 int i = sliceId;
325 while (length > 0) {
326 ChannelBuffer s = slices[i];
327 int adjustment = indices[i];
328 int localLength = Math.min(length, s.capacity() - (index - adjustment));
329 s.setBytes(index - adjustment, src, srcIndex, localLength);
330 index += localLength;
331 srcIndex += localLength;
332 length -= localLength;
333 i ++;
334 }
335 }
336
337 public void setBytes(int index, InputStream in, int length)
338 throws IOException {
339 int sliceId = sliceId(index);
340 if (index + length >= capacity()) {
341 throw new IndexOutOfBoundsException();
342 }
343
344 int i = sliceId;
345 while (length > 0) {
346 ChannelBuffer s = slices[i];
347 int adjustment = indices[i];
348 int localLength = Math.min(length, s.capacity() - (index - adjustment));
349 s.setBytes(index - adjustment, in, localLength);
350 index += localLength;
351 length -= localLength;
352 i ++;
353 }
354 }
355
356 public int setBytes(int index, ScatteringByteChannel in, int length)
357 throws IOException {
358 int sliceId = sliceId(index);
359 if (index + length >= capacity()) {
360 throw new IndexOutOfBoundsException();
361 }
362
363 int i = sliceId;
364 int writtenBytes = 0;
365 while (length > 0) {
366 ChannelBuffer s = slices[i];
367 int adjustment = indices[i];
368 int localLength = Math.min(length, s.capacity() - (index - adjustment));
369 int localWrittenBytes = s.setBytes(index - adjustment, in, localLength);
370 writtenBytes += localWrittenBytes;
371 if (localLength != localWrittenBytes) {
372 break;
373 }
374 index += localLength;
375 length -= localLength;
376 i ++;
377 }
378
379 return writtenBytes;
380 }
381
382 public ChannelBuffer duplicate() {
383 return new CompositeChannelBuffer(this);
384 }
385
386 public ChannelBuffer copy(int index, int length) {
387 int sliceId = sliceId(index);
388 if (index + length >= capacity()) {
389 throw new IndexOutOfBoundsException();
390 }
391
392 ChannelBuffer dst = ChannelBuffers.buffer(length);
393 int dstIndex = 0;
394 int i = sliceId;
395
396 while (length > 0) {
397 ChannelBuffer s = slices[i];
398 int adjustment = indices[i];
399 int localLength = Math.min(length, s.capacity() - (index - adjustment));
400 s.getBytes(index - adjustment, dst, dstIndex, localLength);
401 index += localLength;
402 dstIndex += localLength;
403 length -= localLength;
404 i ++;
405 }
406
407 dst.writerIndex(dst.capacity());
408 return dst;
409 }
410
411 public ChannelBuffer slice(int index, int length) {
412 return new SlicedChannelBuffer(this, index, length);
413 }
414
415 public ByteBuffer toByteBuffer(int index, int length) {
416 ByteBuffer[] buffers = toByteBuffers(index, length);
417 ByteBuffer merged = ByteBuffer.allocate(length);
418 for (ByteBuffer b: buffers) {
419 merged.put(b);
420 }
421 merged.flip();
422 return merged;
423 }
424
425 @Override
426 public ByteBuffer[] toByteBuffers(int index, int length) {
427 int sliceId = sliceId(index);
428 if (index + length > capacity()) {
429 throw new IndexOutOfBoundsException();
430 }
431
432 List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(slices.length);
433
434 int i = sliceId;
435 while (length > 0) {
436 ChannelBuffer s = slices[i];
437 int adjustment = indices[i];
438 int localLength = Math.min(length, s.capacity() - (index - adjustment));
439 buffers.add(s.toByteBuffer(index - adjustment, localLength));
440 index += localLength;
441 length -= localLength;
442 i ++;
443 }
444
445 return buffers.toArray(new ByteBuffer[buffers.size()]);
446 }
447
448 private int sliceId(int index) {
449 int lastSliceId = this.lastSliceId;
450 if (index >= indices[lastSliceId]) {
451 if (index < indices[lastSliceId + 1]) {
452 return lastSliceId;
453 }
454
455 // Search right
456 for (int i = lastSliceId + 1; i < slices.length; i ++) {
457 if (index < indices[i + 1]) {
458 this.lastSliceId = i;
459 return i;
460 }
461 }
462 } else {
463 // Search left
464 for (int i = lastSliceId - 1; i >= 0; i --) {
465 if (index >= indices[i]) {
466 this.lastSliceId = i;
467 return i;
468 }
469 }
470 }
471
472 throw new IndexOutOfBoundsException();
473 }
474 }