Qore Programming Language  0.9.4.6
QoreHashNodeIntern.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  QoreHashNodeIntern.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2020 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 const char* getFirstKey() const {
298  return member_list.empty() ? nullptr : member_list.front()->key.c_str();
299  }
300 
301  DLLLOCAL const char* getLastKey() const {
302  return member_list.empty() ? nullptr : member_list.back()->key.c_str();
303  }
304 
305  DLLLOCAL QoreListNode* getKeys() const;
306 
307  DLLLOCAL void merge(const qore_hash_private& h, ExceptionSink* xsink);
308 
309  DLLLOCAL int getLValue(const char* key, LValueHelper& lvh, bool for_remove, ExceptionSink* xsink);
310 
311  DLLLOCAL void getTypeName(QoreString& str) const {
312  if (hashdecl)
313  str.sprintf("hash<%s>", hashdecl->getName());
314  else if (complexTypeInfo)
315  str.concat(QoreTypeInfo::getName(complexTypeInfo));
316  else
317  str.concat("hash");
318  }
319 
320  // issue #3877: returns a typed list
321  DLLLOCAL QoreListNode* getValues() const;
322 
323  DLLLOCAL QoreHashNode* getCopy() const {
324  QoreHashNode* h = new QoreHashNode;
325  if (hashdecl)
326  h->priv->hashdecl = hashdecl;
327  if (complexTypeInfo)
328  h->priv->complexTypeInfo = complexTypeInfo;
329  return h;
330  }
331 
332  DLLLOCAL QoreHashNode* getEmptyCopy(bool is_value) const {
333  QoreHashNode* h = new QoreHashNode(!is_value);
334  if (hashdecl)
335  h->priv->hashdecl = hashdecl;
336  if (complexTypeInfo)
337  h->priv->complexTypeInfo = complexTypeInfo;
338  return h;
339  }
340 
341  DLLLOCAL QoreHashNode* copyCheckNewType(const qore_hash_private& other) const {
342  QoreHashNode* rv = copy();
343  if (hashdecl || other.hashdecl) {
344  if (!hashdecl || !other.hashdecl || !hashdecl->equal(other.hashdecl)) {
345  rv->priv->hashdecl = nullptr;
346  rv->priv->complexTypeInfo = autoHashTypeInfo;
347  }
348  } else {
349  const QoreTypeInfo* orig_ctype, * ctype;
350  orig_ctype = ctype = QoreTypeInfo::getUniqueReturnComplexHash(complexTypeInfo);
351  const QoreTypeInfo* newElementType = QoreTypeInfo::getUniqueReturnComplexHash(other.complexTypeInfo);
352  if ((!ctype || ctype == anyTypeInfo) && (!newElementType || newElementType == anyTypeInfo)) {
353  rv->priv->complexTypeInfo = nullptr;
354  } else if (QoreTypeInfo::matchCommonType(ctype, newElementType)) {
355  rv->priv->complexTypeInfo = ctype == orig_ctype ? complexTypeInfo : qore_get_complex_hash_type(ctype);
356  } else {
357  rv->priv->complexTypeInfo = autoHashTypeInfo;
358  }
359  }
360  return rv;
361  }
362 
363  DLLLOCAL QoreHashNode* copy(const QoreTypeInfo* newComplexTypeInfo) const {
364  QoreHashNode* h = new QoreHashNode;
365  h->priv->complexTypeInfo = newComplexTypeInfo;
366  copyIntern(*h->priv);
367  return h;
368  }
369 
370  // strip = copy without type information
371  DLLLOCAL QoreHashNode* copy(bool strip = false) const {
372  // issue #2791: perform type stripping at the source
373  if (!strip || (!complexTypeInfo && !hashdecl)) {
374  QoreHashNode* h = getCopy();
375  copyIntern(*h->priv);
376  return h;
377  }
378  QoreHashNode* h = new QoreHashNode;
379  // copy all members to new object
380  for (auto& i : member_list) {
381  hash_assignment_priv ha(*h, i->key.c_str());
382  QoreValue v = copy_strip_complex_types(i->val);
383 #ifdef DEBUG
384  assert(ha.swap(v).isNothing());
385 #else
386  ha.swap(v);
387 #endif
388  }
389 
390  return h;
391  }
392 
393  DLLLOCAL void copyIntern(qore_hash_private& h) const {
394  // copy all members to new object
395  for (auto& i : member_list) {
396  hash_assignment_priv ha(h, i->key.c_str());
397 #ifdef DEBUG
398  assert(ha.swap(i->val.refSelf()).isNothing());
399 #else
400  ha.swap(i->val.refSelf());
401 #endif
402  }
403  }
404 
405  DLLLOCAL QoreHashNode* plusEquals(const QoreHashNode* h, ExceptionSink* xsink) const {
406  // issue #2791: perform type stripping at the source
407  // issue #3429: maintain types unless we have a plain hash; convert to hash<auto> if the types are not compatible
408  ReferenceHolder<QoreHashNode> rv(copyCheckNewType(*h->priv), xsink);
409  rv->priv->merge(*h->priv, xsink);
410  return *xsink ? nullptr : rv.release();
411  }
412 
413  DLLLOCAL QoreHashNode* evalImpl(ExceptionSink* xsink) const {
414  QoreHashNodeHolder h(getCopy(), xsink);
415 
416  for (qhlist_t::const_iterator i = member_list.begin(), e = member_list.end(); i != e; ++i) {
417  h->priv->setKeyValue((*i)->key, (*i)->val.refSelf(), xsink);
418  if (*xsink)
419  return nullptr;
420  }
421 
422  return h.release();
423  }
424 
425  DLLLOCAL void setKeyValueIntern(const char* key, QoreValue v) {
426  hash_assignment_priv ha(*this, key);
427  // in case of assigning keys to an initialized hashdecl, the key may already have a value
428  // if the value is an object of a class that throws an exception in the destructor, then a crash will result
429  ValueHolder old(ha.swap(v), nullptr);
430  }
431 
432  DLLLOCAL void setKeyValue(const char* key, QoreValue val, ExceptionSink* xsink) {
433  hash_assignment_priv ha(*this, key);
434  ha.assign(val, xsink);
435  }
436 
437  DLLLOCAL void setKeyValue(const std::string& key, QoreValue val, ExceptionSink* xsink) {
438  hash_assignment_priv ha(*this, key.c_str());
439  ha.assign(val, xsink);
440  }
441 
442  DLLLOCAL void setKeyValue(const char* key, QoreValue val, qore_object_private* o, ExceptionSink* xsink) {
443  hash_assignment_priv ha(*this, key, false, o);
444  ha.assign(val, xsink);
445  }
446 
447  DLLLOCAL bool derefImpl(ExceptionSink* xsink, bool reverse = false) {
448  if (reverse) {
449  for (qhlist_t::reverse_iterator i = member_list.rbegin(), e = member_list.rend(); i != e; ++i) {
450  (*i)->val.discard(xsink);
451  delete *i;
452  }
453  } else {
454  for (qhlist_t::iterator i = member_list.begin(), e = member_list.end(); i != e; ++i) {
455  (*i)->val.discard(xsink);
456  delete *i;
457  }
458  }
459 
460  member_list.clear();
461  hm.clear();
462  obj_count = 0;
463  return true;
464  }
465 
466  DLLLOCAL QoreValue swapKeyValue(const char* key, QoreValue val, qore_object_private* o) {
467  //printd(0, "qore_hash_private::swapKeyValue() this: %p key: %s val: %p (%s) deprecated API called\n", this, key, val, get_node_type(val));
468  //assert(false);
469  hash_assignment_priv ha(*this, key, false, o);
470  return ha.swap(val);
471  }
472 
473  DLLLOCAL void clear(ExceptionSink* xsink, bool reverse) {
474  derefImpl(xsink, reverse);
475  }
476 
477  DLLLOCAL size_t size() const {
478  return member_list.size();
479  }
480 
481  DLLLOCAL bool empty() const {
482  return member_list.empty();
483  }
484 
485  DLLLOCAL void incScanCount(int dt) {
486  assert(!is_obj);
487  assert(dt);
488  assert(obj_count || dt > 0);
489  //printd(5, "qore_hash_private::incScanCount() this: %p dt: %d: %d -> %d\n", this, dt, obj_count, obj_count + dt);
490  obj_count += dt;
491  }
492 
493  DLLLOCAL const QoreTypeInfo* getValueTypeInfo() const {
494  return complexTypeInfo ? QoreTypeInfo::getComplexHashValueType(complexTypeInfo) : nullptr;
495  }
496 
497  DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
498  if (hashdecl)
499  return hashdecl->getTypeInfo();
500  if (complexTypeInfo)
501  return complexTypeInfo;
502  return hashTypeInfo;
503  }
504 
505  DLLLOCAL const TypedHashDecl* getHashDecl() const {
506  return hashdecl;
507  }
508 
509  DLLLOCAL static QoreHashNode* getPlainHash(QoreHashNode* h) {
510  if (!h->priv->hashdecl && !h->priv->complexTypeInfo)
511  return h;
512  // no exception is possible
513  ReferenceHolder<QoreHashNode> holder(h, nullptr);
514  return h->priv->copy(true);
515  }
516 
517  DLLLOCAL static QoreHashNode* newHashDecl(const TypedHashDecl* hd) {
518  QoreHashNode* rv = new QoreHashNode;
519  rv->priv->hashdecl = hd;
520  return rv;
521  }
522 
523  DLLLOCAL static qore_hash_private* get(QoreHashNode& h) {
524  return h.priv;
525  }
526 
527  DLLLOCAL static const qore_hash_private* get(const QoreHashNode& h) {
528  return h.priv;
529  }
530 
531  // returns -1 if no checks are needed or if an error is raised, 0 if OK to check
532  DLLLOCAL static int parseInitHashInitialization(const QoreProgramLocation* loc, LocalVar *oflag, int pflag, int& lvids, QoreParseListNode* args, const QoreTypeInfo*& argTypeInfo, QoreValue& arg);
533 
534  DLLLOCAL static int parseInitComplexHashInitialization(const QoreProgramLocation* loc, LocalVar *oflag, int pflag, QoreParseListNode* args, const QoreTypeInfo* vti);
535 
536  DLLLOCAL static void parseCheckComplexHashInitialization(const QoreProgramLocation* loc, const QoreTypeInfo* typeInfo, const QoreTypeInfo* expTypeInfo, QoreValue exp, const char* context_action, bool strict_check = true);
537 
538  DLLLOCAL static void parseCheckTypedAssignment(const QoreProgramLocation* loc, QoreValue arg, const QoreTypeInfo* vti, const char* context_action, bool strict_check = true);
539 
540  DLLLOCAL static QoreHashNode* newComplexHash(const QoreTypeInfo* typeInfo, const QoreParseListNode* args, ExceptionSink* xsink);
541 
542  DLLLOCAL static QoreHashNode* newComplexHashFromHash(const QoreTypeInfo* typeInfo, QoreHashNode* init, ExceptionSink* xsink);
543 
544  DLLLOCAL static unsigned getScanCount(const QoreHashNode& h) {
545  assert(!h.priv->is_obj);
546  return h.priv->obj_count;
547  }
548 
549  DLLLOCAL static void incScanCount(const QoreHashNode& h, int dt) {
550  assert(!h.priv->is_obj);
551  h.priv->incScanCount(dt);
552  }
553 
554  DLLLOCAL static QoreValue getFirstKeyValue(const QoreHashNode* h) {
555  return h->priv->member_list.empty() ? QoreValue() : h->priv->member_list.front()->val;
556  }
557 
558  DLLLOCAL static QoreValue getLastKeyValue(const QoreHashNode* h) {
559  return h->priv->member_list.empty() ? QoreValue() : h->priv->member_list.back()->val;
560  }
561 };
562 
563 #endif
DLLLOCAL qore_type_t getType() const
returns the data type
Definition: AbstractQoreNode.h:172
DLLEXPORT const char * getName() const
returns the name of the typed hash
This is the hash or associative list container type in Qore, dynamically allocated only...
Definition: QoreHashNode.h:50
DLLEXPORT bool equal(const TypedHashDecl *other) const
returns true if the hashdecl passed as an arugment is equal to this hashdecl
DLLEXPORT int sprintf(const char *fmt,...)
this will concatentate a formatted string to the existing string according to the format string and t...
The base class for all value and parse types in Qore expression trees.
Definition: AbstractQoreNode.h:54
const qore_type_t NT_OBJECT
type value for QoreObject
Definition: node_types.h:52
class qore_hash_private * priv
private implementation of the class
Definition: QoreHashNode.h:69
Qore&#39;s string type supported by the QoreEncoding class.
Definition: QoreString.h:81
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
DLLEXPORT const QoreTypeInfo * getTypeInfo(bool or_nothing=false) const
returns the type info object for the hashdecl
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false, otherwise does nothing
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
typed hash declaration
Definition: TypedHashDecl.h:44
holds an object and dereferences it in the destructor
Definition: QoreValue.h:452
iterator class for QoreHashNode, to be only created on the stack
Definition: QoreHashNode.h:433