| /* |
| * 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 okio; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InterruptedIOException; |
| import java.io.OutputStream; |
| import java.net.Socket; |
| import java.net.SocketTimeoutException; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import static okio.Util.checkOffsetAndCount; |
| |
| /** Essential APIs for working with Okio. */ |
| public final class Okio { |
| private static final Logger logger = Logger.getLogger(Okio.class.getName()); |
| |
| private Okio() { |
| } |
| |
| /** |
| * Returns a new source that buffers reads from {@code source}. The returned |
| * source will perform bulk reads into its in-memory buffer. Use this wherever |
| * you read a source to get an ergonomic and efficient access to data. |
| */ |
| public static BufferedSource buffer(Source source) { |
| if (source == null) throw new IllegalArgumentException("source == null"); |
| return new RealBufferedSource(source); |
| } |
| |
| /** |
| * Returns a new sink that buffers writes to {@code sink}. The returned sink |
| * will batch writes to {@code sink}. Use this wherever you write to a sink to |
| * get an ergonomic and efficient access to data. |
| */ |
| public static BufferedSink buffer(Sink sink) { |
| if (sink == null) throw new IllegalArgumentException("sink == null"); |
| return new RealBufferedSink(sink); |
| } |
| |
| /** Returns a sink that writes to {@code out}. */ |
| public static Sink sink(final OutputStream out) { |
| return sink(out, new Timeout()); |
| } |
| |
| private static Sink sink(final OutputStream out, final Timeout timeout) { |
| if (out == null) throw new IllegalArgumentException("out == null"); |
| if (timeout == null) throw new IllegalArgumentException("timeout == null"); |
| |
| return new Sink() { |
| @Override public void write(Buffer source, long byteCount) throws IOException { |
| checkOffsetAndCount(source.size, 0, byteCount); |
| while (byteCount > 0) { |
| timeout.throwIfReached(); |
| Segment head = source.head; |
| int toCopy = (int) Math.min(byteCount, head.limit - head.pos); |
| out.write(head.data, head.pos, toCopy); |
| |
| head.pos += toCopy; |
| byteCount -= toCopy; |
| source.size -= toCopy; |
| |
| if (head.pos == head.limit) { |
| source.head = head.pop(); |
| SegmentPool.recycle(head); |
| } |
| } |
| } |
| |
| @Override public void flush() throws IOException { |
| out.flush(); |
| } |
| |
| @Override public void close() throws IOException { |
| out.close(); |
| } |
| |
| @Override public Timeout timeout() { |
| return timeout; |
| } |
| |
| @Override public String toString() { |
| return "sink(" + out + ")"; |
| } |
| }; |
| } |
| |
| /** |
| * Returns a sink that writes to {@code socket}. Prefer this over {@link |
| * #sink(OutputStream)} because this method honors timeouts. When the socket |
| * write times out, the socket is asynchronously closed by a watchdog thread. |
| */ |
| public static Sink sink(final Socket socket) throws IOException { |
| if (socket == null) throw new IllegalArgumentException("socket == null"); |
| AsyncTimeout timeout = timeout(socket); |
| Sink sink = sink(socket.getOutputStream(), timeout); |
| return timeout.sink(sink); |
| } |
| |
| /** Returns a source that reads from {@code in}. */ |
| public static Source source(final InputStream in) { |
| return source(in, new Timeout()); |
| } |
| |
| private static Source source(final InputStream in, final Timeout timeout) { |
| if (in == null) throw new IllegalArgumentException("in == null"); |
| if (timeout == null) throw new IllegalArgumentException("timeout == null"); |
| |
| return new Source() { |
| @Override public long read(Buffer sink, long byteCount) throws IOException { |
| if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); |
| if (byteCount == 0) return 0; |
| timeout.throwIfReached(); |
| Segment tail = sink.writableSegment(1); |
| int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); |
| int bytesRead = in.read(tail.data, tail.limit, maxToCopy); |
| if (bytesRead == -1) return -1; |
| tail.limit += bytesRead; |
| sink.size += bytesRead; |
| return bytesRead; |
| } |
| |
| @Override public void close() throws IOException { |
| in.close(); |
| } |
| |
| @Override public Timeout timeout() { |
| return timeout; |
| } |
| |
| @Override public String toString() { |
| return "source(" + in + ")"; |
| } |
| }; |
| } |
| |
| /** Returns a source that reads from {@code file}. */ |
| public static Source source(File file) throws FileNotFoundException { |
| if (file == null) throw new IllegalArgumentException("file == null"); |
| return source(new FileInputStream(file)); |
| } |
| |
| // ANDROID-BEGIN |
| // /** Returns a source that reads from {@code path}. */ |
| // @IgnoreJRERequirement // Should only be invoked on Java 7+. |
| // public static Source source(Path path, OpenOption... options) throws IOException { |
| // if (path == null) throw new IllegalArgumentException("path == null"); |
| // return source(Files.newInputStream(path, options)); |
| // } |
| // ANDROID-END |
| |
| /** Returns a sink that writes to {@code file}. */ |
| public static Sink sink(File file) throws FileNotFoundException { |
| if (file == null) throw new IllegalArgumentException("file == null"); |
| return sink(new FileOutputStream(file)); |
| } |
| |
| /** Returns a sink that appends to {@code file}. */ |
| public static Sink appendingSink(File file) throws FileNotFoundException { |
| if (file == null) throw new IllegalArgumentException("file == null"); |
| return sink(new FileOutputStream(file, true)); |
| } |
| |
| // ANDROID-BEGIN |
| // /** Returns a sink that writes to {@code path}. */ |
| // @IgnoreJRERequirement // Should only be invoked on Java 7+. |
| // public static Sink sink(Path path, OpenOption... options) throws IOException { |
| // if (path == null) throw new IllegalArgumentException("path == null"); |
| // return sink(Files.newOutputStream(path, options)); |
| // } |
| // ANDROID-END |
| |
| /** |
| * Returns a source that reads from {@code socket}. Prefer this over {@link |
| * #source(InputStream)} because this method honors timeouts. When the socket |
| * read times out, the socket is asynchronously closed by a watchdog thread. |
| */ |
| public static Source source(final Socket socket) throws IOException { |
| if (socket == null) throw new IllegalArgumentException("socket == null"); |
| AsyncTimeout timeout = timeout(socket); |
| Source source = source(socket.getInputStream(), timeout); |
| return timeout.source(source); |
| } |
| |
| private static AsyncTimeout timeout(final Socket socket) { |
| return new AsyncTimeout() { |
| @Override protected IOException newTimeoutException(IOException cause) { |
| InterruptedIOException ioe = new SocketTimeoutException("timeout"); |
| if (cause != null) { |
| ioe.initCause(cause); |
| } |
| return ioe; |
| } |
| |
| @Override protected void timedOut() { |
| try { |
| socket.close(); |
| } catch (Exception e) { |
| logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e); |
| } |
| } |
| }; |
| } |
| } |