1/* 2 * Copyright (C) 2014 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package okio; 17 18/** 19 * A segment of a buffer. 20 * 21 * <p>Each segment in a buffer is a circularly-linked list node referencing the following and 22 * preceding segments in the buffer. 23 * 24 * <p>Each segment in the pool is a singly-linked list node referencing the rest of segments in the 25 * pool. 26 * 27 * <p>The underlying byte arrays of segments may be shared between buffers and byte strings. When a 28 * segment's byte array is shared the segment may not be recycled, nor may its byte data be changed. 29 * The lone exception is that the owner segment is allowed to append to the segment, writing data at 30 * {@code limit} and beyond. There is a single owning segment for each byte array. Positions, 31 * limits, prev, and next references are not shared. 32 */ 33final class Segment { 34 /** The size of all segments in bytes. */ 35 static final int SIZE = 8192; 36 37 final byte[] data; 38 39 /** The next byte of application data byte to read in this segment. */ 40 int pos; 41 42 /** The first byte of available data ready to be written to. */ 43 int limit; 44 45 /** True if other segments or byte strings use the same byte array. */ 46 boolean shared; 47 48 /** True if this segment owns the byte array and can append to it, extending {@code limit}. */ 49 boolean owner; 50 51 /** Next segment in a linked or circularly-linked list. */ 52 Segment next; 53 54 /** Previous segment in a circularly-linked list. */ 55 Segment prev; 56 57 Segment() { 58 this.data = new byte[SIZE]; 59 this.owner = true; 60 this.shared = false; 61 } 62 63 Segment(Segment shareFrom) { 64 this(shareFrom.data, shareFrom.pos, shareFrom.limit); 65 shareFrom.shared = true; 66 } 67 68 Segment(byte[] data, int pos, int limit) { 69 this.data = data; 70 this.pos = pos; 71 this.limit = limit; 72 this.owner = false; 73 this.shared = true; 74 } 75 76 /** 77 * Removes this segment of a circularly-linked list and returns its successor. 78 * Returns null if the list is now empty. 79 */ 80 public Segment pop() { 81 Segment result = next != this ? next : null; 82 prev.next = next; 83 next.prev = prev; 84 next = null; 85 prev = null; 86 return result; 87 } 88 89 /** 90 * Appends {@code segment} after this segment in the circularly-linked list. 91 * Returns the pushed segment. 92 */ 93 public Segment push(Segment segment) { 94 segment.prev = this; 95 segment.next = next; 96 next.prev = segment; 97 next = segment; 98 return segment; 99 } 100 101 /** 102 * Splits this head of a circularly-linked list into two segments. The first 103 * segment contains the data in {@code [pos..pos+byteCount)}. The second 104 * segment contains the data in {@code [pos+byteCount..limit)}. This can be 105 * useful when moving partial segments from one buffer to another. 106 * 107 * <p>Returns the new head of the circularly-linked list. 108 */ 109 public Segment split(int byteCount) { 110 if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException(); 111 Segment prefix = new Segment(this); 112 prefix.limit = prefix.pos + byteCount; 113 pos += byteCount; 114 prev.push(prefix); 115 return prefix; 116 } 117 118 /** 119 * Call this when the tail and its predecessor may both be less than half 120 * full. This will copy data so that segments can be recycled. 121 */ 122 public void compact() { 123 if (prev == this) throw new IllegalStateException(); 124 if (!prev.owner) return; // Cannot compact: prev isn't writable. 125 int byteCount = limit - pos; 126 int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos); 127 if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space. 128 writeTo(prev, byteCount); 129 pop(); 130 SegmentPool.recycle(this); 131 } 132 133 /** Moves {@code byteCount} bytes from this segment to {@code sink}. */ 134 public void writeTo(Segment sink, int byteCount) { 135 if (!sink.owner) throw new IllegalArgumentException(); 136 if (sink.limit + byteCount > SIZE) { 137 // We can't fit byteCount bytes at the sink's current position. Shift sink first. 138 if (sink.shared) throw new IllegalArgumentException(); 139 if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException(); 140 System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos); 141 sink.limit -= sink.pos; 142 sink.pos = 0; 143 } 144 145 System.arraycopy(data, pos, sink.data, sink.limit, byteCount); 146 sink.limit += byteCount; 147 pos += byteCount; 148 } 149} 150