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.CharsetUtil;
19  import io.netty.util.internal.ObjectUtil;
20  
21  import java.io.DataOutput;
22  import java.io.DataOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  
26  /**
27   * An {@link OutputStream} which writes data to a {@link ByteBuf}.
28   * <p>
29   * A write operation against this stream will occur at the {@code writerIndex}
30   * of its underlying buffer and the {@code writerIndex} will increase during
31   * the write operation.
32   * <p>
33   * This stream implements {@link DataOutput} for your convenience.
34   * The endianness of the stream is not always big endian but depends on
35   * the endianness of the underlying buffer.
36   *
37   * @see ByteBufInputStream
38   */
39  public class ByteBufOutputStream extends OutputStream implements DataOutput {
40  
41      private final ByteBuf buffer;
42      private final int startIndex;
43      private DataOutputStream utf8out; // lazily-instantiated
44      private boolean closed;
45      private final boolean releaseOnClose;
46  
47      /**
48       * Creates a new stream which writes data to the specified {@code buffer}.
49       */
50      public ByteBufOutputStream(ByteBuf buffer) {
51          this(buffer, false);
52      }
53  
54      /**
55       * Creates a new stream which writes data to the specified {@code buffer}.
56       *
57       * @param buffer Writes data to the buffer for this {@link OutputStream}.
58       * @param releaseOnClose {@code true} means that when {@link #close()} is called then {@link ByteBuf#release()} will
59       *                       be called on {@code buffer}.
60       */
61      public ByteBufOutputStream(ByteBuf buffer, boolean releaseOnClose) {
62          this.releaseOnClose = releaseOnClose;
63          this.buffer = ObjectUtil.checkNotNull(buffer, "buffer");
64          startIndex = buffer.writerIndex();
65      }
66  
67      /**
68       * Returns the number of written bytes by this stream so far.
69       */
70      public int writtenBytes() {
71          return buffer.writerIndex() - startIndex;
72      }
73  
74      @Override
75      public void write(byte[] b, int off, int len) throws IOException {
76          if (len == 0) {
77              return;
78          }
79  
80          buffer.writeBytes(b, off, len);
81      }
82  
83      @Override
84      public void write(byte[] b) throws IOException {
85          buffer.writeBytes(b);
86      }
87  
88      @Override
89      public void write(int b) throws IOException {
90          buffer.writeByte(b);
91      }
92  
93      @Override
94      public void writeBoolean(boolean v) throws IOException {
95          buffer.writeBoolean(v);
96      }
97  
98      @Override
99      public void writeByte(int v) throws IOException {
100         buffer.writeByte(v);
101     }
102 
103     @Override
104     public void writeBytes(String s) throws IOException {
105         buffer.writeCharSequence(s, CharsetUtil.US_ASCII);
106     }
107 
108     @Override
109     public void writeChar(int v) throws IOException {
110         buffer.writeChar(v);
111     }
112 
113     @Override
114     public void writeChars(String s) throws IOException {
115         int len = s.length();
116         for (int i = 0; i < len; i++) {
117             buffer.writeChar(s.charAt(i));
118         }
119     }
120 
121     @Override
122     public void writeDouble(double v) throws IOException {
123         buffer.writeDouble(v);
124     }
125 
126     @Override
127     public void writeFloat(float v) throws IOException {
128         buffer.writeFloat(v);
129     }
130 
131     @Override
132     public void writeInt(int v) throws IOException {
133         buffer.writeInt(v);
134     }
135 
136     @Override
137     public void writeLong(long v) throws IOException {
138         buffer.writeLong(v);
139     }
140 
141     @Override
142     public void writeShort(int v) throws IOException {
143         buffer.writeShort((short) v);
144     }
145 
146     @Override
147     public void writeUTF(String s) throws IOException {
148         DataOutputStream out = utf8out;
149         if (out == null) {
150             if (closed) {
151                 throw new IOException("The stream is closed");
152             }
153             // Suppress a warning since the stream is closed in the close() method
154             utf8out = out = new DataOutputStream(this);
155         }
156         out.writeUTF(s);
157     }
158 
159     /**
160      * Returns the buffer where this stream is writing data.
161      */
162     public ByteBuf buffer() {
163         return buffer;
164     }
165 
166     @Override
167     public void close() throws IOException {
168         if (closed) {
169             return;
170         }
171         closed = true;
172 
173         try {
174             super.close();
175         } finally {
176             if (utf8out != null) {
177                 utf8out.close();
178             }
179             if (releaseOnClose) {
180                 buffer.release();
181             }
182         }
183     }
184 }