1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.util.pdata;
26 
27 import std.traits;
28 import std.string;
29 
30 import des.util.testsuite;
31 
32 alias immutable(void)[] data_t;
33 
34 bool isPData(T)() { return is( Unqual!T == PData ); }
35 
36 private pure data_t pureDumpData(T)( in T val )
37 {
38     static if( isPData!T ) return val.data.idup;
39     else static if( isArray!T ) return val.idup;
40     else static if( !hasUnsharedAliasing!T ) return [val].idup;
41     else static assert( 0, format( "unsupported type '%s' for pure read data", T.stringof ) );
42 }
43 
44 @property @safe pure nothrow isPureDump(T)()
45 { return is( typeof( pureDumpData( T.init ) ) ); }
46 
47 unittest
48 {
49     static assert( isPureDump!(data_t) );
50     static assert( isPureDump!(string) );
51     static assert( isPureDump!(double) );
52     static assert( isPureDump!(PData) );
53     static assert( isPureDump!(const(PData)) );
54     static assert( isPureDump!(shared(PData)) );
55     static assert( isPureDump!(immutable(PData)) );
56     static assert( isPureDump!(double[]) );
57     static assert( !isPureDump!(int[string]) );
58 
59     static struct TS { int[string] val; }
60     static assert( !isPureDump!(TS) );
61 }
62 
63 private T conv(T)( in data_t data )
64 {
65     static if( isArray!T ) return cast(T)data.dup;
66     else static if( !hasUnsharedAliasing!T )
67     {
68         if( data.length * void.sizeof != T.sizeof )
69             throw new Exception( format( "PData unable convert to '%s': wrong data length", T.stringof ) );
70         return (cast(T[])data.dup)[0];
71     }
72     else static if( __traits(compiles, T.load(data)) )
73         return T.load(data);
74     else static assert( 0, format( "unsupported type '%s'", T.stringof ) );
75 }
76 
77 struct PData
78 {
79     data_t data;
80     alias data this;
81 
82     private void readData(T)( in T val )
83     {
84         static if( isPureDump!T ) 
85             data = pureDumpData( val );
86         else static if( __traits(compiles, val.dump()) )
87             data = val.dump().idup;
88         else static assert( 0, format( "unsupported type '%s'", T.stringof ) );
89     }
90 
91     pure this( in void[] dd ) { data = dd.idup; }
92 
93     pure this(T)( in T val ) if( isPureDump!T ) 
94     { data = pureDumpData( val ); }
95 
96     this(T)( in T val ) if( !isPureDump!T ) 
97     { readData( val ); }
98 
99     T opAssign(T)( in T val )
100     {
101         readData( val );
102         return val;
103     }
104 
105     @property
106     {
107         T as(T)() const { return conv!T( data ); }
108         T as(T)() shared const { return conv!T( data ); }
109         T as(T)() immutable { return conv!T( data ); }
110     }
111 }
112 
113 unittest
114 {
115     static assert( isPureDump!PData );
116 }
117 
118 unittest
119 {
120     auto a = PData( [ .1, .2, .3 ] );
121     assert( eq( a.as!(double[]), [ .1, .2, .3 ] ) );
122     a = "hello";
123     assert( eq( a.as!string, "hello" ) );
124 
125     static struct TestStruct 
126     { double x, y; string info; immutable(int)[] data; }
127     auto ts = TestStruct( 10.1, 12.3, "hello", [1,2,3,4] );
128 
129     auto xx = PData( ts );
130 
131     auto xa = shared PData( xx );
132     auto xb = const PData( xx );
133     auto xc = shared const PData( xx );
134     auto xd = immutable PData( xx );
135     auto xe = shared immutable PData( xx );
136 
137     assert( xx == xa );
138     assert( xx == xb );
139     assert( xx == xc );
140     assert( xx == xd );
141     assert( xx == xe );
142 
143     assert( xa.as!TestStruct == ts );
144     assert( xb.as!TestStruct == ts );
145     assert( xc.as!TestStruct == ts );
146     assert( xd.as!TestStruct == ts );
147     assert( xe.as!TestStruct == ts );
148 
149     auto ax = PData( xa );
150     auto bx = PData( xb );
151     auto cx = PData( xc );
152     auto dx = PData( xd );
153     auto ex = PData( xe );
154 
155     assert( xx == ax );
156     assert( xx == bx );
157     assert( xx == cx );
158     assert( xx == dx );
159     assert( xx == ex );
160 
161     assert( ax.data == xx.data );
162     assert( bx.data == xx.data );
163     assert( cx.data == xx.data );
164     assert( dx.data == xx.data );
165     assert( ex.data == xx.data );
166 
167     assert( ax.as!TestStruct == ts );
168     assert( bx.as!TestStruct == ts );
169     assert( cx.as!TestStruct == ts );
170     assert( dx.as!TestStruct == ts );
171     assert( ex.as!TestStruct == ts );
172 }
173 
174 unittest
175 {
176     import std.conv;
177     static class TestClass
178     {
179         int[string] info;
180 
181         static auto load( in void[] data )
182         {
183             auto str = cast(string)data.dup;
184             auto elems = str.split(",");
185             int[string] buf;
186             foreach( elem; elems )
187             {
188                 auto key = elem.split(":")[0];
189                 auto val = to!int( elem.split(":")[1] );
190                 buf[key] = val;
191             }
192             return new TestClass( buf );
193         }
194 
195         this( in int[string] I ) 
196         {
197             foreach( key, val; I )
198                 info[key] = val;
199             info.rehash();
200         }
201 
202         auto dump() const
203         {
204             string[] buf;
205             foreach( key, val; info ) buf ~= format( "%s:%s", key, val );
206             return cast(void[])( buf.join(",").dup );
207         }
208     }
209 
210     auto tc = new TestClass( [ "ok":1, "no":3, "yes":5 ] );
211 
212     auto a = PData( tc );
213     auto ta = a.as!TestClass;
214     assert( ta.info == tc.info );
215 
216     auto b = a;
217     b = "ok:1,no:3";
218     auto tb = b.as!TestClass;
219     tc.info.remove( "yes" );
220 
221     assert( tb.info == tc.info );
222 
223     tc.info["yes"] = 5;
224     b = a;
225     tb = b.as!TestClass;
226     assert( tb.info == tc.info );
227 }
228 
229 unittest
230 {
231     auto fnc_a() { return cast(immutable(ubyte)[])("hello_a".idup); }
232     auto fnc_b() { return cast(ubyte[])("hello_b".dup); }
233     auto a = PData( fnc_a() );
234     assert( a.as!string == "hello_a" );
235     auto b = PData( fnc_b() );
236     assert( b.as!string == "hello_b" );
237 
238     auto ca = const PData( fnc_a() );
239     assert( ca.as!string == "hello_a" );
240     auto cb = const PData( fnc_b() );
241     assert( cb.as!string == "hello_b" );
242 
243     auto ia = immutable PData( fnc_a() );
244     assert( ia.as!string == "hello_a" );
245     auto ib = immutable PData( fnc_b() );
246     assert( ib.as!string == "hello_b" );
247 
248     auto sa = shared PData( fnc_a() );
249     assert( sa.as!string == "hello_a" );
250     auto sb = shared PData( fnc_b() );
251     assert( sb.as!string == "hello_b" );
252 
253     auto sca = shared const PData( fnc_a() );
254     assert( sca.as!string == "hello_a" );
255     auto scb = shared const PData( fnc_b() );
256     assert( scb.as!string == "hello_b" );
257 
258     auto sia = shared immutable PData( fnc_a() );
259     assert( sia.as!string == "hello_a" );
260     auto sib = shared immutable PData( fnc_b() );
261     assert( sib.as!string == "hello_b" );
262 }