1 | /// -*- c++ -*- |
---|
2 | //***************************************************************************** |
---|
3 | /** @file countedref.h |
---|
4 | * |
---|
5 | * @author Alexander Dreyer |
---|
6 | * @date 2012-08-15 |
---|
7 | * |
---|
8 | * This file defines reusable classes supporting reference counted interpreter |
---|
9 | * objects and initiates the @c blackbox operations for high-level types |
---|
10 | * 'reference' and 'shared'. |
---|
11 | * |
---|
12 | * @note This works was supported by the "Industrial Algebra" project. |
---|
13 | * |
---|
14 | * @par Copyright: |
---|
15 | * (c) 2012 by The Singular Team, see LICENSE file |
---|
16 | **/ |
---|
17 | //***************************************************************************** |
---|
18 | |
---|
19 | |
---|
20 | #ifndef SINGULAR_COUNTEDREF_H_ |
---|
21 | #define SINGULAR_COUNTEDREF_H_ |
---|
22 | |
---|
23 | #include <omalloc/omalloc.h> |
---|
24 | #include <kernel/structs.h> |
---|
25 | #include <kernel/febase.h> |
---|
26 | #include "subexpr.h" |
---|
27 | #include "idrec.h" |
---|
28 | #include "ipid.h" |
---|
29 | /** @class CountedRefPtr |
---|
30 | * This class implements a smart pointer which handles pointer-style access |
---|
31 | * to a reference-counted structure and destructing the latter after use. |
---|
32 | * |
---|
33 | * The template arguments, include the pointer type @c PtrType, and two |
---|
34 | * integral (bool) properties: use @c Nondestructive to disallow destruction |
---|
35 | * and @c NeverNull to assume, that @c PtrType cannot be @c NULL. |
---|
36 | * Finally, @c CountType allows you to select a typ to represent the internal reference count. |
---|
37 | * |
---|
38 | * @note The class of @c PtrType must have an accessible integral attribute @c ref. |
---|
39 | * For convenience use @c RefCounter as public base. |
---|
40 | * In addition you must overload @c void CountedRefPtr_kill(PtrType) accordingly. |
---|
41 | **/ |
---|
42 | template <class PtrType, bool Nondestructive = false, bool NeverNull = false, |
---|
43 | class CountType = short> |
---|
44 | class CountedRefPtr { |
---|
45 | typedef CountedRefPtr self; |
---|
46 | |
---|
47 | public: |
---|
48 | //{ @name Name template arguments |
---|
49 | typedef PtrType ptr_type; |
---|
50 | typedef CountType count_type; |
---|
51 | enum { nondestructive = Nondestructive, never_null = NeverNull }; |
---|
52 | //} |
---|
53 | |
---|
54 | /// Default constructor @note: exisis only if @c NeverNull is false |
---|
55 | CountedRefPtr(): m_ptr(NULL) {} |
---|
56 | |
---|
57 | /// Convert from pointer |
---|
58 | CountedRefPtr(ptr_type ptr): m_ptr(ptr) { reclaim(); } |
---|
59 | |
---|
60 | /// Convert from compatible smart pointer |
---|
61 | template <bool Never> |
---|
62 | CountedRefPtr(const CountedRefPtr<ptr_type, !nondestructive, Never, count_type>& rhs): |
---|
63 | m_ptr(rhs.m_ptr) { reclaim(); } |
---|
64 | |
---|
65 | /// Construct refernce copy |
---|
66 | CountedRefPtr(const self& rhs): |
---|
67 | m_ptr(rhs.m_ptr) { reclaim(); } |
---|
68 | |
---|
69 | /// Unlink one reference |
---|
70 | ~CountedRefPtr() { release(); } |
---|
71 | |
---|
72 | //{ @name Replace data behind reference |
---|
73 | self& operator=(const self& rhs) { return operator=(rhs.m_ptr); } |
---|
74 | self& operator=(ptr_type ptr) { |
---|
75 | release(); |
---|
76 | m_ptr = ptr; |
---|
77 | reclaim(); |
---|
78 | return *this; |
---|
79 | } |
---|
80 | //} |
---|
81 | |
---|
82 | /// Checking equality |
---|
83 | bool operator==(const self& rhs) const { return m_ptr == rhs.m_ptr; } |
---|
84 | |
---|
85 | //{ @name Pointer-style interface |
---|
86 | bool operator==(ptr_type ptr) const { return m_ptr == ptr; } |
---|
87 | operator bool() const { return NeverNull || m_ptr; } |
---|
88 | operator const ptr_type() const { return m_ptr; } |
---|
89 | operator ptr_type() { return m_ptr; } |
---|
90 | const ptr_type operator->() const { return *this; } |
---|
91 | ptr_type operator->() { return *this; } |
---|
92 | //} |
---|
93 | |
---|
94 | /// @name Reference count interface |
---|
95 | //@{ |
---|
96 | count_type count() const { return (*this? m_ptr->ref: 0); } |
---|
97 | void reclaim() { if (*this) ++m_ptr->ref; } |
---|
98 | void release() { |
---|
99 | if (*this && (--m_ptr->ref <= 0) && !nondestructive) |
---|
100 | CountedRefPtr_kill(m_ptr); |
---|
101 | } |
---|
102 | //@} |
---|
103 | |
---|
104 | private: |
---|
105 | /// Store actual pointer |
---|
106 | ptr_type m_ptr; |
---|
107 | }; |
---|
108 | |
---|
109 | /** @class RefCounter |
---|
110 | * This class implements implements a refernce counter which we can use |
---|
111 | * as a public base of objects managed by @CountedRefPtr. |
---|
112 | **/ |
---|
113 | class RefCounter { |
---|
114 | |
---|
115 | public: |
---|
116 | /// Name numerical type for enumbering |
---|
117 | typedef short count_type; |
---|
118 | |
---|
119 | /// Allow our smart pointer to access internals |
---|
120 | template <class, bool, bool, class> friend class CountedRefPtr; |
---|
121 | |
---|
122 | /// Any Constructor resets the counter |
---|
123 | RefCounter(...): ref(0) {} |
---|
124 | |
---|
125 | /// Destructor |
---|
126 | ~RefCounter() { assume(ref == 0); } |
---|
127 | |
---|
128 | private: |
---|
129 | /// Number of references |
---|
130 | count_type ref; // naming consistent with other classes |
---|
131 | }; |
---|
132 | |
---|
133 | |
---|
134 | template <class PtrType> |
---|
135 | class CountedRefWeakPtr; |
---|
136 | |
---|
137 | template <class PtrType> |
---|
138 | class CountedRefIndirectPtr: |
---|
139 | public RefCounter { |
---|
140 | public: |
---|
141 | friend class CountedRefWeakPtr<PtrType>; |
---|
142 | ~CountedRefIndirectPtr() { } |
---|
143 | |
---|
144 | private: |
---|
145 | CountedRefIndirectPtr(PtrType ptr): m_ptr(ptr) { } |
---|
146 | CountedRefIndirectPtr& operator=(PtrType ptr) { m_ptr = ptr; return *this; } |
---|
147 | |
---|
148 | PtrType m_ptr; |
---|
149 | }; |
---|
150 | |
---|
151 | template <class PtrType> |
---|
152 | inline void CountedRefPtr_kill(CountedRefIndirectPtr<PtrType>* pval) { delete pval; } |
---|
153 | |
---|
154 | template <class PtrType> |
---|
155 | class CountedRefWeakPtr { |
---|
156 | typedef CountedRefWeakPtr self; |
---|
157 | |
---|
158 | public: |
---|
159 | |
---|
160 | /// @name Name template arguments |
---|
161 | //@{ Name template arguments |
---|
162 | typedef PtrType ptr_type; |
---|
163 | typedef CountedRefPtr<CountedRefIndirectPtr<ptr_type>*> ptrptr_type; |
---|
164 | //@} |
---|
165 | |
---|
166 | /// Construct unassigned weak reference |
---|
167 | CountedRefWeakPtr(): m_indirect(NULL) { } |
---|
168 | |
---|
169 | /// Convert from pointer |
---|
170 | CountedRefWeakPtr(ptr_type ptr): m_indirect(new CountedRefIndirectPtr<ptr_type>(ptr)) { } |
---|
171 | |
---|
172 | /// Construct copy |
---|
173 | CountedRefWeakPtr(const self& rhs): m_indirect(rhs.m_indirect) { } |
---|
174 | |
---|
175 | /// Unlink one reference (handled by CountedRefPtr) |
---|
176 | ~CountedRefWeakPtr() { } |
---|
177 | |
---|
178 | /// Mark weak reference as invalid |
---|
179 | void invalidate() { *this = NULL; } |
---|
180 | |
---|
181 | /// Test whether reference was never used |
---|
182 | bool unassigned() const { return !m_indirect; } |
---|
183 | /// Pointer-style interface |
---|
184 | //@{ |
---|
185 | operator bool() const { return operator->(); } |
---|
186 | self& operator=(const self& rhs) { |
---|
187 | m_indirect = rhs.m_indirect; |
---|
188 | return *this; |
---|
189 | } |
---|
190 | self& operator=(ptr_type ptr) { |
---|
191 | if (!m_indirect) |
---|
192 | m_indirect = new CountedRefIndirectPtr<ptr_type>(ptr); |
---|
193 | else |
---|
194 | m_indirect->m_ptr = ptr; |
---|
195 | return *this; |
---|
196 | } |
---|
197 | bool operator==(ptr_type ptr) const { |
---|
198 | return m_indirect &&(m_indirect->m_ptr == ptr); |
---|
199 | } |
---|
200 | bool operator!=(ptr_type rhs) const { return !operator==(rhs); } |
---|
201 | const ptr_type operator->() const { return (m_indirect? m_indirect->m_ptr: NULL); } |
---|
202 | ptr_type operator->() { return (m_indirect? m_indirect->m_ptr:NULL); } |
---|
203 | //@} |
---|
204 | private: |
---|
205 | ptrptr_type m_indirect; |
---|
206 | }; |
---|
207 | |
---|
208 | |
---|
209 | |
---|
210 | /** @class LeftvHelper |
---|
211 | * This class implements some recurrent code sniplets to be used with |
---|
212 | * @c leftv and @c idhdl.implements a refernce counter which we can use |
---|
213 | **/ |
---|
214 | class LeftvHelper { |
---|
215 | public: |
---|
216 | static leftv idify(leftv head, idhdl* root) { |
---|
217 | idhdl handle = newid(head, root); |
---|
218 | leftv res = (leftv)omAlloc0(sizeof(*res)); |
---|
219 | res->data =(void*) handle; |
---|
220 | res->rtyp = IDHDL; |
---|
221 | return res; |
---|
222 | } |
---|
223 | |
---|
224 | static idhdl newid(leftv head, idhdl* root) { |
---|
225 | |
---|
226 | static unsigned int counter = 0; |
---|
227 | char* name = (char*) omAlloc0(512); |
---|
228 | sprintf(name, " :%u:%p:_shared_: ", ++counter, head->data); |
---|
229 | if ((*root) == NULL ) |
---|
230 | enterid(name, 0, head->rtyp, root, TRUE, FALSE); |
---|
231 | else |
---|
232 | *root = (*root)->set(name, 0, head->rtyp, TRUE); |
---|
233 | |
---|
234 | IDDATA(*root) = (char*) head->data; |
---|
235 | return *root; |
---|
236 | } |
---|
237 | |
---|
238 | static void clearid(idhdl handle, idhdl* root) { |
---|
239 | IDDATA(handle)=NULL; |
---|
240 | IDTYP(handle)=NONE; |
---|
241 | killhdl2(handle, root, NULL); |
---|
242 | } |
---|
243 | |
---|
244 | template <class Type> |
---|
245 | static Type* cpy(Type* result, Type* data) { |
---|
246 | return (Type*)memcpy(result, data, sizeof(Type)); |
---|
247 | } |
---|
248 | template <class Type> |
---|
249 | static Type* cpy(Type* data) { |
---|
250 | return cpy((Type*)omAlloc0(sizeof(Type)), data); |
---|
251 | } |
---|
252 | template <class Type> |
---|
253 | static Type* recursivecpy(Type* data) { |
---|
254 | if (data == NULL) return data; |
---|
255 | Type* result = cpy(data); |
---|
256 | result->next = recursivecpy(data->next); |
---|
257 | return result; |
---|
258 | } |
---|
259 | template <class Type> |
---|
260 | static Type* shallowcpy(Type* result, Type* data) { |
---|
261 | cpy(result, data)->e = recursivecpy(data->e); |
---|
262 | return result; |
---|
263 | } |
---|
264 | template <class Type> |
---|
265 | static Type* shallowcpy(Type* data) { |
---|
266 | return shallowcpy((Type*) omAlloc0(sizeof(Type)), data); |
---|
267 | } |
---|
268 | template <class Type> |
---|
269 | static void recursivekill(Type* current) { |
---|
270 | if(current == NULL) return; |
---|
271 | recursivekill(current->next); |
---|
272 | omFree(current); |
---|
273 | } |
---|
274 | static leftv allocate() { return (leftv)omAlloc0(sizeof(sleftv)); } |
---|
275 | |
---|
276 | }; |
---|
277 | |
---|
278 | /** @class LeftvShallow |
---|
279 | * Ths class wraps @c leftv by taking into acount memory allocation, destruction |
---|
280 | * as well as shallowly copying of a given @c leftv, i.e. we just copy auxiliary |
---|
281 | * information (like subexpressions), but not the actual data. |
---|
282 | * |
---|
283 | * @note This is useful to avoid invalidating @c leftv while operating on th |
---|
284 | **/ |
---|
285 | class LeftvShallow: |
---|
286 | public LeftvHelper { |
---|
287 | typedef LeftvShallow self; |
---|
288 | |
---|
289 | public: |
---|
290 | /// Just allocate (all-zero) @c leftv |
---|
291 | LeftvShallow(): m_data(allocate()) { } |
---|
292 | /// Shallow copy the input data |
---|
293 | LeftvShallow(leftv data): m_data(shallowcpy(data)) { } |
---|
294 | /// Construct (shallow) copy of @c *this |
---|
295 | LeftvShallow(const self& rhs): m_data(shallowcpy(rhs.m_data)) { } |
---|
296 | |
---|
297 | /// Destruct |
---|
298 | ~LeftvShallow() { |
---|
299 | recursivekill(m_data->e); |
---|
300 | omFree(m_data); |
---|
301 | } |
---|
302 | |
---|
303 | /// Assign shallow copy of the input |
---|
304 | self& operator=(leftv rhs) { |
---|
305 | recursivekill(m_data->e); |
---|
306 | shallowcpy(m_data, rhs); |
---|
307 | return *this; |
---|
308 | } |
---|
309 | /// Assign (shallow) copy of @c *this |
---|
310 | self& operator=(const self& rhs) { return (*this) = rhs.m_data; } |
---|
311 | |
---|
312 | /// @name Pointer-style access |
---|
313 | //@{ |
---|
314 | const leftv operator->() const { return m_data; } |
---|
315 | leftv operator->() { return m_data; } |
---|
316 | //@] |
---|
317 | |
---|
318 | protected: |
---|
319 | /// The actual data pointer |
---|
320 | leftv m_data; |
---|
321 | }; |
---|
322 | |
---|
323 | /** @class LeftvDeep |
---|
324 | * This class wraps @c leftv by taking into acount memory allocation, destruction |
---|
325 | * as well as deeply copying of a given @c leftv, i.e. we also take over |
---|
326 | * ownership of the @c leftv data. |
---|
327 | * |
---|
328 | * We have two variants: |
---|
329 | + LeftvDeep(leftv): treats referenced identifiers as "the data" |
---|
330 | + LeftvDeep(leftv, copy_tag): takes care of a full copy of identifier's data |
---|
331 | * |
---|
332 | * @note It invalidats @c leftv on input. |
---|
333 | **/ |
---|
334 | class LeftvDeep: |
---|
335 | public LeftvHelper { |
---|
336 | typedef LeftvDeep self; |
---|
337 | |
---|
338 | /// @name Do not permit copying (avoid inconsistence) |
---|
339 | //@{ |
---|
340 | self& operator=(const self&); |
---|
341 | LeftvDeep(const self&); |
---|
342 | //@} |
---|
343 | |
---|
344 | public: |
---|
345 | /// Allocate all-zero object by default |
---|
346 | LeftvDeep(): m_data(allocate()) {} |
---|
347 | |
---|
348 | /// Store a deep copy of the data |
---|
349 | /// @ note Occupies the provided @c leftv and invalidates the latter |
---|
350 | LeftvDeep(leftv data): m_data(cpy(data)) { |
---|
351 | data->e = NULL; // occupy subexpression |
---|
352 | if(!isid()) m_data->data=data->CopyD(); |
---|
353 | } |
---|
354 | |
---|
355 | /// Construct even deeper copy: |
---|
356 | /// Skip identifier (if any) and take care of the data on our own |
---|
357 | struct copy_tag {}; |
---|
358 | LeftvDeep(leftv data, copy_tag): m_data(allocate()) { m_data->Copy(data); } |
---|
359 | |
---|
360 | /// Really clear data |
---|
361 | ~LeftvDeep() { m_data->CleanUp(); } |
---|
362 | |
---|
363 | /// @name Access via shallow copy to avoid invalidating the stored handle |
---|
364 | //@{ |
---|
365 | operator LeftvShallow() { return m_data; } |
---|
366 | LeftvShallow operator*() {return *this; } |
---|
367 | //@} |
---|
368 | |
---|
369 | /// Determine whether we point to the same data |
---|
370 | bool like(const self& rhs) const { return m_data->data == rhs.m_data->data; } |
---|
371 | |
---|
372 | /// Reassign a new deep copy by occupieing another @c leftv |
---|
373 | /// @note clears @c *this in the first |
---|
374 | self& operator=(leftv rhs) { |
---|
375 | if(isid()) { |
---|
376 | m_data->e = rhs->e; |
---|
377 | rhs->e = NULL; |
---|
378 | IDTYP((idhdl)m_data->data) = rhs->Typ(); |
---|
379 | IDDATA((idhdl)m_data->data) = (char*) rhs->CopyD(); |
---|
380 | } |
---|
381 | else { |
---|
382 | m_data->CleanUp(); |
---|
383 | m_data->Copy(rhs); |
---|
384 | } |
---|
385 | return *this; |
---|
386 | } |
---|
387 | |
---|
388 | /// Check a given context for our identifier |
---|
389 | BOOLEAN brokenid(idhdl context) const { |
---|
390 | assume(isid()); |
---|
391 | return (context == NULL) || |
---|
392 | ((context != (idhdl) m_data->data) && brokenid(IDNEXT(context))); |
---|
393 | } |
---|
394 | |
---|
395 | /// Put a shallow copy to given @c leftv |
---|
396 | BOOLEAN put(leftv result) { |
---|
397 | leftv next = result->next; |
---|
398 | result->next = NULL; |
---|
399 | result->CleanUp(); |
---|
400 | |
---|
401 | shallowcpy(result, m_data); |
---|
402 | result->next = next; |
---|
403 | return FALSE; |
---|
404 | } |
---|
405 | |
---|
406 | /// Get additional data (e.g. subexpression data) from likewise instances |
---|
407 | BOOLEAN retrieve(leftv res) { |
---|
408 | if (res->data == m_data->data) { |
---|
409 | if(m_data->e != res->e) recursivekill(m_data->e); |
---|
410 | cpy(m_data, res); |
---|
411 | res->Init(); |
---|
412 | return TRUE; |
---|
413 | } |
---|
414 | return FALSE; |
---|
415 | } |
---|
416 | |
---|
417 | |
---|
418 | /// Check for being an identifier |
---|
419 | BOOLEAN isid() const { return m_data->rtyp==IDHDL;} |
---|
420 | /// Test whether we reference to ring-dependent data |
---|
421 | BOOLEAN ringed() { return m_data->RingDependend(); } |
---|
422 | /// Check whether (all-zero) initialized data was never assigned. |
---|
423 | BOOLEAN unassigned() const { return m_data->Typ()==0; } |
---|
424 | |
---|
425 | /// Wrap data by identifier, if not done yet |
---|
426 | leftv idify(idhdl* root) { |
---|
427 | leftv res = (isid()? m_data: LeftvHelper::idify(m_data, root)); |
---|
428 | ++(((idhdl)res->data)->ref); |
---|
429 | return res; |
---|
430 | } |
---|
431 | |
---|
432 | /// Erase identifier handles by @c *this |
---|
433 | /// @note Assumes that we reference an identifier and that we own the latter. |
---|
434 | /// This is useful to clear the result of a subsequent call of @c idify. |
---|
435 | void clearid(idhdl* root) { |
---|
436 | assume(isid()); |
---|
437 | if (--((idhdl)m_data->data)->ref <= 0) // clear only if we own |
---|
438 | LeftvHelper::clearid((idhdl)m_data->data, root); |
---|
439 | } |
---|
440 | |
---|
441 | private: |
---|
442 | /// Store the actual data |
---|
443 | leftv m_data; |
---|
444 | }; |
---|
445 | |
---|
446 | /// Initialize @c blackbox types 'reference' and 'shared', or both |
---|
447 | void countedref_reference_load(); |
---|
448 | void countedref_shared_load(); |
---|
449 | |
---|
450 | //#define SINGULAR_COUNTEDREF_AUTOLOAD |
---|
451 | |
---|
452 | inline void |
---|
453 | countedref_init() |
---|
454 | { |
---|
455 | #ifdef SINGULAR_COUNTEDREF_AUTOLOAD |
---|
456 | countedref_reference_load(); |
---|
457 | countedref_shared_load(); |
---|
458 | #endif |
---|
459 | } |
---|
460 | |
---|
461 | |
---|
462 | #endif /*SINGULAR_COUNTEDREF_H_ */ |
---|
463 | |
---|