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