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 & 0xff) << 8) | (b & 0xff)) 249 * </pre></blockquote> 250 * 251 * @deprecated This method does not properly convert bytes into characters. 252 * As of JDK 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 }