-
Notifications
You must be signed in to change notification settings - Fork 92
/
Copy pathautodata.d
257 lines (229 loc) · 7.97 KB
/
autodata.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
* Copyright 2010, 2011, 2012, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module autodata;
import murmurhash2a;
import std.traits;
public import std.conv;
string addAutoField(string name, bool reverseSort = false)
{
return `mixin(typeof(handler).getMixin!(typeof(` ~ name ~ `), "` ~ name ~ `", ` ~ (reverseSort ? "true" : "false") ~`));`;
}
template AutoCompare()
{
static if (is(typeof(this)==class))
{
alias typeof(this) _AutoDataTypeReference;
alias Object _AutoDataOtherTypeReference;
override hash_t toHash() const { try { return _AutoDataHash(); } catch(object.Exception e) { assert(0, e.msg); } }
override bool opEquals(Object o) const { return _AutoDataEquals(o); }
override int opCmp(Object o) const { return _AutoDataCmp(o); }
}
else // struct
{
alias const(typeof(this)*) _AutoDataTypeReference;
alias const(typeof(this)*) _AutoDataOtherTypeReference;
hash_t toHash() const { return _AutoDataHash(); }
bool opEquals(ref const typeof(this) s) const { return _AutoDataEquals(&s); }
int opCmp(ref const typeof(this) s) const { return _AutoDataCmp(&s); }
}
@trusted private hash_t _AutoDataHash() const
{
HashDataHandler handler;
handler.hasher.Begin();
processData!(void, q{}, q{})(handler);
return handler.hasher.End();
}
private bool _AutoDataEquals(_AutoDataOtherTypeReference other) const
{
auto handler = EqualsDataHandler!_AutoDataTypeReference(cast(_AutoDataTypeReference) other);
if (handler.other is null)
return false;
return processData!(bool, q{auto _AutoDataOther = handler.other;}, q{return true;})(handler);
}
private int _AutoDataCmp(_AutoDataOtherTypeReference other) const
{
auto handler = CmpDataHandler!_AutoDataTypeReference(cast(_AutoDataTypeReference) other);
if (handler.other is null)
return false;
return processData!(int, q{auto _AutoDataOther = handler.other;}, "return 0;")(handler);
}
}
template AutoToString()
{
static if (is(typeof(this)==class))
override string toString() const { return _AutoDataToString(); }
else // struct
string toString() const { return _AutoDataToString(); }
string _AutoDataToString() const
{
ToStringDataHandler handler;
return processData!(string, "string _AutoDataResult;", "return _AutoDataResult;")(handler);
}
}
template ProcessAllData()
{
R processData(R, string prolog, string epilog, H)(ref H handler) const
{
mixin(prolog);
foreach (i, T; this.tupleof)
mixin(addAutoField(this.tupleof[i].stringof[5..$])); // remove "this."
mixin(epilog);
}
}
/// For data handlers that only need to look at the raw data (currently only HashDataHandler)
template RawDataHandlerWrapper()
{
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = getMixinRecursive!(T, "this." ~ name, "");
}
template getMixinRecursive(T, string name, string loopDepth)
{
static if (is(T U : U[]))
enum getMixinRecursive =
"{ bool _AutoDataNullTest = " ~ name ~ " is null; " ~ getRawMixin!("&_AutoDataNullTest", "bool.sizeof") ~ "}" ~
(!hasAliasing!(U) ?
getRawMixin!(name ~ ".ptr", name ~ ".length")
:
"foreach (ref _AutoDataArrayItem" ~ loopDepth ~ "; " ~ name ~ ") {" ~ getMixinRecursive!(U, "_AutoDataArrayItem" ~ loopDepth, loopDepth~"Item") ~ "}"
);
else
static if (!hasAliasing!(T))
enum getMixinRecursive = getRawMixin!("&" ~ name, name ~ ".sizeof");
else
static if (is(T==struct))
enum getMixinRecursive = name ~ ".processData!(void, ``, ``)(handler);";
else
static if (is(T==class))
enum getMixinRecursive = "if ("~name~" !is null) " ~ name ~ ".processData!(void, ``, ``)(handler);";
else
static assert(0, "Don't know how to process type: " ~ T.stringof);
}
}
struct HashDataHandler
{
mixin RawDataHandlerWrapper;
MurmurHash2A hasher;
template getRawMixin(string ptr, string len)
{
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", to!int(" ~ len ~ "));";
}
}
struct EqualsDataHandler(O)
{
O other;
template nullCheck(T, string name)
{
static if (is(typeof(T.init is null)))
enum nullCheck = "if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
else
enum nullCheck = "";
}
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = nullCheck!(T, name) ~ "if (this." ~ name ~ " != _AutoDataOther." ~ name ~ ") return false;";
}
}
struct CmpDataHandler(O)
{
O other;
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = getMixinComposite!(T, name, reverseSort).code;
}
template nullCheck(T, string name, string reverseStr)
{
static if (is(typeof(T.init is null)))
enum nullCheck = "
if (this."~name~" is null && _AutoDataOther."~name~" is null)
{ /* skip */ }
else
if (this."~name~" is null && _AutoDataOther."~name~" !is null)
return " ~ reverseStr ~ "(-1);
else
if (this."~name~" !is null && _AutoDataOther."~name~" is null)
return " ~ reverseStr ~ "( 1);
else";
else
enum nullCheck = "";
}
template getMixinComposite(T, string name, bool reverseSort)
{
enum reverseStr = reverseSort ? "-" : "";
static if (is(T U : U[]))
enum arrCode = "{ int _AutoDataCmp = cast(int)(this." ~ name ~ " !is null) - cast(int)(_AutoDataOther." ~ name ~ " !is null); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
enum arrCode = "";
static if (is(T == string) && is(std.string.cmp))
enum dataCode = "{ int _AutoDataCmp = std.string.cmp(this." ~ name ~ ", _AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
static if (is(T == int))
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
else
static if (is(typeof(T.opCmp)))
enum dataCode = nullCheck!(T, name, reverseStr)
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(cast()_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
enum code = arrCode ~ dataCode;
}
}
struct ToStringDataHandler
{
template getMixinSingle(T, string name)
{
/*
enum getMixinSingle = "
static if (is(typeof(_AutoDataResult ~= " ~ name ~ ".toString())))
_AutoDataResult ~= " ~ name ~ ".toString();
else
_AutoDataResult ~= to!string(" ~ name ~ ");
";
*/
static if (is(typeof(T.init is null)))
enum getMixinSingle = "_AutoDataResult ~= " ~ name ~ " ? to!string(" ~ name ~ ") : `null`;";
else
enum getMixinSingle = "_AutoDataResult ~= to!string(" ~ name ~ ");";
}
template getMixinBody(T, string name)
{
// TODO: arrays of arrays
static if (is(T U : U[]) && !is(T : const(char)[]))
{
enum getMixinBody = "
_AutoDataResult ~= ` [ `;
foreach (_AutoDataArrayIndex, _AutoDataArrayItem; " ~ name ~ ")
{
if (_AutoDataArrayIndex) _AutoDataResult ~= ` , `;
" ~ getMixinSingle!(U, "_AutoDataArrayItem") ~ "
}
_AutoDataResult ~= ` ] `;
";
}
else
enum getMixinBody = getMixinSingle!(T, name);
}
template getMixin(T, string name, bool reverseSort)
{
enum getMixin =
"_AutoDataResult ~= `" ~ name ~ " = `;" ~
getMixinBody!(T, name) ~
"_AutoDataResult ~= ` `;";
}
}