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