/**
-
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();
SegmentPool.recycle(this);
}
/** Moves {@code byteCount} bytes from this segment to {@code sink}. */
public void writeTo(Segment sink, int byteCount) {
if (!sink.owner) throw new IllegalArgumentException();
if (sink.limit + byteCount > SIZE) {
// We can’t fit byteCount bytes at the sink’s current position. Shift sink first.
if (sink.shared) throw new IllegalArgumentException();
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
sink.limit -= sink.pos;
sink.pos = 0;
}
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
sink.limit += byteCount;
pos += byteCount;
}
}
首先,Segment中有几个成员变量:Segment.SIZE
这个值是8192
,也就是8kb
, 是一个Segment对象能处理的数据的大小,byte[] data
这个就是真正的存储数据的字节数组,pos
这个是读取数据的起始位置,limit
是写数据的起始位置,shared
表示当前Segment的字节数组data
是否可以共享的,owner
表示当前Segment是否是data对象的持有者(只有data对象的持有者才能对data进行修改), 只有share为false即表示owner为true是当前的持有者。这里有个概念就是share “共享”,Segment中的data数组是可以在Buffer和ByteString对象之间共享的,怎么来确认这个共享呢,我们看到Segment对象有三个构造函数,其中有参的构造函数:
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;
}
也就是通过外部传递Segment对象和data数组的方式构造出来的Segment就是共享的,而默认的构造函数:
Segment() {
this.data = new byte[SIZE];
this.owner = true;
this.shared = false;
}
这样出来的就是不共享的Segment对象。
继续,next
和prev
就是分别代表后继节点和前驱节点的对象,并以此来形成双向链表,那么怎么形成的双向链表呢?就是通过调用push
方法,具体先放着,后面看Buffer
的时候再细看。Segment中的主要方法为pop()
、push()
、split()
、compact()
,其中pop()
方法的作用是将当前的Segment对象从双向链表中移除,并返回链表中的下一个结点作为头结点,而push()
方法的作用则是在双向链表中当前结点的后面插入一个新的Segment
结点对象,并移动next
指向新插入的结点。后面两个方法主要是对Segment进行分割和合并,
提到Segment,还有一个与之相关的类SegmentPool
类:
/**
-
A collection of unused segments, necessary to avo