Okio源码阅读笔记(四)Buffer

Buffer里缓存了一个双向闭合Segment链表,head引用指向链表的头部,head.prev就是链表尾部

链表的每个元素Segment内部都缓存有一个字节数组,segment的有效起点是pos,有效数据终点是limit

Buffer中封装了对该缓存链表的读、写等操作的方法,其中把其他类型的数据转成字节写入缓存中的代码运用了大量的位运算,我表示看不怎么懂,暂时先不钻研了。

也算是个笔记。

public final class Buffer implements BufferedSource, BufferedSink, Cloneable, ByteChannel {
    private static final byte[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    static final int REPLACEMENT_CHARACTER = '\ufffd';
    @Nullable
    Segment head;//segment链表头节点
    long size;//当前buffer中的字节数
    public Buffer() {
    }
    //返回当前buffer中的字节数
    public long size() {
        return size;
    }
    @Override
    public Buffer buffer() {//返回当前buffer对象
        return this;
    }
    @Override
    public OutputStream outputStream() {
		//通过OupputStream的api来把数据写入当前Buffer的双向闭合链表中
        return new OutputStream() {
            @Override
            public void write(int b) {
                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() {
            }

            @Override
            public String toString() {
                return Buffer.this + ".outputStream()";
            }
        };
    }

    @Override
    public Buffer emitCompleteSegments() {
        return this; // Nowhere to emit to!
    }
    @Override
    public BufferedSink emit() {
        return this; // Nowhere to emit to!
    }
    @Override
    public boolean exhausted() {
        return size == 0;
    }
    @Override
    public void require(long byteCount) throws EOFException {
        if (size < byteCount) {
            throw new EOFException();
        }
    }
    @Override
    public boolean request(long byteCount) {
        return size >= byteCount;
    }
    @Override
    public InputStream inputStream() {
		//利用InputStream的api读取Buffer中的数据
        return new InputStream() {
            @Override
            public int read() {
                if (size > 0) {
                    return readByte() & 0xff;//& 0xff:避免byte强转int时补码影响
                }
                return -1;
            }

            @Override
            public int read(byte[] sink, int offset, int byteCount) {
                return Buffer.this.read(sink, offset, byteCount);//把head节点offset到offset+byteCount的数据读到sink中
            }

            @Override
            public int available() {
                return (int) Math.min(size, Integer.MAX_VALUE);
            }

            @Override
            public void close() {
            }

            @Override
            public String toString() {
                return Buffer.this + ".inputStream()";
            }
        };
    }

    //把Buffer中缓存的数据全部拷贝到out输出流中
    public Buffer copyTo(OutputStream out) throws IOException {
        return copyTo(out, 0, size);
    }

    //把Buffer中Segment链表中从head.pos位置开始算起,第offset位置为拷贝起点,拷贝byteCount个字节的数据,写入到out输出流
    public Buffer copyTo(OutputStream out, long offset, long byteCount) throws IOException {
        if (out == null) {
            throw new IllegalArgumentException("out == null");
        }
        checkOffsetAndCount(size, offset, byteCount);
        if (byteCount == 0) {
            return this;
        }
		//从head.pos节点开始遍历Segment双向链表,到offset位置开始拷贝
        Segment s = head;
        for (; offset >= (s.limit - s.pos); s = s.next) {
			//offset>=s.有效长度; offset-=s.有效长度;s->s.next
            offset -= (s.limit - s.pos);
        }
        //从s.pos + offset开始拷贝,把Buffer.head.data[]写入out输出流中
        for (; byteCount > 0; s = s.next) {
            int pos = (int) (s.pos + offset);
            int toCopy = (int) Math.min(s.limit - pos, byteCount);
            out.write(s.data, pos, toCopy);
            byteCount -= toCopy;
            offset = 0;
        }
        return this;
    }

    //把当前Buffer中Segment链表中从head.pos位置开始算起,第offset位置为拷贝起点,拷贝byteCount个字节的数据,写入out的链表的尾巴
    public Buffer copyTo(Buffer out, long offset, long byteCount) {
        if (out == null) {
            throw new IllegalArgumentException("out == null");
        }
        checkOffsetAndCount(size, offset, byteCount);
        if (byteCount == 0) {
            return this;
        }
        out.size += byteCount;
        //跳过小于拷贝起点offset的segment
        Segment s = head;
        for (; offset >= (s.limit - s.pos); s = s.next) {
            offset -= (s.limit - s.pos);
        }
        //一次拷贝一个segment,直到拷贝狗byteCount个字节为止
        for (; byteCount > 0; s = s.next) {
            Segment copy = s.sharedCopy();//共享拷贝,copy是新的segment,但是copy.data[]跟s.data[]是同一个字节数组
            copy.pos += offset;//把pos移到拷贝起点,pos是一个segment.data[]的有效数据起点
            copy.limit = Math.min(copy.pos + (int) byteCount, copy.limit);
            if (out.head == null) {//输出Buffer的缓存双向闭合链表还没有内容
                out.head = copy.next = copy.prev = copy;//把head指向copy,并让copy.prev/next都指向copy
            } else {
				//如果输出Buffer已经有head,那么缓存链表的尾巴就是head.prev,head.prev.push(..)就是在尾巴上加入新的元素
                out.head.prev.push(copy);
            }
            byteCount -= copy.limit - copy.pos;//拷贝一段后,byteCount减去已经拷贝的字节数
			/* 不管offset多大,上面跳过起点时offset会递减到小于head.limit - head.pos
			 * 所以拷贝完一个segment后offset就要设置为0了
			 */
            offset = 0;
			//开始遍历下一个segment
        }
        return this;
    }

    //跟copyTo的区别是,这里写到out之后,会把完全被拷贝到out的segment从Buffer的缓存中剔除,并把剔除的segment回收到对象池
    public Buffer writeTo(OutputStream out) throws IOException {
        return writeTo(out, size);
    }
    ...
    /** Returns the byte at {@code pos}. */
    public byte getByte(long pos) {
        checkOffsetAndCount(size, pos, 1);
        if (size - pos > pos) {
            for (Segment s = head; true; s = s.next) {
                int segmentByteCount = s.limit - s.pos;
                if (pos < segmentByteCount) {
                    return s.data[s.pos + (int) pos];
                }
                pos -= segmentByteCount;
            }
        } else {
            pos -= size;
            for (Segment s = head.prev; true; s = s.prev) {
                pos += s.limit - s.pos;
                if (pos >= 0) {
                    return s.data[s.pos + (int) pos];
                }
            }
        }
    }

    @Override
    public short readShort() {
        if (size < 2) {
            throw new IllegalStateException("size < 2: " + size);
        }
        Segment segment = head;
        int pos = segment.pos;
        int limit = segment.limit;
        // If the short is split across multiple segments, delegate to readByte().
        if (limit - pos < 2) {
            int s = (readByte() & 0xff) << 8
                    | (readByte() & 0xff);
            return (short) s;
        }
        byte[] data = segment.data;
        int s = (data[pos++] & 0xff) << 8
                | (data[pos++] & 0xff);
        size -= 2;
        if (pos == limit) {
            head = segment.pop();
            SegmentPool.recycle(segment);
        } else {
            segment.pos = pos;
        }
        return (short) s;
    }

    @Override
    public int readInt() {
        if (size < 4) {
            throw new IllegalStateException("size < 4: " + size);
        }
        Segment segment = head;
        int pos = segment.pos;
        int limit = segment.limit;
        // If the int is split across multiple segments, delegate to readByte().
        if (limit - pos < 4) {
            return (readByte() & 0xff) << 24
                    | (readByte() & 0xff) << 16
                    | (readByte() & 0xff) << 8
                    | (readByte() & 0xff);
        }
        byte[] data = segment.data;
        int i = (data[pos++] & 0xff) << 24
                | (data[pos++] & 0xff) << 16
                | (data[pos++] & 0xff) << 8
                | (data[pos++] & 0xff);
        size -= 4;
        if (pos == limit) {
            head = segment.pop();
            SegmentPool.recycle(segment);
        } else {
            segment.pos = pos;
        }
        return i;
    }

    @Override
    public long readLong() {
        if (size < 8) {
            throw new IllegalStateException("size < 8: " + size);
        }
        Segment segment = head;
        int pos = segment.pos;
        int limit = segment.limit;
        // If the long is split across multiple segments, delegate to readInt().
        if (limit - pos < 8) {
            return (readInt() & 0xffffffffL) << 32
                    | (readInt() & 0xffffffffL);
        }
        byte[] data = segment.data;
        long v = (data[pos++] & 0xffL) << 56
                | (data[pos++] & 0xffL) << 48
                | (data[pos++] & 0xffL) << 40
                | (data[pos++] & 0xffL) << 32
                | (data[pos++] & 0xffL) << 24
                | (data[pos++] & 0xffL) << 16
                | (data[pos++] & 0xffL) << 8
                | (data[pos++] & 0xffL);
        size -= 8;
        if (pos == limit) {
            head = segment.pop();
            SegmentPool.recycle(segment);
        } else {
            segment.pos = pos;
        }
        return v;
    }
	...
    @Override
    public int readUtf8CodePoint() throws EOFException {
        if (size == 0) {
            throw new EOFException();
        }
        byte b0 = getByte(0);
        int codePoint;
        int byteCount;
        int min;
        if ((b0 & 0x80) == 0) {
            // 0xxxxxxx.
            codePoint = b0 & 0x7f;
            byteCount = 1; // 7 bits (ASCII).
            min = 0x0;
        } else if ((b0 & 0xe0) == 0xc0) {
            // 0x110xxxxx
            codePoint = b0 & 0x1f;
            byteCount = 2; // 11 bits (5 + 6).
            min = 0x80;
        } else if ((b0 & 0xf0) == 0xe0) {
            // 0x1110xxxx
            codePoint = b0 & 0x0f;
            byteCount = 3; // 16 bits (4 + 6 + 6).
            min = 0x800;
        } else if ((b0 & 0xf8) == 0xf0) {
            // 0x11110xxx
            codePoint = b0 & 0x07;
            byteCount = 4; // 21 bits (3 + 6 + 6 + 6).
            min = 0x10000;
        } else {
            // We expected the first byte of a code point but got something else.
            skip(1);
            return REPLACEMENT_CHARACTER;
        }
        if (size < byteCount) {
            throw new EOFException("size < " + byteCount + ": " + size
                    + " (to read code point prefixed 0x" + Integer.toHexString(b0) + ")");
        }
        // Read the continuation bytes. If we encounter a non-continuation byte, the sequence consumed
        // thus far is truncated and is decoded as the replacement character. That non-continuation byte
        // is left in the stream for processing by the next call to readUtf8CodePoint().
        for (int i = 1; i < byteCount; i++) {
            byte b = getByte(i);
            if ((b & 0xc0) == 0x80) {
                // 0x10xxxxxx
                codePoint <<= 6;
                codePoint |= b & 0x3f;
            } else {
                skip(i);
                return REPLACEMENT_CHARACTER;
            }
        }
        skip(byteCount);
        if (codePoint > 0x10ffff) {
            return REPLACEMENT_CHARACTER; // Reject code points larger than the Unicode maximum.
        }
        if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
            return REPLACEMENT_CHARACTER; // Reject partial surrogates.
        }
        if (codePoint < min) {
            return REPLACEMENT_CHARACTER; // Reject overlong code points.
        }
        return codePoint;
    }
	...
	//把当前buffer缓存的Segment双向链表的head节点指向的Segment的offset~byteCount的内容拷贝到sink字节数组中
    @Override
    public int read(byte[] sink, int offset, int byteCount) {
        checkOffsetAndCount(sink.length, offset, byteCount);
        Segment s = 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;
        size -= toCopy;
        if (s.pos == s.limit) {//如果head节点的Segment的内容已经全部拷贝走了
            head = s.pop();//把该segment从缓存链表中移除,并把head指向链表的下一个节点
            SegmentPool.recycle(s);//把该segment回收到SegmentPool中
        }

        return toCopy;
    }
	...
	//通过位运算把string转成byte写入缓存中!
    @Override
    public Buffer writeUtf8(String string, int beginIndex, int endIndex) {
        if (string == null) {
            throw new IllegalArgumentException("string == null");
        }
        if (beginIndex < 0) {
            throw new IllegalArgumentException("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());
        }
        // Transcode a UTF-16 Java String to UTF-8 bytes.
        for (int i = beginIndex; i < endIndex; ) {
            int c = string.charAt(i);
            if (c < 0x80) {
                Segment tail = writableSegment(1);
                byte[] data = tail.data;
                int segmentOffset = tail.limit - i;
                int runLimit = Math.min(endIndex, Segment.SIZE - segmentOffset);

                // Emit a 7-bit character with 1 byte.
                data[segmentOffset + i++] = (byte) c; // 0xxxxxxx

                // Fast-path contiguous runs of ASCII characters. This is ugly, but yields a ~4x performance
                // improvement over independent calls to writeByte().
                while (i < runLimit) {
                    c = string.charAt(i);
                    if (c >= 0x80) {
                        break;
                    }
                    data[segmentOffset + i++] = (byte) c; // 0xxxxxxx
                }

                int runSize = i + segmentOffset - tail.limit; // Equivalent to i - (previous i).
                tail.limit += runSize;
                size += runSize;

            } else if (c < 0x800) {
                // Emit a 11-bit character with 2 bytes.
                writeByte(c >> 6 | 0xc0); // 110xxxxx
                writeByte(c & 0x3f | 0x80); // 10xxxxxx
                i++;

            } else if (c < 0xd800 || c > 0xdfff) {
                // Emit a 16-bit character with 3 bytes.
                writeByte(c >> 12 | 0xe0); // 1110xxxx
                writeByte(c >> 6 & 0x3f | 0x80); // 10xxxxxx
                writeByte(c & 0x3f | 0x80); // 10xxxxxx
                i++;

            } else {
                // c is a surrogate. Make sure it is a high surrogate & that its successor is a low
                // surrogate. If not, the UTF-16 is invalid, in which case we emit a replacement character.
                int low = i + 1 < endIndex ? string.charAt(i + 1) : 0;
                if (c > 0xdbff || low < 0xdc00 || low > 0xdfff) {
                    writeByte('?');
                    i++;
                    continue;
                }

                // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits)
                // UTF-16 low surrogate:  110111yyyyyyyyyy (10 bits)
                // Unicode code point:    00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits)
                int codePoint = 0x010000 + ((c & ~0xd800) << 10 | low & ~0xdc00);

                // Emit a 21-bit character with 4 bytes.
                writeByte(codePoint >> 18 | 0xf0); // 11110xxx
                writeByte(codePoint >> 12 & 0x3f | 0x80); // 10xxxxxx
                writeByte(codePoint >> 6 & 0x3f | 0x80); // 10xxyyyy
                writeByte(codePoint & 0x3f | 0x80); // 10yyyyyy
                i += 2;
            }
        }

        return this;
    }

    @Override
    public Buffer writeUtf8CodePoint(int codePoint) {
        if (codePoint < 0x80) {
            // Emit a 7-bit code point with 1 byte.
            writeByte(codePoint);
        } else if (codePoint < 0x800) {
            // Emit a 11-bit code point with 2 bytes.
            writeByte(codePoint >> 6 | 0xc0); // 110xxxxx
            writeByte(codePoint & 0x3f | 0x80); // 10xxxxxx
        } else if (codePoint < 0x10000) {
            if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
                // Emit a replacement character for a partial surrogate.
                writeByte('?');
            } else {
                // Emit a 16-bit code point with 3 bytes.
                writeByte(codePoint >> 12 | 0xe0); // 1110xxxx
                writeByte(codePoint >> 6 & 0x3f | 0x80); // 10xxxxxx
                writeByte(codePoint & 0x3f | 0x80); // 10xxxxxx
            }
        } else if (codePoint <= 0x10ffff) {
            // Emit a 21-bit code point with 4 bytes.
            writeByte(codePoint >> 18 | 0xf0); // 11110xxx
            writeByte(codePoint >> 12 & 0x3f | 0x80); // 10xxxxxx
            writeByte(codePoint >> 6 & 0x3f | 0x80); // 10xxxxxx
            writeByte(codePoint & 0x3f | 0x80); // 10xxxxxx
        } else {
            throw new IllegalArgumentException(
                    "Unexpected code point: " + Integer.toHexString(codePoint));
        }
        return this;
    }

    ...

    @Override
    public Buffer writeShort(int s) {
        Segment tail = writableSegment(2);
        byte[] data = tail.data;
        int limit = tail.limit;
        data[limit++] = (byte) ((s >>> 8) & 0xff);
        data[limit++] = (byte) (s & 0xff);
        tail.limit = limit;
        size += 2;
        return this;
    }

    @Override
    public Buffer writeShortLe(int s) {
        return writeShort(Util.reverseBytesShort((short) s));
    }

    @Override
    public Buffer writeInt(int i) {
        Segment tail = 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;
        size += 4;
        return this;
    }

    @Override
    public Buffer writeIntLe(int i) {
        return writeInt(Util.reverseBytesInt(i));
    }

    @Override
    public Buffer writeLong(long v) {
        Segment tail = writableSegment(8);
        byte[] data = tail.data;
        int limit = tail.limit;
        data[limit++] = (byte) ((v >>> 56L) & 0xff);
        data[limit++] = (byte) ((v >>> 48L) & 0xff);
        data[limit++] = (byte) ((v >>> 40L) & 0xff);
        data[limit++] = (byte) ((v >>> 32L) & 0xff);
        data[limit++] = (byte) ((v >>> 24L) & 0xff);
        data[limit++] = (byte) ((v >>> 16L) & 0xff);
        data[limit++] = (byte) ((v >>> 8L) & 0xff);
        data[limit++] = (byte) (v & 0xff);
        tail.limit = limit;
        size += 8;
        return this;
    }

    @Override
    public Buffer writeLongLe(long v) {
        return writeLong(reverseBytesLong(v));
    }
	...
    public static final class UnsafeCursor implements Closeable {
		...
    }
}

猜你喜欢

转载自blog.csdn.net/u010577768/article/details/80055852