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