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.io.ByteArrayOutputStream; 13 14 import hunt.io.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.io.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.io.ByteArrayOutputStream#size() 179 */ 180 byte[] toByteArray() { 181 // FIXME: Needing refactor or cleanup -@zxp at 7/25/2018, 10:02:15 AM 182 // 183 return buf[0 .. count]; // .dup; // Arrays.copyOf(buf, count); 184 } 185 186 byte[] getBuffer() { 187 return buf; 188 } 189 190 /** 191 * Returns the current size of the buffer. 192 * 193 * @return the value of the <code>count</code> field, which is the number 194 * of valid bytes in this output stream. 195 * @see hunt.io.ByteArrayOutputStream#count 196 */ 197 int size() { 198 return count; 199 } 200 201 /** 202 * Converts the buffer's contents into a string decoding bytes using the 203 * platform's default character set. The length of the new <tt>string</tt> 204 * is a function of the character set, and hence may not be equal to the 205 * size of the buffer. 206 * 207 * <p> This method always replaces malformed-input and unmappable-character 208 * sequences with the default replacement string for the platform's 209 * default character set. The {@linkplain java.nio.charset.CharsetDecoder} 210 * class should be used when more control over the decoding process is 211 * required. 212 * 213 * @return string decoded from the buffer's contents. 214 * @since JDK1.1 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 * @since JDK1.1 237 */ 238 string toString(string charsetName) { 239 return cast(string)(buf[0..count]); // , charsetName); 240 } 241 242 /** 243 * Creates a newly allocated string. Its size is the current size of 244 * the output stream and the valid contents of the buffer have been 245 * copied into it. Each character <i>c</i> in the resulting string is 246 * constructed from the corresponding element <i>b</i> in the byte 247 * array such that: 248 * <blockquote><pre> 249 * c == (char)(((hibyte & 0xff) << 8) | (b & 0xff)) 250 * </pre></blockquote> 251 * 252 * @deprecated This method does not properly convert bytes into characters. 253 * As of JDK 1.1, the preferred way to do this is via the 254 * <code>toString(string enc)</code> method, which takes an encoding-name 255 * argument, or the <code>toString()</code> method, which uses the 256 * platform's default character encoding. 257 * 258 * @param hibyte the high byte of each resulting Unicode character. 259 * @return the current contents of the output stream, as a string. 260 * @see hunt.io.ByteArrayOutputStream#size() 261 * @see hunt.io.ByteArrayOutputStream#toString(string) 262 * @see hunt.io.ByteArrayOutputStream#toString() 263 */ 264 // @Deprecated 265 // string toString(int hibyte) { 266 // return new string(buf, hibyte, 0, count); 267 // } 268 269 /** 270 * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in 271 * this class can be called after the stream has been closed without 272 * generating an <tt>IOException</tt>. 273 */ 274 // void close() { 275 // } 276 277 }