Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
QoreHashNodeIntern.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 QoreHashNodeIntern.h
4
5 Qore Programming Language
6
7 Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORE_QOREHASHNODEINTERN_H
33
34#define _QORE_QOREHASHNODEINTERN_H
35
36#include <list>
37
38// to maintain the order of inserts
39class HashMember {
40public:
41 QoreValue val;
42 std::string key;
43
44 DLLLOCAL HashMember(const char* n_key) : key(n_key) {
45 }
46
47 DLLLOCAL ~HashMember() {
48 }
49};
50
51typedef std::list<HashMember*> qhlist_t;
52
53#ifdef HAVE_QORE_HASH_MAP
54//#warning compiling with hash_map
55#include <qore/hash_map_include.h>
56#include "qore/intern/xxhash.h"
57
58typedef HASH_MAP<const char*, qhlist_t::iterator, qore_hash_str, eqstr> hm_hm_t;
59#else
60typedef std::map<const char*, qhlist_t::iterator, ltstr> hm_hm_t;
61#endif
62
63// QoreHashIterator private class
64class qhi_priv {
65public:
66 qhlist_t::iterator i;
67 bool val;
68
69 DLLLOCAL qhi_priv() : val(false) {
70 }
71
72 DLLLOCAL qhi_priv(const qhi_priv& old) : i(old.i), val(old.val) {
73 }
74
75 DLLLOCAL bool valid() const {
76 return val;
77 }
78
79 DLLLOCAL bool next(qhlist_t& ml) {
80 //printd(0, "qhi_priv::next() this: %p val: %d\n", this, val);
81 if (!val) {
82 if (ml.begin() != ml.end()) {
83 i = ml.begin();
84 val = true;
85 }
86 } else {
87 ++i;
88 if (i == ml.end())
89 val = false;
90 }
91 return val;
92 }
93
94 DLLLOCAL bool prev(qhlist_t& ml) {
95 if (!val) {
96 if (ml.begin() != ml.end()) {
97 i = ml.end();
98 --i;
99 val = true;
100 }
101 } else {
102 if (i == ml.begin())
103 val = false;
104 else
105 --i;
106 }
107 return val;
108 }
109
110 DLLLOCAL void reset() {
111 val = false;
112 }
113
114 DLLLOCAL static qhi_priv* get(HashIterator& i) {
115 return i.priv;
116 }
117};
118
119class qore_hash_private {
120public:
121 qhlist_t member_list;
122 hm_hm_t hm;
123 // either hashdecl or complexTypeInfo can be set, but not both
124 const TypedHashDecl* hashdecl = nullptr;
125 const QoreTypeInfo* complexTypeInfo = nullptr;
126 unsigned obj_count = 0;
127#ifdef DEBUG
128 bool is_obj = false;
129#endif
130
131 DLLLOCAL qore_hash_private() {
132 }
133
134 // hashes should always be empty by the time they are deleted
135 // because object destructors need to be run...
136 DLLLOCAL ~qore_hash_private() {
137 assert(member_list.empty());
138 }
139
140 DLLLOCAL QoreValue getKeyValueIntern(const char* key) const;
141
142 DLLLOCAL QoreValue getKeyValueExistence(const char* key, bool& exists, ExceptionSink* xsink) const;
143
144 DLLLOCAL QoreValue getKeyValueExistenceIntern(const char* key, bool& exists) const;
145
146 DLLLOCAL int checkKey(const char* key, ExceptionSink* xsink) const;
147
148 DLLLOCAL QoreValue getReferencedKeyValueIntern(const char* key) const {
149 bool exists;
150 return getReferencedKeyValueIntern(key, exists);
151 }
152
153 DLLLOCAL QoreValue getReferencedKeyValueIntern(const char* key, bool& exists) const {
154 assert(key);
155
156 hm_hm_t::const_iterator i = hm.find(key);
157 if (i != hm.end()) {
158 exists = true;
159 return (*(i->second))->val.refSelf();
160 }
161
162 exists = false;
163 return QoreValue();
164 }
165
166 DLLLOCAL int64 getKeyAsBigInt(const char* key, bool &found) const {
167 assert(key);
168 hm_hm_t::const_iterator i = hm.find(key);
169
170 if (i != hm.end()) {
171 found = true;
172 return (*(i->second))->val.getAsBigInt();
173 }
174
175 found = false;
176 return 0;
177 }
178
179 DLLLOCAL bool getKeyAsBool(const char* key, bool& found) const {
180 assert(key);
181 hm_hm_t::const_iterator i = hm.find(key);
182
183 if (i != hm.end()) {
184 found = true;
185 return (*(i->second))->val.getAsBool();
186 }
187
188 found = false;
189 return false;
190 }
191
192 DLLLOCAL bool existsKey(const char* key) const {
193 assert(key);
194 return hm.find(key) != hm.end();
195 }
196
197 DLLLOCAL bool existsKeyValue(const char* key) const {
198 assert(key);
199 hm_hm_t::const_iterator i = hm.find(key);
200 if (i == hm.end())
201 return false;
202 return !(*(i->second))->val.isNothing();
203 }
204
205 DLLLOCAL HashMember* findMember(const char* key) {
206 assert(key);
207 hm_hm_t::iterator i = hm.find(key);
208 return i != hm.end() ? (*(i->second)) : nullptr;
209 }
210
211 DLLLOCAL HashMember* findCreateMember(const char* key) {
212 // otherwise create the new hash entry
213 HashMember* om = findMember(key);
214 if (om)
215 return om;
216
217 om = new HashMember(key);
218 assert(om->val.isNothing());
219 member_list.push_back(om);
220
221 // add to the map
222 qhlist_t::iterator i = member_list.end();
223 --i;
224 hm[om->key.c_str()] = i;
225
226 // return the new member
227 return om;
228 }
229
230 DLLLOCAL QoreValue& getValueRef(const char* key) {
231 return findCreateMember(key)->val;
232 }
233
234 // NOTE: does not delete the value, this must be done by the caller before this call
235 // also does not delete map entry; must be done outside this call
236 DLLLOCAL void internDeleteKey(qhlist_t::iterator i) {
237 HashMember* om = *i;
238
239 member_list.erase(i);
240
241 // free om memory
242 delete om;
243 }
244
245 DLLLOCAL void deleteKey(const char* key, ExceptionSink *xsink) {
246 assert(key);
247
248 hm_hm_t::iterator i = hm.find(key);
249
250 if (i == hm.end())
251 return;
252
253 qhlist_t::iterator li = i->second;
254 hm.erase(i);
255
256 // dereference node if present
257 AbstractQoreNode* n = (*li)->val.assignNothing();
258 if (n) {
259 if (needs_scan(n))
260 incScanCount(-1);
261
262 if (n->getType() == NT_OBJECT)
263 reinterpret_cast<QoreObject*>(n)->doDelete(xsink);
264 n->deref(xsink);
265 }
266
267 internDeleteKey(li);
268 }
269
270 // removes the value and dereferences it, without performing a delete on it
271 DLLLOCAL void removeKey(const char* key, ExceptionSink *xsink) {
272 takeKeyValueIntern(key).discard(xsink);
273 }
274
275 DLLLOCAL QoreValue takeKeyValueIntern(const char* key) {
276 assert(key);
277
278 hm_hm_t::iterator i = hm.find(key);
279
280 if (i == hm.end())
281 return QoreValue();
282
283 qhlist_t::iterator li = i->second;
284 hm.erase(i);
285
286 QoreValue rv = (*li)->val;
287 internDeleteKey(li);
288
289 if (needs_scan(rv))
290 incScanCount(-1);
291
292 return rv;
293 }
294
295 DLLLOCAL QoreValue takeKeyValueIntern(const char* key, bool& exists) {
296 assert(key);
297
298 hm_hm_t::iterator i = hm.find(key);
299
300 if (i == hm.end()) {
301 exists = false;
302 return QoreValue();
303 }
304 exists = true;
305
306 qhlist_t::iterator li = i->second;
307 hm.erase(i);
308
309 QoreValue rv = (*li)->val;
310 internDeleteKey(li);
311
312 if (needs_scan(rv))
313 incScanCount(-1);
314
315 return rv;
316 }
317
318 DLLLOCAL const char* getFirstKey() const {
319 return member_list.empty() ? nullptr : member_list.front()->key.c_str();
320 }
321
322 DLLLOCAL const char* getLastKey() const {
323 return member_list.empty() ? nullptr : member_list.back()->key.c_str();
324 }
325
326 DLLLOCAL QoreListNode* getKeys() const;
327
328 DLLLOCAL void merge(const qore_hash_private& h0, ExceptionSink* xsink);
329
330 // to be called when a lock is held to avoid dereferencing in the lock
331 DLLLOCAL void merge(const qore_hash_private& h, SafeDerefHelper& sdh, ExceptionSink* xsink);
332
333 DLLLOCAL int getLValue(const char* key, LValueHelper& lvh, bool for_remove, ExceptionSink* xsink);
334
335 DLLLOCAL void getTypeName(QoreString& str) const {
336 if (hashdecl)
337 str.sprintf("hash<%s>", hashdecl->getName());
338 else if (complexTypeInfo)
339 str.concat(QoreTypeInfo::getName(complexTypeInfo));
340 else
341 str.concat("hash");
342 }
343
344 // issue #3877: returns a typed list
345 DLLLOCAL QoreListNode* getValues() const;
346
347 DLLLOCAL QoreHashNode* getCopy() const {
348 QoreHashNode* h = new QoreHashNode;
349 if (hashdecl)
350 h->priv->hashdecl = hashdecl;
351 if (complexTypeInfo)
352 h->priv->complexTypeInfo = complexTypeInfo;
353 return h;
354 }
355
356 DLLLOCAL QoreHashNode* getEmptyCopy(bool is_value) const {
357 QoreHashNode* h = new QoreHashNode(!is_value);
358 if (hashdecl)
359 h->priv->hashdecl = hashdecl;
360 if (complexTypeInfo)
361 h->priv->complexTypeInfo = complexTypeInfo;
362 return h;
363 }
364
365 DLLLOCAL QoreHashNode* copyCheckNewType(const qore_hash_private& other) const {
366 QoreHashNode* rv = copy();
367 if (hashdecl || other.hashdecl) {
368 if (!hashdecl || !other.hashdecl || !hashdecl->equal(other.hashdecl)) {
369 rv->priv->hashdecl = nullptr;
370 rv->priv->complexTypeInfo = autoHashTypeInfo;
371 }
372 } else {
373 const QoreTypeInfo* orig_ctype, * ctype;
374 orig_ctype = ctype = QoreTypeInfo::getUniqueReturnComplexHash(complexTypeInfo);
375 const QoreTypeInfo* newElementType = QoreTypeInfo::getUniqueReturnComplexHash(other.complexTypeInfo);
376 if ((!ctype || ctype == anyTypeInfo) && (!newElementType || newElementType == anyTypeInfo)) {
377 rv->priv->complexTypeInfo = nullptr;
378 } else if (QoreTypeInfo::matchCommonType(ctype, newElementType)) {
379 rv->priv->complexTypeInfo = ctype == orig_ctype ? complexTypeInfo : qore_get_complex_hash_type(ctype);
380 } else {
381 rv->priv->complexTypeInfo = autoHashTypeInfo;
382 }
383 }
384 return rv;
385 }
386
387 DLLLOCAL QoreHashNode* copy(const QoreTypeInfo* newComplexTypeInfo) const {
388 QoreHashNode* h = new QoreHashNode;
389 h->priv->complexTypeInfo = newComplexTypeInfo;
390 copyIntern(*h->priv);
391 return h;
392 }
393
394 // strip = copy without type information
395 DLLLOCAL QoreHashNode* copy(bool strip = false) const {
396 // issue #2791: perform type stripping at the source
397 if (!strip || (!complexTypeInfo && !hashdecl)) {
398 QoreHashNode* h = getCopy();
399 copyIntern(*h->priv);
400 return h;
401 }
402 QoreHashNode* h = new QoreHashNode;
403 // copy all members to new object
404 for (auto& i : member_list) {
405 hash_assignment_priv ha(*h, i->key.c_str());
406 QoreValue v = copy_strip_complex_types(i->val);
407#ifdef DEBUG
408 assert(ha.swap(v).isNothing());
409#else
410 ha.swap(v);
411#endif
412 }
413
414 return h;
415 }
416
417 DLLLOCAL void copyIntern(qore_hash_private& h) const {
418 // copy all members to new object
419 for (auto& i : member_list) {
420 hash_assignment_priv ha(h, i->key.c_str());
421#ifdef DEBUG
422 assert(ha.swap(i->val.refSelf()).isNothing());
423#else
424 ha.swap(i->val.refSelf());
425#endif
426 }
427 }
428
429 DLLLOCAL QoreHashNode* plusEquals(const QoreHashNode* h, ExceptionSink* xsink) const {
430 // issue #2791: perform type stripping at the source
431 // issue #3429: maintain types unless we have a plain hash; convert to hash<auto> if the types are not compatible
432 ReferenceHolder<QoreHashNode> rv(copyCheckNewType(*h->priv), xsink);
433 rv->priv->merge(*h->priv, xsink);
434 return *xsink ? nullptr : rv.release();
435 }
436
437 DLLLOCAL QoreHashNode* evalImpl(ExceptionSink* xsink) const {
438 QoreHashNodeHolder h(getCopy(), xsink);
439
440 for (qhlist_t::const_iterator i = member_list.begin(), e = member_list.end(); i != e; ++i) {
441 h->priv->setKeyValue((*i)->key, (*i)->val.refSelf(), xsink);
442 if (*xsink)
443 return nullptr;
444 }
445
446 return h.release();
447 }
448
449 DLLLOCAL void setKeyValue(const char* key, QoreValue val, SafeDerefHelper& sdh, ExceptionSink* xsink) {
450 hash_assignment_priv ha(*this, key);
451 // in case of assigning keys to an initialized hashdecl, the key may already have a value
452 ha.assign(val, sdh, xsink);
453 }
454
455 DLLLOCAL void setKeyValue(const std::string& key, QoreValue val, SafeDerefHelper& sdh, ExceptionSink* xsink) {
456 hash_assignment_priv ha(*this, key.c_str());
457 // in case of assigning keys to an initialized hashdecl, the key may already have a value
458 ha.assign(val, sdh, xsink);
459 }
460
461 DLLLOCAL void setKeyValueIntern(const char* key, QoreValue v) {
462 hash_assignment_priv ha(*this, key);
463 // in case of assigning keys to an initialized hashdecl, the key may already have a value
464 // if the value is an object of a class that throws an exception in the destructor, then a crash will result
465 ValueHolder old(ha.swap(v), nullptr);
466 }
467
468 DLLLOCAL void setKeyValue(const char* key, QoreValue val, ExceptionSink* xsink) {
469 hash_assignment_priv ha(*this, key);
470 ha.assign(val, xsink);
471 }
472
473 DLLLOCAL void setKeyValue(const std::string& key, QoreValue val, ExceptionSink* xsink) {
474 hash_assignment_priv ha(*this, key.c_str());
475 ha.assign(val, xsink);
476 }
477
478 DLLLOCAL void setKeyValue(const char* key, QoreValue val, qore_object_private* o, ExceptionSink* xsink) {
479 hash_assignment_priv ha(*this, key, false, o);
480 ha.assign(val, xsink);
481 }
482
483 DLLLOCAL bool derefImpl(ExceptionSink* xsink, bool reverse = false) {
484 if (reverse) {
485 for (qhlist_t::reverse_iterator i = member_list.rbegin(), e = member_list.rend(); i != e; ++i) {
486 (*i)->val.discard(xsink);
487 delete *i;
488 }
489 } else {
490 for (qhlist_t::iterator i = member_list.begin(), e = member_list.end(); i != e; ++i) {
491 (*i)->val.discard(xsink);
492 delete *i;
493 }
494 }
495
496 member_list.clear();
497 hm.clear();
498 obj_count = 0;
499 return true;
500 }
501
502 DLLLOCAL QoreValue swapKeyValueIfExists(const char* key, QoreValue val, qore_object_private* o, bool& exists) {
503 hash_assignment_priv ha(*this, key, true, o);
504 if (!ha.exists()) {
505 exists = false;
506 return QoreValue();
507 }
508 exists = true;
509 return ha.swap(val);
510 }
511
512 DLLLOCAL QoreValue swapKeyValue(const char* key, QoreValue val, qore_object_private* o) {
513 //printd(0, "qore_hash_private::swapKeyValue() this: %p key: %s val: %p (%s) deprecated API called\n", this, key, val, get_node_type(val));
514 //assert(false);
515 hash_assignment_priv ha(*this, key, false, o);
516 return ha.swap(val);
517 }
518
519 DLLLOCAL void clear(ExceptionSink* xsink, bool reverse) {
520 derefImpl(xsink, reverse);
521 }
522
523 DLLLOCAL size_t size() const {
524 return member_list.size();
525 }
526
527 DLLLOCAL bool empty() const {
528 return member_list.empty();
529 }
530
531 DLLLOCAL void incScanCount(int dt) {
532 assert(!is_obj);
533 assert(dt);
534 assert(obj_count || dt > 0);
535 //printd(5, "qore_hash_private::incScanCount() this: %p dt: %d: %d -> %d\n", this, dt, obj_count, obj_count + dt);
536 obj_count += dt;
537 }
538
539 DLLLOCAL const QoreTypeInfo* getValueTypeInfo() const {
540 return complexTypeInfo ? QoreTypeInfo::getComplexHashValueType(complexTypeInfo) : nullptr;
541 }
542
543 DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
544 if (hashdecl)
545 return hashdecl->getTypeInfo();
546 if (complexTypeInfo)
547 return complexTypeInfo;
548 return hashTypeInfo;
549 }
550
551 DLLLOCAL const TypedHashDecl* getHashDecl() const {
552 return hashdecl;
553 }
554
555 DLLLOCAL static QoreHashNode* getPlainHash(QoreHashNode* h) {
556 if (!h->priv->hashdecl && !h->priv->complexTypeInfo)
557 return h;
558 // no exception is possible
559 ReferenceHolder<QoreHashNode> holder(h, nullptr);
560 return h->priv->copy(true);
561 }
562
563 DLLLOCAL static QoreHashNode* newHashDecl(const TypedHashDecl* hd) {
564 QoreHashNode* rv = new QoreHashNode;
565 rv->priv->hashdecl = hd;
566 return rv;
567 }
568
569 DLLLOCAL static qore_hash_private* get(QoreHashNode& h) {
570 return h.priv;
571 }
572
573 DLLLOCAL static const qore_hash_private* get(const QoreHashNode& h) {
574 return h.priv;
575 }
576
577 // returns -1 if no checks are needed or if an error is raised, 0 if OK to check
578 DLLLOCAL static int parseInitHashInitialization(const QoreProgramLocation* loc, QoreParseContext& parse_context,
579 QoreParseListNode* args, QoreValue& arg, int& err);
580
581 DLLLOCAL static int parseInitComplexHashInitialization(const QoreProgramLocation* loc, QoreParseContext& parse_context,
582 QoreParseListNode* args);
583
584 DLLLOCAL static int parseCheckComplexHashInitialization(const QoreProgramLocation* loc,
585 const QoreTypeInfo* valueTypeInfo, const QoreTypeInfo* argTypeInfo, QoreValue exp,
586 const char* context_action, bool strict_check = true);
587
588 DLLLOCAL static int parseCheckTypedAssignment(const QoreProgramLocation* loc, QoreValue arg,
589 const QoreTypeInfo* vti, const char* context_action, bool strict_check = true);
590
591 DLLLOCAL static QoreHashNode* newComplexHash(const QoreTypeInfo* typeInfo, const QoreParseListNode* args,
592 ExceptionSink* xsink);
593
594 DLLLOCAL static QoreHashNode* newComplexHashFromHash(const QoreTypeInfo* typeInfo, QoreHashNode* init,
595 ExceptionSink* xsink);
596
597 DLLLOCAL static unsigned getScanCount(const QoreHashNode& h) {
598 assert(!h.priv->is_obj);
599 return h.priv->obj_count;
600 }
601
602 DLLLOCAL static void incScanCount(const QoreHashNode& h, int dt) {
603 assert(!h.priv->is_obj);
604 h.priv->incScanCount(dt);
605 }
606
607 DLLLOCAL static QoreValue getFirstKeyValue(const QoreHashNode* h) {
608 return h->priv->member_list.empty() ? QoreValue() : h->priv->member_list.front()->val;
609 }
610
611 DLLLOCAL static QoreValue getLastKeyValue(const QoreHashNode* h) {
612 return h->priv->member_list.empty() ? QoreValue() : h->priv->member_list.back()->val;
613 }
614};
615
616#endif
The base class for all value and parse types in Qore expression trees.
Definition: AbstractQoreNode.h:57
DLLLOCAL qore_type_t getType() const
returns the data type
Definition: AbstractQoreNode.h:175
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false,...
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:50
iterator class for QoreHashNode, to be only created on the stack
Definition: QoreHashNode.h:444
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
class qore_hash_private * priv
private implementation of the class
Definition: QoreHashNode.h:396
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
the implementation of Qore's object data type, reference counted, dynamically-allocated only
Definition: QoreObject.h:60
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
DLLEXPORT int sprintf(const char *fmt,...)
this will concatentate a formatted string to the existing string according to the format string and t...
a templated class to manage a reference count of an object that can throw a Qore-language exception w...
Definition: ReferenceHolder.h:52
Helps dereference values outside of locks.
Definition: QoreLibIntern.h:543
typed hash declaration
Definition: TypedHashDecl.h:44
DLLEXPORT const char * getName() const
returns the name of the typed hash
DLLEXPORT bool equal(const TypedHashDecl *other) const
returns true if the hashdecl passed as an arugment is equal to this hashdecl
DLLEXPORT const QoreTypeInfo * getTypeInfo(bool or_nothing=false) const
returns the type info object for the hashdecl
holds an object and dereferences it in the destructor
Definition: QoreValue.h:487
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
const qore_type_t NT_OBJECT
type value for QoreObject
Definition: node_types.h:52
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:276