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 trueif 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 trueif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 trueif the entire string was read, {@code falseif 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 == nullreturn;
1968                 final ByteBuffer buf = oldBuf.duplicate();
1969                 new CleanerReference<ByteBuffer, Void>(this.buf, nullnew 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 == nullreturn;
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 trueif 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 trueif all the buffers are direct, {@code falseif 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 trueif all the buffers are direct, {@code falseif 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