Qore Programming Language  1.12.0
QoreHashNodeIntern.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  QoreHashNodeIntern.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 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
39 class HashMember {
40 public:
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 
51 typedef 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 
58 typedef HASH_MAP<const char*, qhlist_t::iterator, qore_hash_str, eqstr> hm_hm_t;
59 #else
60 typedef std::map<const char*, qhlist_t::iterator, ltstr> hm_hm_t;
61 #endif
62 
63 // QoreHashIterator private class
64 class qhi_priv {
65 public:
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  }
87  else {
88  ++i;
89  if (i == ml.end())
90  val = false;
91  }
92  return val;
93  }
94 
95  DLLLOCAL bool prev(qhlist_t& ml) {
96  if (!val) {
97  if (ml.begin() != ml.end()) {
98  i = ml.end();
99  --i;
100  val = true;
101  }
102  }
103  else {
104  if (i == ml.begin())
105  val = false;
106  else
107  --i;
108  }
109  return val;
110  }
111 
112  DLLLOCAL void reset() {
113  val = false;
114  }
115 
116  DLLLOCAL static qhi_priv* get(HashIterator& i) {
117  return i.priv;
118  }
119 };
120 
121 class qore_hash_private {
122 public:
123  qhlist_t member_list;
124  hm_hm_t hm;
125  // either hashdecl or complexTypeInfo can be set, but not both
126  const TypedHashDecl* hashdecl = nullptr;
127  const QoreTypeInfo* complexTypeInfo = nullptr;
128  unsigned obj_count = 0;
129 #ifdef DEBUG
130  bool is_obj = false;
131 #endif
132 
133  DLLLOCAL qore_hash_private() {
134  }
135 
136  // hashes should always be empty by the time they are deleted
137  // because object destructors need to be run...
138  DLLLOCAL ~qore_hash_private() {
139  assert(member_list.empty());
140  }
141 
142  DLLLOCAL QoreValue getKeyValueIntern(const char* key) const;
143 
144  DLLLOCAL QoreValue getKeyValueExistence(const char* key, bool& exists, ExceptionSink* xsink) const;
145 
146  DLLLOCAL QoreValue getKeyValueExistenceIntern(const char* key, bool& exists) const;
147 
148  DLLLOCAL int checkKey(const char* key, ExceptionSink* xsink) const;
149 
150  DLLLOCAL QoreValue getReferencedKeyValueIntern(const char* key) const {
151  bool exists;
152  return getReferencedKeyValueIntern(key, exists);
153  }
154 
155  DLLLOCAL QoreValue getReferencedKeyValueIntern(const char* key, bool& exists) const {
156  assert(key);
157 
158  hm_hm_t::const_iterator i = hm.find(key);
159  if (i != hm.end()) {
160  exists = true;
161  return (*(i->second))->val.refSelf();
162  }
163 
164  exists = false;
165  return QoreValue();
166  }
167 
168  DLLLOCAL int64 getKeyAsBigInt(const char* key, bool &found) const {
169  assert(key);
170  hm_hm_t::const_iterator i = hm.find(key);
171 
172  if (i != hm.end()) {
173  found = true;
174  return (*(i->second))->val.getAsBigInt();
175  }
176 
177  found = false;
178  return 0;
179  }
180 
181  DLLLOCAL bool getKeyAsBool(const char* key, bool& found) const {
182  assert(key);
183  hm_hm_t::const_iterator i = hm.find(key);
184 
185  if (i != hm.end()) {
186  found = true;
187  return (*(i->second))->val.getAsBool();
188  }
189 
190  found = false;
191  return false;
192  }
193 
194  DLLLOCAL bool existsKey(const char* key) const {
195  assert(key);
196  return hm.find(key) != hm.end();
197  }
198 
199  DLLLOCAL bool existsKeyValue(const char* key) const {
200  assert(key);
201  hm_hm_t::const_iterator i = hm.find(key);
202  if (i == hm.end())
203  return false;
204  return !(*(i->second))->val.isNothing();
205  }
206 
207  DLLLOCAL HashMember* findMember(const char* key) {
208  assert(key);
209  hm_hm_t::iterator i = hm.find(key);
210  return i != hm.end() ? (*(i->second)) : nullptr;
211  }
212 
213  DLLLOCAL HashMember* findCreateMember(const char* key) {
214  // otherwise create the new hash entry
215  HashMember* om = findMember(key);
216  if (om)
217  return om;
218 
219  om = new HashMember(key);
220  assert(om->val.isNothing());
221  member_list.push_back(om);
222 
223  // add to the map
224  qhlist_t::iterator i = member_list.end();
225  --i;
226  hm[om->key.c_str()] = i;
227 
228  // return the new member
229  return om;
230  }
231 
232  DLLLOCAL QoreValue& getValueRef(const char* key) {
233  return findCreateMember(key)->val;
234  }
235 
236  // NOTE: does not delete the value, this must be done by the caller before this call
237  // also does not delete map entry; must be done outside this call
238  DLLLOCAL void internDeleteKey(qhlist_t::iterator i) {
239  HashMember* om = *i;
240 
241  member_list.erase(i);
242 
243  // free om memory
244  delete om;
245  }
246 
247  DLLLOCAL void deleteKey(const char* key, ExceptionSink *xsink) {
248  assert(key);
249 
250  hm_hm_t::iterator i = hm.find(key);
251 
252  if (i == hm.end())
253  return;
254 
255  qhlist_t::iterator li = i->second;
256  hm.erase(i);
257 
258  // dereference node if present
259  AbstractQoreNode* n = (*li)->val.assignNothing();
260  if (n) {
261  if (needs_scan(n))
262  incScanCount(-1);
263 
264  if (n->getType() == NT_OBJECT)
265  reinterpret_cast<QoreObject*>(n)->doDelete(xsink);
266  n->deref(xsink);
267  }
268 
269  internDeleteKey(li);
270  }
271 
272  // removes the value and dereferences it, without performing a delete on it
273  DLLLOCAL void removeKey(const char* key, ExceptionSink *xsink) {
274  takeKeyValueIntern(key).discard(xsink);
275  }
276 
277  DLLLOCAL QoreValue takeKeyValueIntern(const char* key) {
278  assert(key);
279 
280  hm_hm_t::iterator i = hm.find(key);
281 
282  if (i == hm.end())
283  return QoreValue();
284 
285  qhlist_t::iterator li = i->second;
286  hm.erase(i);
287 
288  QoreValue rv = (*li)->val;
289  internDeleteKey(li);
290 
291  if (needs_scan(rv))
292  incScanCount(-1);
293 
294  return rv;
295  }
296 
297  DLLLOCAL QoreValue takeKeyValueIntern(const char* key, bool& exists) {
298  assert(key);
299 
300  hm_hm_t::iterator i = hm.find(key);
301 
302  if (i == hm.end()) {
303  exists = false;
304  return QoreValue();
305  }
306  exists = true;
307 
308  qhlist_t::iterator li = i->second;
309  hm.erase(i);
310 
311  QoreValue rv = (*li)->val;
312  internDeleteKey(li);
313 
314  if (needs_scan(rv))
315  incScanCount(-1);
316 
317  return rv;
318  }
319 
320  DLLLOCAL const char* getFirstKey() const {
321  return member_list.empty() ? nullptr : member_list.front()->key.c_str();
322  }
323 
324  DLLLOCAL const char* getLastKey() const {
325  return member_list.empty() ? nullptr : member_list.back()->key.c_str();
326  }
327 
328  DLLLOCAL QoreListNode* getKeys() const;
329 
330  DLLLOCAL void merge(const qore_hash_private& h0, ExceptionSink* xsink);
331 
332  // to be called when a lock is held to avoid dereferencing in the lock
333  DLLLOCAL void merge(const qore_hash_private& h, SafeDerefHelper& sdh, ExceptionSink* xsink);
334 
335  DLLLOCAL int getLValue(const char* key, LValueHelper& lvh, bool for_remove, ExceptionSink* xsink);
336 
337  DLLLOCAL void getTypeName(QoreString& str) const {
338  if (hashdecl)
339  str.sprintf("hash<%s>", hashdecl->getName());
340  else if (complexTypeInfo)
341  str.concat(QoreTypeInfo::getName(complexTypeInfo));
342  else
343  str.concat("hash");
344  }
345 
346  // issue #3877: returns a typed list
347  DLLLOCAL QoreListNode* getValues() const;
348 
349  DLLLOCAL QoreHashNode* getCopy() const {
350  QoreHashNode* h = new QoreHashNode;
351  if (hashdecl)
352  h->priv->hashdecl = hashdecl;
353  if (complexTypeInfo)
354  h->priv->complexTypeInfo = complexTypeInfo;
355  return h;
356  }
357 
358  DLLLOCAL QoreHashNode* getEmptyCopy(bool is_value) const {
359  QoreHashNode* h = new QoreHashNode(!is_value);
360  if (hashdecl)
361  h->priv->hashdecl = hashdecl;
362  if (complexTypeInfo)
363  h->priv->complexTypeInfo = complexTypeInfo;
364  return h;
365  }
366 
367  DLLLOCAL QoreHashNode* copyCheckNewType(const qore_hash_private& other) const {
368  QoreHashNode* rv = copy();
369  if (hashdecl || other.hashdecl) {
370  if (!hashdecl || !other.hashdecl || !hashdecl->equal(other.hashdecl)) {
371  rv->priv->hashdecl = nullptr;
372  rv->priv->complexTypeInfo = autoHashTypeInfo;
373  }
374  } else {
375  const QoreTypeInfo* orig_ctype, * ctype;
376  orig_ctype = ctype = QoreTypeInfo::getUniqueReturnComplexHash(complexTypeInfo);
377  const QoreTypeInfo* newElementType = QoreTypeInfo::getUniqueReturnComplexHash(other.complexTypeInfo);
378  if ((!ctype || ctype == anyTypeInfo) && (!newElementType || newElementType == anyTypeInfo)) {
379  rv->priv->complexTypeInfo = nullptr;
380  } else if (QoreTypeInfo::matchCommonType(ctype, newElementType)) {
381  rv->priv->complexTypeInfo = ctype == orig_ctype ? complexTypeInfo : qore_get_complex_hash_type(ctype);
382  } else {
383  rv->priv->complexTypeInfo = autoHashTypeInfo;
384  }
385  }
386  return rv;
387  }
388 
389  DLLLOCAL QoreHashNode* copy(const QoreTypeInfo* newComplexTypeInfo) const {
390  QoreHashNode* h = new QoreHashNode;
391  h->priv->complexTypeInfo = newComplexTypeInfo;
392  copyIntern(*h->priv);
393  return h;
394  }
395 
396  // strip = copy without type information
397  DLLLOCAL QoreHashNode* copy(bool strip = false) const {
398  // issue #2791: perform type stripping at the source
399  if (!strip || (!complexTypeInfo && !hashdecl)) {
400  QoreHashNode* h = getCopy();
401  copyIntern(*h->priv);
402  return h;
403  }
404  QoreHashNode* h = new QoreHashNode;
405  // copy all members to new object
406  for (auto& i : member_list) {
407  hash_assignment_priv ha(*h, i->key.c_str());
408  QoreValue v = copy_strip_complex_types(i->val);
409 #ifdef DEBUG
410  assert(ha.swap(v).isNothing());
411 #else
412  ha.swap(v);
413 #endif
414  }
415 
416  return h;
417  }
418 
419  DLLLOCAL void copyIntern(qore_hash_private& h) const {
420  // copy all members to new object
421  for (auto& i : member_list) {
422  hash_assignment_priv ha(h, i->key.c_str());
423 #ifdef DEBUG
424  assert(ha.swap(i->val.refSelf()).isNothing());
425 #else
426  ha.swap(i->val.refSelf());
427 #endif
428  }
429  }
430 
431  DLLLOCAL QoreHashNode* plusEquals(const QoreHashNode* h, ExceptionSink* xsink) const {
432  // issue #2791: perform type stripping at the source
433  // issue #3429: maintain types unless we have a plain hash; convert to hash<auto> if the types are not compatible
434  ReferenceHolder<QoreHashNode> rv(copyCheckNewType(*h->priv), xsink);
435  rv->priv->merge(*h->priv, xsink);
436  return *xsink ? nullptr : rv.release();
437  }
438 
439  DLLLOCAL QoreHashNode* evalImpl(ExceptionSink* xsink) const {
440  QoreHashNodeHolder h(getCopy(), xsink);
441 
442  for (qhlist_t::const_iterator i = member_list.begin(), e = member_list.end(); i != e; ++i) {
443  h->priv->setKeyValue((*i)->key, (*i)->val.refSelf(), xsink);
444  if (*xsink)
445  return nullptr;
446  }
447 
448  return h.release();
449  }
450 
451  DLLLOCAL void setKeyValue(const char* key, QoreValue val, SafeDerefHelper& sdh, ExceptionSink* xsink) {
452  hash_assignment_priv ha(*this, key);
453  // in case of assigning keys to an initialized hashdecl, the key may already have a value
454  ha.assign(val, sdh, xsink);
455  }
456 
457  DLLLOCAL void setKeyValue(const std::string& key, QoreValue val, SafeDerefHelper& sdh, ExceptionSink* xsink) {
458  hash_assignment_priv ha(*this, key.c_str());
459  // in case of assigning keys to an initialized hashdecl, the key may already have a value
460  ha.assign(val, sdh, xsink);
461  }
462 
463  DLLLOCAL void setKeyValueIntern(const char* key, QoreValue v) {
464  hash_assignment_priv ha(*this, key);
465  // in case of assigning keys to an initialized hashdecl, the key may already have a value
466  // if the value is an object of a class that throws an exception in the destructor, then a crash will result
467  ValueHolder old(ha.swap(v), nullptr);
468  }
469 
470  DLLLOCAL void setKeyValue(const char* key, QoreValue val, ExceptionSink* xsink) {
471  hash_assignment_priv ha(*this, key);
472  ha.assign(val, xsink);
473  }
474 
475  DLLLOCAL void setKeyValue(const std::string& key, QoreValue val, ExceptionSink* xsink) {
476  hash_assignment_priv ha(*this, key.c_str());
477  ha.assign(val, xsink);
478  }
479 
480  DLLLOCAL void setKeyValue(const char* key, QoreValue val, qore_object_private* o, ExceptionSink* xsink) {
481  hash_assignment_priv ha(*this, key, false, o);
482  ha.assign(val, xsink);
483  }
484 
485  DLLLOCAL bool derefImpl(ExceptionSink* xsink, bool reverse = false) {
486  if (reverse) {
487  for (qhlist_t::reverse_iterator i = member_list.rbegin(), e = member_list.rend(); i != e; ++i) {
488  (*i)->val.discard(xsink);
489  delete *i;
490  }
491  } else {
492  for (qhlist_t::iterator i = member_list.begin(), e = member_list.end(); i != e; ++i) {
493  (*i)->val.discard(xsink);
494  delete *i;
495  }
496  }
497 
498  member_list.clear();
499  hm.clear();
500  obj_count = 0;
501  return true;
502  }
503 
504  DLLLOCAL QoreValue swapKeyValueIfExists(const char* key, QoreValue val, qore_object_private* o, bool& exists) {
505  hash_assignment_priv ha(*this, key, true, o);
506  if (!ha.exists()) {
507  exists = false;
508  return QoreValue();
509  }
510  exists = true;
511  return ha.swap(val);
512  }
513 
514  DLLLOCAL QoreValue swapKeyValue(const char* key, QoreValue val, qore_object_private* o) {
515  //printd(0, "qore_hash_private::swapKeyValue() this: %p key: %s val: %p (%s) deprecated API called\n", this, key, val, get_node_type(val));
516  //assert(false);
517  hash_assignment_priv ha(*this, key, false, o);
518  return ha.swap(val);
519  }
520 
521  DLLLOCAL void clear(ExceptionSink* xsink, bool reverse) {
522  derefImpl(xsink, reverse);
523  }
524 
525  DLLLOCAL size_t size() const {
526  return member_list.size();
527  }
528 
529  DLLLOCAL bool empty() const {
530  return member_list.empty();
531  }
532 
533  DLLLOCAL void incScanCount(int dt) {
534  assert(!is_obj);
535  assert(dt);
536  assert(obj_count || dt > 0);
537  //printd(5, "qore_hash_private::incScanCount() this: %p dt: %d: %d -> %d\n", this, dt, obj_count, obj_count + dt);
538  obj_count += dt;
539  }
540 
541  DLLLOCAL const QoreTypeInfo* getValueTypeInfo() const {
542  return complexTypeInfo ? QoreTypeInfo::getComplexHashValueType(complexTypeInfo) : nullptr;
543  }
544 
545  DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
546  if (hashdecl)
547  return hashdecl->getTypeInfo();
548  if (complexTypeInfo)
549  return complexTypeInfo;
550  return hashTypeInfo;
551  }
552 
553  DLLLOCAL const TypedHashDecl* getHashDecl() const {
554  return hashdecl;
555  }
556 
557  DLLLOCAL static QoreHashNode* getPlainHash(QoreHashNode* h) {
558  if (!h->priv->hashdecl && !h->priv->complexTypeInfo)
559  return h;
560  // no exception is possible
561  ReferenceHolder<QoreHashNode> holder(h, nullptr);
562  return h->priv->copy(true);
563  }
564 
565  DLLLOCAL static QoreHashNode* newHashDecl(const TypedHashDecl* hd) {
566  QoreHashNode* rv = new QoreHashNode;
567  rv->priv->hashdecl = hd;
568  return rv;
569  }
570 
571  DLLLOCAL static qore_hash_private* get(QoreHashNode& h) {
572  return h.priv;
573  }
574 
575  DLLLOCAL static const qore_hash_private* get(const QoreHashNode& h) {
576  return h.priv;
577  }
578 
579  // returns -1 if no checks are needed or if an error is raised, 0 if OK to check
580  DLLLOCAL static int parseInitHashInitialization(const QoreProgramLocation* loc, QoreParseContext& parse_context,
581  QoreParseListNode* args, QoreValue& arg, int& err);
582 
583  DLLLOCAL static int parseInitComplexHashInitialization(const QoreProgramLocation* loc, QoreParseContext& parse_context,
584  QoreParseListNode* args);
585 
586  DLLLOCAL static int parseCheckComplexHashInitialization(const QoreProgramLocation* loc,
587  const QoreTypeInfo* valueTypeInfo, const QoreTypeInfo* argTypeInfo, QoreValue exp,
588  const char* context_action, bool strict_check = true);
589 
590  DLLLOCAL static int parseCheckTypedAssignment(const QoreProgramLocation* loc, QoreValue arg,
591  const QoreTypeInfo* vti, const char* context_action, bool strict_check = true);
592 
593  DLLLOCAL static QoreHashNode* newComplexHash(const QoreTypeInfo* typeInfo, const QoreParseListNode* args,
594  ExceptionSink* xsink);
595 
596  DLLLOCAL static QoreHashNode* newComplexHashFromHash(const QoreTypeInfo* typeInfo, QoreHashNode* init,
597  ExceptionSink* xsink);
598 
599  DLLLOCAL static unsigned getScanCount(const QoreHashNode& h) {
600  assert(!h.priv->is_obj);
601  return h.priv->obj_count;
602  }
603 
604  DLLLOCAL static void incScanCount(const QoreHashNode& h, int dt) {
605  assert(!h.priv->is_obj);
606  h.priv->incScanCount(dt);
607  }
608 
609  DLLLOCAL static QoreValue getFirstKeyValue(const QoreHashNode* h) {
610  return h->priv->member_list.empty() ? QoreValue() : h->priv->member_list.front()->val;
611  }
612 
613  DLLLOCAL static QoreValue getLastKeyValue(const QoreHashNode* h) {
614  return h->priv->member_list.empty() ? QoreValue() : h->priv->member_list.back()->val;
615  }
616 };
617 
618 #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:48
iterator class for QoreHashNode, to be only created on the stack
Definition: QoreHashNode.h:447
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:69
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...
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:476
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:275