1 module dhdf5.dataspec;
2 
3 import std.traits : isStaticArray, isDynamicArray;
4 import std..string: toStringz;
5 
6 private
7 {
8 	import std.typetuple : TypeTuple, staticIndexOf;
9 	import std.traits : Unqual;
10 	import std.range : isInputRange;
11 
12 	// bool is special type and is processed like enum
13 	alias AllowedTypes = TypeTuple!(float, int, double, char, uint, long, ulong, short, ubyte, ushort);
14 	enum string[] VectorHdf5Types =
15 	[
16 		"H5T_NATIVE_FLOAT",
17 		"H5T_NATIVE_INT",
18 		"H5T_NATIVE_DOUBLE",
19 		"H5T_NATIVE_B8",
20 		"H5T_NATIVE_B32",
21 		"H5T_NATIVE_LONG",
22 		"H5T_NATIVE_B64",
23 		"H5T_NATIVE_SHORT",
24 		"H5T_NATIVE_UCHAR",
25 		"H5T_NATIVE_USHORT",
26 	];
27 
28 	template typeToHdf5Type(T)
29 	{
30 		alias U = Unqual!T;
31 		enum index = staticIndexOf!(U, AllowedTypes);
32 		static if (index == -1)
33 		{
34 			static assert(false, "Could not use " ~ T.stringof ~ ", there is no corresponding hdf5 data type");
35 		}
36 		else
37 		{
38 			enum typeToHdf5Type = VectorHdf5Types[index];
39 		}
40 	}
41 }
42 
43 auto countDimensions(T)() if(isStaticArray!T)
44 {
45 	Unqual!T t;
46 
47 	size_t[] dim;
48 
49 	auto countDimensionsImpl(R)()
50 	{
51 		Unqual!R r;
52 		dim ~= R.length;
53 		static if(isStaticArray!(typeof(r[0])))
54 		{
55 			return countDimensionsImpl!(typeof(r[0]));
56 		}
57 		else
58 		{
59 			return dim;
60 		}
61 	}
62 
63 	return countDimensionsImpl!T;
64 }
65 
66 auto countDimensions(T)() if(isInputRange!T)
67 {
68 	import hdf5.hdf5 : H5S_UNLIMITED;
69 
70 	size_t[] dim;
71 
72 	auto countDimensionsImpl(R)()
73 	{
74 		R r;
75 
76 		dim ~= H5S_UNLIMITED;
77 		static if(isDynamicArray!(typeof(r[0])))
78 		{
79 			return countDimensionsImpl!(typeof(r[0]))(r[0]);
80 		}
81 		else
82 		{
83 			return dim;
84 		}
85 	}
86 
87 	return countDimensionsImpl!T;
88 }
89 
90 auto countDimensions(T)(T t) if(isDynamicArray!T)
91 {
92 	size_t[] dim;
93 
94 	auto countDimensionsImpl(R)(R r)
95 	{
96 		dim ~= r.length;
97 		static if(isDynamicArray!(typeof(r[0])))
98 		{
99 			return countDimensionsImpl!(typeof(r[0]))(r[0]);
100 		}
101 		else
102 		{
103 			return dim;
104 		}
105 	}
106 
107 	return countDimensionsImpl(t);
108 }
109 
110 /// Used as UDA to disable a field of struct of class
111 struct HDF5disable
112 {
113 
114 }
115 
116 struct DataAttribute
117 {
118 	import hdf5.hdf5 : hid_t;
119 
120 	hid_t type;
121 	size_t offset;
122 	string varName;
123 }
124 
125 struct DataSpecification(Data)
126 {
127 	import std.traits : isArray, isScalarType, ForeachType, FieldTypeTuple,
128 		FieldNameTuple;
129 	import std.range : ElementType;
130 	import std.typecons : RefCounted;
131 	import hdf5.hdf5;
132 
133 	alias DataType = Data;
134 
135 	private static makeImpl(S)() if(is(S == struct))
136 	{
137 		DataAttribute[] attributes;
138 
139 		template isDisabled(TP...)
140 		{
141 			auto isDisabledImpl()
142 			{
143 				bool disabled = false;
144 				foreach(t; TP)
145 				{
146 					static if(t == "HDF5disabled")
147 					{
148 						disabled = true;
149 						break;
150 					}
151 				}
152 
153 				return disabled;
154 			}
155 
156 			enum isDisabled = isDisabledImpl();
157 		}
158 
159 		// Создаем атрибуты
160 		hid_t createStruct(D)(ref DataAttribute[] attributes) if(is(D == struct))
161 		{
162 			import std.traits : OriginalType;
163 
164 			alias TT = FieldTypeTuple!D;
165 
166 			auto tid = H5Tcreate (H5T_class_t.H5T_COMPOUND, D.sizeof);
167 
168 			foreach (member; FieldNameTuple!D)
169 			{
170 				enum fullName = "D." ~ member;
171 				enum hdf5Name = (Unqual!D).stringof ~ "." ~ member;
172 
173 				mixin("alias T = typeof(" ~ fullName ~ ");");
174 
175 				static if (staticIndexOf!(T, TT) != -1)
176 				{
177 					mixin("alias A = " ~ fullName ~";");
178 					alias TP = TypeTuple!(__traits(getAttributes, A));
179 					enum disabled = isDisabled!TP; // check if field is disabled using UDA
180 
181 					static if(disabled)
182 					{
183 
184 					}
185 					else
186 					static if(is(T == bool))
187 					{
188 						// TODO Don't know wouldn't it be better if bool enum were created
189 						// once per library in static this()?
190 
191 						// Create enum type
192 						hid_t hdf5Type = H5Tenum_create (H5T_NATIVE_INT);
193 
194 						auto val = 0;
195 						auto status = H5Tenum_insert (hdf5Type, "false", &val);
196 						assert(status >= 0);
197 
198 						val = 1;
199 						status = H5Tenum_insert (hdf5Type, "true", &val);
200 						assert(status >= 0);
201 					}
202 					else
203 					static if(is(T == enum))
204 					{
205 						static assert(is(T : int), "hdf5 supports only enumeration based on integer type.");
206 						// Create enum type
207 						alias BaseEnumType = OriginalType!T;
208 						mixin("hid_t hdf5Type = H5Tenum_create (" ~ typeToHdf5Type!BaseEnumType ~ ");");
209 
210 						foreach (size_t cnt, enumMember; __traits(allMembers, T))
211 						{
212 							auto val = cnt;
213 							auto status = H5Tenum_insert (hdf5Type, (T.stringof ~ "." ~ enumMember).toStringz, &val);
214 							assert(status >= 0);
215 						}
216 					}
217 					else static if(isStaticArray!T)
218 					{
219 						alias ElemType = ForeachType!T;
220 						static if(is(ElemType == struct))
221 						{
222 							import std.conv: castFrom;
223 
224 							DataAttribute[] da;
225 							auto elemType = createStruct!ElemType(da);
226 							insertAttributes(elemType, da);
227 							//hid_t hdf5Type = H5Tvlen_create (elemType);
228 							alias dim = countDimensions!T;
229 							//mixin("hid_t hdf5Type = H5Tarray_create2 (" ~ typeToHdf5Type!ElemType ~ ", " ~ dim.length.text ~ ", dim.ptr);");
230 							hid_t hdf5Type = H5Tarray_create2 (elemType, castFrom!size_t.to!uint(dim.length), dim.ptr);
231 						}
232 						else
233 						{
234 							import std.conv: text;
235 
236 							alias dim = countDimensions!T;
237 							mixin("hid_t hdf5Type = H5Tarray_create2 (" ~ typeToHdf5Type!ElemType ~ ", " ~ dim.length.text ~ ", dim.ptr);");
238 						}
239 					}
240 					else static if(isDynamicArray!T)
241 					{
242 						alias ElemType = ElementType!T;
243 						static if(is(ElemType == struct))
244 						{
245 							DataAttribute[] da;
246 							auto elemType = createStruct!ElemType(da);
247 							insertAttributes(elemType, da);
248 							hid_t hdf5Type = H5Tvlen_create (elemType);
249 						}
250 						else
251 							mixin("hid_t hdf5Type = H5Tvlen_create (" ~ typeToHdf5Type!(ForeachType!T) ~ ");");
252 					}
253 					else static if(is(T == struct))
254 					{
255 						DataAttribute[] da;
256 						auto hdf5Type = createStruct!T(da);
257 
258 						insertAttributes(hdf5Type, da);
259 					}
260 					else
261 					{
262 						mixin("hid_t hdf5Type = " ~ typeToHdf5Type!T ~ ";");
263 					}
264 
265 					static if(!disabled)
266 					{
267 						// Add the attribute
268 						mixin("string varName = \"" ~ hdf5Name ~ "\";");
269 						mixin("enum offset = D." ~ member ~ ".offsetof;");
270 						attributes ~= DataAttribute(hdf5Type, offset, varName);
271 					}
272 				}
273 			}
274 
275 			return tid;
276 		}
277 
278 		// Insert attributes of the structure into the datatype
279 		auto insertAttributes(hid_t hdf5Type, DataAttribute[] da)
280 		{
281 			import hdf5.hdf5 : H5Tinsert;
282 
283 			foreach(attr; da)
284 			{
285 				auto status = H5Tinsert(hdf5Type, attr.varName.toStringz, attr.offset, attr.type);
286 				assert(status >= 0);
287 			}
288 		}
289 
290 		auto tid = createStruct!S(attributes);
291 
292 		insertAttributes(tid, attributes);
293 
294 		return RefCounted!(DataSpecification!S)(tid, attributes);
295 	}
296 
297 	private static makeImpl(D)() if(isScalarType!D)
298 	{
299 		mixin("hid_t tid = " ~ typeToHdf5Type!D ~ ";");
300 
301 		return RefCounted!(DataSpecification!D)(tid, (DataAttribute[]).init);
302 	}
303 
304 	private static makeImpl(D)() if(isStaticArray!D)
305 	{
306 		import std.conv: text;
307 
308 		alias ElemType = ForeachType!D;
309 
310 		alias dim = countDimensions!D;
311 		mixin("hid_t tid = H5Tarray_create2 (" ~ typeToHdf5Type!(ElemType) ~ ", " ~ dim.length.text ~ ", dim.ptr);");
312 		return RefCounted!(DataSpecification!D)(tid, (DataAttribute[]).init);
313 	}
314 
315 	private static makeImpl(D)() if(isDynamicArray!D)
316 	{
317 		alias ElemType = ForeachType!D;
318 
319 		mixin("hid_t tid = H5Tvlen_create (" ~ typeToHdf5Type!(ElemType) ~ ");");
320 		return RefCounted!(DataSpecification!D)(tid, (DataAttribute[]).init);
321 	}
322 
323 	static make()
324 	{
325 		import std.range : isInputRange;
326 		static if(isInputRange!Data) // if "outer" type of data is an input range it's processed,
327 		                             // on Dataset level so skip it
328 		{
329 			return makeImpl!(ForeachType!Data);
330 		}
331 		else
332 		{
333 			return makeImpl!(Data);
334 		}
335 	}
336 
337 	this(const(hid_t) tid, DataAttribute[] attributes)
338 	{
339 		_tid = tid;
340 		_attributes = attributes;
341 	}
342 
343 	~this()
344 	{
345 		static if(is(Data == struct))
346 		{
347 			H5Tclose(_tid);
348 		}
349 		else
350 		static if(isScalarType!Data)
351 		{
352 			// do nothing because built-in type is used(?)
353 		}
354 	}
355 
356 	auto tid() const
357 	{
358 		return _tid;
359 	}
360 
361 private:
362 	DataAttribute[] _attributes;
363 	immutable hid_t _tid = -1;
364 }