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