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 18import java.io.IOException; 19import java.io.InputStream; 20import java.io.OutputStream; 21 22import static okio.Util.checkOffsetAndCount; 23 24public final class Okio { 25 private Okio() { 26 } 27 28 public static BufferedSource buffer(Source source) { 29 return new RealBufferedSource(source); 30 } 31 32 public static BufferedSink buffer(Sink sink) { 33 return new RealBufferedSink(sink); 34 } 35 36 /** Copies bytes from {@code source} to {@code sink}. */ 37 public static void copy(OkBuffer source, long offset, long byteCount, OutputStream sink) 38 throws IOException { 39 checkOffsetAndCount(source.size, offset, byteCount); 40 41 // Skip segments that we aren't copying from. 42 Segment s = source.head; 43 while (offset >= (s.limit - s.pos)) { 44 offset -= (s.limit - s.pos); 45 s = s.next; 46 } 47 48 // Copy from one segment at a time. 49 while (byteCount > 0) { 50 int pos = (int) (s.pos + offset); 51 int toWrite = (int) Math.min(s.limit - pos, byteCount); 52 sink.write(s.data, pos, toWrite); 53 byteCount -= toWrite; 54 offset = 0; 55 } 56 } 57 58 /** Returns a sink that writes to {@code out}. */ 59 public static Sink sink(final OutputStream out) { 60 return new Sink() { 61 private Deadline deadline = Deadline.NONE; 62 63 @Override public void write(OkBuffer source, long byteCount) 64 throws IOException { 65 checkOffsetAndCount(source.size, 0, byteCount); 66 while (byteCount > 0) { 67 deadline.throwIfReached(); 68 Segment head = source.head; 69 int toCopy = (int) Math.min(byteCount, head.limit - head.pos); 70 out.write(head.data, head.pos, toCopy); 71 72 head.pos += toCopy; 73 byteCount -= toCopy; 74 source.size -= toCopy; 75 76 if (head.pos == head.limit) { 77 source.head = head.pop(); 78 SegmentPool.INSTANCE.recycle(head); 79 } 80 } 81 } 82 83 @Override public void flush() throws IOException { 84 out.flush(); 85 } 86 87 @Override public void close() throws IOException { 88 out.close(); 89 } 90 91 @Override public Sink deadline(Deadline deadline) { 92 if (deadline == null) throw new IllegalArgumentException("deadline == null"); 93 this.deadline = deadline; 94 return this; 95 } 96 97 @Override public String toString() { 98 return "sink(" + out + ")"; 99 } 100 }; 101 } 102 103 /** Returns a source that reads from {@code in}. */ 104 public static Source source(final InputStream in) { 105 return new Source() { 106 private Deadline deadline = Deadline.NONE; 107 108 @Override public long read(OkBuffer sink, long byteCount) throws IOException { 109 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 110 deadline.throwIfReached(); 111 Segment tail = sink.writableSegment(1); 112 int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); 113 int bytesRead = in.read(tail.data, tail.limit, maxToCopy); 114 if (bytesRead == -1) return -1; 115 tail.limit += bytesRead; 116 sink.size += bytesRead; 117 return bytesRead; 118 } 119 120 @Override public void close() throws IOException { 121 in.close(); 122 } 123 124 @Override public Source deadline(Deadline deadline) { 125 if (deadline == null) throw new IllegalArgumentException("deadline == null"); 126 this.deadline = deadline; 127 return this; 128 } 129 130 @Override public String toString() { 131 return "source(" + in + ")"; 132 } 133 }; 134 } 135} 136