1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.stream.ByteArrayOutputStream;
13 
14 import hunt.stream.Common;
15 import hunt.Exceptions;
16 
17 import std.algorithm;
18 import std.conv;
19 import std.format;
20 
21 /**
22  * This class implements an output stream in which the data is
23  * written into a byte array. The buffer automatically grows as data
24  * is written to it.
25  * The data can be retrieved using <code>toByteArray()</code> and
26  * <code>toString()</code>.
27  * <p>
28  * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
29  * this class can be called after the stream has been closed without
30  * generating an <tt>IOException</tt>.
31  */
32 
33 class ByteArrayOutputStream : OutputStream {
34 
35     /**
36      * The buffer where data is stored.
37      */
38     protected byte[] buf;
39 
40     /**
41      * The number of valid bytes in the buffer.
42      */
43     protected int count;
44 
45     /**
46      * Creates a new byte array output stream. The buffer capacity is
47      * initially 32 bytes, though its size increases if necessary.
48      */
49     this() {
50         this(32);
51     }
52 
53     /**
54      * Creates a new byte array output stream, with a buffer capacity of
55      * the specified size, in bytes.
56      *
57      * @param   size   the initial size.
58      * @exception  IllegalArgumentException if size is negative.
59      */
60     this(size_t size) {
61         buf = new byte[size];
62     }
63 
64     /**
65      * Increases the capacity if necessary to ensure that it can hold
66      * at least the number of elements specified by the minimum
67      * capacity argument.
68      *
69      * @param minCapacity the desired minimum capacity
70      * @throws OutOfMemoryError if {@code minCapacity < 0}.  This is
71      * interpreted as a request for the unsatisfiably large capacity
72      * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
73      */
74     private void ensureCapacity(size_t minCapacity) {
75         // overflow-conscious code
76         if (minCapacity > buf.length)
77             grow(minCapacity);
78     }
79 
80     /**
81      * The maximum size of array to allocate.
82      * Some VMs reserve some header words in an array.
83      * Attempts to allocate larger arrays may result in
84      * OutOfMemoryError: Requested array size exceeds VM limit
85      */
86     private enum int MAX_ARRAY_SIZE = int.max - 8;
87 
88     /**
89      * Increases the capacity to ensure that it can hold at least the
90      * number of elements specified by the minimum capacity argument.
91      *
92      * @param minCapacity the desired minimum capacity
93      */
94     private void grow(size_t minCapacity) {
95         // overflow-conscious code
96         size_t oldCapacity = buf.length;
97         size_t newCapacity = oldCapacity << 1;
98         if (newCapacity < minCapacity)
99             newCapacity = minCapacity;
100         if (newCapacity > MAX_ARRAY_SIZE)
101             newCapacity = hugeCapacity(minCapacity);
102         byte[] newBuf = new byte[newCapacity];
103         newBuf[0..count] = buf[0 .. count];
104         buf = newBuf;
105     }
106 
107     private static size_t hugeCapacity(size_t minCapacity) {
108         // if (minCapacity < 0) // overflow
109         //     throw new OutOfMemoryError();
110         return (minCapacity > MAX_ARRAY_SIZE) ?
111             size_t.max :
112             MAX_ARRAY_SIZE;
113     }
114 
115     alias write = OutputStream.write;
116 
117     /**
118      * Writes the specified byte to this byte array output stream.
119      *
120      * @param   b   the byte to be written.
121      */
122     override void write(int b) {
123         ensureCapacity(count + 1);
124         buf[count] = cast(byte) b;
125         count += 1;
126     }
127 
128     /**
129      * Writes <code>len</code> bytes from the specified byte array
130      * starting at offset <code>off</code> to this byte array output stream.
131      *
132      * @param   b     the data.
133      * @param   off   the start offset in the data.
134      * @param   len   the number of bytes to write.
135      */
136     override void write(byte[] b, int off, int len) {
137         if ((off < 0) || (off > b.length) || (len < 0) ||
138             ((off + len) > b.length)) {
139                 string msg = format("buffer error, size: %d, offset: %d, length: %d",
140                     b.length, off, len);
141             throw new IndexOutOfBoundsException(msg);
142         }
143         ensureCapacity(count + len);
144         buf[count .. count+len] = b[off .. off + len];
145         count += len;
146     }
147 
148     /**
149      * Writes the complete contents of this byte array output stream to
150      * the specified output stream argument, as if by calling the output
151      * stream's write method using <code>out.write(buf, 0, count)</code>.
152      *
153      * @param      out   the output stream to which to write the data.
154      * @exception  IOException  if an I/O error occurs.
155      */
156     void writeTo(OutputStream o) {
157         o.write(buf, 0, count);
158     }
159 
160     /**
161      * Resets the <code>count</code> field of this byte array output
162      * stream to zero, so that all currently accumulated output in the
163      * output stream is discarded. The output stream can be used again,
164      * reusing the already allocated buffer space.
165      *
166      * @see     hunt.stream.ByteArrayInputStream#count
167      */
168     void reset() {
169         count = 0;
170     }
171 
172     /**
173      * Creates a newly allocated byte array. Its size is the current
174      * size of this output stream and the valid contents of the buffer
175      * have been copied into it.
176      *
177      * @return  the current contents of this output stream, as a byte array.
178      * @see     hunt.stream.ByteArrayOutputStream#size()
179      */
180     byte[] toByteArray(bool canCopy = true) {
181         if(canCopy)
182             return buf[0 .. count].dup; 
183         else
184             return buf[0 .. count]; 
185     }
186 
187     byte[] getBuffer() {
188 		return buf;
189 	}
190 
191     /**
192      * Returns the current size of the buffer.
193      *
194      * @return  the value of the <code>count</code> field, which is the number
195      *          of valid bytes in this output stream.
196      * @see     hunt.stream.ByteArrayOutputStream#count
197      */
198     int size() {
199         return count;
200     }
201 
202     /**
203      * Converts the buffer's contents into a string decoding bytes using the
204      * platform's default character set. The length of the new <tt>string</tt>
205      * is a function of the character set, and hence may not be equal to the
206      * size of the buffer.
207      *
208      * <p> This method always replaces malformed-input and unmappable-character
209      * sequences with the default replacement string for the platform's
210      * default character set. The {@linkplain java.nio.charset.CharsetDecoder}
211      * class should be used when more control over the decoding process is
212      * required.
213      *
214      * @return string decoded from the buffer's contents.
215      */
216     override string toString() {
217         return cast(string)(buf[0 .. count]);
218     }
219 
220     /**
221      * Converts the buffer's contents into a string by decoding the bytes using
222      * the named {@link java.nio.charset.Charset charset}. The length of the new
223      * <tt>string</tt> is a function of the charset, and hence may not be equal
224      * to the length of the byte array.
225      *
226      * <p> This method always replaces malformed-input and unmappable-character
227      * sequences with this charset's default replacement string. The {@link
228      * java.nio.charset.CharsetDecoder} class should be used when more control
229      * over the decoding process is required.
230      *
231      * @param      charsetName  the name of a supported
232      *             {@link java.nio.charset.Charset charset}
233      * @return     string decoded from the buffer's contents.
234      * @exception  UnsupportedEncodingException
235      *             If the named charset is not supported
236      */
237     string toString(string charsetName) {
238         return cast(string)(buf[0..count]); // , charsetName);
239     }
240 
241     /**
242      * Creates a newly allocated string. Its size is the current size of
243      * the output stream and the valid contents of the buffer have been
244      * copied into it. Each character <i>c</i> in the resulting string is
245      * constructed from the corresponding element <i>b</i> in the byte
246      * array such that:
247      * <blockquote><pre>
248      *     c == (char)(((hibyte &amp; 0xff) &lt;&lt; 8) | (b &amp; 0xff))
249      * </pre></blockquote>
250      *
251      * @deprecated This method does not properly convert bytes into characters.
252      * As of JDK&nbsp;1.1, the preferred way to do this is via the
253      * <code>toString(string enc)</code> method, which takes an encoding-name
254      * argument, or the <code>toString()</code> method, which uses the
255      * platform's default character encoding.
256      *
257      * @param      hibyte    the high byte of each resulting Unicode character.
258      * @return     the current contents of the output stream, as a string.
259      * @see        hunt.stream.ByteArrayOutputStream#size()
260      * @see        hunt.stream.ByteArrayOutputStream#toString(string)
261      * @see        hunt.stream.ByteArrayOutputStream#toString()
262      */
263     // @Deprecated
264     // string toString(int hibyte) {
265     //     return new string(buf, hibyte, 0, count);
266     // }
267 
268     /**
269      * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
270      * this class can be called after the stream has been closed without
271      * generating an <tt>IOException</tt>.
272      */
273     // void close() {
274     // }
275 
276 }