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 }