1
16 package okio;
17
18 import java.io.Closeable;
19 import java.io.EOFException;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.ByteChannel;
25 import java.nio.charset.Charset;
26 import java.security.InvalidKeyException;
27 import java.security.MessageDigest;
28 import java.security.NoSuchAlgorithmException;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.List;
32 import javax.annotation.Nullable;
33 import javax.crypto.Mac;
34 import javax.crypto.spec.SecretKeySpec;
35
36 import static okio.Util.checkOffsetAndCount;
37 import static okio.Util.reverseBytesLong;
38
39
54 public final class Buffer implements BufferedSource, BufferedSink, Cloneable, ByteChannel {
55 private static final byte[] DIGITS =
56 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
57 static final int REPLACEMENT_CHARACTER = '\ufffd';
58
59 @Nullable Segment head;
60 long size;
61
62 public Buffer() {
63 }
64
65
66 public final long size() {
67 return size;
68 }
69
70 @Override public Buffer buffer() {
71 return this;
72 }
73
74 @Override public Buffer getBuffer() {
75 return this;
76 }
77
78 @Override public OutputStream outputStream() {
79 return new OutputStream() {
80 @Override public void write(int b) {
81 writeByte((byte) b);
82 }
83
84 @Override public void write(byte[] data, int offset, int byteCount) {
85 Buffer.this.write(data, offset, byteCount);
86 }
87
88 @Override public void flush() {
89 }
90
91 @Override public void close() {
92 }
93
94 @Override public String toString() {
95 return Buffer.this + ".outputStream()";
96 }
97 };
98 }
99
100 @Override public Buffer emitCompleteSegments() {
101 return this;
102 }
103
104 @Override public BufferedSink emit() {
105 return this;
106 }
107
108 @Override public boolean exhausted() {
109 return size == 0;
110 }
111
112 @Override public void require(long byteCount) throws EOFException {
113 if (size < byteCount) throw new EOFException();
114 }
115
116 @Override public boolean request(long byteCount) {
117 return size >= byteCount;
118 }
119
120 @Override public BufferedSource peek() {
121 return Okio.buffer(new PeekSource(this));
122 }
123
124 @Override public InputStream inputStream() {
125 return new InputStream() {
126 @Override public int read() {
127 if (size > 0) return readByte() & 0xff;
128 return -1;
129 }
130
131 @Override public int read(byte[] sink, int offset, int byteCount) {
132 return Buffer.this.read(sink, offset, byteCount);
133 }
134
135 @Override public int available() {
136 return (int) Math.min(size, Integer.MAX_VALUE);
137 }
138
139 @Override public void close() {
140 }
141
142 @Override public String toString() {
143 return Buffer.this + ".inputStream()";
144 }
145 };
146 }
147
148
149 public final Buffer copyTo(OutputStream out) throws IOException {
150 return copyTo(out, 0, size);
151 }
152
153
157 public final Buffer copyTo(OutputStream out, long offset, long byteCount) throws IOException {
158 if (out == null) throw new IllegalArgumentException("out == null");
159 checkOffsetAndCount(size, offset, byteCount);
160 if (byteCount == 0) return this;
161
162
163 Segment s = head;
164 for (; offset >= (s.limit - s.pos); s = s.next) {
165 offset -= (s.limit - s.pos);
166 }
167
168
169 for (; byteCount > 0; s = s.next) {
170 int pos = (int) (s.pos + offset);
171 int toCopy = (int) Math.min(s.limit - pos, byteCount);
172 out.write(s.data, pos, toCopy);
173 byteCount -= toCopy;
174 offset = 0;
175 }
176
177 return this;
178 }
179
180
181 public final Buffer copyTo(Buffer out, long offset, long byteCount) {
182 if (out == null) throw new IllegalArgumentException("out == null");
183 checkOffsetAndCount(size, offset, byteCount);
184 if (byteCount == 0) return this;
185
186 out.size += byteCount;
187
188
189 Segment s = head;
190 for (; offset >= (s.limit - s.pos); s = s.next) {
191 offset -= (s.limit - s.pos);
192 }
193
194
195 for (; byteCount > 0; s = s.next) {
196 Segment copy = s.sharedCopy();
197 copy.pos += offset;
198 copy.limit = Math.min(copy.pos + (int) byteCount, copy.limit);
199 if (out.head == null) {
200 out.head = copy.next = copy.prev = copy;
201 } else {
202 out.head.prev.push(copy);
203 }
204 byteCount -= copy.limit - copy.pos;
205 offset = 0;
206 }
207
208 return this;
209 }
210
211
212 public final Buffer writeTo(OutputStream out) throws IOException {
213 return writeTo(out, size);
214 }
215
216
217 public final Buffer writeTo(OutputStream out, long byteCount) throws IOException {
218 if (out == null) throw new IllegalArgumentException("out == null");
219 checkOffsetAndCount(size, 0, byteCount);
220
221 Segment s = head;
222 while (byteCount > 0) {
223 int toCopy = (int) Math.min(byteCount, s.limit - s.pos);
224 out.write(s.data, s.pos, toCopy);
225
226 s.pos += toCopy;
227 size -= toCopy;
228 byteCount -= toCopy;
229
230 if (s.pos == s.limit) {
231 Segment toRecycle = s;
232 head = s = toRecycle.pop();
233 SegmentPool.recycle(toRecycle);
234 }
235 }
236
237 return this;
238 }
239
240
241 public final Buffer readFrom(InputStream in) throws IOException {
242 readFrom(in, Long.MAX_VALUE, true);
243 return this;
244 }
245
246
247 public final Buffer readFrom(InputStream in, long byteCount) throws IOException {
248 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
249 readFrom(in, byteCount, false);
250 return this;
251 }
252
253 private void readFrom(InputStream in, long byteCount, boolean forever) throws IOException {
254 if (in == null) throw new IllegalArgumentException("in == null");
255 while (byteCount > 0 || forever) {
256 Segment tail = writableSegment(1);
257 int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);
258 int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
259 if (bytesRead == -1) {
260 if (forever) return;
261 throw new EOFException();
262 }
263 tail.limit += bytesRead;
264 size += bytesRead;
265 byteCount -= bytesRead;
266 }
267 }
268
269
274 public final long completeSegmentByteCount() {
275 long result = size;
276 if (result == 0) return 0;
277
278
279 Segment tail = head.prev;
280 if (tail.limit < Segment.SIZE && tail.owner) {
281 result -= tail.limit - tail.pos;
282 }
283
284 return result;
285 }
286
287 @Override public byte readByte() {
288 if (size == 0) throw new IllegalStateException("size == 0");
289
290 Segment segment = head;
291 int pos = segment.pos;
292 int limit = segment.limit;
293
294 byte[] data = segment.data;
295 byte b = data[pos++];
296 size -= 1;
297
298 if (pos == limit) {
299 head = segment.pop();
300 SegmentPool.recycle(segment);
301 } else {
302 segment.pos = pos;
303 }
304
305 return b;
306 }
307
308
309 public final byte getByte(long pos) {
310 checkOffsetAndCount(size, pos, 1);
311 if (size - pos > pos) {
312 for (Segment s = head; true; s = s.next) {
313 int segmentByteCount = s.limit - s.pos;
314 if (pos < segmentByteCount) return s.data[s.pos + (int) pos];
315 pos -= segmentByteCount;
316 }
317 } else {
318 pos -= size;
319 for (Segment s = head.prev; true; s = s.prev) {
320 pos += s.limit - s.pos;
321 if (pos >= 0) return s.data[s.pos + (int) pos];
322 }
323 }
324 }
325
326 @Override public short readShort() {
327 if (size < 2) throw new IllegalStateException("size < 2: " + size);
328
329 Segment segment = head;
330 int pos = segment.pos;
331 int limit = segment.limit;
332
333
334 if (limit - pos < 2) {
335 int s = (readByte() & 0xff) << 8
336 | (readByte() & 0xff);
337 return (short) s;
338 }
339
340 byte[] data = segment.data;
341 int s = (data[pos++] & 0xff) << 8
342 | (data[pos++] & 0xff);
343 size -= 2;
344
345 if (pos == limit) {
346 head = segment.pop();
347 SegmentPool.recycle(segment);
348 } else {
349 segment.pos = pos;
350 }
351
352 return (short) s;
353 }
354
355 @Override public int readInt() {
356 if (size < 4) throw new IllegalStateException("size < 4: " + size);
357
358 Segment segment = head;
359 int pos = segment.pos;
360 int limit = segment.limit;
361
362
363 if (limit - pos < 4) {
364 return (readByte() & 0xff) << 24
365 | (readByte() & 0xff) << 16
366 | (readByte() & 0xff) << 8
367 | (readByte() & 0xff);
368 }
369
370 byte[] data = segment.data;
371 int i = (data[pos++] & 0xff) << 24
372 | (data[pos++] & 0xff) << 16
373 | (data[pos++] & 0xff) << 8
374 | (data[pos++] & 0xff);
375 size -= 4;
376
377 if (pos == limit) {
378 head = segment.pop();
379 SegmentPool.recycle(segment);
380 } else {
381 segment.pos = pos;
382 }
383
384 return i;
385 }
386
387 @Override public long readLong() {
388 if (size < 8) throw new IllegalStateException("size < 8: " + size);
389
390 Segment segment = head;
391 int pos = segment.pos;
392 int limit = segment.limit;
393
394
395 if (limit - pos < 8) {
396 return (readInt() & 0xffffffffL) << 32
397 | (readInt() & 0xffffffffL);
398 }
399
400 byte[] data = segment.data;
401 long v = (data[pos++] & 0xffL) << 56
402 | (data[pos++] & 0xffL) << 48
403 | (data[pos++] & 0xffL) << 40
404 | (data[pos++] & 0xffL) << 32
405 | (data[pos++] & 0xffL) << 24
406 | (data[pos++] & 0xffL) << 16
407 | (data[pos++] & 0xffL) << 8
408 | (data[pos++] & 0xffL);
409 size -= 8;
410
411 if (pos == limit) {
412 head = segment.pop();
413 SegmentPool.recycle(segment);
414 } else {
415 segment.pos = pos;
416 }
417
418 return v;
419 }
420
421 @Override public short readShortLe() {
422 return Util.reverseBytesShort(readShort());
423 }
424
425 @Override public int readIntLe() {
426 return Util.reverseBytesInt(readInt());
427 }
428
429 @Override public long readLongLe() {
430 return Util.reverseBytesLong(readLong());
431 }
432
433 @Override public long readDecimalLong() {
434 if (size == 0) throw new IllegalStateException("size == 0");
435
436
437 long value = 0;
438 int seen = 0;
439 boolean negative = false;
440 boolean done = false;
441
442 long overflowZone = Long.MIN_VALUE / 10;
443 long overflowDigit = (Long.MIN_VALUE % 10) + 1;
444
445 do {
446 Segment segment = head;
447
448 byte[] data = segment.data;
449 int pos = segment.pos;
450 int limit = segment.limit;
451
452 for (; pos < limit; pos++, seen++) {
453 byte b = data[pos];
454 if (b >= '0' && b <= '9') {
455 int digit = '0' - b;
456
457
458 if (value < overflowZone || value == overflowZone && digit < overflowDigit) {
459 Buffer buffer = new Buffer().writeDecimalLong(value).writeByte(b);
460 if (!negative) buffer.readByte();
461 throw new NumberFormatException("Number too large: " + buffer.readUtf8());
462 }
463 value *= 10;
464 value += digit;
465 } else if (b == '-' && seen == 0) {
466 negative = true;
467 overflowDigit -= 1;
468 } else {
469 if (seen == 0) {
470 throw new NumberFormatException(
471 "Expected leading [0-9] or '-' character but was 0x" + Integer.toHexString(b));
472 }
473
474 done = true;
475 break;
476 }
477 }
478
479 if (pos == limit) {
480 head = segment.pop();
481 SegmentPool.recycle(segment);
482 } else {
483 segment.pos = pos;
484 }
485 } while (!done && head != null);
486
487 size -= seen;
488 return negative ? value : -value;
489 }
490
491 @Override public long readHexadecimalUnsignedLong() {
492 if (size == 0) throw new IllegalStateException("size == 0");
493
494 long value = 0;
495 int seen = 0;
496 boolean done = false;
497
498 do {
499 Segment segment = head;
500
501 byte[] data = segment.data;
502 int pos = segment.pos;
503 int limit = segment.limit;
504
505 for (; pos < limit; pos++, seen++) {
506 int digit;
507
508 byte b = data[pos];
509 if (b >= '0' && b <= '9') {
510 digit = b - '0';
511 } else if (b >= 'a' && b <= 'f') {
512 digit = b - 'a' + 10;
513 } else if (b >= 'A' && b <= 'F') {
514 digit = b - 'A' + 10;
515 } else {
516 if (seen == 0) {
517 throw new NumberFormatException(
518 "Expected leading [0-9a-fA-F] character but was 0x" + Integer.toHexString(b));
519 }
520
521 done = true;
522 break;
523 }
524
525
526 if ((value & 0xf000000000000000L) != 0) {
527 Buffer buffer = new Buffer().writeHexadecimalUnsignedLong(value).writeByte(b);
528 throw new NumberFormatException("Number too large: " + buffer.readUtf8());
529 }
530
531 value <<= 4;
532 value |= digit;
533 }
534
535 if (pos == limit) {
536 head = segment.pop();
537 SegmentPool.recycle(segment);
538 } else {
539 segment.pos = pos;
540 }
541 } while (!done && head != null);
542
543 size -= seen;
544 return value;
545 }
546
547 @Override public ByteString readByteString() {
548 return new ByteString(readByteArray());
549 }
550
551 @Override public ByteString readByteString(long byteCount) throws EOFException {
552 return new ByteString(readByteArray(byteCount));
553 }
554
555 @Override public int select(Options options) {
556 int index = selectPrefix(options, false);
557 if (index == -1) return -1;
558
559
560 int selectedSize = options.byteStrings[index].size();
561 try {
562 skip(selectedSize);
563 } catch (EOFException e) {
564 throw new AssertionError();
565 }
566 return index;
567 }
568
569
581 int selectPrefix(Options options, boolean selectTruncated) {
582 Segment head = this.head;
583 if (head == null) {
584 if (selectTruncated) return -2;
585 return options.indexOf(ByteString.EMPTY);
586 }
587
588 Segment s = head;
589 byte[] data = head.data;
590 int pos = head.pos;
591 int limit = head.limit;
592
593 int[] trie = options.trie;
594 int triePos = 0;
595
596 int prefixIndex = -1;
597
598 navigateTrie:
599 while (true) {
600 int scanOrSelect = trie[triePos++];
601
602 int possiblePrefixIndex = trie[triePos++];
603 if (possiblePrefixIndex != -1) {
604 prefixIndex = possiblePrefixIndex;
605 }
606
607 int nextStep;
608
609 if (s == null) {
610 break;
611 } else if (scanOrSelect < 0) {
612
613 int scanByteCount = -1 * scanOrSelect;
614 int trieLimit = triePos + scanByteCount;
615 while (true) {
616 int b = data[pos++] & 0xff;
617 if (b != trie[triePos++]) return prefixIndex;
618 boolean scanComplete = (triePos == trieLimit);
619
620
621 if (pos == limit) {
622 s = s.next;
623 pos = s.pos;
624 data = s.data;
625 limit = s.limit;
626 if (s == head) {
627 if (!scanComplete) break navigateTrie;
628 s = null;
629 }
630 }
631
632 if (scanComplete) {
633 nextStep = trie[triePos];
634 break;
635 }
636 }
637 } else {
638
639 int selectChoiceCount = scanOrSelect;
640 int b = data[pos++] & 0xff;
641 int selectLimit = triePos + selectChoiceCount;
642 while (true) {
643 if (triePos == selectLimit) return prefixIndex;
644
645 if (b == trie[triePos]) {
646 nextStep = trie[triePos + selectChoiceCount];
647 break;
648 }
649
650 triePos++;
651 }
652
653
654 if (pos == limit) {
655 s = s.next;
656 pos = s.pos;
657 data = s.data;
658 limit = s.limit;
659 if (s == head) {
660 s = null;
661 }
662 }
663 }
664
665 if (nextStep >= 0) return nextStep;
666 triePos = -nextStep;
667 }
668
669
670 if (selectTruncated) return -2;
671 return prefixIndex;
672 }
673
674 @Override public void readFully(Buffer sink, long byteCount) throws EOFException {
675 if (size < byteCount) {
676 sink.write(this, size);
677 throw new EOFException();
678 }
679 sink.write(this, byteCount);
680 }
681
682 @Override public long readAll(Sink sink) throws IOException {
683 long byteCount = size;
684 if (byteCount > 0) {
685 sink.write(this, byteCount);
686 }
687 return byteCount;
688 }
689
690 @Override public String readUtf8() {
691 try {
692 return readString(size, Util.UTF_8);
693 } catch (EOFException e) {
694 throw new AssertionError(e);
695 }
696 }
697
698 @Override public String readUtf8(long byteCount) throws EOFException {
699 return readString(byteCount, Util.UTF_8);
700 }
701
702 @Override public String readString(Charset charset) {
703 try {
704 return readString(size, charset);
705 } catch (EOFException e) {
706 throw new AssertionError(e);
707 }
708 }
709
710 @Override public String readString(long byteCount, Charset charset) throws EOFException {
711 checkOffsetAndCount(size, 0, byteCount);
712 if (charset == null) throw new IllegalArgumentException("charset == null");
713 if (byteCount > Integer.MAX_VALUE) {
714 throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount);
715 }
716 if (byteCount == 0) return "";
717
718 Segment s = head;
719 if (s.pos + byteCount > s.limit) {
720
721 return new String(readByteArray(byteCount), charset);
722 }
723
724 String result = new String(s.data, s.pos, (int) byteCount, charset);
725 s.pos += byteCount;
726 size -= byteCount;
727
728 if (s.pos == s.limit) {
729 head = s.pop();
730 SegmentPool.recycle(s);
731 }
732
733 return result;
734 }
735
736 @Override public @Nullable String readUtf8Line() throws EOFException {
737 long newline = indexOf((byte) '\n');
738
739 if (newline == -1) {
740 return size != 0 ? readUtf8(size) : null;
741 }
742
743 return readUtf8Line(newline);
744 }
745
746 @Override public String readUtf8LineStrict() throws EOFException {
747 return readUtf8LineStrict(Long.MAX_VALUE);
748 }
749
750 @Override public String readUtf8LineStrict(long limit) throws EOFException {
751 if (limit < 0) throw new IllegalArgumentException("limit < 0: " + limit);
752 long scanLength = limit == Long.MAX_VALUE ? Long.MAX_VALUE : limit + 1;
753 long newline = indexOf((byte) '\n', 0, scanLength);
754 if (newline != -1) return readUtf8Line(newline);
755 if (scanLength < size()
756 && getByte(scanLength - 1) == '\r' && getByte(scanLength) == '\n') {
757 return readUtf8Line(scanLength);
758 }
759 Buffer data = new Buffer();
760 copyTo(data, 0, Math.min(32, size()));
761 throw new EOFException("\\n not found: limit=" + Math.min(size(), limit)
762 + " content=" + data.readByteString().hex() + '…');
763 }
764
765 String readUtf8Line(long newline) throws EOFException {
766 if (newline > 0 && getByte(newline - 1) == '\r') {
767
768 String result = readUtf8((newline - 1));
769 skip(2);
770 return result;
771
772 } else {
773
774 String result = readUtf8(newline);
775 skip(1);
776 return result;
777 }
778 }
779
780 @Override public int readUtf8CodePoint() throws EOFException {
781 if (size == 0) throw new EOFException();
782
783 byte b0 = getByte(0);
784 int codePoint;
785 int byteCount;
786 int min;
787
788 if ((b0 & 0x80) == 0) {
789
790 codePoint = b0 & 0x7f;
791 byteCount = 1;
792 min = 0x0;
793
794 } else if ((b0 & 0xe0) == 0xc0) {
795
796 codePoint = b0 & 0x1f;
797 byteCount = 2;
798 min = 0x80;
799
800 } else if ((b0 & 0xf0) == 0xe0) {
801
802 codePoint = b0 & 0x0f;
803 byteCount = 3;
804 min = 0x800;
805
806 } else if ((b0 & 0xf8) == 0xf0) {
807
808 codePoint = b0 & 0x07;
809 byteCount = 4;
810 min = 0x10000;
811
812 } else {
813
814 skip(1);
815 return REPLACEMENT_CHARACTER;
816 }
817
818 if (size < byteCount) {
819 throw new EOFException("size < " + byteCount + ": " + size
820 + " (to read code point prefixed 0x" + Integer.toHexString(b0) + ")");
821 }
822
823
824
825
826 for (int i = 1; i < byteCount; i++) {
827 byte b = getByte(i);
828 if ((b & 0xc0) == 0x80) {
829
830 codePoint <<= 6;
831 codePoint |= b & 0x3f;
832 } else {
833 skip(i);
834 return REPLACEMENT_CHARACTER;
835 }
836 }
837
838 skip(byteCount);
839
840 if (codePoint > 0x10ffff) {
841 return REPLACEMENT_CHARACTER;
842 }
843
844 if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
845 return REPLACEMENT_CHARACTER;
846 }
847
848 if (codePoint < min) {
849 return REPLACEMENT_CHARACTER;
850 }
851
852 return codePoint;
853 }
854
855 @Override public byte[] readByteArray() {
856 try {
857 return readByteArray(size);
858 } catch (EOFException e) {
859 throw new AssertionError(e);
860 }
861 }
862
863 @Override public byte[] readByteArray(long byteCount) throws EOFException {
864 checkOffsetAndCount(size, 0, byteCount);
865 if (byteCount > Integer.MAX_VALUE) {
866 throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount);
867 }
868
869 byte[] result = new byte[(int) byteCount];
870 readFully(result);
871 return result;
872 }
873
874 @Override public int read(byte[] sink) {
875 return read(sink, 0, sink.length);
876 }
877
878 @Override public void readFully(byte[] sink) throws EOFException {
879 int offset = 0;
880 while (offset < sink.length) {
881 int read = read(sink, offset, sink.length - offset);
882 if (read == -1) throw new EOFException();
883 offset += read;
884 }
885 }
886
887 @Override public int read(byte[] sink, int offset, int byteCount) {
888 checkOffsetAndCount(sink.length, offset, byteCount);
889
890 Segment s = head;
891 if (s == null) return -1;
892 int toCopy = Math.min(byteCount, s.limit - s.pos);
893 System.arraycopy(s.data, s.pos, sink, offset, toCopy);
894
895 s.pos += toCopy;
896 size -= toCopy;
897
898 if (s.pos == s.limit) {
899 head = s.pop();
900 SegmentPool.recycle(s);
901 }
902
903 return toCopy;
904 }
905
906 @Override public int read(ByteBuffer sink) throws IOException {
907 Segment s = head;
908 if (s == null) return -1;
909
910 int toCopy = Math.min(sink.remaining(), s.limit - s.pos);
911 sink.put(s.data, s.pos, toCopy);
912
913 s.pos += toCopy;
914 size -= toCopy;
915
916 if (s.pos == s.limit) {
917 head = s.pop();
918 SegmentPool.recycle(s);
919 }
920
921 return toCopy;
922 }
923
924
928 public final void clear() {
929 try {
930 skip(size);
931 } catch (EOFException e) {
932 throw new AssertionError(e);
933 }
934 }
935
936
937 @Override public void skip(long byteCount) throws EOFException {
938 while (byteCount > 0) {
939 if (head == null) throw new EOFException();
940
941 int toSkip = (int) Math.min(byteCount, head.limit - head.pos);
942 size -= toSkip;
943 byteCount -= toSkip;
944 head.pos += toSkip;
945
946 if (head.pos == head.limit) {
947 Segment toRecycle = head;
948 head = toRecycle.pop();
949 SegmentPool.recycle(toRecycle);
950 }
951 }
952 }
953
954 @Override public Buffer write(ByteString byteString) {
955 if (byteString == null) throw new IllegalArgumentException("byteString == null");
956 byteString.write(this);
957 return this;
958 }
959
960 @Override public Buffer writeUtf8(String string) {
961 return writeUtf8(string, 0, string.length());
962 }
963
964 @Override public Buffer writeUtf8(String string, int beginIndex, int endIndex) {
965 if (string == null) throw new IllegalArgumentException("string == null");
966 if (beginIndex < 0) throw new IllegalArgumentException("beginIndex < 0: " + beginIndex);
967 if (endIndex < beginIndex) {
968 throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex);
969 }
970 if (endIndex > string.length()) {
971 throw new IllegalArgumentException(
972 "endIndex > string.length: " + endIndex + " > " + string.length());
973 }
974
975
976 for (int i = beginIndex; i < endIndex;) {
977 int c = string.charAt(i);
978
979 if (c < 0x80) {
980 Segment tail = writableSegment(1);
981 byte[] data = tail.data;
982 int segmentOffset = tail.limit - i;
983 int runLimit = Math.min(endIndex, Segment.SIZE - segmentOffset);
984
985
986 data[segmentOffset + i++] = (byte) c;
987
988
989
990 while (i < runLimit) {
991 c = string.charAt(i);
992 if (c >= 0x80) break;
993 data[segmentOffset + i++] = (byte) c;
994 }
995
996 int runSize = i + segmentOffset - tail.limit;
997 tail.limit += runSize;
998 size += runSize;
999
1000 } else if (c < 0x800) {
1001
1002 writeByte(c >> 6 | 0xc0);
1003 writeByte(c & 0x3f | 0x80);
1004 i++;
1005
1006 } else if (c < 0xd800 || c > 0xdfff) {
1007
1008 writeByte(c >> 12 | 0xe0);
1009 writeByte(c >> 6 & 0x3f | 0x80);
1010 writeByte(c & 0x3f | 0x80);
1011 i++;
1012
1013 } else {
1014
1015
1016 int low = i + 1 < endIndex ? string.charAt(i + 1) : 0;
1017 if (c > 0xdbff || low < 0xdc00 || low > 0xdfff) {
1018 writeByte('?');
1019 i++;
1020 continue;
1021 }
1022
1023
1024
1025
1026 int codePoint = 0x010000 + ((c & ~0xd800) << 10 | low & ~0xdc00);
1027
1028
1029 writeByte(codePoint >> 18 | 0xf0);
1030 writeByte(codePoint >> 12 & 0x3f | 0x80);
1031 writeByte(codePoint >> 6 & 0x3f | 0x80);
1032 writeByte(codePoint & 0x3f | 0x80);
1033 i += 2;
1034 }
1035 }
1036
1037 return this;
1038 }
1039
1040 @Override public Buffer writeUtf8CodePoint(int codePoint) {
1041 if (codePoint < 0x80) {
1042
1043 writeByte(codePoint);
1044
1045 } else if (codePoint < 0x800) {
1046
1047 writeByte(codePoint >> 6 | 0xc0);
1048 writeByte(codePoint & 0x3f | 0x80);
1049
1050 } else if (codePoint < 0x10000) {
1051 if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
1052
1053 writeByte('?');
1054 } else {
1055
1056 writeByte(codePoint >> 12 | 0xe0);
1057 writeByte(codePoint >> 6 & 0x3f | 0x80);
1058 writeByte(codePoint & 0x3f | 0x80);
1059 }
1060
1061 } else if (codePoint <= 0x10ffff) {
1062
1063 writeByte(codePoint >> 18 | 0xf0);
1064 writeByte(codePoint >> 12 & 0x3f | 0x80);
1065 writeByte(codePoint >> 6 & 0x3f | 0x80);
1066 writeByte(codePoint & 0x3f | 0x80);
1067
1068 } else {
1069 throw new IllegalArgumentException(
1070 "Unexpected code point: " + Integer.toHexString(codePoint));
1071 }
1072
1073 return this;
1074 }
1075
1076 @Override public Buffer writeString(String string, Charset charset) {
1077 return writeString(string, 0, string.length(), charset);
1078 }
1079
1080 @Override
1081 public Buffer writeString(String string, int beginIndex, int endIndex, Charset charset) {
1082 if (string == null) throw new IllegalArgumentException("string == null");
1083 if (beginIndex < 0) throw new IllegalAccessError("beginIndex < 0: " + beginIndex);
1084 if (endIndex < beginIndex) {
1085 throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex);
1086 }
1087 if (endIndex > string.length()) {
1088 throw new IllegalArgumentException(
1089 "endIndex > string.length: " + endIndex + " > " + string.length());
1090 }
1091 if (charset == null) throw new IllegalArgumentException("charset == null");
1092 if (charset.equals(Util.UTF_8)) return writeUtf8(string, beginIndex, endIndex);
1093 byte[] data = string.substring(beginIndex, endIndex).getBytes(charset);
1094 return write(data, 0, data.length);
1095 }
1096
1097 @Override public Buffer write(byte[] source) {
1098 if (source == null) throw new IllegalArgumentException("source == null");
1099 return write(source, 0, source.length);
1100 }
1101
1102 @Override public Buffer write(byte[] source, int offset, int byteCount) {
1103 if (source == null) throw new IllegalArgumentException("source == null");
1104 checkOffsetAndCount(source.length, offset, byteCount);
1105
1106 int limit = offset + byteCount;
1107 while (offset < limit) {
1108 Segment tail = writableSegment(1);
1109
1110 int toCopy = Math.min(limit - offset, Segment.SIZE - tail.limit);
1111 System.arraycopy(source, offset, tail.data, tail.limit, toCopy);
1112
1113 offset += toCopy;
1114 tail.limit += toCopy;
1115 }
1116
1117 size += byteCount;
1118 return this;
1119 }
1120
1121 @Override public int write(ByteBuffer source) throws IOException {
1122 if (source == null) throw new IllegalArgumentException("source == null");
1123
1124 int byteCount = source.remaining();
1125 int remaining = byteCount;
1126 while (remaining > 0) {
1127 Segment tail = writableSegment(1);
1128
1129 int toCopy = Math.min(remaining, Segment.SIZE - tail.limit);
1130 source.get(tail.data, tail.limit, toCopy);
1131
1132 remaining -= toCopy;
1133 tail.limit += toCopy;
1134 }
1135
1136 size += byteCount;
1137 return byteCount;
1138 }
1139
1140 @Override public long writeAll(Source source) throws IOException {
1141 if (source == null) throw new IllegalArgumentException("source == null");
1142 long totalBytesRead = 0;
1143 for (long readCount; (readCount = source.read(this, Segment.SIZE)) != -1; ) {
1144 totalBytesRead += readCount;
1145 }
1146 return totalBytesRead;
1147 }
1148
1149 @Override public BufferedSink write(Source source, long byteCount) throws IOException {
1150 while (byteCount > 0) {
1151 long read = source.read(this, byteCount);
1152 if (read == -1) throw new EOFException();
1153 byteCount -= read;
1154 }
1155 return this;
1156 }
1157
1158 @Override public Buffer writeByte(int b) {
1159 Segment tail = writableSegment(1);
1160 tail.data[tail.limit++] = (byte) b;
1161 size += 1;
1162 return this;
1163 }
1164
1165 @Override public Buffer writeShort(int s) {
1166 Segment tail = writableSegment(2);
1167 byte[] data = tail.data;
1168 int limit = tail.limit;
1169 data[limit++] = (byte) ((s >>> 8) & 0xff);
1170 data[limit++] = (byte) (s & 0xff);
1171 tail.limit = limit;
1172 size += 2;
1173 return this;
1174 }
1175
1176 @Override public Buffer writeShortLe(int s) {
1177 return writeShort(Util.reverseBytesShort((short) s));
1178 }
1179
1180 @Override public Buffer writeInt(int i) {
1181 Segment tail = writableSegment(4);
1182 byte[] data = tail.data;
1183 int limit = tail.limit;
1184 data[limit++] = (byte) ((i >>> 24) & 0xff);
1185 data[limit++] = (byte) ((i >>> 16) & 0xff);
1186 data[limit++] = (byte) ((i >>> 8) & 0xff);
1187 data[limit++] = (byte) (i & 0xff);
1188 tail.limit = limit;
1189 size += 4;
1190 return this;
1191 }
1192
1193 @Override public Buffer writeIntLe(int i) {
1194 return writeInt(Util.reverseBytesInt(i));
1195 }
1196
1197 @Override public Buffer writeLong(long v) {
1198 Segment tail = writableSegment(8);
1199 byte[] data = tail.data;
1200 int limit = tail.limit;
1201 data[limit++] = (byte) ((v >>> 56L) & 0xff);
1202 data[limit++] = (byte) ((v >>> 48L) & 0xff);
1203 data[limit++] = (byte) ((v >>> 40L) & 0xff);
1204 data[limit++] = (byte) ((v >>> 32L) & 0xff);
1205 data[limit++] = (byte) ((v >>> 24L) & 0xff);
1206 data[limit++] = (byte) ((v >>> 16L) & 0xff);
1207 data[limit++] = (byte) ((v >>> 8L) & 0xff);
1208 data[limit++] = (byte) (v & 0xff);
1209 tail.limit = limit;
1210 size += 8;
1211 return this;
1212 }
1213
1214 @Override public Buffer writeLongLe(long v) {
1215 return writeLong(reverseBytesLong(v));
1216 }
1217
1218 @Override public Buffer writeDecimalLong(long v) {
1219 if (v == 0) {
1220
1221 return writeByte('0');
1222 }
1223
1224 boolean negative = false;
1225 if (v < 0) {
1226 v = -v;
1227 if (v < 0) {
1228 return writeUtf8("-9223372036854775808");
1229 }
1230 negative = true;
1231 }
1232
1233
1234 int width =
1235 v < 100000000L
1236 ? v < 10000L
1237 ? v < 100L
1238 ? v < 10L ? 1 : 2
1239 : v < 1000L ? 3 : 4
1240 : v < 1000000L
1241 ? v < 100000L ? 5 : 6
1242 : v < 10000000L ? 7 : 8
1243 : v < 1000000000000L
1244 ? v < 10000000000L
1245 ? v < 1000000000L ? 9 : 10
1246 : v < 100000000000L ? 11 : 12
1247 : v < 1000000000000000L
1248 ? v < 10000000000000L ? 13
1249 : v < 100000000000000L ? 14 : 15
1250 : v < 100000000000000000L
1251 ? v < 10000000000000000L ? 16 : 17
1252 : v < 1000000000000000000L ? 18 : 19;
1253 if (negative) {
1254 ++width;
1255 }
1256
1257 Segment tail = writableSegment(width);
1258 byte[] data = tail.data;
1259 int pos = tail.limit + width;
1260 while (v != 0) {
1261 int digit = (int) (v % 10);
1262 data[--pos] = DIGITS[digit];
1263 v /= 10;
1264 }
1265 if (negative) {
1266 data[--pos] = '-';
1267 }
1268
1269 tail.limit += width;
1270 this.size += width;
1271 return this;
1272 }
1273
1274 @Override public Buffer writeHexadecimalUnsignedLong(long v) {
1275 if (v == 0) {
1276
1277 return writeByte('0');
1278 }
1279
1280 int width = Long.numberOfTrailingZeros(Long.highestOneBit(v)) / 4 + 1;
1281
1282 Segment tail = writableSegment(width);
1283 byte[] data = tail.data;
1284 for (int pos = tail.limit + width - 1, start = tail.limit; pos >= start; pos--) {
1285 data[pos] = DIGITS[(int) (v & 0xF)];
1286 v >>>= 4;
1287 }
1288 tail.limit += width;
1289 size += width;
1290 return this;
1291 }
1292
1293
1297 Segment writableSegment(int minimumCapacity) {
1298 if (minimumCapacity < 1 || minimumCapacity > Segment.SIZE) throw new IllegalArgumentException();
1299
1300 if (head == null) {
1301 head = SegmentPool.take();
1302 return head.next = head.prev = head;
1303 }
1304
1305 Segment tail = head.prev;
1306 if (tail.limit + minimumCapacity > Segment.SIZE || !tail.owner) {
1307 tail = tail.push(SegmentPool.take());
1308 }
1309 return tail;
1310 }
1311
1312 @Override public void write(Buffer source, long byteCount) {
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363 if (source == null) throw new IllegalArgumentException("source == null");
1364 if (source == this) throw new IllegalArgumentException("source == this");
1365 checkOffsetAndCount(source.size, 0, byteCount);
1366
1367 while (byteCount > 0) {
1368
1369 if (byteCount < (source.head.limit - source.head.pos)) {
1370 Segment tail = head != null ? head.prev : null;
1371 if (tail != null && tail.owner
1372 && (byteCount + tail.limit - (tail.shared ? 0 : tail.pos) <= Segment.SIZE)) {
1373
1374 source.head.writeTo(tail, (int) byteCount);
1375 source.size -= byteCount;
1376 size += byteCount;
1377 return;
1378 } else {
1379
1380
1381 source.head = source.head.split((int) byteCount);
1382 }
1383 }
1384
1385
1386 Segment segmentToMove = source.head;
1387 long movedByteCount = segmentToMove.limit - segmentToMove.pos;
1388 source.head = segmentToMove.pop();
1389 if (head == null) {
1390 head = segmentToMove;
1391 head.next = head.prev = head;
1392 } else {
1393 Segment tail = head.prev;
1394 tail = tail.push(segmentToMove);
1395 tail.compact();
1396 }
1397 source.size -= movedByteCount;
1398 size += movedByteCount;
1399 byteCount -= movedByteCount;
1400 }
1401 }
1402
1403 @Override public long read(Buffer sink, long byteCount) {
1404 if (sink == null) throw new IllegalArgumentException("sink == null");
1405 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
1406 if (size == 0) return -1L;
1407 if (byteCount > size) byteCount = size;
1408 sink.write(this, byteCount);
1409 return byteCount;
1410 }
1411
1412 @Override public long indexOf(byte b) {
1413 return indexOf(b, 0, Long.MAX_VALUE);
1414 }
1415
1416
1420 @Override public long indexOf(byte b, long fromIndex) {
1421 return indexOf(b, fromIndex, Long.MAX_VALUE);
1422 }
1423
1424 @Override public long indexOf(byte b, long fromIndex, long toIndex) {
1425 if (fromIndex < 0 || toIndex < fromIndex) {
1426 throw new IllegalArgumentException(
1427 String.format("size=%s fromIndex=%s toIndex=%s", size, fromIndex, toIndex));
1428 }
1429
1430 if (toIndex > size) toIndex = size;
1431 if (fromIndex == toIndex) return -1L;
1432
1433 Segment s;
1434 long offset;
1435
1436
1437 findSegmentAndOffset: {
1438
1439 s = head;
1440 if (s == null) {
1441
1442 return -1L;
1443 } else if (size - fromIndex < fromIndex) {
1444
1445 offset = size;
1446 while (offset > fromIndex) {
1447 s = s.prev;
1448 offset -= (s.limit - s.pos);
1449 }
1450 } else {
1451
1452 offset = 0L;
1453 for (long nextOffset; (nextOffset = offset + (s.limit - s.pos)) < fromIndex; ) {
1454 s = s.next;
1455 offset = nextOffset;
1456 }
1457 }
1458 }
1459
1460
1461 while (offset < toIndex) {
1462 byte[] data = s.data;
1463 int limit = (int) Math.min(s.limit, s.pos + toIndex - offset);
1464 int pos = (int) (s.pos + fromIndex - offset);
1465 for (; pos < limit; pos++) {
1466 if (data[pos] == b) {
1467 return pos - s.pos + offset;
1468 }
1469 }
1470
1471
1472 offset += (s.limit - s.pos);
1473 fromIndex = offset;
1474 s = s.next;
1475 }
1476
1477 return -1L;
1478 }
1479
1480 @Override public long indexOf(ByteString bytes) throws IOException {
1481 return indexOf(bytes, 0);
1482 }
1483
1484 @Override public long indexOf(ByteString bytes, long fromIndex) throws IOException {
1485 if (bytes.size() == 0) throw new IllegalArgumentException("bytes is empty");
1486 if (fromIndex < 0) throw new IllegalArgumentException("fromIndex < 0");
1487
1488 Segment s;
1489 long offset;
1490
1491
1492 findSegmentAndOffset: {
1493
1494 s = head;
1495 if (s == null) {
1496
1497 return -1L;
1498 } else if (size - fromIndex < fromIndex) {
1499
1500 offset = size;
1501 while (offset > fromIndex) {
1502 s = s.prev;
1503 offset -= (s.limit - s.pos);
1504 }
1505 } else {
1506
1507 offset = 0L;
1508 for (long nextOffset; (nextOffset = offset + (s.limit - s.pos)) < fromIndex; ) {
1509 s = s.next;
1510 offset = nextOffset;
1511 }
1512 }
1513 }
1514
1515
1516
1517 byte b0 = bytes.getByte(0);
1518 int bytesSize = bytes.size();
1519 long resultLimit = size - bytesSize + 1;
1520 while (offset < resultLimit) {
1521
1522 byte[] data = s.data;
1523 int segmentLimit = (int) Math.min(s.limit, s.pos + resultLimit - offset);
1524 for (int pos = (int) (s.pos + fromIndex - offset); pos < segmentLimit; pos++) {
1525 if (data[pos] == b0 && rangeEquals(s, pos + 1, bytes, 1, bytesSize)) {
1526 return pos - s.pos + offset;
1527 }
1528 }
1529
1530
1531 offset += (s.limit - s.pos);
1532 fromIndex = offset;
1533 s = s.next;
1534 }
1535
1536 return -1L;
1537 }
1538
1539 @Override public long indexOfElement(ByteString targetBytes) {
1540 return indexOfElement(targetBytes, 0);
1541 }
1542
1543 @Override public long indexOfElement(ByteString targetBytes, long fromIndex) {
1544 if (fromIndex < 0) throw new IllegalArgumentException("fromIndex < 0");
1545
1546 Segment s;
1547 long offset;
1548
1549
1550 findSegmentAndOffset: {
1551
1552 s = head;
1553 if (s == null) {
1554
1555 return -1L;
1556 } else if (size - fromIndex < fromIndex) {
1557
1558 offset = size;
1559 while (offset > fromIndex) {
1560 s = s.prev;
1561 offset -= (s.limit - s.pos);
1562 }
1563 } else {
1564
1565 offset = 0L;
1566 for (long nextOffset; (nextOffset = offset + (s.limit - s.pos)) < fromIndex; ) {
1567 s = s.next;
1568 offset = nextOffset;
1569 }
1570 }
1571 }
1572
1573
1574
1575
1576 if (targetBytes.size() == 2) {
1577
1578 byte b0 = targetBytes.getByte(0);
1579 byte b1 = targetBytes.getByte(1);
1580 while (offset < size) {
1581 byte[] data = s.data;
1582 for (int pos = (int) (s.pos + fromIndex - offset), limit = s.limit; pos < limit; pos++) {
1583 int b = data[pos];
1584 if (b == b0 || b == b1) {
1585 return pos - s.pos + offset;
1586 }
1587 }
1588
1589
1590 offset += (s.limit - s.pos);
1591 fromIndex = offset;
1592 s = s.next;
1593 }
1594 } else {
1595
1596 byte[] targetByteArray = targetBytes.internalArray();
1597 while (offset < size) {
1598 byte[] data = s.data;
1599 for (int pos = (int) (s.pos + fromIndex - offset), limit = s.limit; pos < limit; pos++) {
1600 int b = data[pos];
1601 for (byte t : targetByteArray) {
1602 if (b == t) return pos - s.pos + offset;
1603 }
1604 }
1605
1606
1607 offset += (s.limit - s.pos);
1608 fromIndex = offset;
1609 s = s.next;
1610 }
1611 }
1612
1613 return -1L;
1614 }
1615
1616 @Override public boolean rangeEquals(long offset, ByteString bytes) {
1617 return rangeEquals(offset, bytes, 0, bytes.size());
1618 }
1619
1620 @Override public boolean rangeEquals(
1621 long offset, ByteString bytes, int bytesOffset, int byteCount) {
1622 if (offset < 0
1623 || bytesOffset < 0
1624 || byteCount < 0
1625 || size - offset < byteCount
1626 || bytes.size() - bytesOffset < byteCount) {
1627 return false;
1628 }
1629 for (int i = 0; i < byteCount; i++) {
1630 if (getByte(offset + i) != bytes.getByte(bytesOffset + i)) {
1631 return false;
1632 }
1633 }
1634 return true;
1635 }
1636
1637
1641 private boolean rangeEquals(
1642 Segment segment, int segmentPos, ByteString bytes, int bytesOffset, int bytesLimit) {
1643 int segmentLimit = segment.limit;
1644 byte[] data = segment.data;
1645
1646 for (int i = bytesOffset; i < bytesLimit; ) {
1647 if (segmentPos == segmentLimit) {
1648 segment = segment.next;
1649 data = segment.data;
1650 segmentPos = segment.pos;
1651 segmentLimit = segment.limit;
1652 }
1653
1654 if (data[segmentPos] != bytes.getByte(i)) {
1655 return false;
1656 }
1657
1658 segmentPos++;
1659 i++;
1660 }
1661
1662 return true;
1663 }
1664
1665 @Override public void flush() {
1666 }
1667
1668 @Override public boolean isOpen() {
1669 return true;
1670 }
1671
1672 @Override public void close() {
1673 }
1674
1675 @Override public Timeout timeout() {
1676 return Timeout.NONE;
1677 }
1678
1679
1680 List<Integer> segmentSizes() {
1681 if (head == null) return Collections.emptyList();
1682 List<Integer> result = new ArrayList<>();
1683 result.add(head.limit - head.pos);
1684 for (Segment s = head.next; s != head; s = s.next) {
1685 result.add(s.limit - s.pos);
1686 }
1687 return result;
1688 }
1689
1690
1691 public final ByteString md5() {
1692 return digest("MD5");
1693 }
1694
1695
1696 public final ByteString sha1() {
1697 return digest("SHA-1");
1698 }
1699
1700
1701 public final ByteString sha256() {
1702 return digest("SHA-256");
1703 }
1704
1705
1706 public final ByteString sha512() {
1707 return digest("SHA-512");
1708 }
1709
1710 private ByteString digest(String algorithm) {
1711 try {
1712 MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
1713 if (head != null) {
1714 messageDigest.update(head.data, head.pos, head.limit - head.pos);
1715 for (Segment s = head.next; s != head; s = s.next) {
1716 messageDigest.update(s.data, s.pos, s.limit - s.pos);
1717 }
1718 }
1719 return ByteString.of(messageDigest.digest());
1720 } catch (NoSuchAlgorithmException e) {
1721 throw new AssertionError();
1722 }
1723 }
1724
1725
1726 public final ByteString hmacSha1(ByteString key) {
1727 return hmac("HmacSHA1", key);
1728 }
1729
1730
1731 public final ByteString hmacSha256(ByteString key) {
1732 return hmac("HmacSHA256", key);
1733 }
1734
1735
1736 public final ByteString hmacSha512(ByteString key) {
1737 return hmac("HmacSHA512", key);
1738 }
1739
1740 private ByteString hmac(String algorithm, ByteString key) {
1741 try {
1742 Mac mac = Mac.getInstance(algorithm);
1743 mac.init(new SecretKeySpec(key.toByteArray(), algorithm));
1744 if (head != null) {
1745 mac.update(head.data, head.pos, head.limit - head.pos);
1746 for (Segment s = head.next; s != head; s = s.next) {
1747 mac.update(s.data, s.pos, s.limit - s.pos);
1748 }
1749 }
1750 return ByteString.of(mac.doFinal());
1751 } catch (NoSuchAlgorithmException e) {
1752 throw new AssertionError();
1753 } catch (InvalidKeyException e) {
1754 throw new IllegalArgumentException(e);
1755 }
1756 }
1757
1758 @Override public boolean equals(Object o) {
1759 if (this == o) return true;
1760 if (!(o instanceof Buffer)) return false;
1761 Buffer that = (Buffer) o;
1762 if (size != that.size) return false;
1763 if (size == 0) return true;
1764
1765 Segment sa = this.head;
1766 Segment sb = that.head;
1767 int posA = sa.pos;
1768 int posB = sb.pos;
1769
1770 for (long pos = 0, count; pos < size; pos += count) {
1771 count = Math.min(sa.limit - posA, sb.limit - posB);
1772
1773 for (int i = 0; i < count; i++) {
1774 if (sa.data[posA++] != sb.data[posB++]) return false;
1775 }
1776
1777 if (posA == sa.limit) {
1778 sa = sa.next;
1779 posA = sa.pos;
1780 }
1781
1782 if (posB == sb.limit) {
1783 sb = sb.next;
1784 posB = sb.pos;
1785 }
1786 }
1787
1788 return true;
1789 }
1790
1791 @Override public int hashCode() {
1792 Segment s = head;
1793 if (s == null) return 0;
1794 int result = 1;
1795 do {
1796 for (int pos = s.pos, limit = s.limit; pos < limit; pos++) {
1797 result = 31 * result + s.data[pos];
1798 }
1799 s = s.next;
1800 } while (s != head);
1801 return result;
1802 }
1803
1804
1808 @Override public String toString() {
1809 return snapshot().toString();
1810 }
1811
1812
1813 @Override public Buffer clone() {
1814 Buffer result = new Buffer();
1815 if (size == 0) return result;
1816
1817 result.head = head.sharedCopy();
1818 result.head.next = result.head.prev = result.head;
1819 for (Segment s = head.next; s != head; s = s.next) {
1820 result.head.prev.push(s.sharedCopy());
1821 }
1822 result.size = size;
1823 return result;
1824 }
1825
1826
1827 public final ByteString snapshot() {
1828 if (size > Integer.MAX_VALUE) {
1829 throw new IllegalArgumentException("size > Integer.MAX_VALUE: " + size);
1830 }
1831 return snapshot((int) size);
1832 }
1833
1834
1837 public final ByteString snapshot(int byteCount) {
1838 if (byteCount == 0) return ByteString.EMPTY;
1839 return new SegmentedByteString(this, byteCount);
1840 }
1841
1842 public final UnsafeCursor readUnsafe() {
1843 return readUnsafe(new UnsafeCursor());
1844 }
1845
1846 public final UnsafeCursor readUnsafe(UnsafeCursor unsafeCursor) {
1847 if (unsafeCursor.buffer != null) {
1848 throw new IllegalStateException("already attached to a buffer");
1849 }
1850
1851 unsafeCursor.buffer = this;
1852 unsafeCursor.readWrite = false;
1853 return unsafeCursor;
1854 }
1855
1856 public final UnsafeCursor readAndWriteUnsafe() {
1857 return readAndWriteUnsafe(new UnsafeCursor());
1858 }
1859
1860 public final UnsafeCursor readAndWriteUnsafe(UnsafeCursor unsafeCursor) {
1861 if (unsafeCursor.buffer != null) {
1862 throw new IllegalStateException("already attached to a buffer");
1863 }
1864
1865 unsafeCursor.buffer = this;
1866 unsafeCursor.readWrite = true;
1867 return unsafeCursor;
1868 }
1869
1870
2070 public static final class UnsafeCursor implements Closeable {
2071 public Buffer buffer;
2072 public boolean readWrite;
2073
2074 private Segment segment;
2075 public long offset = -1L;
2076 public byte[] data;
2077 public int start = -1;
2078 public int end = -1;
2079
2080
2085 public final int next() {
2086 if (offset == buffer.size) throw new IllegalStateException();
2087 if (offset == -1L) return seek(0L);
2088 return seek(offset + (end - start));
2089 }
2090
2091
2096 public final int seek(long offset) {
2097 if (offset < -1 || offset > buffer.size) {
2098 throw new ArrayIndexOutOfBoundsException(
2099 String.format("offset=%s > size=%s", offset, buffer.size));
2100 }
2101
2102 if (offset == -1 || offset == buffer.size) {
2103 this.segment = null;
2104 this.offset = offset;
2105 this.data = null;
2106 this.start = -1;
2107 this.end = -1;
2108 return -1;
2109 }
2110
2111
2112 long min = 0L;
2113 long max = buffer.size;
2114 Segment head = buffer.head;
2115 Segment tail = buffer.head;
2116 if (this.segment != null) {
2117 long segmentOffset = this.offset - (this.start - this.segment.pos);
2118 if (segmentOffset > offset) {
2119
2120 max = segmentOffset;
2121 tail = this.segment;
2122 } else {
2123
2124 min = segmentOffset;
2125 head = this.segment;
2126 }
2127 }
2128
2129 Segment next;
2130 long nextOffset;
2131 if (max - offset > offset - min) {
2132
2133 next = head;
2134 nextOffset = min;
2135 while (offset >= nextOffset + (next.limit - next.pos)) {
2136 nextOffset += (next.limit - next.pos);
2137 next = next.next;
2138 }
2139 } else {
2140
2141 next = tail;
2142 nextOffset = max;
2143 while (nextOffset > offset) {
2144 next = next.prev;
2145 nextOffset -= (next.limit - next.pos);
2146 }
2147 }
2148
2149
2150 if (readWrite && next.shared) {
2151 Segment unsharedNext = next.unsharedCopy();
2152 if (buffer.head == next) {
2153 buffer.head = unsharedNext;
2154 }
2155 next = next.push(unsharedNext);
2156 next.prev.pop();
2157 }
2158
2159
2160 this.segment = next;
2161 this.offset = offset;
2162 this.data = next.data;
2163 this.start = next.pos + (int) (offset - nextOffset);
2164 this.end = next.limit;
2165 return end - start;
2166 }
2167
2168
2185 public final long resizeBuffer(long newSize) {
2186 if (buffer == null) {
2187 throw new IllegalStateException("not attached to a buffer");
2188 }
2189 if (!readWrite) {
2190 throw new IllegalStateException("resizeBuffer() only permitted for read/write buffers");
2191 }
2192
2193 long oldSize = buffer.size;
2194 if (newSize <= oldSize) {
2195 if (newSize < 0) {
2196 throw new IllegalArgumentException("newSize < 0: " + newSize);
2197 }
2198
2199 for (long bytesToSubtract = oldSize - newSize; bytesToSubtract > 0; ) {
2200 Segment tail = buffer.head.prev;
2201 int tailSize = tail.limit - tail.pos;
2202 if (tailSize <= bytesToSubtract) {
2203 buffer.head = tail.pop();
2204 SegmentPool.recycle(tail);
2205 bytesToSubtract -= tailSize;
2206 } else {
2207 tail.limit -= bytesToSubtract;
2208 break;
2209 }
2210 }
2211
2212 this.segment = null;
2213 this.offset = newSize;
2214 this.data = null;
2215 this.start = -1;
2216 this.end = -1;
2217 } else if (newSize > oldSize) {
2218
2219 boolean needsToSeek = true;
2220 for (long bytesToAdd = newSize - oldSize; bytesToAdd > 0; ) {
2221 Segment tail = buffer.writableSegment(1);
2222 int segmentBytesToAdd = (int) Math.min(bytesToAdd, Segment.SIZE - tail.limit);
2223 tail.limit += segmentBytesToAdd;
2224 bytesToAdd -= segmentBytesToAdd;
2225
2226
2227 if (needsToSeek) {
2228 this.segment = tail;
2229 this.offset = oldSize;
2230 this.data = tail.data;
2231 this.start = tail.limit - segmentBytesToAdd;
2232 this.end = tail.limit;
2233 needsToSeek = false;
2234 }
2235 }
2236 }
2237
2238 buffer.size = newSize;
2239
2240 return oldSize;
2241 }
2242
2243
2266 public final long expandBuffer(int minByteCount) {
2267 if (minByteCount <= 0) {
2268 throw new IllegalArgumentException("minByteCount <= 0: " + minByteCount);
2269 }
2270 if (minByteCount > Segment.SIZE) {
2271 throw new IllegalArgumentException("minByteCount > Segment.SIZE: " + minByteCount);
2272 }
2273 if (buffer == null) {
2274 throw new IllegalStateException("not attached to a buffer");
2275 }
2276 if (!readWrite) {
2277 throw new IllegalStateException("expandBuffer() only permitted for read/write buffers");
2278 }
2279
2280 long oldSize = buffer.size;
2281 Segment tail = buffer.writableSegment(minByteCount);
2282 int result = Segment.SIZE - tail.limit;
2283 tail.limit = Segment.SIZE;
2284 buffer.size = oldSize + result;
2285
2286
2287 this.segment = tail;
2288 this.offset = oldSize;
2289 this.data = tail.data;
2290 this.start = Segment.SIZE - result;
2291 this.end = Segment.SIZE;
2292
2293 return result;
2294 }
2295
2296 @Override public void close() {
2297
2298 if (buffer == null) {
2299 throw new IllegalStateException("not attached to a buffer");
2300 }
2301
2302 buffer = null;
2303 segment = null;
2304 offset = -1L;
2305 data = null;
2306 start = -1;
2307 end = -1;
2308 }
2309 }
2310 }
2311