1 module vayne.serializer;
2 
3 
4 import std.algorithm;
5 import std.range;
6 import std.traits;
7 
8 import vayne.compiler;
9 import vayne.op;
10 import vayne.source.source;
11 
12 
13 const(ubyte)[] serialize(CompiledCode code) {
14 	ByteStreamWriter writer;
15 
16 	void put(T)(T x) {
17 		writer.put!T(x);
18 	}
19 
20 	put!uint(MagicString);
21 	put!uint(FormatVersion);
22 	put!uint(OpCodesVersion);
23 	put!uint(code.registerCount);
24 
25 	if (code.constants.length) {
26 		put!ubyte(Chunks.Constants);
27 		put!uint(cast(uint)code.constants.length);
28 		foreach (i, k; code.constants) {
29 			put!ubyte(k.type);
30 			put!string(k.value);
31 		}
32 	}
33 
34 	if (code.instrs.length) {
35 		put!ubyte(Chunks.Instructions);
36 		put!uint(cast(uint)code.instrs.length);
37 		foreach (instr; code.instrs) {
38 			put!ulong(instr.code0);
39 			put!ulong(instr.code1);
40 		}
41 	}
42 
43 	if (code.locs.length) {
44 		put!ubyte(Chunks.InstructionSourceLocations);
45 		put!uint(cast(uint)code.locs.length);
46 		foreach (loc; code.locs) {
47 			put!uint(loc.id);
48 			put!uint(loc.line);
49 			//writer.put!uint(loc.column);
50 		}
51 	}
52 
53 	if (code.sources.length) {
54 		put!ubyte(Chunks.SourceNames);
55 		put!uint(cast(uint)code.sources.length);
56 		foreach (source; code.sources)
57 			put!string(source);
58 	}
59 
60 	if (false && code.dependencies.length) {
61 		put!ubyte(Chunks.Dependencies);
62 		put!uint(cast(uint)code.dependencies.length);
63 		foreach (dependency; code.dependencies)
64 			put!string(dependency);
65 	}
66 
67 	return writer.data;
68 }
69 
70 
71 CompiledCode unserialize(const(ubyte)[] bytes) {
72 	auto reader = ByteStreamReader(bytes);
73 
74 	auto take(T)() {
75 		return reader.take!T;
76 	}
77 
78 	CompiledCode result;
79 
80 	auto magic = take!uint;
81 	auto format = take!uint;
82 	auto opcodes = take!uint;
83 	result.registerCount = take!uint;
84 
85 	assert(magic == MagicString);
86 	assert(format == FormatVersion);
87 	assert(opcodes == OpCodesVersion);
88 
89 	while (!reader.eos) {
90 		auto chunk = take!ubyte;
91 		final switch (cast(Chunks)chunk) with (Chunks) {
92 		case Instructions:
93 			assert(result.instrs.empty);
94 			auto count = take!uint;
95 			result.instrs.reserve(count);
96 			foreach (i; 0..count) {
97 				auto code0 = take!ulong;
98 				auto code1 = take!ulong;
99 				result.instrs ~= Instr(code0, code1);
100 			}
101 			break;
102 		case Constants:
103 			assert(result.constants.empty);
104 			auto count = take!uint;
105 			result.constants.reserve(count);
106 			foreach (i; 0..count) {
107 				auto type = cast(ConstantType)take!ubyte;
108 				auto value = take!string;
109 				result.constants ~= CompiledCode.Constant(type, value);
110 			}
111 			break;
112 		case InstructionSourceLocations:
113 			assert(result.locs.empty);
114 			auto count = take!uint;
115 			result.locs.reserve(count);
116 			foreach (i; 0..count) {
117 				auto source = take!uint;
118 				auto line = take!uint;
119 				result.locs ~= SourceLoc(source, line, 0);
120 			}
121 			break;
122 		case SourceNames:
123 			assert(result.sources.empty);
124 			auto count = take!uint;
125 			result.sources.reserve(count);
126 			foreach (i; 0..count)
127 				result.sources ~= take!string;
128 			break;
129 		case Dependencies:
130 			assert(result.dependencies.empty);
131 			auto count = take!uint;
132 			result.dependencies.reserve(count);
133 			foreach (i; 0..count)
134 				result.dependencies ~= take!string;
135 			break;
136 		}
137 	}
138 
139 	return result;
140 }
141 
142 
143 private:
144 
145 enum Chunks : ubyte {
146 	Instructions = 0,
147 	Constants,
148 	InstructionSourceLocations,
149 	SourceNames,
150 	Dependencies,
151 }
152 
153 
154 enum : uint { MagicString = 0xBB9D1BC5 };
155 enum : uint { FormatVersion = 1 };
156 enum : uint { ExtendSize = 32 * 1024 }
157 
158 
159 struct ByteStreamWriter {
160 	void put(T)(T x) if (isIntegral!T) {
161 		if ((offset_ + T.sizeof) > stream_.length)
162 			stream_.length += ExtendSize;
163 		// TODO: always write as little-endian
164 		*cast(T*)(stream_.ptr + offset_) = x;
165 		offset_ += T.sizeof;
166 	}
167 
168 	void put(T)(T x) if (is(Unqual!T == string)) {
169 		auto req = offset_ + uint.sizeof + x.length;
170 		if (req > stream_.length) {
171 			auto len = stream_.length + ExtendSize;
172 			while (req >= len)
173 				len += ExtendSize;
174 			stream_.length = len;
175 		}
176 
177 		put!uint(cast(uint)x.length);
178 
179 		(cast(char*)stream_.ptr)[offset_..offset_ + x.length] = x[];
180 		offset_ += x.length;
181 	}
182 
183 	@property size_t length() const {
184 		return offset_;
185 	}
186 
187 	@property const(ubyte)[] data() const {
188 		return stream_[0..offset_];
189 	}
190 
191 	private ubyte[] stream_;
192 	private size_t offset_;
193 }
194 
195 
196 struct ByteStreamReader {
197 	this(const(ubyte)[] stream) {
198 		stream_ = stream;
199 	}
200 
201 	@property size_t length() const {
202 		return stream_.length;
203 	}
204 
205 	@property bool eos() const {
206 		return offset_ == stream_.length;
207 	}
208 
209 	T take(T)() if (isIntegral!T) {
210 		if (offset_ + T.sizeof > stream_.length)
211 			throw new Exception("reading past end of stream");
212 		// TODO: always read as little-endian
213 		scope(exit) offset_ += T.sizeof;
214 		return *cast(T*)(stream_.ptr + offset_);
215 	}
216 
217 	T take(T)() if (is(Unqual!T == string)) {
218 		if (offset_ + uint.sizeof > stream_.length)
219 			throw new Exception("reading past end of stream");
220 
221 		auto length = take!uint;
222 		auto result = ((cast(char*)stream_.ptr)[offset_..offset_ + length]).idup;
223 		offset_ += length;
224 		return result;
225 	}
226 
227 	private const(ubyte)[] stream_;
228 	private size_t offset_;
229 }