1 module tagion.hibon.HiBONFile; 2 import file = std.file; 3 import tagion.hibon.Document; 4 import tagion.hibon.HiBON; 5 import tagion.hibon.HiBONRecord; 6 import LEB128 = tagion.utils.LEB128; 7 import std.exception : assumeUnique; 8 import std.stdio : File; 9 import std.typecons : No; 10 11 /++ 12 Serialize the hibon and writes it a file 13 Params: 14 filename = is the name of the file 15 hibon = is the HiBON object 16 +/ 17 @safe void fwrite(const(char[]) filename, const HiBON hibon) { 18 file.write(filename, hibon.serialize); 19 } 20 21 /++ 22 Serialize the hibon and writes it a file 23 Params: 24 filename = is the name of the file 25 hibon = is the HiBON object 26 +/ 27 @safe void fwrite(const(char[]) filename, const Document doc) { 28 file.write(filename, doc.serialize); 29 } 30 31 @safe void fwrite(T)(const(char[]) filename, const T rec) if (isHiBONRecord!T) { 32 fwrite(filename, rec.toDoc); 33 } 34 35 /++ 36 Reads a HiBON document from a file 37 Params: 38 filename = is the name of the file 39 Returns: 40 The Document read from the file 41 +/ 42 @trusted Document fread(const(char[]) filename, const size_t max_size = 0) { 43 import std.format; 44 import tagion.hibon.HiBONException : check; 45 46 immutable data = assumeUnique(cast(ubyte[]) file.read(filename)); 47 const doc = Document(data); 48 const error_code = doc.valid(null, No.Reserved); 49 check(error_code is Document.Element.ErrorCode.NONE, format("HiBON Document format %s failed in %s", error_code, filename)); 50 return doc; 51 } 52 53 @safe 54 T fread(T, Args...)(const(char[]) filename, Args args) if (isHiBONRecord!T) { 55 const doc = filename.fread; 56 return T(doc, args); 57 } 58 59 @safe 60 Document fread(ref File file, const size_t max_size = 0) { 61 import LEB128 = tagion.utils.LEB128; 62 import std.format; 63 import tagion.hibon.HiBONException : check; 64 65 enum LEB128_SIZE = LEB128.DataSize!size_t; 66 ubyte[LEB128_SIZE] _buf; 67 ubyte[] buf = _buf; 68 const doc_start = file.tell; 69 file.rawRead(buf); 70 const doc_size = LEB128.read!size_t(buf); 71 const data_size = doc_size.size + doc_size.value; 72 check(max_size == 0 || (data_size <= max_size), format("Document size exceeds the max limit of %d", max_size)); 73 auto data = new ubyte[doc_size.size + doc_size.value]; 74 file.seek(doc_start); 75 file.rawRead(data); 76 return (() @trusted => Document(assumeUnique(data)))(); 77 } 78 79 @safe 80 T fread(T)(ref File file) if (isHiBONRecord!T) { 81 const doc = file.fread; 82 return T(doc); 83 } 84 85 @safe 86 void fwrite(ref File file, const Document doc) { 87 file.rawWrite(doc.serialize); 88 } 89 90 @safe 91 void fwrite(T)(ref File file, const T rec) if (isHiBONRecord!T) { 92 fwrite(file, rec.toDoc); 93 } 94 95 @safe 96 unittest { 97 import std.file : deleteme, remove; 98 99 static struct Simple { 100 int x; 101 mixin HiBONRecord!(q{ 102 this(int _x) { 103 x = _x; 104 } 105 }); 106 } 107 108 auto fout = File(deleteme, "w"); 109 scope (exit) { 110 deleteme.remove; 111 } 112 113 const expected_s = Simple(42); 114 115 fout.fwrite(expected_s); 116 fout.close; 117 { 118 fout = File(deleteme, "r"); 119 const result = fout.fread!Simple; 120 assert(expected_s == result); 121 } 122 } 123 124 struct HiBONRange { 125 File file; 126 this(File file) { 127 this.file = file; 128 popFront; 129 } 130 131 Document doc; 132 private ubyte[] buf; 133 @property 134 bool empty() pure const { 135 return file.eof; 136 } 137 138 @property 139 Document front() pure nothrow const @nogc { 140 return doc; 141 } 142 143 void popFront() { 144 if (!empty) { 145 buf.length = LEB128.DataSize!size_t; 146 const read_size = file.rawRead(buf).length; 147 const doc_size = LEB128.decode!size_t(buf); 148 buf.length = doc_size.size + doc_size.value; 149 file.rawRead(buf[read_size .. $]); 150 doc = Document(buf.idup); 151 } 152 } 153 }