1 /** 2 In-memory streams 3 4 Copyright: © 2012-2016 Sönke Ludwig 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module vibe.stream.memory; 9 10 import vibe.core.stream; 11 import vibe.utils.array; 12 import vibe.internal.allocator; 13 14 import std.algorithm; 15 import std.array; 16 import std.exception; 17 import std.typecons; 18 19 MemoryOutputStream createMemoryOutputStream(IAllocator alloc = vibeThreadAllocator()) 20 @safe nothrow { 21 return new MemoryOutputStream(alloc, true); 22 } 23 24 /** Creates a new stream with the given data array as its contents. 25 26 Params: 27 data = The data array 28 writable = Flag that controls whether the data array may be changed 29 initial_size = The initial value that size returns - the file can grow up to data.length in size 30 */ 31 MemoryStream createMemoryStream(ubyte[] data, bool writable = true, size_t initial_size = size_t.max) 32 @safe nothrow { 33 return new MemoryStream(data, writable, initial_size, true); 34 } 35 36 37 /** OutputStream that collects the written data in memory and allows to query it 38 as a byte array. 39 */ 40 final class MemoryOutputStream : OutputStream { 41 @safe: 42 43 private { 44 AllocAppender!(ubyte[]) m_destination; 45 } 46 47 deprecated("Use createMemoryOutputStream isntead.") 48 this(IAllocator alloc = vibeThreadAllocator()) 49 { 50 this(alloc, true); 51 } 52 53 /// private 54 this(IAllocator alloc, bool dummy) 55 nothrow { 56 m_destination = AllocAppender!(ubyte[])(alloc); 57 } 58 59 /// An array with all data written to the stream so far. 60 @property ubyte[] data() nothrow { return m_destination.data(); } 61 62 /// Resets the stream to its initial state containing no data. 63 void reset(AppenderResetMode mode = AppenderResetMode.keepData) 64 @system { 65 m_destination.reset(mode); 66 } 67 68 /// Reserves space for data - useful for optimization. 69 void reserve(size_t nbytes) 70 { 71 m_destination.reserve(nbytes); 72 } 73 74 size_t write(in ubyte[] bytes, IOMode) 75 { 76 () @trusted { m_destination.put(bytes); } (); 77 return bytes.length; 78 } 79 80 alias write = OutputStream.write; 81 82 void flush() 83 nothrow { 84 } 85 86 void finalize() 87 nothrow { 88 } 89 } 90 91 mixin validateOutputStream!MemoryOutputStream; 92 93 94 /** 95 Provides a random access stream interface for accessing an array of bytes. 96 */ 97 final class MemoryStream : RandomAccessStream { 98 @safe: 99 100 private { 101 ubyte[] m_data; 102 size_t m_size; 103 bool m_writable; 104 size_t m_ptr = 0; 105 size_t m_peekWindow; 106 } 107 108 deprecated("Use createMemoryStream instead.") 109 this(ubyte[] data, bool writable = true, size_t initial_size = size_t.max) 110 { 111 this(data, writable, initial_size, true); 112 } 113 114 /// private 115 this(ubyte[] data, bool writable, size_t initial_size, bool dummy) 116 nothrow { 117 m_data = data; 118 m_size = min(initial_size, data.length); 119 m_writable = writable; 120 m_peekWindow = m_data.length; 121 } 122 123 /** Controls the maximum size of the array returned by peek(). 124 125 This property is mainly useful for debugging purposes. 126 */ 127 @property void peekWindow(size_t size) { m_peekWindow = size; } 128 129 @property bool empty() { return leastSize() == 0; } 130 @property ulong leastSize() { return m_size - m_ptr; } 131 @property bool dataAvailableForRead() { return leastSize() > 0; } 132 @property ulong size() const nothrow { return m_size; } 133 @property size_t capacity() const nothrow { return m_data.length; } 134 @property bool readable() const nothrow { return true; } 135 @property bool writable() const nothrow { return m_writable; } 136 137 void truncate(ulong size) 138 { 139 enforce(size < m_data.length, "Size limit of memory stream reached."); 140 m_size = cast(size_t)size; 141 } 142 143 void seek(ulong offset) { assert(offset <= m_data.length); m_ptr = cast(size_t)offset; } 144 ulong tell() nothrow { return m_ptr; } 145 const(ubyte)[] peek() { return m_data[m_ptr .. min(m_size, m_ptr+m_peekWindow)]; } 146 147 size_t read(scope ubyte[] dst, IOMode mode) 148 { 149 enforce(mode != IOMode.all || dst.length <= leastSize, "Reading past end of memory stream."); 150 auto len = min(leastSize, dst.length); 151 dst[0 .. len] = m_data[m_ptr .. m_ptr+len]; 152 m_ptr += len; 153 return len; 154 } 155 156 alias read = RandomAccessStream.read; 157 158 size_t write(in ubyte[] bytes, IOMode) 159 { 160 assert(writable); 161 enforce(bytes.length <= m_data.length - m_ptr, "Size limit of memory stream reached."); 162 m_data[m_ptr .. m_ptr+bytes.length] = bytes[]; 163 m_ptr += bytes.length; 164 m_size = max(m_size, m_ptr); 165 return bytes.length; 166 } 167 168 alias write = RandomAccessStream.write; 169 170 void flush() {} 171 void finalize() {} 172 } 173 174 mixin validateRandomAccessStream!MemoryStream;