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