1 /++
2 $(H1 Thread-safe reference-counted context implementation).
3 +/
4 module mir.rc.context;
5 
6 import mir.type_info;
7 
8 /++
9 +/
10 struct mir_rc_context
11 {
12     ///
13     extern (C) void function(mir_rc_context*) @system nothrow @nogc pure deallocator;
14     ///
15     immutable(mir_type_info)* typeInfo;
16     ///
17     shared size_t counter;
18     ///
19     size_t length;
20 }
21 
22 /++
23 Increase counter by 1.
24 
25 Params:
26     context = shared_ptr context (not null)
27 +/
28 export extern(C)
29 void mir_rc_increase_counter(ref mir_rc_context context) @system nothrow @nogc pure
30 {
31     import core.atomic: atomicOp;
32     with(context)
33     {
34         if (counter)
35         {
36             counter.atomicOp!"+="(1);
37         }
38     }
39 }
40 
41 /++
42 Decrease counter by 1.
43 Destroys data if counter decreased from 1 to 0.
44 
45 Params:
46     context = shared_ptr context (not null)
47 +/
48 export extern(C)
49 void mir_rc_decrease_counter(ref mir_rc_context context) @system nothrow @nogc pure
50 {
51     pragma(inline, true);
52     import core.atomic: atomicOp;
53     with(context)
54     {
55         if (counter)
56         {
57             if (counter.atomicOp!"-="(1) == 0)
58             {
59                 mir_rc_delete(context);
60             }
61         }
62         // else
63         // {
64         //     assert(0);
65         // }
66     }
67 }
68 
69 /++
70 +/
71 export extern(C)
72 void mir_rc_delete(ref mir_rc_context context)
73     @system nothrow @nogc pure
74 {
75     assert(context.deallocator);
76     with(context)
77     {
78         with(typeInfo)
79         {
80             if (destructor)
81             {
82                 auto ptr = cast(void*)(&context + 1);
83                 auto i = length;
84                 assert(i);
85                 do
86                 {
87                     destructor(ptr);
88                     ptr += size;
89                 }
90                 while(--i);
91             }
92         }
93     }
94     if (context.counter)
95         assert(0);
96     version (mir_secure_memory)
97     {
98         (cast(ubyte*)(&context + 1))[0 .. context.length * context.typeInfo.size] = 0;
99     }
100     context.deallocator(&context);
101 }
102 
103 /++
104 +/
105 export extern(C)
106 mir_rc_context* mir_rc_create(
107     ref immutable(mir_type_info) typeInfo,
108     size_t length,
109     scope const void* payload = null,
110     bool initialize = true,
111     bool deallocate = true,
112     ) @system nothrow @nogc pure
113 {
114     import mir.internal.memory: malloc, free;
115     import core.stdc..string: memset, memcpy;
116 
117     assert(length);
118     auto size = length * typeInfo.size;
119     auto fullSize = mir_rc_context.sizeof + size;
120     if (auto p = malloc(fullSize))
121     {
122         version (mir_secure_memory)
123         {
124             (cast(ubyte*)p)[0 .. fullSize] = 0;
125         }
126         auto context = cast(mir_rc_context*)p;
127         context.deallocator = &free;
128         context.typeInfo = &typeInfo;
129         context.counter = deallocate;
130         context.length = length;
131 
132         if (initialize)
133         {
134             auto ptr = cast(void*)(context + 1);
135             if (payload)
136             {
137                 switch(typeInfo.size)
138                 {
139                     case 1:
140                         memset(ptr, *cast(ubyte*)payload, size);
141                         break;
142                     case 2:
143                         (cast(ushort*)ptr)[0 .. length] = *cast(ushort*)payload;
144                         break;
145                     case 4:
146                         (cast(uint*)ptr)[0 .. length] = *cast(uint*)payload;
147                         break;
148                     case 8:
149                         (cast(ulong*)ptr)[0 .. length] = *cast(ulong*)payload;
150                         break;
151                     static if (is(ucent))
152                     {
153                     case 16:
154                         (cast(ucent*)ptr)[0 .. length] = *cast(ucent*)payload;
155                         break;
156                     }
157                     default:
158                         foreach(i; 0 .. length)
159                         {
160                             memcpy(ptr, payload, typeInfo.size);
161                             ptr += typeInfo.size;
162                         }
163                 }
164             }
165             else
166             {
167                 memset(ptr, 0, size);
168             }
169         }
170         return context;
171     }
172     return null;
173 }
174 
175 ///
176 package mixin template CommonRCImpl()
177 {
178     ///
179     ThisTemplate!(const T) lightConst()() scope return const @nogc nothrow @trusted @property
180     { return *cast(typeof(return)*) &this; }
181 
182     /// ditto
183     ThisTemplate!(immutable T) lightImmutable()() scope return immutable @nogc nothrow @trusted @property
184     { return *cast(typeof(return)*) &this; }
185 
186     ///
187     ThisTemplate!(const Unqual!T) moveToConst()() scope return @nogc nothrow @trusted @property
188     {
189         import core.lifetime: move;
190         return move(*cast(typeof(return)*) &this);
191     }
192 
193     ///
194     pragma(inline, true)
195     size_t _counter() @trusted scope pure nothrow @nogc const @property
196     {
197         return cast(bool)this ? context.counter : 0;
198     }
199 
200     ///
201     C opCast(C)() const
202         if (is(Unqual!C == bool))
203     {
204         return _thisPtr !is null;
205     }
206 
207     ///
208     ref C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc @trusted
209         if (isImplicitlyConvertible!(T*, Q*))
210     {
211         return *cast(typeof(return)*)&this;
212     }
213 
214     /// ditto
215     C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc const @trusted
216         if (isImplicitlyConvertible!(const(T)*, Q*))
217     {
218         return *cast(typeof(return)*)&this;
219     }
220 
221     /// ditto
222     C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc immutable @trusted
223         if (isImplicitlyConvertible!(immutable(T)*, Q*))
224     {
225         return *cast(typeof(return)*)&this;
226     }
227 
228     /// ditto
229     C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc const @system
230         if (isImplicitlyConvertible!(immutable(T)*, Q*) && !isImplicitlyConvertible!(const(T)*, Q*))
231     {
232         return *cast(typeof(return)*)&this;
233     }
234 }