Home | History | Annotate | Download | only in okio
      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  */
     16 package okio;
     17 
     18 import java.io.IOException;
     19 import java.io.OutputStream;
     20 
     21 final class RealBufferedSink implements BufferedSink {
     22   public final OkBuffer buffer;
     23   public final Sink sink;
     24   private boolean closed;
     25 
     26   public RealBufferedSink(Sink sink, OkBuffer buffer) {
     27     if (sink == null) throw new IllegalArgumentException("sink == null");
     28     this.buffer = buffer;
     29     this.sink = sink;
     30   }
     31 
     32   public RealBufferedSink(Sink sink) {
     33     this(sink, new OkBuffer());
     34   }
     35 
     36   @Override public OkBuffer buffer() {
     37     return buffer;
     38   }
     39 
     40   @Override public void write(OkBuffer source, long byteCount)
     41       throws IOException {
     42     if (closed) throw new IllegalStateException("closed");
     43     buffer.write(source, byteCount);
     44     emitCompleteSegments();
     45   }
     46 
     47   @Override public BufferedSink write(ByteString byteString) throws IOException {
     48     if (closed) throw new IllegalStateException("closed");
     49     buffer.write(byteString);
     50     return emitCompleteSegments();
     51   }
     52 
     53   @Override public BufferedSink writeUtf8(String string) throws IOException {
     54     if (closed) throw new IllegalStateException("closed");
     55     buffer.writeUtf8(string);
     56     return emitCompleteSegments();
     57   }
     58 
     59   @Override public BufferedSink write(byte[] source) throws IOException {
     60     if (closed) throw new IllegalStateException("closed");
     61     buffer.write(source);
     62     return emitCompleteSegments();
     63   }
     64 
     65   @Override public BufferedSink write(byte[] source, int offset, int byteCount) throws IOException {
     66     if (closed) throw new IllegalStateException("closed");
     67     buffer.write(source, offset, byteCount);
     68     return emitCompleteSegments();
     69   }
     70 
     71   @Override public BufferedSink writeByte(int b) throws IOException {
     72     if (closed) throw new IllegalStateException("closed");
     73     buffer.writeByte(b);
     74     return emitCompleteSegments();
     75   }
     76 
     77   @Override public BufferedSink writeShort(int s) throws IOException {
     78     if (closed) throw new IllegalStateException("closed");
     79     buffer.writeShort(s);
     80     return emitCompleteSegments();
     81   }
     82 
     83   @Override public BufferedSink writeShortLe(int s) throws IOException {
     84     if (closed) throw new IllegalStateException("closed");
     85     buffer.writeShortLe(s);
     86     return emitCompleteSegments();
     87   }
     88 
     89   @Override public BufferedSink writeInt(int i) throws IOException {
     90     if (closed) throw new IllegalStateException("closed");
     91     buffer.writeInt(i);
     92     return emitCompleteSegments();
     93   }
     94 
     95   @Override public BufferedSink writeIntLe(int i) throws IOException {
     96     if (closed) throw new IllegalStateException("closed");
     97     buffer.writeIntLe(i);
     98     return emitCompleteSegments();
     99   }
    100 
    101   @Override public BufferedSink writeLong(long v) throws IOException {
    102     if (closed) throw new IllegalStateException("closed");
    103     buffer.writeLong(v);
    104     return emitCompleteSegments();
    105   }
    106 
    107   @Override public BufferedSink writeLongLe(long v) throws IOException {
    108     if (closed) throw new IllegalStateException("closed");
    109     buffer.writeLongLe(v);
    110     return emitCompleteSegments();
    111   }
    112 
    113   @Override public BufferedSink emitCompleteSegments() throws IOException {
    114     if (closed) throw new IllegalStateException("closed");
    115     long byteCount = buffer.completeSegmentByteCount();
    116     if (byteCount > 0) sink.write(buffer, byteCount);
    117     return this;
    118   }
    119 
    120   @Override public OutputStream outputStream() {
    121     return new OutputStream() {
    122       @Override public void write(int b) throws IOException {
    123         if (closed) throw new IOException("closed");
    124         buffer.writeByte((byte) b);
    125         emitCompleteSegments();
    126       }
    127 
    128       @Override public void write(byte[] data, int offset, int byteCount) throws IOException {
    129         if (closed) throw new IOException("closed");
    130         buffer.write(data, offset, byteCount);
    131         emitCompleteSegments();
    132       }
    133 
    134       @Override public void flush() throws IOException {
    135         // For backwards compatibility, a flush() on a closed stream is a no-op.
    136         if (!closed) {
    137           RealBufferedSink.this.flush();
    138         }
    139       }
    140 
    141       @Override public void close() throws IOException {
    142         RealBufferedSink.this.close();
    143       }
    144 
    145       @Override public String toString() {
    146         return RealBufferedSink.this + ".outputStream()";
    147       }
    148     };
    149   }
    150 
    151   @Override public void flush() throws IOException {
    152     if (closed) throw new IllegalStateException("closed");
    153     if (buffer.size > 0) {
    154       sink.write(buffer, buffer.size);
    155     }
    156     sink.flush();
    157   }
    158 
    159   @Override public void close() throws IOException {
    160     if (closed) return;
    161 
    162     // Emit buffered data to the underlying sink. If this fails, we still need
    163     // to close the sink; otherwise we risk leaking resources.
    164     Throwable thrown = null;
    165     try {
    166       if (buffer.size > 0) {
    167         sink.write(buffer, buffer.size);
    168       }
    169     } catch (Throwable e) {
    170       thrown = e;
    171     }
    172 
    173     try {
    174       sink.close();
    175     } catch (Throwable e) {
    176       if (thrown == null) thrown = e;
    177     }
    178     closed = true;
    179 
    180     if (thrown != null) Util.sneakyRethrow(thrown);
    181   }
    182 
    183   @Override public Sink deadline(Deadline deadline) {
    184     sink.deadline(deadline);
    185     return this;
    186   }
    187 
    188   @Override public String toString() {
    189     return "buffer(" + sink + ")";
    190   }
    191 }
    192