override fun read(sink: ByteArray, offset: Int, byteCount: Int): Int {
checkOffsetAndCount(sink.size.toLong(), offset.toLong(), byteCount.toLong())
//如果buffer的数据为空,则先调用被装饰的对象(也就是InputStreamSource)将数据读到buffer当中
if (buffer.size == 0L) {
val read = source.read(buffer, Segment.SIZE.toLong())
if (read == -1L) return -1
}
val toRead = minOf(byteCount, buffer.size).toInt()
return buffer.read(sink, offset, toRead)
}
override fun write(source: ByteArray): BufferedSink {
check(!closed) { “closed” }
buffer.write(source)
return emitCompleteSegments()
}
此外,Sink和Source它门还各自有一个支持gzip压缩的实现类GzipSink
和GzipSource
;一个具有委托功能的抽象类ForwardingSink
和ForwardingSource
;还有一个实现类便是InflaterSource和DeflaterSink,这两个类主要用于压缩,为GzipSink和GzipSource服务,这里就不详细看了。Sink和Source还有其他的类,如HashingSink, HashingSource, 也是装饰者,这里就不一一列举了,其实Okio的Source和Sink装饰者家族类似于java的InputStream和OutStream家族。
Segment
在前面的RealBufferedSource
和RealBufferedSink
的读写方法中都是调用buffer对象进行操作的,但其实Buffer类的内部读写对象则最终是Segment
对象,因此要想了解Buffer
的机制首先要了解一下Segment
,Segment
是Buffer
的核心依赖对象,也是Okio中最底层数据的持有者。
在Buffer中的每一个Segment都是双向循环链表中的一个节点,该节点分别拥有指向前驱节点的Segment对象引用以及指向后驱节点的Segment对象引用。而在Segment池中的Segment则是一个单向链表的节点,Segment池持有对下一个Segment节点对象的引用。如果Segment中的字节数据是在buffer和byte string间共享的,那么该Segment对象是不可以被回收的,也是不能修改其中的数据的,除非是它的持有者。
其实Segment
的源码注释都写的很清楚了,直接贴出来看一下好了:
/**
-
A segment of a buffer.
-
Each segment in a buffer is a circularly-linked list node referencing the following and
-
preceding segments in the buffer.
-
Each segment in the pool is a singly-linked list node referencing the rest of segments in the
-
pool.
-
The underlying byte arrays of segments may be shared between buffers and byte strings. When a
-
segment’s byte array is shared the segment may not be recycled, nor may its byte data be changed.
-
The lone exception is that the owner segment is allowed to append to the segment, writing data at
-
{@code limit} and beyond. There is a single owning segment for each byte array. Positions,
-
limits, prev, and next references are not shared.
*/
final class Segment {
/** The size of all segments in bytes. */
static final int SIZE = 8192;
/** Segments will be shared when doing so avoids {@code arraycopy()} of this many bytes. */
static final int SHARE_MINIMUM = 1024;
final byte[] data;
/** The next byte of application data byte to read in this segment. */
int pos;
/** The first byte of available data ready to be written to. */
int limit;
/** True if other segments or byte strings use the same byte array. */
boolean shared;
/** True if this segment owns the byte array and can append to it, extending {@code limit}. */
boolean owner;
/** Next segment in a linked or circularly-linked list. */
Segment next;
/** Previous segment in a circularly-linked list. */
Segment prev;
Segment() {
this.data = new byte[SIZE];
this.owner = true;
this.shared = false;
}
Segment(Segment shareFrom) {
this(shareFrom.data, shareFrom.pos, shareFrom.limit);
shareFrom.shared = true;
}
Segment(byte[] data, int pos, int limit) {
this.data = data;
this.pos = pos;
this.limit = limit;
this.owner = false;
this.shared = true;
}
/**
-
Removes this segment of a circularly-linked list and returns its successor.
-
Returns null if the list is now empty.
*/
public @Nullable Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
}
/**
-
Appends {@code segment} after this segment in the circularly-linked list.
-
Returns the pushed segment.
*/
public Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
/**
-
Splits this head of a circularly-linked list into two segments. The first
-
segment contains the data in {@code [pos…pos+byteCount)}. The second
-
segment contains the data in {@code [pos+byteCount…limit)}. This can be
-
useful when moving partial segments from one buffer to another.
-
Returns the new head of the circularly-linked list.
*/
public Segment split(int byteCount) {
if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
Segment prefix;
// We have two competing performance goals:
// - Avoid copying data. We accomplish this by sharing segments.
// - Avoid short shared segments. These are bad for performance because they are readonly and
// may lead to long chains of short segments.
// To balance these goals we only share segments when the copy will be large.
if (byteCount >= SHARE_MINIMUM) {
prefix = new Segment(this);
} else {
prefix = SegmentPool.take();
System.arraycopy(data, pos, prefix.data, 0, byteCount);
}
prefix.limit = prefix.pos + byteCount;
pos += byteCount;
prev.push(prefix);
return prefix;
}
/**
-
Call this when the tail and its predecessor may both be less than half
-
full. This will copy data so that segments can be recycled.
*/
public void compact() {
if (prev == this) throw new IllegalStateException();
if (!prev.owner) return; // Cannot compact: prev isn’t writable.
int byteCount = limit - pos;
int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space.
writeTo(prev, byteCount);
pop();
Se