| /* GENERATED SOURCE. DO NOT MODIFY. */ |
| /* |
| * Copyright (C) 2014 Square, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.okhttp.okio; |
| |
| /** |
| * A segment of a buffer. |
| * |
| * <p>Each segment in a buffer is a circularly-linked list node referencing the following and |
| * preceding segments in the buffer. |
| * |
| * <p>Each segment in the pool is a singly-linked list node referencing the rest of segments in the |
| * pool. |
| * |
| * <p>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; |
| |
| 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 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. |
| * |
| * <p>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 = new Segment(this); |
| 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; |
| } |
| } |