1 module vfile; 2 3 import core.stdc.stdlib; 4 import core.stdc.string : memcpy; 5 import std.stdio; 6 import std.typecons : Flag, Yes, No; 7 8 /** 9 * Implements the virtual file. 10 */ 11 public struct VFile{ 12 enum Orientation { unknown, narrow, wide } 13 private void[] datastream; 14 private size_t position; 15 private string name; ///mostly a placeholder, but can be set if needed 16 17 /** 18 * Creates a virtual file out from an array. 19 */ 20 public this(T)(ref T[] target, string name = null){ 21 datastream = cast(void[])target; 22 this.name = name; 23 } 24 /** 25 * Creates a virtual file out from a pointer and a length indicator. 26 */ 27 public this(void* ptr, size_t length, string name = null){ 28 datastream = ptr[0..length]; 29 this.name = name; 30 } 31 /** 32 * Indicates if the virtual file is opened. 33 * If the datastream isn't null, it must be open. 34 */ 35 public @safe @property @nogc bool isOpen(){ 36 return datastream !is null; 37 } 38 /** 39 * Closes the datastream. 40 */ 41 public @safe void close(){ 42 datastream = null; 43 } 44 /** 45 * Copies the data into the buffer and moves the file forward the length of the buffer. 46 * Throws exception if EOF reached. 47 */ 48 public T[] rawRead(T)(T[] buffer){ 49 //const size_t remaining = datastream.length - position; 50 if(position + (buffer.length * T.sizeof) <= datastream.length){ 51 memcpy(buffer.ptr, datastream.ptr + position, buffer.length * T.sizeof); 52 position += buffer.length * T.sizeof; 53 return buffer; 54 }else{ 55 import std.conv : to; 56 throw new Exception("EOF reached at position " ~ to!string(position)); 57 } 58 } 59 /** 60 * Reads a single element from the stream. 61 * Throws Exception if EOF reached. 62 * Important: Does not provide any complex serialization method, so structs must avoid heap managed fields like dynamic 63 * arrays. They should implement a custom serializer. 64 */ 65 public T read(T)(){ 66 T buffer; 67 if(remaining + T.sizeof <= datastream.length){ 68 memcpy(&buffer, datastream.ptr + position, T.sizeof); 69 position += T.sizeof; 70 return buffer; 71 }else{ 72 throw new Exception("EOF has been reached"); 73 } 74 } 75 /** 76 * Writes data into the datastream. 77 * If the stream is shorter, then it'll be extended. 78 */ 79 public void rawWrite(T)(T[] buffer){ 80 if(buffer.length + position >= datastream.length){ 81 datastream.length = position; 82 datastream.length += buffer.length * T.sizeof; 83 //datastream ~= cast(void[])buffer; 84 }//else{ 85 memcpy(datastream.ptr + position, buffer.ptr, buffer.length * T.sizeof); 86 //} 87 position += buffer.length * T.sizeof; 88 assert(position <= datastream.length); 89 } 90 /** 91 * Writes a single element to the stream. 92 * Throws Exception if EOF reached. 93 * Important: Does not provide any complex serialization method, so structs must avoid heap managed fields like dynamic 94 * arrays. They should implement a custom serializer. 95 */ 96 public T write(T)(T buffer){ 97 if(remaining + T.sizeof <= datastream.length){ 98 memcpy(datastream.ptr + position, &buffer, T.sizeof); 99 position += T.sizeof; 100 return buffer; 101 }else{ 102 throw new Exception("EOF has been reached"); 103 } 104 } 105 /** 106 * Jumps to the given location. 107 */ 108 @nogc @property public void seek(long offset, int origin = SEEK_SET) @trusted{ 109 assert(position <= datastream.length); 110 final switch(origin){ 111 case SEEK_SET: 112 position = cast(sizediff_t)offset; 113 break; 114 case SEEK_CUR: 115 position += cast(sizediff_t)offset; 116 break; 117 case SEEK_END: 118 position = datastream.length + cast(sizediff_t)offset; 119 break; 120 } 121 position = position > datastream.length ? datastream.length : position; 122 } 123 /** 124 * Returns the current position. 125 */ 126 @nogc @property public size_t tell(){ 127 return position; 128 } 129 /** 130 * Return an input range to read the data stream by line. 131 */ 132 auto byLine(Term = char, Char = char)(Flag!"keepTerm" keepTerm = No.keepTerm, Term term = '\x0a'){ 133 class ByLineRange{ 134 public: 135 @property bool empty(){ return lines == null; } 136 @property Char[] front() { return curline; } 137 void popFront(){ 138 import std.algorithm.searching : countUntil; 139 auto len = lines.countUntil(term); 140 if(len < 0){ // For files lacking a final newline. 141 curline = lines; 142 lines = null; 143 }else{ 144 curline = keepTerm ? lines[0..len+term.sizeof] : lines[0..len]; 145 lines = lines[len+term.sizeof..$]; 146 } 147 } 148 149 private: 150 Char[] lines; 151 Char[] curline; 152 bool keepTerm; 153 Term term; 154 155 this(Char[] data, bool keepTerm, Term term){ 156 lines = data; 157 this.keepTerm = keepTerm; 158 this.term = term; 159 popFront(); 160 } 161 } 162 return new ByLineRange(cast(Char[])datastream, keepTerm, term); 163 } 164 /** 165 * Returns the size of the datastream. 166 */ 167 @nogc @property public size_t size(){ 168 return datastream.length; 169 } 170 171 } 172 unittest{ 173 immutable string a = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum et libero dignissim, porta odio id, 174 sollicitudin quam. Suspendisse ultrices quis erat nec elementum. Phasellus sollicitudin vehicula nunc, vel 175 laoreet magna dictum eget. Pellentesque sit amet tellus ac mauris elementum bibendum sed in eros. Nullam nec ligula 176 id ligula egestas malesuada. Etiam et nulla nec nibh faucibus suscipit non id felis. Integer vulputate lobortis 177 neque, ut congue nisl suscipit at. Nullam ac ante eu orci viverra dapibus. \n"; 178 immutable string b = "Vivamus quis finibus mi. Proin lectus enim, convallis a libero a, condimentum eleifend nisl. Vestibulum 179 luctus malesuada orci a ornare. Curabitur orci dolor, ultricies ut malesuada nec, semper dictum justo. Ut vestibulum 180 nisl id velit congue, tristique porta sapien rutrum. Pellentesque ultricies id arcu non fermentum. Ut at est ex. Sed 181 elementum gravida risus, et cursus magna tincidunt nec. Morbi hendrerit efficitur neque, non ultricies quam maximus 182 in. Sed bibendum bibendum dui, egestas dignissim orci tincidunt porta. Sed nec aliquet leo, eu posuere massa. \n"; 183 string s; 184 VFile file = VFile(s); 185 file.rawWrite(a); 186 file.rawWrite(b); 187 assert(file.size == a.length + b.length); 188 char[] c; 189 c.length = a.length + b.length; 190 file.seek(0); 191 file.rawRead(c); 192 assert(c == a ~ b); 193 file.seek(0); 194 c.length = 5; 195 file.rawRead(c); 196 assert(c == "Lorem"); 197 } 198 unittest{ 199 immutable string a = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n" ~ 200 "Suspendisse ultrices quis erat nec elementum.\n" ~ 201 "Pellentesque sit amet tellus ac mauris elementum bibendum sed in eros.\n"; 202 string s; 203 VFile file = VFile(s); 204 file.rawWrite(a); 205 auto l = file.byLine(); 206 assert(l.front() == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", l.front()); 207 l.popFront(); 208 assert(l.front() == "Suspendisse ultrices quis erat nec elementum.", l.front()); 209 l.popFront(); 210 assert(l.front() == "Pellentesque sit amet tellus ac mauris elementum bibendum sed in eros.", l.front()); 211 l.popFront(); 212 assert(l.empty(), l.front()); 213 214 l = file.byLine(Yes.keepTerm); 215 assert(l.front() == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n", l.front()); 216 }