1 /*
2 * JBoss, Home of Professional Open Source
3 *
4 * Copyright 2008 Red Hat, Inc. and/or its affiliates.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.xnio;
20
21 import java.io.DataInput;
22 import java.io.DataOutput;
23 import java.io.InputStream;
24 import java.io.InterruptedIOException;
25 import java.io.OutputStream;
26 import java.nio.Buffer;
27 import java.nio.BufferUnderflowException;
28 import java.nio.ByteBuffer;
29 import java.nio.CharBuffer;
30 import java.nio.IntBuffer;
31 import java.nio.LongBuffer;
32 import java.nio.ReadOnlyBufferException;
33 import java.nio.ShortBuffer;
34 import java.nio.BufferOverflowException;
35 import java.nio.charset.CharsetDecoder;
36 import java.nio.charset.CoderResult;
37 import java.util.Arrays;
38 import java.io.IOException;
39 import java.util.Random;
40 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
41
42 import static org.xnio._private.Messages.msg;
43
44 import org.wildfly.common.ref.CleanerReference;
45 import org.wildfly.common.ref.Reaper;
46 import org.wildfly.common.ref.Reference;
47
48 /**
49 * Buffer utility methods.
50 *
51 * @apiviz.exclude
52 */
53 public final class Buffers {
54 private Buffers() {}
55
56 /**
57 * Flip a buffer.
58 *
59 * @see Buffer#flip()
60 * @param <T> the buffer type
61 * @param buffer the buffer to flip
62 * @return the buffer instance
63 */
64 public static <T extends Buffer> T flip(T buffer) {
65 buffer.flip();
66 return buffer;
67 }
68
69 /**
70 * Clear a buffer.
71 *
72 * @see Buffer#clear()
73 * @param <T> the buffer type
74 * @param buffer the buffer to clear
75 * @return the buffer instance
76 */
77 public static <T extends Buffer> T clear(T buffer) {
78 buffer.clear();
79 return buffer;
80 }
81
82 /**
83 * Set the buffer limit.
84 *
85 * @see Buffer#limit(int)
86 * @param <T> the buffer type
87 * @param buffer the buffer to set
88 * @param limit the new limit
89 * @return the buffer instance
90 */
91 public static <T extends Buffer> T limit(T buffer, int limit) {
92 buffer.limit(limit);
93 return buffer;
94 }
95
96 /**
97 * Set the buffer mark.
98 *
99 * @see Buffer#mark()
100 * @param <T> the buffer type
101 * @param buffer the buffer to mark
102 * @return the buffer instance
103 */
104 public static <T extends Buffer> T mark(T buffer) {
105 buffer.mark();
106 return buffer;
107 }
108
109 /**
110 * Set the buffer position.
111 *
112 * @see Buffer#position(int)
113 * @param <T> the buffer type
114 * @param buffer the buffer to set
115 * @param position the new position
116 * @return the buffer instance
117 */
118 public static <T extends Buffer> T position(T buffer, int position) {
119 buffer.position(position);
120 return buffer;
121 }
122
123 /**
124 * Reset the buffer.
125 *
126 * @see Buffer#reset()
127 * @param <T> the buffer type
128 * @param buffer the buffer to reset
129 * @return the buffer instance
130 */
131 public static <T extends Buffer> T reset(T buffer) {
132 buffer.reset();
133 return buffer;
134 }
135
136 /**
137 * Rewind the buffer.
138 *
139 * @see Buffer#rewind()
140 * @param <T> the buffer type
141 * @param buffer the buffer to rewind
142 * @return the buffer instance
143 */
144 public static <T extends Buffer> T rewind(T buffer) {
145 buffer.rewind();
146 return buffer;
147 }
148
149 /**
150 * Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
151 *
152 * @see ByteBuffer#slice()
153 * @param buffer the buffer to slice
154 * @param sliceSize the size of the slice
155 * @return the buffer slice
156 */
157 public static ByteBuffer slice(ByteBuffer buffer, int sliceSize) {
158 final int oldRem = buffer.remaining();
159 if (sliceSize > oldRem || sliceSize < -oldRem) {
160 throw msg.bufferUnderflow();
161 }
162 final int oldPos = buffer.position();
163 final int oldLim = buffer.limit();
164 if (sliceSize < 0) {
165 // count from end (sliceSize is NEGATIVE)
166 buffer.limit(oldLim + sliceSize);
167 try {
168 return buffer.slice();
169 } finally {
170 buffer.limit(oldLim);
171 buffer.position(oldLim + sliceSize);
172 }
173 } else {
174 // count from start
175 buffer.limit(oldPos + sliceSize);
176 try {
177 return buffer.slice();
178 } finally {
179 buffer.limit(oldLim);
180 buffer.position(oldPos + sliceSize);
181 }
182 }
183 }
184
185 /**
186 * Copy a portion of the buffer into a newly allocated buffer. The original buffer's position will be moved up past the copy that was taken.
187 *
188 * @param buffer the buffer to slice
189 * @param count the size of the copy
190 * @param allocator the buffer allocator to use
191 * @return the buffer slice
192 */
193 public static ByteBuffer copy(ByteBuffer buffer, int count, BufferAllocator<ByteBuffer> allocator) {
194 final int oldRem = buffer.remaining();
195 if (count > oldRem || count < -oldRem) {
196 throw msg.bufferUnderflow();
197 }
198 final int oldPos = buffer.position();
199 final int oldLim = buffer.limit();
200 if (count < 0) {
201 // count from end (sliceSize is NEGATIVE)
202 final ByteBuffer target = allocator.allocate(-count);
203 buffer.position(oldLim + count);
204 try {
205 target.put(buffer);
206 return target;
207 } finally {
208 buffer.limit(oldLim);
209 buffer.position(oldLim + count);
210 }
211 } else {
212 // count from start
213 final ByteBuffer target = allocator.allocate(count);
214 buffer.limit(oldPos + count);
215 try {
216 target.put(buffer);
217 return target;
218 } finally {
219 buffer.limit(oldLim);
220 buffer.position(oldPos + count);
221 }
222 }
223 }
224
225 /**
226 * Copy as many bytes as possible from {@code source} into {@code destination}.
227 *
228 * @param destination the destination buffer
229 * @param source the source buffer
230 * @return the number of bytes put into the destination buffer
231 */
232 public static int copy(final ByteBuffer destination, final ByteBuffer source) {
233 final int sr = source.remaining();
234 final int dr = destination.remaining();
235 if (dr >= sr) {
236 destination.put(source);
237 return sr;
238 } else {
239 destination.put(slice(source, dr));
240 return dr;
241 }
242 }
243
244 /**
245 * Copy as many bytes as possible from {@code sources} into {@code destinations} in a "scatter" fashion.
246 *
247 * @param destinations the destination buffers
248 * @param offset the offset into the destination buffers array
249 * @param length the number of buffers to update
250 * @param source the source buffer
251 * @return the number of bytes put into the destination buffers
252 */
253 public static int copy(final ByteBuffer[] destinations, final int offset, final int length, final ByteBuffer source) {
254 int t = 0;
255 for (int i = 0; i < length; i ++) {
256 final ByteBuffer buffer = destinations[i + offset];
257 final int rem = buffer.remaining();
258 if (rem == 0) {
259 continue;
260 } else if (rem < source.remaining()) {
261 buffer.put(slice(source, rem));
262 t += rem;
263 } else {
264 t += source.remaining();
265 buffer.put(source);
266 return t;
267 }
268 }
269 return t;
270 }
271
272 /**
273 * Copy as many bytes as possible from {@code sources} into {@code destination} in a "gather" fashion.
274 *
275 * @param destination the destination buffer
276 * @param sources the source buffers
277 * @param offset the offset into the source buffers array
278 * @param length the number of buffers to read from
279 * @return the number of bytes put into the destination buffers
280 */
281 public static int copy(final ByteBuffer destination, final ByteBuffer[] sources, final int offset, final int length) {
282 int t = 0;
283 for (int i = 0; i < length; i ++) {
284 final ByteBuffer buffer = sources[i + offset];
285 final int rem = buffer.remaining();
286 if (rem == 0) {
287 continue;
288 } else if (rem > destination.remaining()) {
289 t += destination.remaining();
290 destination.put(slice(buffer, destination.remaining()));
291 return t;
292 } else {
293 destination.put(buffer);
294 t += rem;
295 }
296 }
297 return t;
298 }
299
300 /**
301 * Copy as many bytes as possible from {@code sources} into {@code destinations} by a combined "scatter"/"gather" operation.
302 *
303 * @param destinations the destination buffers
304 * @param destOffset the offset into the destination buffers array
305 * @param destLength the number of buffers to write to
306 * @param sources the source buffers
307 * @param srcOffset the offset into the source buffers array
308 * @param srcLength the number of buffers to read from
309 * @return the number of bytes put into the destination buffers
310 */
311 public static long copy(final ByteBuffer[] destinations, final int destOffset, final int destLength, final ByteBuffer[] sources, final int srcOffset, final int srcLength) {
312 long t = 0L;
313 int s = 0, d = 0;
314 if (destLength == 0 || srcLength == 0) {
315 return 0L;
316 }
317 ByteBuffer source = sources[srcOffset];
318 ByteBuffer dest = destinations[destOffset];
319 while (s < srcLength && d < destLength) {
320 source = sources[srcOffset + s];
321 dest = destinations[destOffset + d];
322 final int sr = source.remaining();
323 final int dr = dest.remaining();
324 if (sr < dr) {
325 dest.put(source);
326 s++;
327 t += sr;
328 } else if (sr > dr) {
329 dest.put(slice(source, dr));
330 d++;
331 t += dr;
332 } else {
333 dest.put(source);
334 s++;
335 d++;
336 t += sr;
337 }
338 }
339 return t;
340 }
341
342 /**
343 * Copy at most {@code count} bytes from {@code source} into {@code destination}.
344 *
345 * @param count the maximum number of bytes to copy
346 * @param destination the destination buffer
347 * @param source the source buffer
348 * @return the number of bytes put into the destination buffer
349 */
350 public static int copy(int count, final ByteBuffer destination, final ByteBuffer source) {
351 int cnt = count >= 0? Math.min(Math.min(count, source.remaining()), destination.remaining()):
352 Math.max(Math.max(count, - source.remaining()), - destination.remaining());
353 final ByteBuffer copy = slice(source, cnt);
354 destination.put(copy);
355 return copy.position(); // cnt could be negative, so it is safer to return copy.position() instead of cnt
356 }
357
358 /**
359 * Copy at most {@code count} bytes from {@code sources} into {@code destinations} in a "scatter" fashion.
360 *
361 * @param count the maximum number of bytes to copy
362 * @param destinations the destination buffers
363 * @param offset the offset into the destination buffers array
364 * @param length the number of buffers to update
365 * @param source the source buffer
366 * @return the number of bytes put into the destination buffers
367 */
368 public static int copy(int count, final ByteBuffer[] destinations, final int offset, final int length, final ByteBuffer source) {
369 if (source.remaining() > count) {
370 final int oldLimit = source.limit();
371 if (count < 0) {
372 // count from end (count is NEGATIVE)
373 throw msg.copyNegative();
374 } else {
375 try {
376 source.limit(source.position() + count);
377 return copy(destinations, offset, length, source);
378 } finally {
379 source.limit(oldLimit);
380 }
381 }
382 } else {
383 return copy(destinations, offset, length, source);
384 }
385 }
386
387 /**
388 * Copy at most {@code count} bytes from {@code sources} into {@code destination} in a "gather" fashion.
389 *
390 * @param count the maximum number of bytes to copy
391 * @param destination the destination buffer
392 * @param sources the source buffers
393 * @param offset the offset into the source buffers array
394 * @param length the number of buffers to read from
395 * @return the number of bytes put into the destination buffers
396 */
397 public static int copy(int count, final ByteBuffer destination, final ByteBuffer[] sources, final int offset, final int length) {
398 if (destination.remaining() > count) {
399 if (count < 0) {
400 // count from end (count is NEGATIVE)
401 throw msg.copyNegative();
402 } else {
403 final int oldLimit = destination.limit();
404 try {
405 destination.limit(destination.position() + Math.min(count, destination.remaining()));
406 return copy(destination, sources, offset, length);
407 } finally {
408 destination.limit(oldLimit);
409 }
410 }
411 } else {
412 return copy(destination, sources, offset, length);
413 }
414 }
415
416 /**
417 * Copy at most {@code count} bytes from {@code sources} into {@code destinations} by a combined "scatter"/"gather" operation.
418 *
419 * @param count the maximum number of bytes to copy
420 * @param destinations the destination buffers
421 * @param destOffset the offset into the destination buffers array
422 * @param destLength the number of buffers to write to
423 * @param sources the source buffers
424 * @param srcOffset the offset into the source buffers array
425 * @param srcLength the number of buffers to read from
426 * @return the number of bytes put into the destination buffers
427 */
428 public static long copy(long count, final ByteBuffer[] destinations, final int destOffset, final int destLength, final ByteBuffer[] sources, final int srcOffset, final int srcLength) {
429 long t = 0L;
430 int s = 0, d = 0;
431 if (count < 0) {
432 // count from end (count is NEGATIVE)
433 throw msg.copyNegative();
434 }
435 if (destLength == 0 || srcLength == 0 || count == 0L) {
436 return 0L;
437 }
438 while (s < srcLength && d < destLength) {
439 final ByteBuffer source = sources[srcOffset + s];
440 final ByteBuffer dest = destinations[destOffset + d];
441 final int sr = source.remaining();
442 final int dr = (int) Math.min(count, (long) dest.remaining());
443 if (sr < dr) {
444 dest.put(source);
445 s++;
446 t += sr;
447 count -= (long)sr;
448 } else if (sr > dr) {
449 dest.put(slice(source, dr));
450 d++;
451 t += dr;
452 count -= (long)dr;
453 } else {
454 dest.put(source);
455 s++;
456 d++;
457 t += sr;
458 count -= (long)sr;
459 }
460 }
461 return t;
462 }
463
464 /**
465 * Fill a buffer with a repeated value.
466 *
467 * @param buffer the buffer to fill
468 * @param value the value to fill
469 * @param count the number of bytes to fill
470 * @return the buffer instance
471 */
472 public static ByteBuffer fill(ByteBuffer buffer, int value, int count) {
473 if (count > buffer.remaining()) {
474 throw msg.bufferUnderflow();
475 }
476 if (buffer.hasArray()) {
477 final int offs = buffer.arrayOffset();
478 Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), (byte) value);
479 skip(buffer, count);
480 } else {
481 for (int i = count; i > 0; i--) {
482 buffer.put((byte)value);
483 }
484 }
485 return buffer;
486 }
487
488 /**
489 * Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
490 *
491 * @see CharBuffer#slice()
492 * @param buffer the buffer to slice
493 * @param sliceSize the size of the slice
494 * @return the buffer slice
495 */
496 public static CharBuffer slice(CharBuffer buffer, int sliceSize) {
497 if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
498 throw msg.bufferUnderflow();
499 }
500 final int oldPos = buffer.position();
501 final int oldLim = buffer.limit();
502 if (sliceSize < 0) {
503 // count from end (sliceSize is NEGATIVE)
504 buffer.limit(oldLim + sliceSize);
505 try {
506 return buffer.slice();
507 } finally {
508 buffer.limit(oldLim);
509 buffer.position(oldLim + sliceSize);
510 }
511 } else {
512 // count from start
513 buffer.limit(oldPos + sliceSize);
514 try {
515 return buffer.slice();
516 } finally {
517 buffer.limit(oldLim);
518 buffer.position(oldPos + sliceSize);
519 }
520 }
521 }
522
523 /**
524 * Fill a buffer with a repeated value.
525 *
526 * @param buffer the buffer to fill
527 * @param value the value to fill
528 * @param count the number of chars to fill
529 * @return the buffer instance
530 */
531 public static CharBuffer fill(CharBuffer buffer, int value, int count) {
532 if (count > buffer.remaining()) {
533 throw msg.bufferUnderflow();
534 }
535 if (buffer.hasArray()) {
536 final int offs = buffer.arrayOffset();
537 Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), (char) value);
538 skip(buffer, count);
539 } else {
540 for (int i = count; i > 0; i--) {
541 buffer.put((char)value);
542 }
543 }
544 return buffer;
545 }
546
547 /**
548 * Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
549 *
550 * @see ShortBuffer#slice()
551 * @param buffer the buffer to slice
552 * @param sliceSize the size of the slice
553 * @return the buffer slice
554 */
555 public static ShortBuffer slice(ShortBuffer buffer, int sliceSize) {
556 if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
557 throw msg.bufferUnderflow();
558 }
559 final int oldPos = buffer.position();
560 final int oldLim = buffer.limit();
561 if (sliceSize < 0) {
562 // count from end (sliceSize is NEGATIVE)
563 buffer.limit(oldLim + sliceSize);
564 try {
565 return buffer.slice();
566 } finally {
567 buffer.limit(oldLim);
568 buffer.position(oldLim + sliceSize);
569 }
570 } else {
571 // count from start
572 buffer.limit(oldPos + sliceSize);
573 try {
574 return buffer.slice();
575 } finally {
576 buffer.limit(oldLim);
577 buffer.position(oldPos + sliceSize);
578 }
579 }
580 }
581
582 /**
583 * Fill a buffer with a repeated value.
584 *
585 * @param buffer the buffer to fill
586 * @param value the value to fill
587 * @param count the number of shorts to fill
588 * @return the buffer instance
589 */
590 public static ShortBuffer fill(ShortBuffer buffer, int value, int count) {
591 if (count > buffer.remaining()) {
592 throw msg.bufferUnderflow();
593 }
594 if (buffer.hasArray()) {
595 final int offs = buffer.arrayOffset();
596 Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), (short) value);
597 skip(buffer, count);
598 } else {
599 for (int i = count; i > 0; i--) {
600 buffer.put((short)value);
601 }
602 }
603 return buffer;
604 }
605
606 /**
607 * Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
608 *
609 * @see IntBuffer#slice()
610 * @param buffer the buffer to slice
611 * @param sliceSize the size of the slice
612 * @return the buffer slice
613 */
614 public static IntBuffer slice(IntBuffer buffer, int sliceSize) {
615 if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
616 throw msg.bufferUnderflow();
617 }
618 final int oldPos = buffer.position();
619 final int oldLim = buffer.limit();
620 if (sliceSize < 0) {
621 // count from end (sliceSize is NEGATIVE)
622 buffer.limit(oldLim + sliceSize);
623 try {
624 return buffer.slice();
625 } finally {
626 buffer.limit(oldLim);
627 buffer.position(oldLim + sliceSize);
628 }
629 } else {
630 // count from start
631 buffer.limit(oldPos + sliceSize);
632 try {
633 return buffer.slice();
634 } finally {
635 buffer.limit(oldLim);
636 buffer.position(oldPos + sliceSize);
637 }
638 }
639 }
640
641 /**
642 * Fill a buffer with a repeated value.
643 *
644 * @param buffer the buffer to fill
645 * @param value the value to fill
646 * @param count the number of ints to fill
647 * @return the buffer instance
648 */
649 public static IntBuffer fill(IntBuffer buffer, int value, int count) {
650 if (count > buffer.remaining()) {
651 throw msg.bufferUnderflow();
652 }
653 if (buffer.hasArray()) {
654 final int offs = buffer.arrayOffset();
655 Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), value);
656 skip(buffer, count);
657 } else {
658 for (int i = count; i > 0; i--) {
659 buffer.put(value);
660 }
661 }
662 return buffer;
663 }
664
665 /**
666 * Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
667 *
668 * @see LongBuffer#slice()
669 * @param buffer the buffer to slice
670 * @param sliceSize the size of the slice
671 * @return the buffer slice
672 */
673 public static LongBuffer slice(LongBuffer buffer, int sliceSize) {
674 if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
675 throw msg.bufferUnderflow();
676 }
677 final int oldPos = buffer.position();
678 final int oldLim = buffer.limit();
679 if (sliceSize < 0) {
680 // count from end (sliceSize is NEGATIVE)
681 buffer.limit(oldLim + sliceSize);
682 try {
683 return buffer.slice();
684 } finally {
685 buffer.limit(oldLim);
686 buffer.position(oldLim + sliceSize);
687 }
688 } else {
689 // count from start
690 buffer.limit(oldPos + sliceSize);
691 try {
692 return buffer.slice();
693 } finally {
694 buffer.limit(oldLim);
695 buffer.position(oldPos + sliceSize);
696 }
697 }
698 }
699
700 /**
701 * Fill a buffer with a repeated value.
702 *
703 * @param buffer the buffer to fill
704 * @param value the value to fill
705 * @param count the number of longs to fill
706 * @return the buffer instance
707 */
708 public static LongBuffer fill(LongBuffer buffer, long value, int count) {
709 if (count > buffer.remaining()) {
710 throw msg.bufferUnderflow();
711 }
712 if (buffer.hasArray()) {
713 final int offs = buffer.arrayOffset();
714 Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), value);
715 skip(buffer, count);
716 } else {
717 for (int i = count; i > 0; i--) {
718 buffer.put(value);
719 }
720 }
721 return buffer;
722 }
723
724 /**
725 * Advance a buffer's position relative to its current position.
726 *
727 * @see Buffer#position(int)
728 * @param <T> the buffer type
729 * @param buffer the buffer to set
730 * @param cnt the distance to skip
731 * @return the buffer instance
732 * @throws BufferUnderflowException if there are fewer than {@code cnt} bytes remaining
733 */
734 public static <T extends Buffer> T skip(T buffer, int cnt) throws BufferUnderflowException {
735 if (cnt < 0) {
736 throw msg.parameterOutOfRange("cnt");
737 }
738 if (cnt > buffer.remaining()) {
739 throw msg.bufferUnderflow();
740 }
741 buffer.position(buffer.position() + cnt);
742 return buffer;
743 }
744
745 /**
746 * Attempt to advance a buffer's position relative to its current position.
747 *
748 * @see Buffer#position(int)
749 * @param buffer the buffer to set
750 * @param cnt the distance to skip
751 * @return the actual number of bytes skipped
752 */
753 public static int trySkip(Buffer buffer, int cnt) {
754 if (cnt < 0) {
755 throw msg.parameterOutOfRange("cnt");
756 }
757 final int rem = buffer.remaining();
758 if (cnt > rem) {
759 cnt = rem;
760 }
761 buffer.position(buffer.position() + cnt);
762 return cnt;
763 }
764
765 /**
766 * Attempt to advance a series of buffers' overall position relative to its current position.
767 *
768 * @see Buffer#position(int)
769 * @param buffers the buffers to set
770 * @param offs the offset into the buffers array
771 * @param len the number of buffers to consider
772 * @param cnt the distance to skip
773 * @return the actual number of bytes skipped
774 */
775 public static long trySkip(Buffer[] buffers, int offs, int len, long cnt) {
776 if (cnt < 0L) {
777 throw msg.parameterOutOfRange("cnt");
778 }
779 if (len < 0) {
780 throw msg.parameterOutOfRange("len");
781 }
782 if (offs < 0) {
783 throw msg.parameterOutOfRange("offs");
784 }
785 if (offs > buffers.length) {
786 throw msg.parameterOutOfRange("offs");
787 }
788 if (offs + len > buffers.length) {
789 throw msg.parameterOutOfRange("offs");
790 }
791 long c = 0L;
792 for (int i = 0; i < len; i ++) {
793 final Buffer buffer = buffers[offs + i];
794 final int rem = buffer.remaining();
795 if (rem < cnt) {
796 buffer.position(buffer.position() + rem);
797 cnt -= (long) rem;
798 c += (long) rem;
799 } else {
800 buffer.position(buffer.position() + (int) cnt);
801 return c + cnt;
802 }
803 }
804 return c;
805 }
806
807 /**
808 * Rewind a buffer's position relative to its current position.
809 *
810 * @see Buffer#position(int)
811 * @param <T> the buffer type
812 * @param buffer the buffer to set
813 * @param cnt the distance to skip backwards
814 * @return the buffer instance
815 */
816 public static <T extends Buffer> T unget(T buffer, int cnt) {
817 if (cnt < 0) {
818 throw msg.parameterOutOfRange("cnt");
819 }
820 if (cnt > buffer.position()) {
821 throw msg.bufferUnderflow();
822 }
823 buffer.position(buffer.position() - cnt);
824 return buffer;
825 }
826
827 /**
828 * Take a certain number of bytes from the buffer and return them in an array.
829 *
830 * @param buffer the buffer to read
831 * @param cnt the number of bytes to take
832 * @return the bytes
833 */
834 public static byte[] take(ByteBuffer buffer, int cnt) {
835 if (cnt < 0) {
836 throw msg.parameterOutOfRange("cnt");
837 }
838 if (buffer.hasArray()) {
839 final int pos = buffer.position();
840 final int lim = buffer.limit();
841 if (lim - pos < cnt) {
842 throw new BufferUnderflowException();
843 }
844 final byte[] array = buffer.array();
845 final int offset = buffer.arrayOffset();
846 buffer.position(pos + cnt);
847 final int start = offset + pos;
848 return Arrays.copyOfRange(array, start, start + cnt);
849 }
850 final byte[] bytes = new byte[cnt];
851 buffer.get(bytes);
852 return bytes;
853 }
854
855 /**
856 * Take a certain number of chars from the buffer and return them in an array.
857 *
858 * @param buffer the buffer to read
859 * @param cnt the number of chars to take
860 * @return the chars
861 */
862 public static char[] take(CharBuffer buffer, int cnt) {
863 if (cnt < 0) {
864 throw msg.parameterOutOfRange("cnt");
865 }
866 if (buffer.hasArray()) {
867 final int pos = buffer.position();
868 final int lim = buffer.limit();
869 if (lim - pos < cnt) {
870 throw new BufferUnderflowException();
871 }
872 final char[] array = buffer.array();
873 final int offset = buffer.arrayOffset();
874 buffer.position(pos + cnt);
875 final int start = offset + pos;
876 return Arrays.copyOfRange(array, start, start + cnt);
877 }
878 final char[] chars = new char[cnt];
879 buffer.get(chars);
880 return chars;
881 }
882
883 /**
884 * Take a certain number of shorts from the buffer and return them in an array.
885 *
886 * @param buffer the buffer to read
887 * @param cnt the number of shorts to take
888 * @return the shorts
889 */
890 public static short[] take(ShortBuffer buffer, int cnt) {
891 if (cnt < 0) {
892 throw msg.parameterOutOfRange("cnt");
893 }
894 if (buffer.hasArray()) {
895 final int pos = buffer.position();
896 final int lim = buffer.limit();
897 if (lim - pos < cnt) {
898 throw new BufferUnderflowException();
899 }
900 final short[] array = buffer.array();
901 final int offset = buffer.arrayOffset();
902 buffer.position(pos + cnt);
903 final int start = offset + pos;
904 return Arrays.copyOfRange(array, start, start + cnt);
905 }
906 final short[] shorts = new short[cnt];
907 buffer.get(shorts);
908 return shorts;
909 }
910
911 /**
912 * Take a certain number of ints from the buffer and return them in an array.
913 *
914 * @param buffer the buffer to read
915 * @param cnt the number of ints to take
916 * @return the ints
917 */
918 public static int[] take(IntBuffer buffer, int cnt) {
919 if (cnt < 0) {
920 throw msg.parameterOutOfRange("cnt");
921 }
922 if (buffer.hasArray()) {
923 final int pos = buffer.position();
924 final int lim = buffer.limit();
925 if (lim - pos < cnt) {
926 throw new BufferUnderflowException();
927 }
928 final int[] array = buffer.array();
929 final int offset = buffer.arrayOffset();
930 buffer.position(pos + cnt);
931 final int start = offset + pos;
932 return Arrays.copyOfRange(array, start, start + cnt);
933 }
934 final int[] ints = new int[cnt];
935 buffer.get(ints);
936 return ints;
937 }
938
939 /**
940 * Take a certain number of longs from the buffer and return them in an array.
941 *
942 * @param buffer the buffer to read
943 * @param cnt the number of longs to take
944 * @return the longs
945 */
946 public static long[] take(LongBuffer buffer, int cnt) {
947 if (cnt < 0) {
948 throw msg.parameterOutOfRange("cnt");
949 }
950 if (buffer.hasArray()) {
951 final int pos = buffer.position();
952 final int lim = buffer.limit();
953 if (lim - pos < cnt) {
954 throw new BufferUnderflowException();
955 }
956 final long[] array = buffer.array();
957 final int offset = buffer.arrayOffset();
958 buffer.position(pos + cnt);
959 final int start = offset + pos;
960 return Arrays.copyOfRange(array, start, start + cnt);
961 }
962 final long[] longs = new long[cnt];
963 buffer.get(longs);
964 return longs;
965 }
966
967 private static final byte[] NO_BYTES = new byte[0];
968
969 /**
970 * Take all of the remaining bytes from the buffer and return them in an array.
971 *
972 * @param buffer the buffer to read
973 * @return the bytes
974 */
975 public static byte[] take(ByteBuffer buffer) {
976 final int remaining = buffer.remaining();
977 if (remaining == 0) return NO_BYTES;
978 if (buffer.hasArray()) {
979 final int pos = buffer.position();
980 final int lim = buffer.limit();
981 final byte[] array = buffer.array();
982 final int offset = buffer.arrayOffset();
983 buffer.position(lim);
984 return Arrays.copyOfRange(array, offset + pos, offset + lim);
985 }
986 final byte[] bytes = new byte[remaining];
987 buffer.get(bytes);
988 return bytes;
989 }
990
991 /**
992 * Take all of the remaining bytes from the buffers and return them in an array.
993 *
994 * @param buffers the buffer to read
995 * @param offs the offset into the array
996 * @param len the number of buffers
997 * @return the bytes
998 */
999 public static byte[] take(final ByteBuffer[] buffers, final int offs, final int len) {
1000 if (len == 1) return take(buffers[offs]);
1001 final long remaining = Buffers.remaining(buffers, offs, len);
1002 if (remaining == 0L) return NO_BYTES;
1003 if (remaining > Integer.MAX_VALUE) throw new OutOfMemoryError("Array too large");
1004 final byte[] bytes = new byte[(int) remaining];
1005 int o = 0;
1006 int rem;
1007 ByteBuffer buffer;
1008 for (int i = 0; i < len; i ++) {
1009 buffer = buffers[i + offs];
1010 rem = buffer.remaining();
1011 buffer.get(bytes, o, rem);
1012 o += rem;
1013 }
1014 return bytes;
1015 }
1016
1017 /**
1018 * Take all of the remaining chars from the buffer and return them in an array.
1019 *
1020 * @param buffer the buffer to read
1021 * @return the chars
1022 */
1023 public static char[] take(CharBuffer buffer) {
1024 final char[] chars = new char[buffer.remaining()];
1025 buffer.get(chars);
1026 return chars;
1027 }
1028
1029 /**
1030 * Take all of the remaining shorts from the buffer and return them in an array.
1031 *
1032 * @param buffer the buffer to read
1033 * @return the shorts
1034 */
1035 public static short[] take(ShortBuffer buffer) {
1036 final short[] shorts = new short[buffer.remaining()];
1037 buffer.get(shorts);
1038 return shorts;
1039 }
1040
1041 /**
1042 * Take all of the remaining ints from the buffer and return them in an array.
1043 *
1044 * @param buffer the buffer to read
1045 * @return the ints
1046 */
1047 public static int[] take(IntBuffer buffer) {
1048 final int[] ints = new int[buffer.remaining()];
1049 buffer.get(ints);
1050 return ints;
1051 }
1052
1053 /**
1054 * Take all of the remaining longs from the buffer and return them in an array.
1055 *
1056 * @param buffer the buffer to read
1057 * @return the longs
1058 */
1059 public static long[] take(LongBuffer buffer) {
1060 final long[] longs = new long[buffer.remaining()];
1061 buffer.get(longs);
1062 return longs;
1063 }
1064
1065 /**
1066 * Create an object that returns the dumped form of the given byte buffer when its {@code toString()} method is called.
1067 * Useful for logging byte buffers; if the {@code toString()} method is never called, the process of dumping the
1068 * buffer is never performed.
1069 *
1070 * @param buffer the buffer
1071 * @param indent the indentation to use
1072 * @param columns the number of 8-byte columns
1073 * @return a stringable object
1074 */
1075 public static Object createDumper(final ByteBuffer buffer, final int indent, final int columns) {
1076 if (columns <= 0) {
1077 throw msg.parameterOutOfRange("columns");
1078 }
1079 if (indent < 0) {
1080 throw msg.parameterOutOfRange("indent");
1081 }
1082 return new Object() {
1083 public String toString() {
1084 StringBuilder b = new StringBuilder();
1085 try {
1086 dump(buffer, b, indent, columns);
1087 } catch (IOException e) {
1088 // ignore, not possible!
1089 }
1090 return b.toString();
1091 }
1092 };
1093 }
1094
1095 /**
1096 * Dump a byte buffer to the given target.
1097 *
1098 * @param buffer the buffer
1099 * @param dest the target
1100 * @param indent the indentation to use
1101 * @param columns the number of 8-byte columns
1102 * @throws IOException if an error occurs during append
1103 */
1104 public static void dump(final ByteBuffer buffer, final Appendable dest, final int indent, final int columns) throws IOException {
1105 if (columns <= 0) {
1106 throw msg.parameterOutOfRange("columns");
1107 }
1108 if (indent < 0) {
1109 throw msg.parameterOutOfRange("indent");
1110 }
1111 final int pos = buffer.position();
1112 final int remaining = buffer.remaining();
1113 final int rowLength = (8 << (columns - 1));
1114 final int n = Math.max(Integer.toString(buffer.remaining(), 16).length(), 4);
1115 for (int idx = 0; idx < remaining; idx += rowLength) {
1116 // state: start of line
1117 for (int i = 0; i < indent; i ++) {
1118 dest.append(' ');
1119 }
1120 final String s = Integer.toString(idx, 16);
1121 for (int i = n - s.length(); i > 0; i --) {
1122 dest.append('0');
1123 }
1124 dest.append(s);
1125 dest.append(" - ");
1126 appendHexRow(buffer, dest, pos + idx, columns);
1127 appendTextRow(buffer, dest, pos + idx, columns);
1128 dest.append('\n');
1129 }
1130 }
1131
1132 private static void appendHexRow(final ByteBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
1133 final int limit = buffer.limit();
1134 int pos = startPos;
1135 for (int c = 0; c < columns; c ++) {
1136 for (int i = 0; i < 8; i ++) {
1137 if (pos >= limit) {
1138 dest.append(" ");
1139 } else {
1140 final int v = buffer.get(pos++) & 0xff;
1141 final String hexVal = Integer.toString(v, 16);
1142 if (v < 16) {
1143 dest.append('0');
1144 }
1145 dest.append(hexVal);
1146 }
1147 dest.append(' ');
1148 }
1149 dest.append(' ');
1150 dest.append(' ');
1151 }
1152 }
1153
1154 private static void appendTextRow(final ByteBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
1155 final int limit = buffer.limit();
1156 int pos = startPos;
1157 dest.append('[');
1158 dest.append(' ');
1159 for (int c = 0; c < columns; c ++) {
1160 for (int i = 0; i < 8; i ++) {
1161 if (pos >= limit) {
1162 dest.append(' ');
1163 } else {
1164 final char v = (char) (buffer.get(pos++) & 0xff);
1165 if (Character.isISOControl(v)) {
1166 dest.append('.');
1167 } else {
1168 dest.append(v);
1169 }
1170 }
1171 }
1172 dest.append(' ');
1173 }
1174 dest.append(']');
1175 }
1176
1177 /**
1178 * Create an object that returns the dumped form of the given character buffer when its {@code toString()} method is called.
1179 * Useful for logging character buffers; if the {@code toString()} method is never called, the process of dumping the
1180 * buffer is never performed.
1181 *
1182 * @param buffer the buffer
1183 * @param indent the indentation to use
1184 * @param columns the number of 8-byte columns
1185 * @return a stringable object
1186 */
1187 public static Object createDumper(final CharBuffer buffer, final int indent, final int columns) {
1188 if (columns <= 0) {
1189 throw msg.parameterOutOfRange("columns");
1190 }
1191 if (indent < 0) {
1192 throw msg.parameterOutOfRange("indent");
1193 }
1194 return new Object() {
1195 public String toString() {
1196 StringBuilder b = new StringBuilder();
1197 try {
1198 dump(buffer, b, indent, columns);
1199 } catch (IOException e) {
1200 // ignore, not possible!
1201 }
1202 return b.toString();
1203 }
1204 };
1205 }
1206
1207 /**
1208 * Dump a character buffer to the given target.
1209 *
1210 * @param buffer the buffer
1211 * @param dest the target
1212 * @param indent the indentation to use
1213 * @param columns the number of 8-byte columns
1214 * @throws IOException if an error occurs during append
1215 */
1216 public static void dump(final CharBuffer buffer, final Appendable dest, final int indent, final int columns) throws IOException {
1217 if (columns <= 0) {
1218 throw msg.parameterOutOfRange("columns");
1219 }
1220 if (indent < 0) {
1221 throw msg.parameterOutOfRange("indent");
1222 }
1223 final int pos = buffer.position();
1224 final int remaining = buffer.remaining();
1225 final int rowLength = (8 << (columns - 1));
1226 final int n = Math.max(Integer.toString(buffer.remaining(), 16).length(), 4);
1227 for (int idx = 0; idx < remaining; idx += rowLength) {
1228 // state: start of line
1229 for (int i = 0; i < indent; i ++) {
1230 dest.append(' ');
1231 }
1232 final String s = Integer.toString(idx, 16);
1233 for (int i = n - s.length(); i > 0; i --) {
1234 dest.append('0');
1235 }
1236 dest.append(s);
1237 dest.append(" - ");
1238 appendHexRow(buffer, dest, pos + idx, columns);
1239 appendTextRow(buffer, dest, pos + idx, columns);
1240 dest.append('\n');
1241 }
1242 }
1243
1244 private static void appendHexRow(final CharBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
1245 final int limit = buffer.limit();
1246 int pos = startPos;
1247 for (int c = 0; c < columns; c ++) {
1248 for (int i = 0; i < 8; i ++) {
1249 if (pos >= limit) {
1250 dest.append(" ");
1251 } else {
1252 final char v = buffer.get(pos++);
1253 final String hexVal = Integer.toString(v, 16);
1254 dest.append("0000".substring(hexVal.length()));
1255 dest.append(hexVal);
1256 }
1257 dest.append(' ');
1258 }
1259 dest.append(' ');
1260 dest.append(' ');
1261 }
1262 }
1263
1264 private static void appendTextRow(final CharBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
1265 final int limit = buffer.limit();
1266 int pos = startPos;
1267 dest.append('[');
1268 dest.append(' ');
1269 for (int c = 0; c < columns; c ++) {
1270 for (int i = 0; i < 8; i ++) {
1271 if (pos >= limit) {
1272 dest.append(' ');
1273 } else {
1274 final char v = buffer.get(pos++);
1275 if (Character.isISOControl(v) || Character.isHighSurrogate(v) || Character.isLowSurrogate(v)) {
1276 dest.append('.');
1277 } else {
1278 dest.append(v);
1279 }
1280 }
1281 }
1282 dest.append(' ');
1283 }
1284 dest.append(']');
1285 }
1286
1287 /**
1288 * The empty byte buffer.
1289 */
1290 public static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
1291
1292 /**
1293 * The empty pooled byte buffer. Freeing or discarding this buffer has no effect.
1294 */
1295 public static final Pooled<ByteBuffer> EMPTY_POOLED_BYTE_BUFFER = emptyPooledByteBuffer();
1296
1297 /**
1298 * Determine whether any of the buffers has remaining data.
1299 *
1300 * @param buffers the buffers
1301 * @param offs the offset into the buffers array
1302 * @param len the number of buffers to check
1303 * @return {@code true} if any of the selected buffers has remaining data
1304 */
1305 public static boolean hasRemaining(final Buffer[] buffers, final int offs, final int len) {
1306 for (int i = 0; i < len; i ++) {
1307 if (buffers[i + offs].hasRemaining()) {
1308 return true;
1309 }
1310 }
1311 return false;
1312 }
1313
1314 /**
1315 * Determine whether any of the buffers has remaining data.
1316 *
1317 * @param buffers the buffers
1318 * @return {@code true} if any of the selected buffers has remaining data
1319 */
1320 public static boolean hasRemaining(final Buffer[] buffers) {
1321 return hasRemaining(buffers, 0, buffers.length);
1322 }
1323
1324 /**
1325 * Get the total remaining size of all the given buffers.
1326 *
1327 * @param buffers the buffers
1328 * @param offs the offset into the buffers array
1329 * @param len the number of buffers to check
1330 * @return the number of remaining elements
1331 */
1332 public static long remaining(final Buffer[] buffers, final int offs, final int len) {
1333 long t = 0L;
1334 for (int i = 0; i < len; i ++) {
1335 t += buffers[i + offs].remaining();
1336 }
1337 return t;
1338 }
1339
1340 /**
1341 * Get the total remaining size of all the given buffers.
1342 *
1343 * @param buffers the buffers
1344 * @return the number of remaining elements
1345 */
1346 public static long remaining(final Buffer[] buffers) {
1347 return remaining(buffers, 0, buffers.length);
1348 }
1349
1350 /**
1351 * Put the string into the byte buffer, encoding it using "modified UTF-8" encoding.
1352 *
1353 * @param dest the byte buffer
1354 * @param orig the source bytes
1355 * @return the byte buffer
1356 * @throws BufferOverflowException if there is not enough space in the buffer for the complete string
1357 * @see DataOutput#writeUTF(String)
1358 */
1359 public static ByteBuffer putModifiedUtf8(ByteBuffer dest, String orig) throws BufferOverflowException {
1360 final char[] chars = orig.toCharArray();
1361 for (char c : chars) {
1362 if (c > 0 && c <= 0x7f) {
1363 dest.put((byte) c);
1364 } else if (c <= 0x07ff) {
1365 dest.put((byte)(0xc0 | 0x1f & c >> 6));
1366 dest.put((byte)(0x80 | 0x3f & c));
1367 } else {
1368 dest.put((byte)(0xe0 | 0x0f & c >> 12));
1369 dest.put((byte)(0x80 | 0x3f & c >> 6));
1370 dest.put((byte)(0x80 | 0x3f & c));
1371 }
1372 }
1373 return dest;
1374 }
1375
1376 /**
1377 * Get a 0-terminated string from the byte buffer, decoding it using "modified UTF-8" encoding.
1378 *
1379 * @param src the source buffer
1380 * @return the string
1381 * @throws BufferUnderflowException if the end of the buffer was reached before encountering a {@code 0}
1382 */
1383 public static String getModifiedUtf8Z(ByteBuffer src) throws BufferUnderflowException {
1384 final StringBuilder builder = new StringBuilder();
1385 for (;;) {
1386 final int ch = readUTFChar(src);
1387 if (ch == -1) {
1388 return builder.toString();
1389 }
1390 builder.append((char) ch);
1391 }
1392 }
1393
1394 /**
1395 * Get a modified UTF-8 string from the remainder of the buffer.
1396 *
1397 * @param src the buffer
1398 * @return the modified UTF-8 string
1399 * @throws BufferUnderflowException if the buffer ends abruptly in the midst of a single character
1400 */
1401 public static String getModifiedUtf8(ByteBuffer src) throws BufferUnderflowException {
1402 final StringBuilder builder = new StringBuilder();
1403 while (src.hasRemaining()) {
1404 final int ch = readUTFChar(src);
1405 if (ch == -1) {
1406 builder.append('\0');
1407 } else {
1408 builder.append((char) ch);
1409 }
1410 }
1411 return builder.toString();
1412 }
1413
1414 private static int readUTFChar(final ByteBuffer src) throws BufferUnderflowException {
1415 final int a = src.get() & 0xff;
1416 if (a == 0) {
1417 return -1;
1418 } else if (a < 0x80) {
1419 return (char)a;
1420 } else if (a < 0xc0) {
1421 return '?';
1422 } else if (a < 0xe0) {
1423 final int b = src.get() & 0xff;
1424 if ((b & 0xc0) != 0x80) {
1425 return '?';
1426 }
1427 return (a & 0x1f) << 6 | b & 0x3f;
1428 } else if (a < 0xf0) {
1429 final int b = src.get() & 0xff;
1430 if ((b & 0xc0) != 0x80) {
1431 return '?';
1432 }
1433 final int c = src.get() & 0xff;
1434 if ((c & 0xc0) != 0x80) {
1435 return '?';
1436 }
1437 return (a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f;
1438 }
1439 return '?';
1440 }
1441
1442 /**
1443 * Read an ASCIIZ ({@code NUL}-terminated) string from a byte buffer, appending the results to the given string
1444 * builder. If no {@code NUL} character is encountered, {@code false} is returned, indicating that more data needs
1445 * to be acquired before the operation can be complete. On return, there may be data remaining
1446 * in the source buffer. If an invalid byte is read, the character {@code '?'} is written
1447 * to the string builder in its place.
1448 *
1449 * @param src the source buffer
1450 * @param builder the destination builder
1451 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1452 */
1453 public static boolean readAsciiZ(final ByteBuffer src, final StringBuilder builder) {
1454 return readAsciiZ(src, builder, '?');
1455 }
1456
1457 /**
1458 * Read an ASCIIZ ({@code NUL}-terminated) string from a byte buffer, appending the results to the given string
1459 * builder. If no {@code NUL} character is encountered, {@code false} is returned, indicating that more data needs
1460 * to be acquired before the operation can be complete. On return, there may be data remaining
1461 * in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
1462 * to the string builder in its place.
1463 *
1464 * @param src the source buffer
1465 * @param builder the destination builder
1466 * @param replacement the replacement character for invalid bytes
1467 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1468 */
1469 public static boolean readAsciiZ(final ByteBuffer src, final StringBuilder builder, final char replacement) {
1470 for (;;) {
1471 if (! src.hasRemaining()) {
1472 return false;
1473 }
1474 final byte b = src.get();
1475 if (b == 0) {
1476 return true;
1477 }
1478 builder.append(b < 0 ? replacement : (char) b);
1479 }
1480 }
1481
1482 /**
1483 * Read a single line of ASCII text from a byte buffer, appending the results to the given string
1484 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1485 * to be acquired before the operation can be complete. On return, there may be data remaining
1486 * in the source buffer. If an invalid byte is read, the character {@code '?'} is written
1487 * to the string builder in its place. The {@code EOL} character will be included in the resultant string.
1488 *
1489 * @param src the source buffer
1490 * @param builder the destination builder
1491 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1492 */
1493 public static boolean readAsciiLine(final ByteBuffer src, final StringBuilder builder) {
1494 return readAsciiLine(src, builder, '?', '\n');
1495 }
1496
1497 /**
1498 * Read a single line of ASCII text from a byte buffer, appending the results to the given string
1499 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1500 * to be acquired before the operation can be complete. On return, there may be data remaining
1501 * in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
1502 * to the string builder in its place. The {@code EOL} character will be included in the resultant string.
1503 *
1504 * @param src the source buffer
1505 * @param builder the destination builder
1506 * @param replacement the replacement character for invalid bytes
1507 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1508 */
1509 public static boolean readAsciiLine(final ByteBuffer src, final StringBuilder builder, final char replacement) {
1510 return readAsciiLine(src, builder, replacement, '\n');
1511 }
1512
1513 /**
1514 * Read a single line of ASCII text from a byte buffer, appending the results to the given string
1515 * builder, using the given delimiter character instead of {@code EOL}. If no delimiter character is encountered,
1516 * {@code false} is returned, indicating that more data needs
1517 * to be acquired before the operation can be complete. On return, there may be data remaining
1518 * in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
1519 * to the string builder in its place. The delimiter character will be included in the resultant string.
1520 *
1521 * @param src the source buffer
1522 * @param builder the destination builder
1523 * @param replacement the replacement character for invalid bytes
1524 * @param delimiter the character which marks the end of the line
1525 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1526 */
1527 public static boolean readAsciiLine(final ByteBuffer src, final StringBuilder builder, final char replacement, final char delimiter) {
1528 for (;;) {
1529 if (! src.hasRemaining()) {
1530 return false;
1531 }
1532 final byte b = src.get();
1533 builder.append(b < 0 ? replacement : (char) b);
1534 if (b == delimiter) {
1535 return true;
1536 }
1537 }
1538 }
1539
1540 /**
1541 * Read the remainder of a buffer as ASCII text, appending the results to the given string
1542 * builder. If an invalid byte is read, the character {@code '?'} is written
1543 * to the string builder in its place.
1544 *
1545 * @param src the source buffer
1546 * @param builder the destination builder
1547 */
1548 public static void readAscii(final ByteBuffer src, final StringBuilder builder) {
1549 readAscii(src, builder, '?');
1550 }
1551
1552 /**
1553 * Read the remainder of a buffer as ASCII text, appending the results to the given string
1554 * builder. If an invalid byte is read, the character designated by {@code replacement} is written
1555 * to the string builder in its place.
1556 *
1557 * @param src the source buffer
1558 * @param builder the destination builder
1559 * @param replacement the replacement character for invalid bytes
1560 */
1561 public static void readAscii(final ByteBuffer src, final StringBuilder builder, final char replacement) {
1562 for (;;) {
1563 if (! src.hasRemaining()) {
1564 return;
1565 }
1566 final byte b = src.get();
1567 builder.append(b < 0 ? replacement : (char) b);
1568 }
1569 }
1570
1571 /**
1572 * Read the remainder of a buffer as ASCII text, up to a certain limit, appending the results to the given string
1573 * builder. If an invalid byte is read, the character designated by {@code replacement} is written
1574 * to the string builder in its place.
1575 *
1576 * @param src the source buffer
1577 * @param builder the destination builder
1578 * @param limit the maximum number of characters to write
1579 * @param replacement the replacement character for invalid bytes
1580 */
1581 public static void readAscii(final ByteBuffer src, final StringBuilder builder, int limit, final char replacement) {
1582 while (limit > 0) {
1583 if (! src.hasRemaining()) {
1584 return;
1585 }
1586 final byte b = src.get();
1587 builder.append(b < 0 ? replacement : (char) b);
1588 limit--;
1589 }
1590 }
1591
1592 /**
1593 * Read a {@code NUL}-terminated Latin-1 string from a byte buffer, appending the results to the given string
1594 * builder. If no {@code NUL} character is encountered, {@code false} is returned, indicating that more data needs
1595 * to be acquired before the operation can be complete. On return, there may be data remaining
1596 * in the source buffer.
1597 *
1598 * @param src the source buffer
1599 * @param builder the destination builder
1600 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1601 */
1602 public static boolean readLatin1Z(final ByteBuffer src, final StringBuilder builder) {
1603 for (;;) {
1604 if (! src.hasRemaining()) {
1605 return false;
1606 }
1607 final byte b = src.get();
1608 if (b == 0) {
1609 return true;
1610 }
1611 builder.append((char) (b & 0xff));
1612 }
1613 }
1614
1615 /**
1616 * Read a single line of Latin-1 text from a byte buffer, appending the results to the given string
1617 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1618 * to be acquired before the operation can be complete. On return, there may be data remaining
1619 * in the source buffer. The {@code EOL} character will be included in the resultant string.
1620 *
1621 * @param src the source buffer
1622 * @param builder the destination builder
1623 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1624 */
1625 public static boolean readLatin1Line(final ByteBuffer src, final StringBuilder builder) {
1626 for (;;) {
1627 if (! src.hasRemaining()) {
1628 return false;
1629 }
1630 final byte b = src.get();
1631 builder.append((char) (b & 0xff));
1632 if (b == '\n') {
1633 return true;
1634 }
1635 }
1636 }
1637
1638 /**
1639 * Read a single line of Latin-1 text from a byte buffer, appending the results to the given string
1640 * builder. If no delimiter character is encountered, {@code false} is returned, indicating that more data needs
1641 * to be acquired before the operation can be complete. On return, there may be data remaining
1642 * in the source buffer. The delimiter character will be included in the resultant string.
1643 *
1644 * @param src the source buffer
1645 * @param builder the destination builder
1646 * @param delimiter the character which marks the end of the line
1647 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1648 */
1649 public static boolean readLatin1Line(final ByteBuffer src, final StringBuilder builder, final char delimiter) {
1650 for (;;) {
1651 if (! src.hasRemaining()) {
1652 return false;
1653 }
1654 final byte b = src.get();
1655 builder.append((char) (b & 0xff));
1656 if (b == delimiter) {
1657 return true;
1658 }
1659 }
1660 }
1661
1662 /**
1663 * Read the remainder of a buffer as Latin-1 text, appending the results to the given string
1664 * builder.
1665 *
1666 * @param src the source buffer
1667 * @param builder the destination builder
1668 */
1669 public static void readLatin1(final ByteBuffer src, final StringBuilder builder) {
1670 for (;;) {
1671 if (! src.hasRemaining()) {
1672 return;
1673 }
1674 final byte b = src.get();
1675 builder.append((char) (b & 0xff));
1676 }
1677 }
1678
1679 /**
1680 * Read a {@code NUL}-terminated {@link DataInput modified UTF-8} string from a byte buffer, appending the results to the given string
1681 * builder. If no {@code NUL} byte is encountered, {@code false} is returned, indicating that more data needs
1682 * to be acquired before the operation can be complete. On return, there may be data remaining
1683 * in the source buffer. If an invalid byte sequence is read, the character {@code '?'} is written
1684 * to the string builder in its place.
1685 *
1686 * @param src the source buffer
1687 * @param builder the destination builder
1688 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1689 */
1690 public static boolean readModifiedUtf8Z(final ByteBuffer src, final StringBuilder builder) {
1691 return readModifiedUtf8Z(src, builder, '?');
1692 }
1693
1694 /**
1695 * Read a {@code NUL}-terminated {@link DataInput modified UTF-8} string from a byte buffer, appending the results to the given string
1696 * builder. If no {@code NUL} byte is encountered, {@code false} is returned, indicating that more data needs
1697 * to be acquired before the operation can be complete. On return, there may be data remaining
1698 * in the source buffer. If an invalid byte sequence is read, the character designated by {@code replacement} is written
1699 * to the string builder in its place.
1700 *
1701 * @param src the source buffer
1702 * @param builder the destination builder
1703 * @param replacement the replacement character to use
1704 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1705 */
1706 public static boolean readModifiedUtf8Z(final ByteBuffer src, final StringBuilder builder, final char replacement) {
1707 for (;;) {
1708 if (! src.hasRemaining()) {
1709 return false;
1710 }
1711 final int a = src.get() & 0xff;
1712 if (a == 0) {
1713 return true;
1714 } else if (a < 0x80) {
1715 builder.append((char)a);
1716 } else if (a < 0xc0) {
1717 builder.append(replacement);
1718 } else if (a < 0xe0) {
1719 if (src.hasRemaining()) {
1720 final int b = src.get() & 0xff;
1721 if ((b & 0xc0) != 0x80) {
1722 builder.append(replacement);
1723 } else {
1724 builder.append((char) ((a & 0x1f) << 6 | b & 0x3f));
1725 }
1726 } else {
1727 unget(src, 1);
1728 return false;
1729 }
1730 } else if (a < 0xf0) {
1731 if (src.hasRemaining()) {
1732 final int b = src.get() & 0xff;
1733 if ((b & 0xc0) != 0x80) {
1734 builder.append(replacement);
1735 } else {
1736 if (src.hasRemaining()) {
1737 final int c = src.get() & 0xff;
1738 if ((c & 0xc0) != 0x80) {
1739 builder.append(replacement);
1740 } else {
1741 builder.append((char) ((a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f));
1742 }
1743 } else {
1744 unget(src, 2);
1745 return false;
1746 }
1747 }
1748 } else {
1749 unget(src, 1);
1750 return false;
1751 }
1752 } else {
1753 builder.append(replacement);
1754 }
1755 }
1756 }
1757
1758 /**
1759 * Read a single line of {@link DataInput modified UTF-8} text from a byte buffer, appending the results to the given string
1760 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1761 * to be acquired before the operation can be complete. On return, there may be data remaining
1762 * in the source buffer. If an invalid byte is read, the character {@code '?'} is written
1763 * to the string builder in its place. The {@code EOL} character will be included in the resultant string.
1764 *
1765 * @param src the source buffer
1766 * @param builder the destination builder
1767 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1768 */
1769 public static boolean readModifiedUtf8Line(final ByteBuffer src, final StringBuilder builder) {
1770 return readModifiedUtf8Line(src, builder, '?');
1771 }
1772
1773 /**
1774 * Read a single line of {@link DataInput modified UTF-8} text from a byte buffer, appending the results to the given string
1775 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1776 * to be acquired before the operation can be complete. On return, there may be data remaining
1777 * in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
1778 * to the string builder in its place. The {@code EOL} character will be included in the resultant string.
1779 *
1780 * @param src the source buffer
1781 * @param builder the destination builder
1782 * @param replacement the replacement character for invalid bytes
1783 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1784 */
1785 public static boolean readModifiedUtf8Line(final ByteBuffer src, final StringBuilder builder, final char replacement) {
1786 return readModifiedUtf8Line(src, builder, replacement, '\n');
1787 }
1788
1789 /**
1790 * Read a single line of {@link DataInput modified UTF-8} text from a byte buffer, appending the results to the given string
1791 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1792 * to be acquired before the operation can be complete. On return, there may be data remaining
1793 * in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
1794 * to the string builder in its place. The delimiter character will be included in the resultant string.
1795 *
1796 * @param src the source buffer
1797 * @param builder the destination builder
1798 * @param replacement the replacement character for invalid bytes
1799 * @param delimiter the character which marks the end of the line
1800 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1801 */
1802 public static boolean readModifiedUtf8Line(final ByteBuffer src, final StringBuilder builder, final char replacement, final char delimiter) {
1803 for (;;) {
1804 if (! src.hasRemaining()) {
1805 return false;
1806 }
1807 final int a = src.get() & 0xff;
1808 if (a < 0x80) {
1809 builder.append((char)a);
1810 if (a == delimiter) {
1811 return true;
1812 }
1813 } else if (a < 0xc0) {
1814 builder.append(replacement);
1815 } else if (a < 0xe0) {
1816 if (src.hasRemaining()) {
1817 final int b = src.get() & 0xff;
1818 if ((b & 0xc0) != 0x80) {
1819 builder.append(replacement);
1820 } else {
1821 final char ch = (char) ((a & 0x1f) << 6 | b & 0x3f);
1822 builder.append(ch);
1823 if (ch == delimiter) {
1824 return true;
1825 }
1826 }
1827 } else {
1828 unget(src, 1);
1829 return false;
1830 }
1831 } else if (a < 0xf0) {
1832 if (src.hasRemaining()) {
1833 final int b = src.get() & 0xff;
1834 if ((b & 0xc0) != 0x80) {
1835 builder.append(replacement);
1836 } else {
1837 if (src.hasRemaining()) {
1838 final int c = src.get() & 0xff;
1839 if ((c & 0xc0) != 0x80) {
1840 builder.append(replacement);
1841 } else {
1842 final char ch = (char) ((a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f);
1843 builder.append(ch);
1844 if (ch == delimiter) {
1845 return true;
1846 }
1847 }
1848 } else {
1849 unget(src, 2);
1850 return false;
1851 }
1852 }
1853 } else {
1854 unget(src, 1);
1855 return false;
1856 }
1857 } else {
1858 builder.append(replacement);
1859 }
1860 }
1861 }
1862
1863 /**
1864 * Read a single line of text from a byte buffer, appending the results to the given string
1865 * builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
1866 * to be acquired before the operation can be complete. On return, there may be data remaining
1867 * in the source buffer. Invalid bytes are handled according to the policy specified by the {@code decoder} instance.
1868 * Since this method decodes only one character at a time, it should not be expected to have the same performance
1869 * as the other optimized, character set-specific methods specified in this class.
1870 * The {@code EOL} character will be included in the resultant string.
1871 *
1872 * @param src the source buffer
1873 * @param builder the destination builder
1874 * @param decoder the decoder to use
1875 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1876 */
1877 public static boolean readLine(final ByteBuffer src, final StringBuilder builder, final CharsetDecoder decoder) {
1878 return readLine(src, builder, decoder, '\n');
1879 }
1880
1881 /**
1882 * Read a single line of text from a byte buffer, appending the results to the given string
1883 * builder. If no delimiter character is encountered, {@code false} is returned, indicating that more data needs
1884 * to be acquired before the operation can be complete. On return, there may be data remaining
1885 * in the source buffer. Invalid bytes are handled according to the policy specified by the {@code decoder} instance.
1886 * Since this method decodes only one character at a time, it should not be expected to have the same performance
1887 * as the other optimized, character set-specific methods specified in this class. The delimiter character will be
1888 * included in the resultant string.
1889 *
1890 * @param src the source buffer
1891 * @param builder the destination builder
1892 * @param decoder the decoder to use
1893 * @param delimiter the character which marks the end of the line
1894 * @return {@code true} if the entire string was read, {@code false} if more data is needed
1895 */
1896 public static boolean readLine(final ByteBuffer src, final StringBuilder builder, final CharsetDecoder decoder, final char delimiter) {
1897 final CharBuffer oneChar = CharBuffer.allocate(1);
1898 for (;;) {
1899 final CoderResult coderResult = decoder.decode(src, oneChar, false);
1900 if (coderResult.isUnderflow()) {
1901 if (oneChar.hasRemaining()) {
1902 return false;
1903 }
1904 } else if (oneChar.hasRemaining()) {
1905 throw new IllegalStateException();
1906 }
1907 final char ch = oneChar.get(0);
1908 builder.append(ch);
1909 if (ch == delimiter) {
1910 return true;
1911 }
1912 oneChar.clear();
1913 }
1914 }
1915
1916 /**
1917 * Create a pooled wrapper around a buffer. The buffer is unreferenced for garbage collection when
1918 * freed or discarded.
1919 *
1920 * @param buffer the buffer to wrap
1921 * @param <B> the buffer type
1922 * @return the pooled wrapper
1923 */
1924 public static <B extends Buffer> Pooled<B> pooledWrapper(final B buffer) {
1925 return new Pooled<B>() {
1926 private volatile B buf = buffer;
1927
1928 public void discard() {
1929 buf = null;
1930 }
1931
1932 public void free() {
1933 buf = null;
1934 }
1935
1936 public B getResource() throws IllegalStateException {
1937 final B buffer = buf;
1938 if (buffer == null) {
1939 throw new IllegalStateException();
1940 }
1941 return buffer;
1942 }
1943
1944 public void close() {
1945 free();
1946 }
1947
1948 public String toString() {
1949 return "Pooled wrapper around " + buffer;
1950 }
1951 };
1952 }
1953
1954 /**
1955 * Create a pooled wrapper around a buffer that was allocated via {@link ByteBufferPool}. The buffer is freed to the
1956 * global pool when freed.
1957 *
1958 * @param buffer the buffer to wrap
1959 * @return the pooled wrapper
1960 */
1961 public static Pooled<ByteBuffer> globalPooledWrapper(final ByteBuffer buffer) {
1962 return new Pooled<ByteBuffer>() {
1963 private volatile ByteBuffer buf = buffer;
1964
1965 public void discard() {
1966 ByteBuffer oldBuf = this.buf;
1967 if (oldBuf == null) return;
1968 final ByteBuffer buf = oldBuf.duplicate();
1969 new CleanerReference<ByteBuffer, Void>(this.buf, null, new Reaper<ByteBuffer, Void>() {
1970 public void reap(final Reference<ByteBuffer, Void> reference) {
1971 // free the duplicate
1972 ByteBufferPool.free(buf);
1973 }
1974 });
1975 this.buf = null;
1976 }
1977
1978 public void free() {
1979 ByteBuffer oldBuf = this.buf;
1980 if (oldBuf == null) return;
1981 ByteBufferPool.free(oldBuf);
1982 buf = null;
1983 }
1984
1985 public ByteBuffer getResource() throws IllegalStateException {
1986 final ByteBuffer buffer = buf;
1987 if (buffer == null) {
1988 throw new IllegalStateException();
1989 }
1990 return buffer;
1991 }
1992
1993 public void close() {
1994 free();
1995 }
1996
1997 public String toString() {
1998 return "Globally pooled wrapper around " + buffer;
1999 }
2000 };
2001 }
2002
2003 /**
2004 * Create a "pooled" empty buffer. Discarding or freeing the buffer has no effect; the returned buffer is
2005 * always empty.
2006 *
2007 * @return a new pooled empty buffer
2008 */
2009 public static Pooled<ByteBuffer> emptyPooledByteBuffer() {
2010 return new Pooled<ByteBuffer>() {
2011 public void discard() {
2012 }
2013
2014 public void free() {
2015 }
2016
2017 public ByteBuffer getResource() throws IllegalStateException {
2018 return EMPTY_BYTE_BUFFER;
2019 }
2020
2021 public void close() {
2022 }
2023 };
2024 }
2025
2026 /**
2027 * A buffer allocator which allocates slices off of the given buffer. Once the buffer is exhausted, further
2028 * attempts to allocate buffers will result in {@link BufferUnderflowException}.
2029 *
2030 * @param buffer the source buffer
2031 * @return the slice allocator
2032 */
2033 public static BufferAllocator<ByteBuffer> sliceAllocator(final ByteBuffer buffer) {
2034 return new BufferAllocator<ByteBuffer>() {
2035 public ByteBuffer allocate(final int size) throws IllegalArgumentException {
2036 return Buffers.slice(buffer, size);
2037 }
2038 };
2039 }
2040
2041 /**
2042 * A buffer pool which allocates a new buffer on every allocate request, and discards buffers on free.
2043 *
2044 * @param allocator the buffer allocator
2045 * @param size the buffer size
2046 * @param <B> the buffer type
2047 * @return the buffer pool
2048 */
2049 public static <B extends Buffer> Pool<B> allocatedBufferPool(final BufferAllocator<B> allocator, final int size) {
2050 return new Pool<B>() {
2051 public Pooled<B> allocate() {
2052 return pooledWrapper(allocator.allocate(size));
2053 }
2054 };
2055 }
2056
2057 /**
2058 * A byte buffer pool which zeroes the content of the buffer before re-pooling it.
2059 *
2060 * @param delegate the delegate pool
2061 * @return the wrapper pool
2062 */
2063 public static Pool<ByteBuffer> secureBufferPool(final Pool<ByteBuffer> delegate) {
2064 return new SecureByteBufferPool(delegate);
2065 }
2066
2067 /**
2068 * Determine whether the given pool is a secure pool. Note that this test will fail if used on a pool
2069 * which wraps a secure pool.
2070 *
2071 * @param pool the pool to test
2072 * @return {@code true} if it is a secure pool instance
2073 */
2074 public static boolean isSecureBufferPool(Pool<?> pool) {
2075 return pool instanceof SecureByteBufferPool;
2076 }
2077
2078 /**
2079 * Zero a buffer. Ensures that any potentially sensitive information in the buffer is
2080 * overwritten.
2081 *
2082 * @param buffer the buffer
2083 */
2084 public static void zero(ByteBuffer buffer) {
2085 buffer.clear();
2086 while (buffer.remaining() >= 8) {
2087 buffer.putLong(0L);
2088 }
2089 while (buffer.hasRemaining()) {
2090 buffer.put((byte) 0);
2091 }
2092 buffer.clear();
2093 }
2094
2095 /**
2096 * Zero a buffer. Ensures that any potentially sensitive information in the buffer is
2097 * overwritten.
2098 *
2099 * @param buffer the buffer
2100 */
2101 public static void zero(CharBuffer buffer) {
2102 buffer.clear();
2103 while (buffer.remaining() >= 32) {
2104 buffer.put("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
2105 }
2106 while (buffer.hasRemaining()) {
2107 buffer.put('\0');
2108 }
2109 buffer.clear();
2110 }
2111
2112 /**
2113 * Determine whether the given buffers list is comprised solely of direct buffers or solely of heap buffers.
2114 *
2115 * @param buffers the buffers
2116 * @return {@code true} if all the buffers are direct, {@code false} if they are all heap buffers
2117 * @throws IllegalArgumentException if both direct and heap buffers were found, or if a buffer is {@code null}
2118 */
2119 public static boolean isDirect(Buffer... buffers) throws IllegalArgumentException {
2120 return isDirect(buffers, 0, buffers.length);
2121 }
2122
2123 /**
2124 * Determine whether the given buffers list is comprised solely of direct buffers or solely of heap buffers.
2125 *
2126 * @param buffers the buffers
2127 * @return {@code true} if all the buffers are direct, {@code false} if they are all heap buffers
2128 * @throws IllegalArgumentException if both direct and heap buffers were found, or if a buffer is {@code null}
2129 */
2130 public static boolean isDirect(final Buffer[] buffers, final int offset, final int length) {
2131 boolean foundDirect = false;
2132 boolean foundHeap = false;
2133 for (int i = 0; i < length; i ++) {
2134 final Buffer buffer = buffers[i + offset];
2135 if (buffer == null) {
2136 throw msg.nullParameter("buffer");
2137 }
2138 if (buffer.isDirect()) {
2139 if (foundHeap) {
2140 throw msg.mixedDirectAndHeap();
2141 }
2142 foundDirect = true;
2143 } else {
2144 if (foundDirect) {
2145 throw msg.mixedDirectAndHeap();
2146 }
2147 foundHeap = true;
2148 }
2149 }
2150 return foundDirect;
2151 }
2152
2153 /**
2154 * Assert the writability of the given buffers.
2155 *
2156 * @param buffers the buffers array
2157 * @param offs the offset in the array to start searching
2158 * @param len the number of buffers to check
2159 * @throws ReadOnlyBufferException if any of the buffers are read-only
2160 */
2161 public static void assertWritable(Buffer[] buffers, int offs, int len) throws ReadOnlyBufferException {
2162 for (int i = 0; i < len; i ++) {
2163 if (buffers[i + offs].isReadOnly()) {
2164 throw msg.readOnlyBuffer();
2165 }
2166 }
2167 }
2168
2169 /**
2170 * Assert the writability of the given buffers.
2171 *
2172 * @param buffers the buffers array
2173 * @throws ReadOnlyBufferException if any of the buffers are read-only
2174 */
2175 public static void assertWritable(Buffer... buffers) throws ReadOnlyBufferException {
2176 assertWritable(buffers, 0, buffers.length);
2177 }
2178
2179 /**
2180 * Add {@code count} bytes of random data to the target buffer.
2181 *
2182 * @param target the target buffer
2183 * @param random the RNG
2184 * @param count the number of bytes to add
2185 */
2186 public static void addRandom(ByteBuffer target, Random random, int count) {
2187 final byte[] bytes = new byte[count];
2188 random.nextBytes(bytes);
2189 target.put(bytes);
2190 }
2191
2192 /**
2193 * Add {@code count} bytes of random data to the target buffer using the thread-local RNG.
2194 *
2195 * @param target the target buffer
2196 * @param count the number of bytes to add
2197 */
2198 public static void addRandom(ByteBuffer target, int count) {
2199 addRandom(target, IoUtils.getThreadLocalRandom(), count);
2200 }
2201
2202 /**
2203 * Add a random amount of random data to the target buffer.
2204 *
2205 * @param target the target buffer
2206 * @param random the RNG
2207 */
2208 public static void addRandom(ByteBuffer target, Random random) {
2209 if (target.remaining() == 0) {
2210 return;
2211 }
2212 addRandom(target, random, random.nextInt(target.remaining()));
2213 }
2214
2215 /**
2216 * Add a random amount of random data to the target buffer using the thread-local RNG.
2217 *
2218 * @param target the target buffer
2219 */
2220 public static void addRandom(ByteBuffer target) {
2221 addRandom(target, IoUtils.getThreadLocalRandom());
2222 }
2223
2224 /**
2225 * Fill a buffer from an input stream. Specially optimized for heap buffers. If a partial transfer occurs
2226 * due to interruption, the buffer's position is updated accordingly.
2227 *
2228 * @param target the target buffer
2229 * @param source the source stream
2230 * @return the number of bytes transferred, or {@code -1} if no bytes were moved due to end-of-stream
2231 * @throws IOException if the stream read fails
2232 */
2233 public static int fillFromStream(ByteBuffer target, InputStream source) throws IOException {
2234 final int remaining = target.remaining();
2235 if (remaining == 0) {
2236 return 0;
2237 } else {
2238 final int p = target.position();
2239 if (target.hasArray()) {
2240 // fast path
2241 final int res;
2242 try {
2243 res = source.read(target.array(), p + target.arrayOffset(), remaining);
2244 } catch (InterruptedIOException e) {
2245 target.position(p + e.bytesTransferred);
2246 throw e;
2247 }
2248 if (res > 0) {
2249 target.position(p + res);
2250 }
2251 return res;
2252 } else {
2253 byte[] tmp = new byte[remaining];
2254 final int res;
2255 try {
2256 res = source.read(tmp);
2257 } catch (InterruptedIOException e) {
2258 final int n = e.bytesTransferred;
2259 target.put(tmp, 0, n);
2260 target.position(p + n);
2261 throw e;
2262 }
2263 if (res > 0) {
2264 target.put(tmp, 0, res);
2265 }
2266 return res;
2267 }
2268 }
2269 }
2270
2271 /**
2272 * Get a debug-friendly description of the buffer.
2273 *
2274 * @param buffer the buffer to describe
2275 * @return the string
2276 */
2277 public static String debugString(ByteBuffer buffer) {
2278 StringBuilder b = new StringBuilder();
2279 b.append("1 buffer of ").append(buffer.remaining()).append(" bytes");
2280 return b.toString();
2281 }
2282
2283 /**
2284 * Get a debug-friendly description of the buffer.
2285 *
2286 * @param buffers the buffers to describe
2287 * @param offs the offset into the array
2288 * @param len the number of buffers
2289 * @return the string
2290 */
2291 public static String debugString(ByteBuffer[] buffers, int offs, int len) {
2292 StringBuilder b = new StringBuilder();
2293 b.append(len).append(" buffer(s)");
2294 if (len > 0) {
2295 b.append(" of ").append(Buffers.remaining(buffers, offs, len)).append(" bytes");
2296 }
2297 return b.toString();
2298 }
2299
2300 /**
2301 * Empty a buffer to an output stream. Specially optimized for heap buffers. If a partial transfer occurs
2302 * due to interruption, the buffer's position is updated accordingly.
2303 *
2304 * @param target the target stream
2305 * @param source the source buffer
2306 * @throws IOException if the stream write fails
2307 */
2308 public static void emptyToStream(OutputStream target, ByteBuffer source) throws IOException {
2309 final int remaining = source.remaining();
2310 if (remaining == 0) {
2311 return;
2312 } else {
2313 final int p = source.position();
2314 if (source.hasArray()) {
2315 // fast path
2316 try {
2317 target.write(source.array(), p + source.arrayOffset(), remaining);
2318 } catch (InterruptedIOException e) {
2319 source.position(p + e.bytesTransferred);
2320 throw e;
2321 }
2322 source.position(source.limit());
2323 return;
2324 } else {
2325 byte[] tmp = take(source);
2326 try {
2327 target.write(tmp);
2328 } catch (InterruptedIOException e) {
2329 source.position(p + e.bytesTransferred);
2330 throw e;
2331 } catch (IOException e) {
2332 source.position(p);
2333 throw e;
2334 }
2335 }
2336 }
2337 }
2338
2339 private static class SecureByteBufferPool implements Pool<ByteBuffer> {
2340
2341 private final Pool<ByteBuffer> delegate;
2342
2343 SecureByteBufferPool(final Pool<ByteBuffer> delegate) {
2344 this.delegate = delegate;
2345 }
2346
2347 public Pooled<ByteBuffer> allocate() {
2348 return new SecurePooledByteBuffer(delegate.allocate());
2349 }
2350 }
2351
2352 private static class SecurePooledByteBuffer implements Pooled<ByteBuffer> {
2353
2354 private static final AtomicIntegerFieldUpdater<SecurePooledByteBuffer> freedUpdater = AtomicIntegerFieldUpdater.newUpdater(SecurePooledByteBuffer.class, "freed");
2355
2356 private final Pooled<ByteBuffer> allocated;
2357 @SuppressWarnings("unused")
2358 private volatile int freed;
2359
2360 SecurePooledByteBuffer(final Pooled<ByteBuffer> allocated) {
2361 this.allocated = allocated;
2362 }
2363
2364 public void discard() {
2365 if (freedUpdater.compareAndSet(this, 0, 1)) {
2366 zero(allocated.getResource());
2367 allocated.discard();
2368 }
2369 }
2370
2371 public void free() {
2372 if (freedUpdater.compareAndSet(this, 0, 1)) {
2373 zero(allocated.getResource());
2374 allocated.free();
2375 }
2376 }
2377
2378 public ByteBuffer getResource() throws IllegalStateException {
2379 // trust the delegate to handle illegal state since we can't do it securely by ourselves
2380 return allocated.getResource();
2381 }
2382
2383 public void close() {
2384 free();
2385 }
2386
2387 public String toString() {
2388 return "Secure wrapper around " + allocated;
2389 }
2390 }
2391 }
2392