1 module des.util.data.type;
2 
3 import std..string : format, join;
4 import std.algorithm;
5 import std.conv : to;
6 import std.traits;
7 import std.math : abs;
8 import std.exception : enforce;
9 import des.math.linear.vector;
10 import des.math.linear.matrix;
11 import des.math.util.flatdata;
12 import des.util.testsuite;
13 
14 /// data types description for untyped `void[]` arrays
15 enum DataType
16 {
17     RAWBYTE,      /// untyped data `ubyte`
18     BYTE,         /// `byte`
19     UBYTE,        /// `ubyte`
20     NORM_QUART,   /// fixed point quartered [-1,1] `byte`
21     UNORM_QUART,  /// fixed point quartered [0,1] `ubyte`
22 
23     SHORT,        /// `short`
24     USHORT,       /// `ushort`
25     NORM_HALF,    /// fixed point half [-1,1] `short`
26     UNORM_HALF,   /// fixed point half [0,1] `ushort`
27 
28     INT,          /// `int`
29     UINT,         /// `uint`
30     NORM_FIXED,   /// fixed point [-1,1] `int`
31     UNORM_FIXED,  /// fixed point [0,1] `uint`
32 
33     LONG,         /// `long`
34     ULONG,        /// `ulong`
35     NORM_DOUBLE,  /// fixed point double [-1,1] `long`
36     UNORM_DOUBLE, /// fixed point double [0,1] `ulong`
37 
38     FLOAT,        /// `float`
39     DOUBLE        /// `double`
40 }
41 
42 /// data types that has direct correspondence with Dlang data types
43 enum StoreDataType : DataType
44 {
45     BYTE   = DataType.BYTE,  /// `DataType.BYTE`
46     UBYTE  = DataType.UBYTE, /// `DataType.UBYTE`
47 
48     SHORT  = DataType.SHORT, /// `DataType.SHORT`
49     USHORT = DataType.USHORT,/// `DataType.USHORT`
50 
51     INT    = DataType.INT,   /// `DataType.INT`
52     UINT   = DataType.UINT,  /// `DataType.UINT`
53 
54     LONG   = DataType.LONG,  /// `DataType.LONG`
55     ULONG  = DataType.ULONG, /// `DataType.ULONG`
56 
57     FLOAT  = DataType.FLOAT, /// `DataType.FLOAT`
58     DOUBLE = DataType.DOUBLE /// `DataType.DOUBLE`
59 }
60 
61 /++
62 returns associated with type T DataType
63 
64 * `byte`   = `DataType.BYTE`
65 * `ubyte`  = `DataType.UBYTE`
66 * `short`  = `DataType.SHORT`
67 * `ushort` = `DataType.USHORT`
68 * `int`    = `DataType.INT`
69 * `uint`   = `DataType.UINT`
70 * `long`   = `DataType.LONG`
71 * `ulong`  = `DataType.ULONG`
72 * `float`  = `DataType.FLOAT`
73 * `double` = `DataType.DOUBLE`
74 * `else`   = `DataType.RAWBYTE`
75 returns:
76     enum DataType
77  +/
78 template assocDataType(T)
79 {
80          static if( is( T == byte ) )   enum assocDataType = DataType.BYTE;
81     else static if( is( T == ubyte ) )  enum assocDataType = DataType.UBYTE;
82     else static if( is( T == short ) )  enum assocDataType = DataType.SHORT;
83     else static if( is( T == ushort ) ) enum assocDataType = DataType.USHORT;
84     else static if( is( T == int ) )    enum assocDataType = DataType.INT;
85     else static if( is( T == uint ) )   enum assocDataType = DataType.UINT;
86     else static if( is( T == long ) )   enum assocDataType = DataType.LONG;
87     else static if( is( T == ulong ) )  enum assocDataType = DataType.ULONG;
88     else static if( is( T == float ) )  enum assocDataType = DataType.FLOAT;
89     else static if( is( T == double ) ) enum assocDataType = DataType.DOUBLE;
90     else                                enum assocDataType = DataType.RAWBYTE;
91 }
92 
93 /// size of associated data type
94 size_t dataTypeSize( DataType dt ) pure nothrow @nogc @safe
95 {
96     final switch( dt )
97     {
98         case DataType.RAWBYTE:
99         case DataType.BYTE:
100         case DataType.UBYTE:
101         case DataType.NORM_QUART:
102         case DataType.UNORM_QUART:
103             return byte.sizeof;
104 
105         case DataType.SHORT:
106         case DataType.USHORT:
107         case DataType.NORM_HALF:
108         case DataType.UNORM_HALF:
109             return short.sizeof;
110 
111         case DataType.INT:
112         case DataType.UINT:
113         case DataType.NORM_FIXED:
114         case DataType.UNORM_FIXED:
115             return int.sizeof;
116 
117         case DataType.LONG:
118         case DataType.ULONG:
119         case DataType.NORM_DOUBLE:
120         case DataType.UNORM_DOUBLE:
121             return long.sizeof;
122 
123         case DataType.FLOAT:
124             return float.sizeof;
125 
126         case DataType.DOUBLE:
127             return double.sizeof;
128     }
129 }
130 
131 /++
132  alias for assocated store type
133 
134  * `DataType.RAWBYTE`      = `ubyte`
135  * `DataType.BYTE`         = `byte`
136  * `DataType.UBYTE`        = `ubyte`
137  * `DataType.NORM_QUART`   = `byte`
138  * `DataType.UNORM_QUART`  = `ubyte`
139  * `DataType.SHORT`        = `short`
140  * `DataType.USHORT`       = `ushort`
141  * `DataType.NORM_HALF`    = `short`
142  * `DataType.UNORM_HALF`   = `ushort`
143  * `DataType.INT`          = `int`
144  * `DataType.UINT`         = `uint`
145  * `DataType.NORM_FIXED`   = `int`
146  * `DataType.UNORM_FIXED`  = `uint`
147  * `DataType.LONG`         = `long`
148  * `DataType.ULONG`        = `ulong`
149  * `DataType.NORM_DOUBLE`  = `long`
150  * `DataType.UNORM_DOUBLE` = `ulong`
151  * `DataType.FLOAT`        = `float`
152  * `DataType.DOUBLE`       = `double`
153 
154  See_Also:
155  [DataType](des/util/data/type/DataType.html)
156  +/
157 template storeDataType( DataType DT )
158 {
159          static if( DT == DataType.RAWBYTE )      alias storeDataType = ubyte;
160     else static if( DT == DataType.BYTE )         alias storeDataType = byte;
161     else static if( DT == DataType.UBYTE )        alias storeDataType = ubyte;
162     else static if( DT == DataType.NORM_QUART )   alias storeDataType = byte;
163     else static if( DT == DataType.UNORM_QUART )  alias storeDataType = ubyte;
164     else static if( DT == DataType.SHORT )        alias storeDataType = short;
165     else static if( DT == DataType.USHORT )       alias storeDataType = ushort;
166     else static if( DT == DataType.NORM_HALF )    alias storeDataType = short;
167     else static if( DT == DataType.UNORM_HALF )   alias storeDataType = ushort;
168     else static if( DT == DataType.INT )          alias storeDataType = int;
169     else static if( DT == DataType.UINT )         alias storeDataType = uint;
170     else static if( DT == DataType.NORM_FIXED )   alias storeDataType = int;
171     else static if( DT == DataType.UNORM_FIXED )  alias storeDataType = uint;
172     else static if( DT == DataType.LONG )         alias storeDataType = long;
173     else static if( DT == DataType.ULONG )        alias storeDataType = ulong;
174     else static if( DT == DataType.NORM_DOUBLE )  alias storeDataType = long;
175     else static if( DT == DataType.UNORM_DOUBLE ) alias storeDataType = ulong;
176     else static if( DT == DataType.FLOAT )        alias storeDataType = float;
177     else static if( DT == DataType.DOUBLE )       alias storeDataType = double;
178 }
179 
180 /++
181  alias for conformation type
182 
183  diff with [storeDataType](des/util/data/type/storeDataType.html):
184 
185  * `DataType.NORM_QUART`   = `float`
186  * `DataType.UNORM_QUART`  = `float`
187  * `DataType.NORM_HALF`    = `float`
188  * `DataType.UNORM_HALF`   = `float`
189  * `DataType.NORM_FIXED`   = `float`
190  * `DataType.UNORM_FIXED`  = `float`
191  * `DataType.NORM_DOUBLE`  = `double`
192  * `DataType.UNORM_DOUBLE` = `double`
193 
194  See_Also:
195 
196  * [DataType](des/util/data/type/DataType.html)
197  * [storeDataType](des/util/data/type/storeDataType.html):
198 
199  +/
200 template conformDataType( DataType DT )
201 {
202          static if( DT == DataType.RAWBYTE )      alias conformDataType = void;
203     else static if( DT == DataType.BYTE )         alias conformDataType = byte;
204     else static if( DT == DataType.UBYTE )        alias conformDataType = ubyte;
205     else static if( DT == DataType.NORM_QUART )   alias conformDataType = float;
206     else static if( DT == DataType.UNORM_QUART )  alias conformDataType = float;
207     else static if( DT == DataType.SHORT )        alias conformDataType = short;
208     else static if( DT == DataType.USHORT )       alias conformDataType = ushort;
209     else static if( DT == DataType.NORM_HALF )    alias conformDataType = float;
210     else static if( DT == DataType.UNORM_HALF )   alias conformDataType = float;
211     else static if( DT == DataType.INT )          alias conformDataType = int;
212     else static if( DT == DataType.UINT )         alias conformDataType = uint;
213     else static if( DT == DataType.NORM_FIXED )   alias conformDataType = float;
214     else static if( DT == DataType.UNORM_FIXED )  alias conformDataType = float;
215     else static if( DT == DataType.LONG )         alias conformDataType = long;
216     else static if( DT == DataType.ULONG )        alias conformDataType = ulong;
217     else static if( DT == DataType.NORM_DOUBLE )  alias conformDataType = double;
218     else static if( DT == DataType.UNORM_DOUBLE ) alias conformDataType = double;
219     else static if( DT == DataType.FLOAT )        alias conformDataType = float;
220     else static if( DT == DataType.DOUBLE )       alias conformDataType = double;
221 }
222 
223 /// check `is( storeDataType!DT == conformDataType!DT )` for DataType DT
224 template isDirectDataType( DataType DT )
225 { enum isDirectDataType = is( storeDataType!DT == conformDataType!DT ); }
226 
227 unittest
228 {
229     import std..string;
230 
231     template F( DataType DT )
232     {
233         enum F = (storeDataType!DT).sizeof == dataTypeSize(DT);
234         static assert( F, format("%s",DT) );
235     }
236 
237     static assert( F!( DataType.RAWBYTE ) );
238     static assert( F!( DataType.BYTE ) );
239     static assert( F!( DataType.UBYTE ) );
240     static assert( F!( DataType.NORM_QUART ) );
241     static assert( F!( DataType.UNORM_QUART ) );
242     static assert( F!( DataType.SHORT ) );
243     static assert( F!( DataType.USHORT ) );
244     static assert( F!( DataType.NORM_HALF ) );
245     static assert( F!( DataType.UNORM_HALF ) );
246     static assert( F!( DataType.INT ) );
247     static assert( F!( DataType.UINT ) );
248     static assert( F!( DataType.NORM_FIXED ) );
249     static assert( F!( DataType.UNORM_FIXED ) );
250     static assert( F!( DataType.LONG ) );
251     static assert( F!( DataType.ULONG ) );
252     static assert( F!( DataType.NORM_DOUBLE ) );
253     static assert( F!( DataType.UNORM_DOUBLE ) );
254     static assert( F!( DataType.FLOAT ) );
255     static assert( F!( DataType.DOUBLE ) );
256 
257     static assert( F!( StoreDataType.BYTE ) );
258     static assert( F!( StoreDataType.UBYTE ) );
259     static assert( F!( StoreDataType.SHORT ) );
260     static assert( F!( StoreDataType.USHORT ) );
261     static assert( F!( StoreDataType.INT ) );
262     static assert( F!( StoreDataType.UINT ) );
263     static assert( F!( StoreDataType.LONG ) );
264     static assert( F!( StoreDataType.ULONG ) );
265     static assert( F!( StoreDataType.FLOAT ) );
266     static assert( F!( StoreDataType.DOUBLE ) );
267 }
268 
269 unittest
270 {
271     static assert( !isDirectDataType!( DataType.RAWBYTE ) );
272     static assert(  isDirectDataType!( DataType.BYTE ) );
273     static assert(  isDirectDataType!( DataType.UBYTE ) );
274     static assert( !isDirectDataType!( DataType.NORM_QUART ) );
275     static assert( !isDirectDataType!( DataType.UNORM_QUART ) );
276     static assert(  isDirectDataType!( DataType.SHORT ) );
277     static assert(  isDirectDataType!( DataType.USHORT ) );
278     static assert( !isDirectDataType!( DataType.NORM_HALF ) );
279     static assert( !isDirectDataType!( DataType.UNORM_HALF ) );
280     static assert(  isDirectDataType!( DataType.INT ) );
281     static assert(  isDirectDataType!( DataType.UINT ) );
282     static assert( !isDirectDataType!( DataType.NORM_FIXED ) );
283     static assert( !isDirectDataType!( DataType.UNORM_FIXED ) );
284     static assert(  isDirectDataType!( DataType.LONG ) );
285     static assert(  isDirectDataType!( DataType.ULONG ) );
286     static assert( !isDirectDataType!( DataType.NORM_DOUBLE ) );
287     static assert( !isDirectDataType!( DataType.UNORM_DOUBLE ) );
288     static assert(  isDirectDataType!( DataType.FLOAT ) );
289     static assert(  isDirectDataType!( DataType.DOUBLE ) );
290 
291     static assert(  isDirectDataType!( StoreDataType.BYTE ) );
292     static assert(  isDirectDataType!( StoreDataType.UBYTE ) );
293     static assert(  isDirectDataType!( StoreDataType.SHORT ) );
294     static assert(  isDirectDataType!( StoreDataType.USHORT ) );
295     static assert(  isDirectDataType!( StoreDataType.INT ) );
296     static assert(  isDirectDataType!( StoreDataType.UINT ) );
297     static assert(  isDirectDataType!( StoreDataType.LONG ) );
298     static assert(  isDirectDataType!( StoreDataType.ULONG ) );
299     static assert(  isDirectDataType!( StoreDataType.FLOAT ) );
300     static assert(  isDirectDataType!( StoreDataType.DOUBLE ) );
301 }
302 
303 /++
304  Description for untyped arrays with multidimension elements
305  +/
306 struct ElemInfo
307 {
308     /// count of components in element
309     size_t comp = 1;
310 
311     /// type of one component
312     DataType type = DataType.RAWBYTE;
313 
314     invariant() { assert( comp > 0 ); }
315 
316     pure @safe nothrow @nogc
317     {
318         /++ get ElemInfo from type
319          +
320          + works with:
321          + * single numeric
322          + * static arrays
323          + * static [vector](des/math/linear/vector/Vector.html)
324          + * static [matrix](des/math/linear/matrix/Matrix.html)
325          +/
326         static ElemInfo fromType(T)() @property
327             if( !hasIndirections!T )
328         {
329             static if( isNumeric!T )
330                 return ElemInfo( 1, assocDataType!T );
331             else static if( isStaticArray!T )
332                 return ElemInfo( T.length, assocDataType!( typeof(T.init[0]) ) );
333             else static if( isStaticVector!T )
334                 return ElemInfo( T.length, assocDataType!( T.datatype ) );
335             else static if( isStaticMatrix!T )
336                 return ElemInfo( T.width * T.height, assocDataType!( T.datatype ) );
337             else static assert(0,"unsupported type");
338         }
339 
340         ///
341         unittest
342         {
343             static assert( ElemInfo.fromType!vec2 == ElemInfo( 2, DataType.FLOAT ) );
344             static assert( ElemInfo.fromType!mat4 == ElemInfo( 16, DataType.FLOAT ) );
345             static assert( ElemInfo.fromType!(int[2]) == ElemInfo( 2, DataType.INT ) );
346             static assert( ElemInfo.fromType!float == ElemInfo( 1, DataType.FLOAT ) );
347 
348             static class A{}
349 
350             static assert( !__traits(compiles, ElemInfo.fromType!A ) );
351             static assert( !__traits(compiles, ElemInfo.fromType!(int[]) ) );
352             static assert( !__traits(compiles, ElemInfo.fromType!dvec ) );
353         }
354 
355         ///
356         this( size_t comp, DataType type=DataType.RAWBYTE )
357         {
358             this.comp = comp;
359             this.type = type;
360         }
361 
362         ///
363         this( in ElemInfo ei )
364         {
365             this.comp = ei.comp;
366             this.type = ei.type;
367         }
368 
369         const @property
370         {
371             /++ bytes per element
372                 returns:
373                     typeSize * comp
374              +/
375             size_t bpe() { return typeSize * comp; }
376 
377             /// size of component
378             size_t typeSize() { return dataTypeSize(type); }
379         }
380     }
381 }
382 
383 ///
384 class DataTypeException : Exception
385 {
386     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
387     { super( msg, file, line ); }
388 }
389 
390 ///
391 struct ArrayData
392 {
393     ///
394     size_t size;
395     ///
396     void* ptr;
397 
398     this(T)( size_t size, T* data )
399     {
400         this.size = size;
401         ptr = cast(void*)data;
402     }
403 
404     this(T)( size_t size, in T* data ) const
405     {
406         this.size = size;
407         ptr = cast(void*)data;
408     }
409 }
410 
411 ///
412 union AlienArray(T)
413 {
414     ///
415     ArrayData raw;
416     ///
417     T[] arr;
418     ///
419     alias type=T;
420     ///
421     alias arr this;
422 }
423 
424 ///
425 auto getTypedArray(T,X)( size_t sz, X* addr ) pure nothrow
426 {
427     static if( is( Unqual!(X) == X ) )
428         return AlienArray!T( ArrayData( sz, addr ) );
429     else
430         return const AlienArray!T( const ArrayData( sz, addr ) );
431 }
432 
433 ///
434 unittest
435 {
436     float[] buf = [ 1.1f, 2.2, 3.3, 4.4, 5.5 ];
437     auto a = getTypedArray!float( 2, cast(void*)(buf.ptr + 1) );
438     import std.stdio;
439     assert( eq( a, [2.2, 3.3] ) );
440     a[0] = 10;
441     assert( eq( buf, [1.1, 10, 3.3, 4.4, 5.5] ) );
442 }
443 
444 ///
445 unittest
446 {
447 
448     ubyte[] buf = [ 1, 2, 3, 4 ];
449     auto a = getTypedArray!void( 4, cast(void*)buf );
450     assert( eq( cast(ubyte[])a, buf ) );
451 }
452 
453 ///
454 unittest
455 {
456     static struct TT { ubyte val; }
457 
458     ubyte[] fnc( in TT[] data ) pure
459     { return getTypedArray!ubyte( data.length, data.ptr ).arr.dup; }
460 
461     auto tt = [ TT(0), TT(1), TT(3) ];
462     assert( eq( fnc( tt ), cast(ubyte[])[0,1,3] ) );
463 }
464 
465 ///
466 template convertValue( DataType DT )
467 {
468     auto convertValue(T)( T val ) pure
469     {
470         alias SDT = storeDataType!DT;
471 
472         static if( isDirectDataType!DT )
473             return cast(SDT)( val );
474         else static if( DT == DataType.RAWBYTE )
475             static assert( 0, "can't convert any value to RAWBYTE" );
476         else
477         {
478             static if( isFloatingPoint!T )
479             {
480                 static if( isSigned!SDT )
481                 {
482                     enforce( val >= -1 && val <= 1, "value exceed signed limits [-1,1]" );
483                     return cast(SDT)( (val>0?SDT.max:SDT.min) * abs(val) );
484                 }
485                 else
486                 {
487                     enforce( val >= 0 && val <= 1, "value exceed unsigned limits [0,1]" );
488                     return cast(SDT)( SDT.max * val );
489                 }
490             }
491             else static assert(0, "can't convert int value to fixed point value" );
492         }
493     }
494 }
495 
496 ///
497 unittest
498 {
499     static assert( convertValue!(DataType.NORM_FIXED)(1.0)  == int.max );
500     static assert( convertValue!(DataType.NORM_FIXED)(0.5)  == int.max/2 );
501     static assert( convertValue!(DataType.NORM_FIXED)(0.0)  == 0 );
502     static assert( convertValue!(DataType.NORM_FIXED)(-0.5) == int.min/2 );
503     static assert( convertValue!(DataType.NORM_FIXED)(-1.0) == int.min );
504     static assert( is( typeof( convertValue!(DataType.NORM_FIXED)(1.0) ) == int ) );
505 }
506 
507 unittest
508 {
509     static assert( convertValue!(DataType.BYTE)(1) == 1 );
510     static assert( is( typeof( convertValue!(DataType.BYTE)(1) ) == byte ) );
511 
512     static assert( convertValue!(DataType.FLOAT)(1) == 1 );
513     static assert( convertValue!(DataType.FLOAT)(1.1) == 1.1 );
514     static assert( is( typeof( convertValue!(DataType.FLOAT)(1) ) == float ) );
515 
516     static assert( convertValue!(DataType.UNORM_QUART)(1.0) == ubyte.max );
517     static assert( convertValue!(DataType.UNORM_QUART)(0.5) == ubyte.max/2 );
518     static assert( convertValue!(DataType.UNORM_QUART)(0.0) == 0 );
519     static assert( is( typeof( convertValue!(DataType.UNORM_QUART)(1.0) ) == ubyte ) );
520 
521     static assert( convertValue!(DataType.UNORM_HALF)(1.0) == ushort.max );
522     static assert( convertValue!(DataType.UNORM_HALF)(0.5) == ushort.max/2 );
523     static assert( convertValue!(DataType.UNORM_HALF)(0.0) == 0 );
524     static assert( is( typeof( convertValue!(DataType.UNORM_HALF)(1.0) ) == ushort ) );
525 
526     static assert( convertValue!(DataType.UNORM_FIXED)(1.0) == uint.max );
527     static assert( convertValue!(DataType.UNORM_FIXED)(0.5) == uint.max/2 );
528     static assert( convertValue!(DataType.UNORM_FIXED)(0.0) == 0 );
529     static assert( is( typeof( convertValue!(DataType.UNORM_FIXED)(1.0) ) == uint ) );
530 
531     static assert( convertValue!(DataType.UNORM_DOUBLE)(1.0) == ulong.max );
532     static assert( convertValue!(DataType.UNORM_DOUBLE)(0.5) == ulong.max/2 );
533     static assert( convertValue!(DataType.UNORM_DOUBLE)(0.0) == 0 );
534     static assert( is( typeof( convertValue!(DataType.UNORM_DOUBLE)(1.0) ) == ulong ) );
535 
536     static assert( convertValue!(DataType.NORM_QUART)(1.0)  == byte.max );
537     static assert( convertValue!(DataType.NORM_QUART)(0.5)  == byte.max/2 );
538     static assert( convertValue!(DataType.NORM_QUART)(0.0)  == 0 );
539     static assert( convertValue!(DataType.NORM_QUART)(-0.5) == byte.min/2 );
540     static assert( convertValue!(DataType.NORM_QUART)(-1.0) == byte.min );
541     static assert( is( typeof( convertValue!(DataType.NORM_QUART)(1.0) ) == byte ) );
542 
543     static assert( convertValue!(DataType.NORM_HALF)(1.0)  == short.max );
544     static assert( convertValue!(DataType.NORM_HALF)(0.5)  == short.max/2 );
545     static assert( convertValue!(DataType.NORM_HALF)(0.0)  == 0 );
546     static assert( convertValue!(DataType.NORM_HALF)(-0.5) == short.min/2 );
547     static assert( convertValue!(DataType.NORM_HALF)(-1.0) == short.min );
548     static assert( is( typeof( convertValue!(DataType.NORM_HALF)(1.0) ) == short ) );
549 
550     static assert( convertValue!(DataType.NORM_FIXED)(1.0)  == int.max );
551     static assert( convertValue!(DataType.NORM_FIXED)(0.5)  == int.max/2 );
552     static assert( convertValue!(DataType.NORM_FIXED)(0.0)  == 0 );
553     static assert( convertValue!(DataType.NORM_FIXED)(-0.5) == int.min/2 );
554     static assert( convertValue!(DataType.NORM_FIXED)(-1.0) == int.min );
555     static assert( is( typeof( convertValue!(DataType.NORM_FIXED)(1.0) ) == int ) );
556 
557     static assert( convertValue!(DataType.NORM_DOUBLE)(1.0)  == long.max );
558     static assert( convertValue!(DataType.NORM_DOUBLE)(0.5)  == long.max/2 );
559     static assert( convertValue!(DataType.NORM_DOUBLE)(0.0)  == 0 );
560     static assert( convertValue!(DataType.NORM_DOUBLE)(-0.5) == long.min/2 );
561     static assert( convertValue!(DataType.NORM_DOUBLE)(-1.0) == long.min );
562     static assert( is( typeof( convertValue!(DataType.NORM_DOUBLE)(1.0) ) == long ) );
563 
564     assert(  mustExcept({ convertValue!(DataType.NORM_FIXED)(1.1); }) );
565     assert( !mustExcept({ convertValue!(DataType.NORM_FIXED)(-0.1); }) );
566 
567     assert(  mustExcept({ convertValue!(DataType.UNORM_FIXED)(1.1); }) );
568     assert(  mustExcept({ convertValue!(DataType.UNORM_FIXED)(-0.1); }) );
569 }
570 
571 /// untyped data assign
572 void utDataAssign(T...)( in ElemInfo elem, void* buffer, in T vals ) pure
573     if( is(typeof(flatData!real(vals))) )
574 in
575 {
576     assert( buffer !is null );
577     assert( elem.comp == flatData!real(vals).length );
578 }
579 body
580 {
581     enum fmt = q{
582         auto dst = getTypedArray!(storeDataType!(%1$s))( elem.comp, buffer );
583         auto src = flatData!real(vals);
584         foreach( i, ref t; dst )
585             t = convertValue!(%1$s)( src[i] );
586     };
587     mixin( getSwitchDataType( "elem.type", fmt, ["RAWBYTE": "can't operate RAWBYTE"] ) );
588 }
589 
590 ///
591 unittest
592 {
593     float[] buf = [ 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 ];
594 
595     utDataAssign( ElemInfo( 3, DataType.FLOAT ), cast(void*)buf.ptr, vec3(8,9,10) );
596 
597     assert( eq( buf, [8,9,10,4.4,5.5,6.6] ) );
598 }
599 
600 /++
601     binary operation between untyped buffers
602 
603     Params:
604         info = common to all buffers information
605         dst = result buffer ptr
606         uta = buffer ptr A
607         utb = buffer ptr B
608  +/
609 void utDataOp(string op)( in ElemInfo elem, void* dst, void* utb ) pure
610 if( ( op == "+" || op == "-" || op == "*" || op == "/" ) )
611 in
612 {
613     assert( dst !is null );
614     assert( utb !is null );
615 }
616 body
617 {
618     enum fmt = format( q{
619         alias SDT = storeDataType!(%%1$s);
620         auto ta = getTypedArray!SDT( elem.comp, dst );
621         auto tb = getTypedArray!SDT( elem.comp, utb );
622         foreach( i, ref r; ta )
623             r = cast(SDT)( ta[i] %s tb[i] );
624     }, op );
625     mixin( getSwitchDataType( "elem.type", fmt, ["RAWBYTE": "can't operate RAWBYTE"] ) );
626 }
627 
628 ///
629 unittest
630 {
631     ubyte[] a = [ 10, 20, 30, 40 ];
632     ubyte[] b = [ 60, 70, 40, 20 ];
633 
634     utDataOp!"+"( ElemInfo( 4, DataType.UNORM_QUART ), a.ptr, b.ptr );
635 
636     assert( eq( a, [70,90,70,60] ) );
637 
638     utDataOp!"+"( ElemInfo( 2, DataType.UBYTE ), a.ptr, a.ptr + 2 );
639 
640     assert( eq( a, [140,150,70,60] ) );
641 }
642 
643 /++ generate switch for all DataType elements
644 
645 Params:
646 subj = past as `switch( subj )`
647 fmt = body of all cases past as formated with one argument (DataType) string
648 except = exception in selected cases
649 
650 Example:
651     enum fmt = q{
652         auto dst = getTypedArray!(storeDataType!(%1$s))( info.comp, buffer );
653         auto src = flatData!real(vals);
654         foreach( i, ref t; dst )
655             t = convertValue!(%1$s)( src[i] );
656     };
657     writeln( genSwitchDataType( "info.type", fmt, ["RAWBYTE": "can't operate RAWBYTE"] ) );
658 
659 Output like this:
660 
661     final switch( info.type ) {
662     case DataType.RAWBYTE: throw new DataTypeException( "can't operate RAWBYTE" );
663     case DataType.BYTE:
664             auto dst = getTypedArray!(storeDataType!(DataType.BYTE))( info.comp, buffer );
665             auto src = flatData!real(vals);
666             foreach( i, ref t; dst )
667                 t = convertValue!(DataType.BYTE)( src[i] );
668     break;
669     case DataType.UBYTE:
670             auto dst = getTypedArray!(storeDataType!(DataType.UBYTE))( info.comp, buffer );
671             auto src = flatData!real(vals);
672             foreach( i, ref t; dst )
673                 t = convertValue!(DataType.UBYTE)( src[i] );
674     break;
675     ...
676     }
677 +/
678 string getSwitchDataType( string subj, string fmt, string[string] except ) pure
679 {
680     string[] ret = [ format( `final switch( %s ) {`, subj ) ];
681 
682     auto _type( string dt ) { return "DataType." ~ dt; }
683     auto _case( string dt ) { return "case " ~ _type(dt) ~ ": "; }
684 
685     auto fmt_case( string dt, string fmt )
686     { return _case(dt) ~ "\n" ~ format( fmt, _type(dt) ) ~ "\nbreak;"; }
687 
688     auto fmt_except( string dt, string msg )
689     { return _case( dt ) ~ format( `throw new DataTypeException( "%s" );`, msg ); }
690 
691     foreach( dt; [EnumMembers!DataType].map!(a=>to!string(a)) )
692     {
693         if( dt in except ) ret ~= fmt_except( dt, except[dt] );
694         else ret ~= fmt_case( dt, fmt );
695     }
696 
697     ret ~= "}";
698 
699     return ret.join("\n");
700 }