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,  ///
46     UBYTE  = DataType.UBYTE, ///
47 
48     SHORT  = DataType.SHORT, ///
49     USHORT = DataType.USHORT,///
50 
51     INT    = DataType.INT,   ///
52     UINT   = DataType.UINT,  ///
53 
54     LONG   = DataType.LONG,  ///
55     ULONG  = DataType.ULONG, ///
56 
57     FLOAT  = DataType.FLOAT, ///
58     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     /// type of one component
309     DataType comp = DataType.RAWBYTE;
310 
311     /// count of components in element
312     size_t channels = 1;
313 
314     invariant() { assert( channels > 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( assocDataType!T, 1 );
331             else static if( isStaticArray!T )
332                 return ElemInfo( assocDataType!( typeof(T.init[0]) ), T.length );
333             else static if( isStaticVector!T )
334                 return ElemInfo( assocDataType!( T.datatype ), T.length );
335             else static if( isStaticMatrix!T )
336                 return ElemInfo( assocDataType!( T.datatype ), T.width * T.height );
337             else static assert(0,"unsupported type");
338         }
339 
340         ///
341         unittest
342         {
343             static assert( ElemInfo.fromType!vec2 == ElemInfo( DataType.FLOAT, 2 ) );
344             static assert( ElemInfo.fromType!mat4 == ElemInfo( DataType.FLOAT, 16 ) );
345             static assert( ElemInfo.fromType!(int[2]) == ElemInfo( DataType.INT, 2 ) );
346             static assert( ElemInfo.fromType!float == ElemInfo( DataType.FLOAT, 1 ) );
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( DataType ict, size_t channels )
357         {
358             comp = ict;
359             this.channels = channels;
360         }
361 
362         /// comp = `DataType.RAWBYTE`
363         this( size_t channels )
364         {
365             comp = DataType.RAWBYTE;
366             this.channels = channels;
367         }
368 
369         const @property
370         {
371             /++ bytes per element
372                 returns:
373                     compSize * channels
374              +/
375             size_t bpe() { return compSize * channels; }
376 
377             /// size of component
378             size_t compSize() { return dataTypeSize(comp); }
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 { /++ +/size_t size, ptr; }
392 
393 ///
394 union AlienArray(T)
395 {
396     ///
397     ArrayData raw;
398     ///
399     T[] arr;
400     ///
401     alias type=T;
402     ///
403     alias arr this;
404 }
405 
406 ///
407 auto getTypedArray(T)( size_t sz, void* addr ) pure nothrow
408 { return AlienArray!T( ArrayData( sz, cast(size_t)addr ) ); }
409 
410 ///
411 unittest
412 {
413     float[] buf = [ 1.1f, 2.2, 3.3, 4.4, 5.5 ];
414     auto a = getTypedArray!float( 2, cast(void*)(buf.ptr + 1) );
415     import std.stdio;
416     assert( eq( a, [2.2, 3.3] ) );
417     a[0] = 10;
418     assert( eq( buf, [1.1, 10, 3.3, 4.4, 5.5] ) );
419 }
420 
421 ///
422 template convertValue( DataType DT )
423 {
424     auto convertValue(T)( T val ) pure
425     {
426         alias SDT = storeDataType!DT;
427 
428         static if( isDirectDataType!DT )
429             return cast(SDT)( val );
430         else static if( DT == DataType.RAWBYTE )
431             static assert( 0, "can't convert any value to RAWBYTE" );
432         else
433         {
434             static if( isFloatingPoint!T )
435             {
436                 static if( isSigned!SDT )
437                 {
438                     enforce( val >= -1 && val <= 1, "value exceed signed limits [-1,1]" );
439                     return cast(SDT)( (val>0?SDT.max:SDT.min) * abs(val) );
440                 }
441                 else
442                 {
443                     enforce( val >= 0 && val <= 1, "value exceed unsigned limits [0,1]" );
444                     return cast(SDT)( SDT.max * val );
445                 }
446             }
447             else static assert(0, "can't convert int value to fixed point value" );
448         }
449     }
450 }
451 
452 ///
453 unittest
454 {
455     static assert( convertValue!(DataType.NORM_FIXED)(1.0)  == int.max );
456     static assert( convertValue!(DataType.NORM_FIXED)(0.5)  == int.max/2 );
457     static assert( convertValue!(DataType.NORM_FIXED)(0.0)  == 0 );
458     static assert( convertValue!(DataType.NORM_FIXED)(-0.5) == int.min/2 );
459     static assert( convertValue!(DataType.NORM_FIXED)(-1.0) == int.min );
460     static assert( is( typeof( convertValue!(DataType.NORM_FIXED)(1.0) ) == int ) );
461 }
462 
463 unittest
464 {
465     static assert( convertValue!(DataType.BYTE)(1) == 1 );
466     static assert( is( typeof( convertValue!(DataType.BYTE)(1) ) == byte ) );
467 
468     static assert( convertValue!(DataType.FLOAT)(1) == 1 );
469     static assert( convertValue!(DataType.FLOAT)(1.1) == 1.1 );
470     static assert( is( typeof( convertValue!(DataType.FLOAT)(1) ) == float ) );
471 
472     static assert( convertValue!(DataType.UNORM_QUART)(1.0) == ubyte.max );
473     static assert( convertValue!(DataType.UNORM_QUART)(0.5) == ubyte.max/2 );
474     static assert( convertValue!(DataType.UNORM_QUART)(0.0) == 0 );
475     static assert( is( typeof( convertValue!(DataType.UNORM_QUART)(1.0) ) == ubyte ) );
476 
477     static assert( convertValue!(DataType.UNORM_HALF)(1.0) == ushort.max );
478     static assert( convertValue!(DataType.UNORM_HALF)(0.5) == ushort.max/2 );
479     static assert( convertValue!(DataType.UNORM_HALF)(0.0) == 0 );
480     static assert( is( typeof( convertValue!(DataType.UNORM_HALF)(1.0) ) == ushort ) );
481 
482     static assert( convertValue!(DataType.UNORM_FIXED)(1.0) == uint.max );
483     static assert( convertValue!(DataType.UNORM_FIXED)(0.5) == uint.max/2 );
484     static assert( convertValue!(DataType.UNORM_FIXED)(0.0) == 0 );
485     static assert( is( typeof( convertValue!(DataType.UNORM_FIXED)(1.0) ) == uint ) );
486 
487     static assert( convertValue!(DataType.UNORM_DOUBLE)(1.0) == ulong.max );
488     static assert( convertValue!(DataType.UNORM_DOUBLE)(0.5) == ulong.max/2 );
489     static assert( convertValue!(DataType.UNORM_DOUBLE)(0.0) == 0 );
490     static assert( is( typeof( convertValue!(DataType.UNORM_DOUBLE)(1.0) ) == ulong ) );
491 
492     static assert( convertValue!(DataType.NORM_QUART)(1.0)  == byte.max );
493     static assert( convertValue!(DataType.NORM_QUART)(0.5)  == byte.max/2 );
494     static assert( convertValue!(DataType.NORM_QUART)(0.0)  == 0 );
495     static assert( convertValue!(DataType.NORM_QUART)(-0.5) == byte.min/2 );
496     static assert( convertValue!(DataType.NORM_QUART)(-1.0) == byte.min );
497     static assert( is( typeof( convertValue!(DataType.NORM_QUART)(1.0) ) == byte ) );
498 
499     static assert( convertValue!(DataType.NORM_HALF)(1.0)  == short.max );
500     static assert( convertValue!(DataType.NORM_HALF)(0.5)  == short.max/2 );
501     static assert( convertValue!(DataType.NORM_HALF)(0.0)  == 0 );
502     static assert( convertValue!(DataType.NORM_HALF)(-0.5) == short.min/2 );
503     static assert( convertValue!(DataType.NORM_HALF)(-1.0) == short.min );
504     static assert( is( typeof( convertValue!(DataType.NORM_HALF)(1.0) ) == short ) );
505 
506     static assert( convertValue!(DataType.NORM_FIXED)(1.0)  == int.max );
507     static assert( convertValue!(DataType.NORM_FIXED)(0.5)  == int.max/2 );
508     static assert( convertValue!(DataType.NORM_FIXED)(0.0)  == 0 );
509     static assert( convertValue!(DataType.NORM_FIXED)(-0.5) == int.min/2 );
510     static assert( convertValue!(DataType.NORM_FIXED)(-1.0) == int.min );
511     static assert( is( typeof( convertValue!(DataType.NORM_FIXED)(1.0) ) == int ) );
512 
513     static assert( convertValue!(DataType.NORM_DOUBLE)(1.0)  == long.max );
514     static assert( convertValue!(DataType.NORM_DOUBLE)(0.5)  == long.max/2 );
515     static assert( convertValue!(DataType.NORM_DOUBLE)(0.0)  == 0 );
516     static assert( convertValue!(DataType.NORM_DOUBLE)(-0.5) == long.min/2 );
517     static assert( convertValue!(DataType.NORM_DOUBLE)(-1.0) == long.min );
518     static assert( is( typeof( convertValue!(DataType.NORM_DOUBLE)(1.0) ) == long ) );
519 
520     assert(  mustExcept({ convertValue!(DataType.NORM_FIXED)(1.1); }) );
521     assert( !mustExcept({ convertValue!(DataType.NORM_FIXED)(-0.1); }) );
522 
523     assert(  mustExcept({ convertValue!(DataType.UNORM_FIXED)(1.1); }) );
524     assert(  mustExcept({ convertValue!(DataType.UNORM_FIXED)(-0.1); }) );
525 }
526 
527 /// untyped data assign
528 void utDataAssign(T...)( in ElemInfo info, void* buffer, in T vals ) pure
529     if( is(typeof(flatData!real(vals))) )
530 in
531 {
532     assert( buffer !is null );
533     assert( info.channels == flatData!real(vals).length );
534 }
535 body
536 {
537     enum fmt = q{
538         auto dst = getTypedArray!(storeDataType!(%1$s))( info.channels, buffer );
539         auto src = flatData!real(vals);
540         foreach( i, ref t; dst )
541             t = convertValue!(%1$s)( src[i] );
542     };
543     mixin( getSwitchDataType( "info.comp", fmt, ["RAWBYTE": "can't operate RAWBYTE"] ) );
544 }
545 
546 ///
547 unittest
548 {
549     float[] buf = [ 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 ];
550 
551     utDataAssign( ElemInfo( DataType.FLOAT, 3 ), cast(void*)buf.ptr, vec3(8,9,10) );
552 
553     assert( eq( buf, [8,9,10,4.4,5.5,6.6] ) );
554 }
555 
556 /++
557     binary operation between untyped buffers
558 
559     Params:
560         info = common to all buffers information
561         dst = result buffer ptr
562         uta = buffer ptr A
563         utb = buffer ptr B
564  +/
565 void utDataOp(string op)( in ElemInfo info, void* dst, void* utb ) pure
566 if( ( op == "+" || op == "-" || op == "*" || op == "/" ) )
567 in
568 {
569     assert( dst !is null );
570     assert( utb !is null );
571 }
572 body
573 {
574     enum fmt = format( q{
575         alias SDT = storeDataType!(%%1$s);
576         auto ta = getTypedArray!SDT( info.channels, dst );
577         auto tb = getTypedArray!SDT( info.channels, utb );
578         foreach( i, ref r; ta )
579             r = cast(SDT)( ta[i] %s tb[i] );
580     }, op );
581     mixin( getSwitchDataType( "info.comp", fmt, ["RAWBYTE": "can't operate RAWBYTE"] ) );
582 }
583 
584 ///
585 unittest
586 {
587     ubyte[] a = [ 10, 20, 30, 40 ];
588     ubyte[] b = [ 60, 70, 40, 20 ];
589 
590     utDataOp!"+"( ElemInfo( DataType.UNORM_QUART, 4 ), a.ptr, b.ptr );
591 
592     assert( eq( a, [70,90,70,60] ) );
593 
594     utDataOp!"+"( ElemInfo( DataType.UBYTE, 2 ), a.ptr, a.ptr + 2 );
595 
596     assert( eq( a, [140,150,70,60] ) );
597 }
598 
599 /++ generate switch for all DataType elements
600 
601 Params:
602 subj = past as `switch( subj )`
603 fmt = body of all cases past as formated with one argument (DataType) string
604 except = exception in selected cases
605 
606 Example:
607     enum fmt = q{
608         auto dst = getTypedArray!(storeDataType!(%1$s))( info.channels, buffer );
609         auto src = flatData!real(vals);
610         foreach( i, ref t; dst )
611             t = convertValue!(%1$s)( src[i] );
612     };
613     writeln( genSwitchDataType( "info.comp", fmt, ["RAWBYTE": "can't operate RAWBYTE"] ) );
614 
615 Output like this:
616 
617     final switch( info.comp ) {
618     case DataType.RAWBYTE: throw new DataTypeException( "can't operate RAWBYTE" );
619     case DataType.BYTE: 
620             auto dst = getTypedArray!(storeDataType!(DataType.BYTE))( info.channels, buffer );
621             auto src = flatData!real(vals);
622             foreach( i, ref t; dst )
623                 t = convertValue!(DataType.BYTE)( src[i] );
624     break;
625     case DataType.UBYTE: 
626             auto dst = getTypedArray!(storeDataType!(DataType.UBYTE))( info.channels, buffer );
627             auto src = flatData!real(vals);
628             foreach( i, ref t; dst )
629                 t = convertValue!(DataType.UBYTE)( src[i] );
630     break;
631     ...
632     }
633 +/
634 string getSwitchDataType( string subj, string fmt, string[string] except ) pure
635 {
636     string[] ret = [ format( `final switch( %s ) {`, subj ) ];
637 
638     auto _type( string dt ) { return "DataType." ~ dt; }
639     auto _case( string dt ) { return "case " ~ _type(dt) ~ ": "; }
640 
641     auto fmt_case( string dt, string fmt )
642     { return _case(dt) ~ "\n" ~ format( fmt, _type(dt) ) ~ "\nbreak;"; }
643 
644     auto fmt_except( string dt, string msg )
645     { return _case( dt ) ~ format( `throw new DataTypeException( "%s" );`, msg ); }
646 
647     foreach( dt; [EnumMembers!DataType].map!(a=>to!string(a)) )
648     {
649         if( dt in except ) ret ~= fmt_except( dt, except[dt] );
650         else ret ~= fmt_case( dt, fmt );
651     }
652 
653     ret ~= "}";
654 
655     return ret.join("\n");
656 }