1 /******************************************************************************* 2 3 Simulates a device that you can write to and read from, behaves pretty much 4 like a file 5 6 This was created as an alternative to ocean.io.device.Array, whose write() 7 function has the unreasonable limitation of always appending instead of 8 respecting the current seek position and thus not properly simulating a file 9 10 Copyright: 11 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 12 All rights reserved. 13 14 License: 15 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 16 Alternatively, this file may be distributed under the terms of the Tango 17 3-Clause BSD License (see LICENSE_BSD.txt for details). 18 19 *******************************************************************************/ 20 21 module ocean.io.device.MemoryDevice; 22 23 import ocean.meta.types.Qualifiers; 24 import ocean.io.model.IConduit; 25 26 import core.stdc.string : memmove; 27 28 /******************************************************************************* 29 30 Simulates a device that you can write to and read from, behaves pretty much 31 like a file 32 33 *******************************************************************************/ 34 35 class MemoryDevice : IConduit 36 { 37 /*************************************************************************** 38 39 Buffer to keep the data 40 41 ***************************************************************************/ 42 43 private ubyte[] data; 44 45 /*************************************************************************** 46 47 Current read/write position 48 49 ***************************************************************************/ 50 51 private size_t position; 52 53 /*************************************************************************** 54 55 Returns: 56 Return the current size of the buffer 57 58 ***************************************************************************/ 59 60 override size_t bufferSize ( ) 61 { 62 return data.length; 63 } 64 65 /*************************************************************************** 66 67 Returns: 68 Current buffer as string 69 70 ***************************************************************************/ 71 72 public const(void)[] peek () 73 { 74 return data; 75 } 76 77 /*************************************************************************** 78 79 Returns: 80 Name of this IConduit 81 82 ***************************************************************************/ 83 84 override istring toString ( ) 85 { 86 return MemoryDevice.stringof; 87 } 88 89 /*************************************************************************** 90 91 Implemented because interfaces demand it, returns always true 92 93 ***************************************************************************/ 94 95 override bool isAlive ( ) 96 { 97 return true; 98 } 99 100 /*************************************************************************** 101 102 Implemented because interfaces demand it, does nothing 103 104 ***************************************************************************/ 105 106 override void detach ( ) {} 107 108 /*************************************************************************** 109 110 Throws an exception 111 112 Params: 113 msg = message to use in the exception 114 115 ***************************************************************************/ 116 117 override void error ( istring msg ) 118 { 119 throw new Exception ( msg ); 120 } 121 122 /*************************************************************************** 123 124 Write into this device, starting at the current seek position 125 126 Params: 127 src = data to write 128 129 Returns: 130 amount of written data, always src.length 131 132 ***************************************************************************/ 133 134 override size_t write ( const(void)[] src ) 135 { 136 if ( this.position + src.length > this.data.length ) 137 { 138 this.data.length = this.position + src.length; 139 } 140 141 memmove(&this.data[this.position], src.ptr, src.length); 142 143 this.position += src.length; 144 145 return src.length; 146 } 147 148 /*************************************************************************** 149 150 Copies src into this stream, overwriting any existing data 151 152 Params: 153 src = stream to copy from 154 max = amount of bytes to copy, -1 means infinite 155 156 Returns: 157 this class for chaining 158 159 ***************************************************************************/ 160 161 override OutputStream copy ( InputStream src, size_t max = -1 ) 162 { 163 size_t len; 164 165 if ( max == -1 ) 166 { 167 len = max > this.data.length ? this.data.length : max; 168 } 169 else 170 { 171 len = this.data.length; 172 } 173 174 this.position = 0; 175 176 auto new_data = src.load(); 177 178 this.data.length = new_data.length; 179 this.data[] = cast(ubyte[])new_data[]; 180 181 return this; 182 } 183 184 /*************************************************************************** 185 186 Returns: 187 the stream this stream writes to, which is none, so always null 188 189 ***************************************************************************/ 190 191 override OutputStream output ( ) 192 { 193 return null; 194 } 195 196 /*************************************************************************** 197 198 Reads into dst, starting to read from the current seek position 199 200 Params: 201 dst = array to read into 202 203 Returns: 204 Eof (and no action) if current seek position is at the end of the 205 buffer, 206 Else the amount of bytes read. 207 208 ***************************************************************************/ 209 210 override size_t read ( void[] dst ) 211 { 212 if ( this.position < this.data.length ) 213 { 214 auto end_pos = this.position + dst.length > this.data.length ? 215 this.data.length : this.position + dst.length; 216 217 dst[0..end_pos-this.position] = this.data[this.position .. end_pos]; 218 219 scope(exit) this.position = end_pos; 220 221 return end_pos - this.position; 222 } 223 else 224 { 225 return IConduit.Eof; 226 } 227 } 228 229 /*************************************************************************** 230 231 Params: 232 max = maximum amount of bytes to return 233 234 Returns: 235 slice to the internal buffer up to max bytes (-1 means infinite, 236 thus the whole buffer) 237 238 ***************************************************************************/ 239 240 override void[] load ( size_t max = -1 ) 241 { 242 size_t len; 243 244 if ( max == -1 ) 245 { 246 len = max > this.data.length ? this.data.length : max; 247 } 248 else 249 { 250 len = this.data.length; 251 } 252 253 return this.data[0..len]; 254 } 255 256 /*************************************************************************** 257 258 Returns: 259 This streams input stream, alas this stream has no input stream, 260 so null 261 262 ***************************************************************************/ 263 264 override InputStream input ( ) 265 { 266 return null; 267 } 268 269 /*************************************************************************** 270 271 Change the internal seeker position 272 273 Params: 274 offset = amount of bytes to add to the anchor to seek the new 275 position 276 anchor = specifies which point should be used as basis for the 277 offset 278 279 Returns: 280 the new seeker position 281 282 ***************************************************************************/ 283 284 override long seek ( long offset, Anchor anchor = Anchor.Begin ) 285 { 286 with ( Anchor ) switch ( anchor ) 287 { 288 case Begin: 289 break; 290 case Current: 291 offset = this.position + offset; 292 break; 293 case End: 294 offset = this.data.length + offset; 295 break; 296 default: 297 assert(false); 298 } 299 300 if ( offset > this.data.length ) 301 { 302 return this.position = this.data.length; 303 } 304 305 return this.position = offset; 306 } 307 308 /*************************************************************************** 309 310 Used by FormatOutput and other streams. 311 312 ***************************************************************************/ 313 314 override IConduit conduit () 315 { 316 return this; 317 } 318 319 /*************************************************************************** 320 321 Does nothing 322 323 ***************************************************************************/ 324 325 override IOStream flush ( ) 326 { 327 return this; 328 } 329 330 /*************************************************************************** 331 332 Deletes the buffer and resets position 333 334 ***************************************************************************/ 335 336 override void close ( ) 337 { 338 import core.memory; 339 GC.free(this.data.ptr); 340 this.data = null; 341 this.position = 0; 342 } 343 } 344 345 version (unittest) 346 { 347 import ocean.core.Test; 348 } 349 350 unittest 351 { 352 auto m = new MemoryDevice; 353 354 auto data = "This is a string"; 355 356 auto dst = new void[data.length]; 357 358 test!("==")(m.position, 0); 359 m.write(data); 360 test(m.data == cast(ubyte[])data); 361 test!("==")(m.position, data.length); 362 363 m.seek(0); 364 m.read(dst); 365 test(dst == data); 366 test!("==")(m.position, data.length); 367 368 m.seek(0); 369 m.write(data); 370 test(m.data == cast(ubyte[])data); 371 test!("==")(m.position, data.length); 372 }