1 module support; // dockerfile_parser;
2 import std.traits: ReturnType;
3 import std.experimental.allocator : make, makeArray;
4 import std.experimental.allocator.building_blocks.region: Region;
5 import std.experimental.allocator.building_blocks.allocator_list: AllocatorList;
6 import std.experimental.allocator.gc_allocator : GCAllocator;
7 import std.experimental.allocator.mallocator : Mallocator;
8 import std.experimental.allocator.showcase : mmapRegionList;
9 import std.experimental.allocator.typed: TypedAllocator, AllocFlag;
10 import std.experimental.allocator.building_blocks.scoped_allocator : ScopedAllocator;
11 import std.algorithm : max;
12 //@nogc:
13 
14 //ScopedAllocator!Mallocator regionalAllocator;
15 alias MyTypedAllocator = AllocatorList!((size_t n) => Region!Mallocator(max(n,1024*1024)));
16 
17 // MyTypedAllocator regionalAllocator = MyTypedAllocator();
18 Region!Mallocator regionalAllocator;
19 
20 shared static this()
21 {
22 	regionalAllocator = Region!Mallocator(1024*1024*40); // Mallocator(1024*1024*100);
23 }
24 
25 struct Result(T)
26 {
27 	const(char)[] errorMessage;
28 	T* result;
29 }
30 
31 struct Command
32 {
33     string command;
34     string subCommand;
35     bool json;
36     string original;
37     int startLine;
38     int endLine;
39     string[] flags;
40     string[] values;
41 /+
42 	string toString()
43 	{
44 		import std.conv;
45 		return this.to!string;
46 	}
47 +/
48 	Command deepCopy()
49 	{
50 		import std.algorithm : map;
51 		import std.array : array;
52 		Command ret;
53 		ret.command = this.command.idup;
54 		ret.subCommand = this.subCommand.idup;
55 		ret.json =  this.json;
56 		ret.original = this.original.idup;
57 		ret.startLine = this.startLine;
58 		ret.endLine = this.endLine;
59 		ret.flags = this.flags.map!(flag => flag.idup).array;
60 		ret.values = this.values.map!(value => value.idup).array;
61 		return ret;
62 	}
63 
64 }
65 /+
66 auto makeRegionalAllocator()
67 {
68 	//alias RawAllocator = Region!MmapAllocator(1024);
69 	//regionalTypedAllocator!RawAllocator regionalAllocator;
70 }
71 +/
72 export extern(C)
73 void* return_success(void* ret)
74 {
75 	import std.experimental.allocator: make;
76 	auto result = regionalAllocator.make!(Result!(char))(null,cast(char*)ret);
77 	return cast(void*) result;
78 }
79 
80 T[] allocatorDup(T)(T[] values)
81 {
82 	import std.traits: Unqual;
83 	auto ret = regionalAllocator.makeArray!(Unqual!T)(values.length);
84 	ret[0..values.length] = cast(Unqual!T[]) values;
85 	return ret;
86 }
87 
88 immutable(T)[] allocatorIdup(T)(T[] values)
89 {
90 	import std.traits : Unqual;
91 	auto ret = regionalAllocator.makeArray!(Unqual!T)(values.length);
92 	ret[0..values.length] = values;
93 	return cast(immutable(T)[]) ret;
94 }
95 
96 const(char)* toStringz(const(char)[] s)
97 {
98 	import std.traits: Unqual;
99 	if (s.length == 0)
100 		return null;
101 	bool needsTerminator = (s.length == 0 || (s[$-1]!='\0'));
102 	auto requiredLength = s.length + (needsTerminator ? 1 : 0);
103 	auto p = regionalAllocator.makeArray!char(requiredLength);
104 	assert(p.length == requiredLength);
105 	p[0 .. s.length] = s[0.. $];
106 	if(needsTerminator)
107 		p[s.length] = '\0';
108 	return cast(const(char)*) p.ptr;
109 }
110 
111 string fromStringz(const(char)* p)
112 {
113 	if (p is null)
114 		return null;
115 	size_t maxLen = 16384;
116 	auto s= regionalAllocator.makeArray!char(maxLen);
117 	assert(s.length == maxLen);
118 	size_t i = 0;
119 	while(p[i] != '\0')
120 	{
121 		s[i] = p[i];
122 		++i;
123 		if (i == maxLen)
124 		{
125 			maxLen *= 2;
126 			auto s2 = regionalAllocator.makeArray!char(maxLen);
127 			assert(s2.length == maxLen);
128 			s2[0..s.length] = s[];
129 			regionalAllocator.deallocate(s);
130 			s=s2;
131 		}
132 	}
133 	s = s[0..i];
134 	return cast(string) s;
135 }
136 
137 
138 export extern(C)
139 void* raise(const(char)* errorMessage)
140 {
141 	import std.experimental.allocator: make;
142 	import core.stdc.stdlib : free;
143 	bool shouldFree = true;
144 	auto niceErrorMessage = (errorMessage is null) ? "UNKNOWN GOLANG ERROR" : errorMessage.fromStringz;
145 	auto result = regionalAllocator.make!(Result!(char));
146 	result.errorMessage = niceErrorMessage;
147 	if (errorMessage !is null)
148 		free(cast(void*)errorMessage);
149 	return cast(void*) result;
150 }
151 
152 
153 export extern(C)
154 void* command(char* cmd, char* sub_cmd, int json, char* original, int start_line, int end_line, void* flags, void* value)
155 {
156 	import core.stdc.stdlib : free;
157 	import std.experimental.allocator: make;
158 
159 	auto ret = regionalAllocator.make!Command;
160 	ret.command = cmd.fromStringz.allocatorIdup;
161 	ret.subCommand = sub_cmd.fromStringz.allocatorIdup;
162 	ret.json = (json != 0);
163 	ret.original = original.fromStringz.allocatorIdup;
164 	ret.startLine = start_line;
165 	ret.endLine = end_line;
166 	auto flagsV = cast(StringSlice*) flags;
167 	ret.flags = (*flagsV).values.allocatorDup;
168 	auto valuesV = cast(StringSlice*) value;
169 	ret.values = (*valuesV).values.allocatorDup;
170 	free(cmd);
171 	free(sub_cmd);
172 	free(original);
173 	return cast(void*) regionalAllocator.make!(Result!Command)(null,ret);
174 }
175 
176 extern(C) void* parse_string(const(char)* s);
177 extern(C) void* parse_file(const(char)* filename);
178 extern(C) void* all_commands();
179 
180 alias CommandResult = Result!Command;
181 alias CommandSlice = Slice!Command;
182 alias CommandSliceResult = Result!(Slice!Command);
183 alias StringSlice = Slice!string;
184 alias StringSliceResult = Result!(Slice!string);
185 
186 Command[] parseString(string s)
187 {
188 	import std.algorithm : map;
189 	import std.array : array;
190 	import std.exception : enforce;
191 	scope(exit)
192 	{
193 		regionalAllocator.deallocateAll();
194 	}
195 	auto p = cast(CommandSliceResult*) parse_string(s.toStringz);
196 	enforce(p !is null, "parse_string failed");
197 	enforce(p.errorMessage.length ==0, p.errorMessage);
198 	auto result = *((*p).result);
199 	return result.values.map!(command => command.deepCopy).array;
200 }
201 
202 Command[] parseFile(string filename)
203 {
204 	import std.algorithm : map;
205 	import std.array : array;
206 	import std.exception : enforce;
207 	scope(exit)
208 	{
209 		regionalAllocator.deallocateAll();
210 	}
211 	auto p = cast(CommandSliceResult*) parse_file(filename.toStringz);
212 	enforce(p !is null, "parse_file failed");
213 	enforce(p.errorMessage.length ==0, p.errorMessage);
214 	auto result = *((*p).result);
215 	return result.values.map!(command => command.deepCopy).array;
216 }
217 
218 string[] allCommands()
219 {
220 	import std.algorithm : map;
221 	import std.array : array;
222 	import std.exception : enforce;
223 	//regionalAllocator = TypedAllocator!(ScopedAllocator!(Mallocator))(Mallocator.instance);
224 	scope(exit)
225 	{
226 		regionalAllocator.deallocateAll();
227 	}
228 	auto p = cast(StringSliceResult*) all_commands();
229 	enforce(p !is null, "all_commands failed");
230 	enforce(p.errorMessage.length ==0, p.errorMessage);
231 	auto result = *p.result;
232 	return result.values.map!(value => value.idup).array;
233 }
234 
235 
236 struct Slice(T)
237 {
238 	T[] values;
239 }
240 
241 export extern(C)
242 void* stringSlice(size_t length)
243 {
244 	import std.experimental.allocator : make;
245 	assert(length >= 0);
246 	auto ret = regionalAllocator.make!(Slice!string);
247 	assert(ret !is null);
248 	auto buf = regionalAllocator.makeArray!string(length,"");
249 	assert(buf.length == length);
250 	ret.values = buf;
251 	assert(ret.values.length == length);
252 	return cast(void*) ret;
253 }
254 
255 export extern(C)
256 void* commandSlice(size_t length)
257 {
258 	import std.experimental.allocator : make;
259 	auto ret = regionalAllocator.make!(Slice!Command);
260 	assert(ret !is null);
261 	ret.values = regionalAllocator.makeArray!Command(length,Command.init);
262 	return cast(void*) ret;
263 }
264 
265 export extern(C)
266 void setStringElement(void* sliceV, size_t i, const(char)* s)
267 {
268 	import core.stdc.stdlib : free;
269 	import std.exception : enforce;
270 
271 	assert(s !is null);
272 	auto slice = cast(StringSlice*) sliceV;
273 	assert(slice !is null);
274 	assert(i >=0);
275 	assert(slice.values.length > i);
276 	slice.values[i] = fromStringz(s);
277 	free(cast(void*)s);
278 }
279 
280 export extern(C)
281 void setCommandElement(void* sliceV, size_t i, void* p)
282 {
283 	import std.exception : enforce;
284 	assert(p !is null, "setCommandElement called with null");
285 	auto slice = cast(CommandSlice*) sliceV;
286 	enforce(slice.values.length > i);
287 	auto pTyped = *cast(CommandResult*) p;
288 	slice.values[i] = *pTyped.result;
289 }
290