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 }