/* * Decompiled with CFR 0.152. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import okio.BufferedSink; import okio.BufferedSource; import okio.ByteString; import okio.Segment; import okio.SegmentPool; import okio.SegmentedByteString; import okio.Sink; import okio.Source; import okio.Timeout; import okio.Util; public final class Buffer implements BufferedSource, BufferedSink, Cloneable { private static final byte[] DIGITS = new byte[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102}; static final int REPLACEMENT_CHARACTER = 65533; Segment head; long size; public long size() { return this.size; } @Override public Buffer buffer() { return this; } @Override public OutputStream outputStream() { return new OutputStream(){ @Override public void write(int b) { Buffer.this.writeByte((byte)b); } @Override public void write(byte[] data, int offset, int byteCount) { Buffer.this.write(data, offset, byteCount); } @Override public void flush() { } @Override public void close() { } public String toString() { return this + ".outputStream()"; } }; } @Override public Buffer emitCompleteSegments() { return this; } @Override public BufferedSink emit() { return this; } @Override public boolean exhausted() { return this.size == 0L; } @Override public void require(long byteCount) throws EOFException { if (this.size < byteCount) { throw new EOFException(); } } @Override public boolean request(long byteCount) { return this.size >= byteCount; } @Override public InputStream inputStream() { return new InputStream(){ @Override public int read() { if (Buffer.this.size > 0L) { return Buffer.this.readByte() & 0xFF; } return -1; } @Override public int read(byte[] sink, int offset, int byteCount) { return Buffer.this.read(sink, offset, byteCount); } @Override public int available() { return (int)Math.min(Buffer.this.size, Integer.MAX_VALUE); } @Override public void close() { } public String toString() { return Buffer.this + ".inputStream()"; } }; } public Buffer copyTo(OutputStream out) throws IOException { return this.copyTo(out, 0L, this.size); } public Buffer copyTo(OutputStream out, long offset, long byteCount) throws IOException { if (out == null) { throw new IllegalArgumentException("out == null"); } Util.checkOffsetAndCount(this.size, offset, byteCount); if (byteCount == 0L) { return this; } Segment s = this.head; while (offset >= (long)(s.limit - s.pos)) { offset -= (long)(s.limit - s.pos); s = s.next; } while (byteCount > 0L) { int pos = (int)((long)s.pos + offset); int toCopy = (int)Math.min((long)(s.limit - pos), byteCount); out.write(s.data, pos, toCopy); byteCount -= (long)toCopy; offset = 0L; s = s.next; } return this; } public Buffer copyTo(Buffer out, long offset, long byteCount) { if (out == null) { throw new IllegalArgumentException("out == null"); } Util.checkOffsetAndCount(this.size, offset, byteCount); if (byteCount == 0L) { return this; } out.size += byteCount; Segment s = this.head; while (offset >= (long)(s.limit - s.pos)) { offset -= (long)(s.limit - s.pos); s = s.next; } while (byteCount > 0L) { Segment copy = new Segment(s); copy.pos = (int)((long)copy.pos + offset); copy.limit = Math.min(copy.pos + (int)byteCount, copy.limit); if (out.head == null) { copy.next = copy.prev = copy; out.head = copy.prev; } else { out.head.prev.push(copy); } byteCount -= (long)(copy.limit - copy.pos); offset = 0L; s = s.next; } return this; } public Buffer writeTo(OutputStream out) throws IOException { return this.writeTo(out, this.size); } public Buffer writeTo(OutputStream out, long byteCount) throws IOException { if (out == null) { throw new IllegalArgumentException("out == null"); } Util.checkOffsetAndCount(this.size, 0L, byteCount); Segment s = this.head; while (byteCount > 0L) { int toCopy = (int)Math.min(byteCount, (long)(s.limit - s.pos)); out.write(s.data, s.pos, toCopy); s.pos += toCopy; this.size -= (long)toCopy; byteCount -= (long)toCopy; if (s.pos != s.limit) continue; Segment toRecycle = s; this.head = s = toRecycle.pop(); SegmentPool.recycle(toRecycle); } return this; } public Buffer readFrom(InputStream in) throws IOException { this.readFrom(in, Long.MAX_VALUE, true); return this; } public Buffer readFrom(InputStream in, long byteCount) throws IOException { if (byteCount < 0L) { throw new IllegalArgumentException("byteCount < 0: " + byteCount); } this.readFrom(in, byteCount, false); return this; } private void readFrom(InputStream in, long byteCount, boolean forever) throws IOException { if (in == null) { throw new IllegalArgumentException("in == null"); } while (byteCount > 0L || forever) { Segment tail = this.writableSegment(1); int maxToCopy = (int)Math.min(byteCount, (long)(2048 - tail.limit)); int bytesRead = in.read(tail.data, tail.limit, maxToCopy); if (bytesRead == -1) { if (forever) { return; } throw new EOFException(); } tail.limit += bytesRead; this.size += (long)bytesRead; byteCount -= (long)bytesRead; } } public long completeSegmentByteCount() { long result = this.size; if (result == 0L) { return 0L; } Segment tail = this.head.prev; if (tail.limit < 2048 && tail.owner) { result -= (long)(tail.limit - tail.pos); } return result; } @Override public byte readByte() { if (this.size == 0L) { throw new IllegalStateException("size == 0"); } Segment segment = this.head; int pos = segment.pos; int limit = segment.limit; byte[] data = segment.data; byte b = data[pos++]; --this.size; if (pos == limit) { this.head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return b; } public byte getByte(long pos) { Util.checkOffsetAndCount(this.size, pos, 1L); Segment s = this.head; int segmentByteCount; while (pos >= (long)(segmentByteCount = s.limit - s.pos)) { pos -= (long)segmentByteCount; s = s.next; } return s.data[s.pos + (int)pos]; } @Override public short readShort() { if (this.size < 2L) { throw new IllegalStateException("size < 2: " + this.size); } Segment segment = this.head; int limit = segment.limit; int pos = segment.pos; if (limit - pos < 2) { int s = (this.readByte() & 0xFF) << 8 | this.readByte() & 0xFF; return (short)s; } byte[] data = segment.data; int s = (data[pos++] & 0xFF) << 8 | data[pos++] & 0xFF; this.size -= 2L; if (pos == limit) { this.head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return (short)s; } @Override public int readInt() { if (this.size < 4L) { throw new IllegalStateException("size < 4: " + this.size); } Segment segment = this.head; int limit = segment.limit; int pos = segment.pos; if (limit - pos < 4) { return (this.readByte() & 0xFF) << 24 | (this.readByte() & 0xFF) << 16 | (this.readByte() & 0xFF) << 8 | this.readByte() & 0xFF; } byte[] data = segment.data; int i = (data[pos++] & 0xFF) << 24 | (data[pos++] & 0xFF) << 16 | (data[pos++] & 0xFF) << 8 | data[pos++] & 0xFF; this.size -= 4L; if (pos == limit) { this.head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return i; } @Override public long readLong() { if (this.size < 8L) { throw new IllegalStateException("size < 8: " + this.size); } Segment segment = this.head; int limit = segment.limit; int pos = segment.pos; if (limit - pos < 8) { return ((long)this.readInt() & 0xFFFFFFFFL) << 32 | (long)this.readInt() & 0xFFFFFFFFL; } byte[] data = segment.data; long v = ((long)data[pos++] & 0xFFL) << 56 | ((long)data[pos++] & 0xFFL) << 48 | ((long)data[pos++] & 0xFFL) << 40 | ((long)data[pos++] & 0xFFL) << 32 | ((long)data[pos++] & 0xFFL) << 24 | ((long)data[pos++] & 0xFFL) << 16 | ((long)data[pos++] & 0xFFL) << 8 | (long)data[pos++] & 0xFFL; this.size -= 8L; if (pos == limit) { this.head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return v; } @Override public short readShortLe() { return Util.reverseBytesShort(this.readShort()); } @Override public int readIntLe() { return Util.reverseBytesInt(this.readInt()); } @Override public long readLongLe() { return Util.reverseBytesLong(this.readLong()); } @Override public long readDecimalLong() { if (this.size == 0L) { throw new IllegalStateException("size == 0"); } long value = 0L; int seen = 0; boolean negative = false; boolean done = false; long overflowZone = -922337203685477580L; long overflowDigit = -7L; do { Segment segment = this.head; byte[] data = segment.data; int pos = segment.pos; int limit = segment.limit; while (pos < limit) { byte b = data[pos]; if (b >= 48 && b <= 57) { int digit = 48 - b; if (value < overflowZone || value == overflowZone && (long)digit < overflowDigit) { Buffer buffer = new Buffer().writeDecimalLong(value).writeByte(b); if (!negative) { buffer.readByte(); } throw new NumberFormatException("Number too large: " + buffer.readUtf8()); } value *= 10L; value += (long)digit; } else if (b == 45 && seen == 0) { negative = true; --overflowDigit; } else { if (seen == 0) { throw new NumberFormatException("Expected leading [0-9] or '-' character but was 0x" + Integer.toHexString(b)); } done = true; break; } ++pos; ++seen; } if (pos == limit) { this.head = segment.pop(); SegmentPool.recycle(segment); continue; } segment.pos = pos; } while (!done && this.head != null); this.size -= (long)seen; return negative ? value : -value; } @Override public long readHexadecimalUnsignedLong() { if (this.size == 0L) { throw new IllegalStateException("size == 0"); } long value = 0L; int seen = 0; boolean done = false; do { Segment segment = this.head; byte[] data = segment.data; int pos = segment.pos; int limit = segment.limit; while (pos < limit) { int digit; byte b = data[pos]; if (b >= 48 && b <= 57) { digit = b - 48; } else if (b >= 97 && b <= 102) { digit = b - 97 + 10; } else if (b >= 65 && b <= 70) { digit = b - 65 + 10; } else { if (seen == 0) { throw new NumberFormatException("Expected leading [0-9a-fA-F] character but was 0x" + Integer.toHexString(b)); } done = true; break; } if ((value & 0xF000000000000000L) != 0L) { Buffer buffer = new Buffer().writeHexadecimalUnsignedLong(value).writeByte(b); throw new NumberFormatException("Number too large: " + buffer.readUtf8()); } value <<= 4; value |= (long)digit; ++pos; ++seen; } if (pos == limit) { this.head = segment.pop(); SegmentPool.recycle(segment); continue; } segment.pos = pos; } while (!done && this.head != null); this.size -= (long)seen; return value; } @Override public ByteString readByteString() { return new ByteString(this.readByteArray()); } @Override public ByteString readByteString(long byteCount) throws EOFException { return new ByteString(this.readByteArray(byteCount)); } @Override public void readFully(Buffer sink, long byteCount) throws EOFException { if (this.size < byteCount) { sink.write(this, this.size); throw new EOFException(); } sink.write(this, byteCount); } @Override public long readAll(Sink sink) throws IOException { long byteCount = this.size; if (byteCount > 0L) { sink.write(this, byteCount); } return byteCount; } @Override public String readUtf8() { try { return this.readString(this.size, Util.UTF_8); } catch (EOFException e) { throw new AssertionError((Object)e); } } @Override public String readUtf8(long byteCount) throws EOFException { return this.readString(byteCount, Util.UTF_8); } @Override public String readString(Charset charset) { try { return this.readString(this.size, charset); } catch (EOFException e) { throw new AssertionError((Object)e); } } @Override public String readString(long byteCount, Charset charset) throws EOFException { Util.checkOffsetAndCount(this.size, 0L, byteCount); if (charset == null) { throw new IllegalArgumentException("charset == null"); } if (byteCount > Integer.MAX_VALUE) { throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount); } if (byteCount == 0L) { return ""; } Segment s = this.head; if ((long)s.pos + byteCount > (long)s.limit) { return new String(this.readByteArray(byteCount), charset); } String result = new String(s.data, s.pos, (int)byteCount, charset); s.pos = (int)((long)s.pos + byteCount); this.size -= byteCount; if (s.pos == s.limit) { this.head = s.pop(); SegmentPool.recycle(s); } return result; } @Override public String readUtf8Line() throws EOFException { long newline = this.indexOf((byte)10); if (newline == -1L) { return this.size != 0L ? this.readUtf8(this.size) : null; } return this.readUtf8Line(newline); } @Override public String readUtf8LineStrict() throws EOFException { long newline = this.indexOf((byte)10); if (newline == -1L) { Buffer data = new Buffer(); this.copyTo(data, 0L, Math.min(32L, this.size)); throw new EOFException("\\n not found: size=" + this.size() + " content=" + data.readByteString().hex() + "..."); } return this.readUtf8Line(newline); } String readUtf8Line(long newline) throws EOFException { if (newline > 0L && this.getByte(newline - 1L) == 13) { String result = this.readUtf8(newline - 1L); this.skip(2L); return result; } String result = this.readUtf8(newline); this.skip(1L); return result; } @Override public int readUtf8CodePoint() throws EOFException { int min; int byteCount; int codePoint; if (this.size == 0L) { throw new EOFException(); } byte b0 = this.getByte(0L); if ((b0 & 0x80) == 0) { codePoint = b0 & 0x7F; byteCount = 1; min = 0; } else if ((b0 & 0xE0) == 192) { codePoint = b0 & 0x1F; byteCount = 2; min = 128; } else if ((b0 & 0xF0) == 224) { codePoint = b0 & 0xF; byteCount = 3; min = 2048; } else if ((b0 & 0xF8) == 240) { codePoint = b0 & 7; byteCount = 4; min = 65536; } else { this.skip(1L); return 65533; } if (this.size < (long)byteCount) { throw new EOFException("size < " + byteCount + ": " + this.size + " (to read code point prefixed 0x" + Integer.toHexString(b0) + ")"); } for (int i = 1; i < byteCount; ++i) { byte b = this.getByte(i); if ((b & 0xC0) == 128) { codePoint <<= 6; codePoint |= b & 0x3F; continue; } this.skip(i); return 65533; } this.skip(byteCount); if (codePoint > 0x10FFFF) { return 65533; } if (codePoint >= 55296 && codePoint <= 57343) { return 65533; } if (codePoint < min) { return 65533; } return codePoint; } @Override public byte[] readByteArray() { try { return this.readByteArray(this.size); } catch (EOFException e) { throw new AssertionError((Object)e); } } @Override public byte[] readByteArray(long byteCount) throws EOFException { Util.checkOffsetAndCount(this.size, 0L, byteCount); if (byteCount > Integer.MAX_VALUE) { throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount); } byte[] result = new byte[(int)byteCount]; this.readFully(result); return result; } @Override public int read(byte[] sink) { return this.read(sink, 0, sink.length); } @Override public void readFully(byte[] sink) throws EOFException { int read; for (int offset = 0; offset < sink.length; offset += read) { read = this.read(sink, offset, sink.length - offset); if (read != -1) continue; throw new EOFException(); } } @Override public int read(byte[] sink, int offset, int byteCount) { Util.checkOffsetAndCount(sink.length, offset, byteCount); Segment s = this.head; if (s == null) { return -1; } int toCopy = Math.min(byteCount, s.limit - s.pos); System.arraycopy(s.data, s.pos, sink, offset, toCopy); s.pos += toCopy; this.size -= (long)toCopy; if (s.pos == s.limit) { this.head = s.pop(); SegmentPool.recycle(s); } return toCopy; } public void clear() { try { this.skip(this.size); } catch (EOFException e) { throw new AssertionError((Object)e); } } @Override public void skip(long byteCount) throws EOFException { while (byteCount > 0L) { if (this.head == null) { throw new EOFException(); } int toSkip = (int)Math.min(byteCount, (long)(this.head.limit - this.head.pos)); this.size -= (long)toSkip; byteCount -= (long)toSkip; this.head.pos += toSkip; if (this.head.pos != this.head.limit) continue; Segment toRecycle = this.head; this.head = toRecycle.pop(); SegmentPool.recycle(toRecycle); } } @Override public Buffer write(ByteString byteString) { if (byteString == null) { throw new IllegalArgumentException("byteString == null"); } byteString.write(this); return this; } @Override public Buffer writeUtf8(String string) { return this.writeUtf8(string, 0, string.length()); } @Override public Buffer writeUtf8(String string, int beginIndex, int endIndex) { if (string == null) { throw new IllegalArgumentException("string == null"); } if (beginIndex < 0) { throw new IllegalAccessError("beginIndex < 0: " + beginIndex); } if (endIndex < beginIndex) { throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex); } if (endIndex > string.length()) { throw new IllegalArgumentException("endIndex > string.length: " + endIndex + " > " + string.length()); } int i = beginIndex; while (i < endIndex) { char low; char c = string.charAt(i); if (c < '\u0080') { Segment tail = this.writableSegment(1); byte[] data = tail.data; int segmentOffset = tail.limit - i; int runLimit = Math.min(endIndex, 2048 - segmentOffset); data[segmentOffset + i++] = (byte)c; while (i < runLimit && (c = string.charAt(i)) < '\u0080') { data[segmentOffset + i++] = (byte)c; } int runSize = i + segmentOffset - tail.limit; tail.limit += runSize; this.size += (long)runSize; continue; } if (c < '\u0800') { this.writeByte(c >> 6 | 0xC0); this.writeByte(c & 0x3F | 0x80); ++i; continue; } if (c < '\ud800' || c > '\udfff') { this.writeByte(c >> 12 | 0xE0); this.writeByte(c >> 6 & 0x3F | 0x80); this.writeByte(c & 0x3F | 0x80); ++i; continue; } char c2 = low = i + 1 < endIndex ? string.charAt(i + 1) : (char)'\u0000'; if (c > '\udbff' || low < '\udc00' || low > '\udfff') { this.writeByte(63); ++i; continue; } int codePoint = 65536 + ((c & 0xFFFF27FF) << 10 | low & 0xFFFF23FF); this.writeByte(codePoint >> 18 | 0xF0); this.writeByte(codePoint >> 12 & 0x3F | 0x80); this.writeByte(codePoint >> 6 & 0x3F | 0x80); this.writeByte(codePoint & 0x3F | 0x80); i += 2; } return this; } @Override public Buffer writeUtf8CodePoint(int codePoint) { if (codePoint < 128) { this.writeByte(codePoint); } else if (codePoint < 2048) { this.writeByte(codePoint >> 6 | 0xC0); this.writeByte(codePoint & 0x3F | 0x80); } else if (codePoint < 65536) { if (codePoint >= 55296 && codePoint <= 57343) { throw new IllegalArgumentException("Unexpected code point: " + Integer.toHexString(codePoint)); } this.writeByte(codePoint >> 12 | 0xE0); this.writeByte(codePoint >> 6 & 0x3F | 0x80); this.writeByte(codePoint & 0x3F | 0x80); } else if (codePoint <= 0x10FFFF) { this.writeByte(codePoint >> 18 | 0xF0); this.writeByte(codePoint >> 12 & 0x3F | 0x80); this.writeByte(codePoint >> 6 & 0x3F | 0x80); this.writeByte(codePoint & 0x3F | 0x80); } else { throw new IllegalArgumentException("Unexpected code point: " + Integer.toHexString(codePoint)); } return this; } @Override public Buffer writeString(String string, Charset charset) { return this.writeString(string, 0, string.length(), charset); } @Override public Buffer writeString(String string, int beginIndex, int endIndex, Charset charset) { if (string == null) { throw new IllegalArgumentException("string == null"); } if (beginIndex < 0) { throw new IllegalAccessError("beginIndex < 0: " + beginIndex); } if (endIndex < beginIndex) { throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex); } if (endIndex > string.length()) { throw new IllegalArgumentException("endIndex > string.length: " + endIndex + " > " + string.length()); } if (charset == null) { throw new IllegalArgumentException("charset == null"); } if (charset.equals(Util.UTF_8)) { return this.writeUtf8(string); } byte[] data = string.substring(beginIndex, endIndex).getBytes(charset); return this.write(data, 0, data.length); } @Override public Buffer write(byte[] source) { if (source == null) { throw new IllegalArgumentException("source == null"); } return this.write(source, 0, source.length); } @Override public Buffer write(byte[] source, int offset, int byteCount) { if (source == null) { throw new IllegalArgumentException("source == null"); } Util.checkOffsetAndCount(source.length, offset, byteCount); int limit = offset + byteCount; while (offset < limit) { Segment tail = this.writableSegment(1); int toCopy = Math.min(limit - offset, 2048 - tail.limit); System.arraycopy(source, offset, tail.data, tail.limit, toCopy); offset += toCopy; tail.limit += toCopy; } this.size += (long)byteCount; return this; } @Override public long writeAll(Source source) throws IOException { long readCount; if (source == null) { throw new IllegalArgumentException("source == null"); } long totalBytesRead = 0L; while ((readCount = source.read(this, 2048L)) != -1L) { totalBytesRead += readCount; } return totalBytesRead; } @Override public BufferedSink write(Source source, long byteCount) throws IOException { while (byteCount > 0L) { long read = source.read(this, byteCount); if (read == -1L) { throw new EOFException(); } byteCount -= read; } return this; } @Override public Buffer writeByte(int b) { Segment tail = this.writableSegment(1); tail.data[tail.limit++] = (byte)b; ++this.size; return this; } @Override public Buffer writeShort(int s) { Segment tail = this.writableSegment(2); byte[] data = tail.data; int limit = tail.limit; data[limit++] = (byte)(s >>> 8 & 0xFF); data[limit++] = (byte)(s & 0xFF); tail.limit = limit; this.size += 2L; return this; } @Override public Buffer writeShortLe(int s) { return this.writeShort(Util.reverseBytesShort((short)s)); } @Override public Buffer writeInt(int i) { Segment tail = this.writableSegment(4); byte[] data = tail.data; int limit = tail.limit; data[limit++] = (byte)(i >>> 24 & 0xFF); data[limit++] = (byte)(i >>> 16 & 0xFF); data[limit++] = (byte)(i >>> 8 & 0xFF); data[limit++] = (byte)(i & 0xFF); tail.limit = limit; this.size += 4L; return this; } @Override public Buffer writeIntLe(int i) { return this.writeInt(Util.reverseBytesInt(i)); } @Override public Buffer writeLong(long v) { Segment tail = this.writableSegment(8); byte[] data = tail.data; int limit = tail.limit; data[limit++] = (byte)(v >>> 56 & 0xFFL); data[limit++] = (byte)(v >>> 48 & 0xFFL); data[limit++] = (byte)(v >>> 40 & 0xFFL); data[limit++] = (byte)(v >>> 32 & 0xFFL); data[limit++] = (byte)(v >>> 24 & 0xFFL); data[limit++] = (byte)(v >>> 16 & 0xFFL); data[limit++] = (byte)(v >>> 8 & 0xFFL); data[limit++] = (byte)(v & 0xFFL); tail.limit = limit; this.size += 8L; return this; } @Override public Buffer writeLongLe(long v) { return this.writeLong(Util.reverseBytesLong(v)); } @Override public Buffer writeDecimalLong(long v) { int width; if (v == 0L) { return this.writeByte(48); } boolean negative = false; if (v < 0L) { if ((v = -v) < 0L) { return this.writeUtf8("-9223372036854775808"); } negative = true; } int n = v < 100000000L ? (v < 10000L ? (v < 100L ? (v < 10L ? 1 : 2) : (v < 1000L ? 3 : 4)) : (v < 1000000L ? (v < 100000L ? 5 : 6) : (v < 10000000L ? 7 : 8))) : (v < 1000000000000L ? (v < 10000000000L ? (v < 1000000000L ? 9 : 10) : (v < 100000000000L ? 11 : 12)) : (v < 1000000000000000L ? (v < 10000000000000L ? 13 : (v < 100000000000000L ? 14 : 15)) : (v < 100000000000000000L ? (v < 10000000000000000L ? 16 : 17) : (width = v < 1000000000000000000L ? 18 : 19)))); if (negative) { ++width; } Segment tail = this.writableSegment(width); byte[] data = tail.data; int pos = tail.limit + width; while (v != 0L) { int digit = (int)(v % 10L); data[--pos] = DIGITS[digit]; v /= 10L; } if (negative) { data[--pos] = 45; } tail.limit += width; this.size += (long)width; return this; } @Override public Buffer writeHexadecimalUnsignedLong(long v) { if (v == 0L) { return this.writeByte(48); } int width = Long.numberOfTrailingZeros(Long.highestOneBit(v)) / 4 + 1; Segment tail = this.writableSegment(width); byte[] data = tail.data; int start = tail.limit; for (int pos = tail.limit + width - 1; pos >= start; --pos) { data[pos] = DIGITS[(int)(v & 0xFL)]; v >>>= 4; } tail.limit += width; this.size += (long)width; return this; } Segment writableSegment(int minimumCapacity) { if (minimumCapacity < 1 || minimumCapacity > 2048) { throw new IllegalArgumentException(); } if (this.head == null) { this.head.next = this.head.prev = (this.head = SegmentPool.take()); return this.head.prev; } Segment tail = this.head.prev; if (tail.limit + minimumCapacity > 2048 || !tail.owner) { tail = tail.push(SegmentPool.take()); } return tail; } @Override public void write(Buffer source, long byteCount) { if (source == null) { throw new IllegalArgumentException("source == null"); } if (source == this) { throw new IllegalArgumentException("source == this"); } Util.checkOffsetAndCount(source.size, 0L, byteCount); while (byteCount > 0L) { if (byteCount < (long)(source.head.limit - source.head.pos)) { Segment tail; Segment segment = tail = this.head != null ? this.head.prev : null; if (tail != null && tail.owner && byteCount + (long)tail.limit - (long)(tail.shared ? 0 : tail.pos) <= 2048L) { source.head.writeTo(tail, (int)byteCount); source.size -= byteCount; this.size += byteCount; return; } source.head = source.head.split((int)byteCount); } Segment segmentToMove = source.head; long movedByteCount = segmentToMove.limit - segmentToMove.pos; source.head = segmentToMove.pop(); if (this.head == null) { this.head.next = this.head.prev = (this.head = segmentToMove); } else { Segment tail = this.head.prev; tail = tail.push(segmentToMove); tail.compact(); } source.size -= movedByteCount; this.size += movedByteCount; byteCount -= movedByteCount; } } @Override public long read(Buffer sink, long byteCount) { if (sink == null) { throw new IllegalArgumentException("sink == null"); } if (byteCount < 0L) { throw new IllegalArgumentException("byteCount < 0: " + byteCount); } if (this.size == 0L) { return -1L; } if (byteCount > this.size) { byteCount = this.size; } sink.write(this, byteCount); return byteCount; } @Override public long indexOf(byte b) { return this.indexOf(b, 0L); } @Override public long indexOf(byte b, long fromIndex) { if (fromIndex < 0L) { throw new IllegalArgumentException("fromIndex < 0"); } Segment s = this.head; if (s == null) { return -1L; } long offset = 0L; do { int segmentByteCount; if (fromIndex >= (long)(segmentByteCount = s.limit - s.pos)) { fromIndex -= (long)segmentByteCount; } else { byte[] data = s.data; int limit = s.limit; for (int pos = (int)((long)s.pos + fromIndex); pos < limit; ++pos) { if (data[pos] != b) continue; return offset + (long)pos - (long)s.pos; } fromIndex = 0L; } offset += (long)segmentByteCount; } while ((s = s.next) != this.head); return -1L; } @Override public long indexOf(ByteString bytes) throws IOException { return this.indexOf(bytes, 0L); } @Override public long indexOf(ByteString bytes, long fromIndex) throws IOException { if (bytes.size() == 0) { throw new IllegalArgumentException("bytes is empty"); } while ((fromIndex = this.indexOf(bytes.getByte(0), fromIndex)) != -1L) { if (this.rangeEquals(fromIndex, bytes)) { return fromIndex; } ++fromIndex; } return -1L; } @Override public long indexOfElement(ByteString targetBytes) { return this.indexOfElement(targetBytes, 0L); } @Override public long indexOfElement(ByteString targetBytes, long fromIndex) { if (fromIndex < 0L) { throw new IllegalArgumentException("fromIndex < 0"); } Segment s = this.head; if (s == null) { return -1L; } long offset = 0L; byte[] toFind = targetBytes.toByteArray(); do { int segmentByteCount; if (fromIndex >= (long)(segmentByteCount = s.limit - s.pos)) { fromIndex -= (long)segmentByteCount; } else { byte[] data = s.data; long limit = s.limit; for (long pos = (long)s.pos + fromIndex; pos < limit; ++pos) { byte b = data[(int)pos]; for (byte targetByte : toFind) { if (b != targetByte) continue; return offset + pos - (long)s.pos; } } fromIndex = 0L; } offset += (long)segmentByteCount; } while ((s = s.next) != this.head); return -1L; } boolean rangeEquals(long offset, ByteString bytes) { int byteCount = bytes.size(); if (this.size - offset < (long)byteCount) { return false; } for (int i = 0; i < byteCount; ++i) { if (this.getByte(offset + (long)i) == bytes.getByte(i)) continue; return false; } return true; } @Override public void flush() { } @Override public void close() { } @Override public Timeout timeout() { return Timeout.NONE; } List segmentSizes() { if (this.head == null) { return Collections.emptyList(); } ArrayList result = new ArrayList(); result.add(this.head.limit - this.head.pos); Segment s = this.head.next; while (s != this.head) { result.add(s.limit - s.pos); s = s.next; } return result; } public boolean equals(Object o) { long count; if (this == o) { return true; } if (!(o instanceof Buffer)) { return false; } Buffer that = (Buffer)o; if (this.size != that.size) { return false; } if (this.size == 0L) { return true; } Segment sa = this.head; Segment sb = that.head; int posA = sa.pos; int posB = sb.pos; for (long pos = 0L; pos < this.size; pos += count) { count = Math.min(sa.limit - posA, sb.limit - posB); int i = 0; while ((long)i < count) { if (sa.data[posA++] != sb.data[posB++]) { return false; } ++i; } if (posA == sa.limit) { sa = sa.next; posA = sa.pos; } if (posB != sb.limit) continue; sb = sb.next; posB = sb.pos; } return true; } public int hashCode() { Segment s = this.head; if (s == null) { return 0; } int result = 1; do { int limit = s.limit; for (int pos = s.pos; pos < limit; ++pos) { result = 31 * result + s.data[pos]; } } while ((s = s.next) != this.head); return result; } public String toString() { if (this.size == 0L) { return "Buffer[size=0]"; } if (this.size <= 16L) { ByteString data = this.clone().readByteString(); return String.format("Buffer[size=%s data=%s]", this.size, data.hex()); } try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(this.head.data, this.head.pos, this.head.limit - this.head.pos); Segment s = this.head.next; while (s != this.head) { md5.update(s.data, s.pos, s.limit - s.pos); s = s.next; } return String.format("Buffer[size=%s md5=%s]", this.size, ByteString.of(md5.digest()).hex()); } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } } public Buffer clone() { Buffer result = new Buffer(); if (this.size == 0L) { return result; } result.head.next = result.head.prev = (result.head = new Segment(this.head)); Segment s = this.head.next; while (s != this.head) { result.head.prev.push(new Segment(s)); s = s.next; } result.size = this.size; return result; } public ByteString snapshot() { if (this.size > Integer.MAX_VALUE) { throw new IllegalArgumentException("size > Integer.MAX_VALUE: " + this.size); } return this.snapshot((int)this.size); } public ByteString snapshot(int byteCount) { if (byteCount == 0) { return ByteString.EMPTY; } return new SegmentedByteString(this, byteCount); } }