View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.buffer;
17  
18  import io.netty.util.internal.ObjectUtil;
19  
20  import java.io.DataOutput;
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  
25  /**
26   * An {@link OutputStream} which writes data to a {@link ByteBuf}.
27   * <p>
28   * A write operation against this stream will occur at the {@code writerIndex}
29   * of its underlying buffer and the {@code writerIndex} will increase during
30   * the write operation.
31   * <p>
32   * This stream implements {@link DataOutput} for your convenience.
33   * The endianness of the stream is not always big endian but depends on
34   * the endianness of the underlying buffer.
35   *
36   * @see ByteBufInputStream
37   */
38  public class ByteBufOutputStream extends OutputStream implements DataOutput {
39  
40      private final ByteBuf buffer;
41      private final int startIndex;
42      private DataOutputStream utf8out; // lazily-instantiated
43      private boolean closed;
44      private final boolean releaseOnClose;
45  
46      /**
47       * Creates a new stream which writes data to the specified {@code buffer}.
48       */
49      public ByteBufOutputStream(ByteBuf buffer) {
50          this(buffer, false);
51      }
52  
53      /**
54       * Creates a new stream which writes data to the specified {@code buffer}.
55       *
56       * @param buffer Writes data to the buffer for this {@link OutputStream}.
57       * @param releaseOnClose {@code true} means that when {@link #close()} is called then {@link ByteBuf#release()} will
58       *                       be called on {@code buffer}.
59       */
60      public ByteBufOutputStream(ByteBuf buffer, boolean releaseOnClose) {
61          this.releaseOnClose = releaseOnClose;
62          this.buffer = ObjectUtil.checkNotNull(buffer, "buffer");
63          startIndex = buffer.writerIndex();
64      }
65  
66      /**
67       * Returns the number of written bytes by this stream so far.
68       */
69      public int writtenBytes() {
70          return buffer.writerIndex() - startIndex;
71      }
72  
73      @Override
74      public void write(byte[] b, int off, int len) throws IOException {
75          buffer.writeBytes(b, off, len);
76      }
77  
78      @Override
79      public void write(byte[] b) throws IOException {
80          buffer.writeBytes(b);
81      }
82  
83      @Override
84      public void write(int b) throws IOException {
85          buffer.writeByte(b);
86      }
87  
88      @Override
89      public void writeBoolean(boolean v) throws IOException {
90          buffer.writeBoolean(v);
91      }
92  
93      @Override
94      public void writeByte(int v) throws IOException {
95          buffer.writeByte(v);
96      }
97  
98      @Override
99      public void writeBytes(String s) throws IOException {
100         // We don't use `ByteBuf.writeCharSequence` here, because `writeBytes` is specified to only write the
101         // lower-order by of multibyte characters (exactly one byte per character in the string), while
102         // `writeCharSequence` will instead write a '?' replacement character.
103         int length = s.length();
104         buffer.ensureWritable(length);
105         int offset = buffer.writerIndex();
106         for (int i = 0; i < length; i++) {
107             buffer.setByte(offset + i, (byte) s.charAt(i));
108         }
109         buffer.writerIndex(offset + length);
110     }
111 
112     @Override
113     public void writeChar(int v) throws IOException {
114         buffer.writeChar(v);
115     }
116 
117     @Override
118     public void writeChars(String s) throws IOException {
119         int len = s.length();
120         for (int i = 0; i < len; i++) {
121             buffer.writeChar(s.charAt(i));
122         }
123     }
124 
125     @Override
126     public void writeDouble(double v) throws IOException {
127         buffer.writeDouble(v);
128     }
129 
130     @Override
131     public void writeFloat(float v) throws IOException {
132         buffer.writeFloat(v);
133     }
134 
135     @Override
136     public void writeInt(int v) throws IOException {
137         buffer.writeInt(v);
138     }
139 
140     @Override
141     public void writeLong(long v) throws IOException {
142         buffer.writeLong(v);
143     }
144 
145     @Override
146     public void writeShort(int v) throws IOException {
147         buffer.writeShort((short) v);
148     }
149 
150     @Override
151     public void writeUTF(String s) throws IOException {
152         DataOutputStream out = utf8out;
153         if (out == null) {
154             if (closed) {
155                 throw new IOException("The stream is closed");
156             }
157             // Suppress a warning since the stream is closed in the close() method
158             utf8out = out = new DataOutputStream(this);
159         }
160         out.writeUTF(s);
161     }
162 
163     /**
164      * Returns the buffer where this stream is writing data.
165      */
166     public ByteBuf buffer() {
167         return buffer;
168     }
169 
170     @Override
171     public void close() throws IOException {
172         if (closed) {
173             return;
174         }
175         closed = true;
176 
177         try {
178             super.close();
179         } finally {
180             if (utf8out != null) {
181                 utf8out.close();
182             }
183             if (releaseOnClose) {
184                 buffer.release();
185             }
186         }
187     }
188 }