Qore Programming Language  1.12.0
qore_list_private.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  qore_list_private.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_QORELISTPRIVATE_H
33 #define _QORE_QORELISTPRIVATE_H
34 
35 #include <cstring>
36 
38 
39 #define LIST_PAD 15
40 
41 hashdecl qore_list_private {
42  QoreValue* entry = nullptr;
43  size_t length = 0;
44  size_t allocated = 0;
45  unsigned obj_count = 0;
46  const QoreTypeInfo* complexTypeInfo = nullptr;
47  bool finalized : 1;
48  bool vlist : 1;
49 
50  DLLLOCAL qore_list_private() : finalized(false), vlist(false) {
51  }
52 
53  DLLLOCAL ~qore_list_private() {
54  assert(!length);
55 
56  if (entry) {
57  free(entry);
58  }
59  }
60 
61  DLLLOCAL const QoreTypeInfo* getValueTypeInfo() const {
62  return complexTypeInfo ? QoreTypeInfo::getComplexListValueType(complexTypeInfo) : nullptr;
63  }
64 
65  DLLLOCAL const QoreTypeInfo* getTypeInfo() const {
66  return complexTypeInfo ? complexTypeInfo : listTypeInfo;
67  }
68 
69  DLLLOCAL void getTypeName(QoreString& str) const {
70  if (complexTypeInfo)
71  str.concat(QoreTypeInfo::getName(complexTypeInfo));
72  else
73  str.concat("list");
74  }
75 
76  DLLLOCAL QoreListNode* getCopy() const {
77  QoreListNode* l = new QoreListNode;
78  if (complexTypeInfo) {
79  l->priv->complexTypeInfo = complexTypeInfo;
80  }
81  return l;
82  }
83 
84  DLLLOCAL QoreListNode* getEmptyCopy(bool is_value) const {
85  QoreListNode* l = new QoreListNode(!is_value);
86  if (complexTypeInfo) {
87  l->priv->complexTypeInfo = complexTypeInfo;
88  }
89  return l;
90  }
91 
92  DLLLOCAL QoreListNode* copyCheckNewElementType(const QoreTypeInfo* newElementType) const {
93  QoreListNode* rv = copy();
94  rv->priv->setListTypeFromNewElementType(newElementType);
95  return rv;
96  }
97 
98  DLLLOCAL void setListTypeFromNewElementType(const QoreTypeInfo* newElementType) {
99  const QoreTypeInfo* orig_ctype, * ctype;
100  orig_ctype = ctype = QoreTypeInfo::getUniqueReturnComplexList(complexTypeInfo);
101  if ((!ctype || ctype == anyTypeInfo) && (!newElementType || newElementType == anyTypeInfo)) {
102  complexTypeInfo = nullptr;
103  } else if (QoreTypeInfo::matchCommonType(ctype, newElementType)) {
104  if (ctype != orig_ctype) {
105  complexTypeInfo = qore_get_complex_list_type(ctype);
106  }
107  } else {
108  complexTypeInfo = autoListTypeInfo;
109  }
110  }
111 
112  DLLLOCAL QoreListNode* copy(const QoreTypeInfo* newComplexTypeInfo) const {
113  QoreListNode* l = new QoreListNode;
114  l->priv->complexTypeInfo = newComplexTypeInfo;
115  copyIntern(*l->priv);
116  return l;
117  }
118 
119  // strip = copy without type information
120  DLLLOCAL QoreListNode* copy(bool strip = false) const {
121  // issue #2791 perform type stripping at the source
122  if (!strip || !complexTypeInfo) {
123  QoreListNode* l = getCopy();
124  copyIntern(*l->priv);
125  return l;
126  }
127  QoreListNode* l = new QoreListNode;
128  l->priv->reserve(length);
129  for (size_t i = 0; i < length; ++i) {
130  l->priv->pushIntern(copy_strip_complex_types(entry[i]));
131  }
132  return l;
133  }
134 
135  DLLLOCAL void copyIntern(qore_list_private& l) const {
136  l.reserve(length);
137  for (size_t i = 0; i < length; ++i) {
138  l.pushIntern(entry[i].refSelf());
139  }
140  }
141 
142  DLLLOCAL QoreListNode* concatenate(const QoreListNode* l, ExceptionSink* xsink) const {
143  // issue #3429: maintain types unless we have a plain list; convert to list<auto> if the types are not compatible
144  ReferenceHolder<QoreListNode> rv(copyCheckNewElementType(QoreTypeInfo::getUniqueReturnComplexList(l->priv->complexTypeInfo)), xsink);
145  return mergeWithoutTypeConversion(rv, *l->priv);
146  }
147 
148  DLLLOCAL QoreListNode* concatenateElement(QoreValue e, ExceptionSink* xsink) const {
149  // issue #3429: maintain types unless we have a plain list; convert to list<auto> if the types are not compatible
150  ReferenceHolder<QoreListNode> rv(copyCheckNewElementType(e.getTypeInfo()), xsink);
151  if (rv->priv->pushWithoutTypeConversion(e, xsink)) {
152  return nullptr;
153  }
154  return rv.release();
155  }
156 
157  DLLLOCAL QoreListNode* prependElement(QoreValue e, ExceptionSink* xsink) const {
158  // issue #3429: maintain types unless we have a plain list; convert to list<auto> if the types are not compatible
159  ReferenceHolder<QoreListNode> rv(new QoreListNode(complexTypeInfo), xsink);
160  // set type of new list with the new element
161  rv->priv->setListTypeFromNewElementType(e.getTypeInfo());
162  if (rv->priv->pushWithoutTypeConversion(e, xsink)) {
163  return nullptr;
164  }
165  // add the rest of the list elements
166  return mergeWithoutTypeConversion(rv, *this);
167  }
168 
169  DLLLOCAL int checkVal(ValueHolder& holder, ExceptionSink* xsink) const {
170  if (complexTypeInfo) {
171  const QoreTypeInfo* vti = QoreTypeInfo::getUniqueReturnComplexList(complexTypeInfo);
172  if (QoreTypeInfo::hasType(vti) && !QoreTypeInfo::superSetOf(vti, holder->getTypeInfo())) {
173  QoreValue v(holder.release());
174  QoreTypeInfo::acceptAssignment(vti, "<list element assignment>", v, xsink);
175  holder = v;
176  if (xsink && *xsink) {
177  xsink->appendLastDescription(" (while converting types for list<%s> subtype)",
178  QoreTypeInfo::getName(vti));
179  return -1;
180  }
181  return 0;
182  }
183  return 0;
184  } else {
185  return stripVal(holder, xsink);
186  }
187  return 0;
188  }
189 
190  DLLLOCAL int stripVal(ValueHolder& holder, ExceptionSink* xsink) const {
191  switch (holder->getType()) {
192  case NT_LIST:
193  case NT_HASH: {
194  {
195  ValueHolder v(holder.release(), xsink);
196  holder = copy_strip_complex_types(*v);
197  }
198  return xsink && *xsink ? -1 : 0;
199  }
200  default:
201  break;
202  }
203  return 0;
204  }
205 
206  // assumes types are compatible; performs type stripping if necessary
207  DLLLOCAL int pushWithoutTypeConversion(QoreValue val, ExceptionSink* xsink) {
208  if (!complexTypeInfo) {
209  return pushStrip(val, xsink);
210  }
211  pushIntern(val);
212  return 0;
213  }
214 
215  DLLLOCAL int pushStrip(QoreValue val, ExceptionSink* xsink) {
216  ValueHolder holder(val, xsink);
217  if (stripVal(holder, xsink)) {
218  return -1;
219  }
220  pushIntern(holder.release());
221  return 0;
222  }
223 
224  DLLLOCAL int push(QoreValue val, ExceptionSink* xsink) {
225  ValueHolder holder(val, xsink);
226  if (checkVal(holder, xsink)) {
227  return -1;
228  }
229  pushIntern(holder.release());
230  return 0;
231  }
232 
233  DLLLOCAL int merge(const QoreListNode* list, ExceptionSink* xsink) {
234  reserve(length + list->size());
235  ConstListIterator i(list);
236  while (i.next()) {
237  if (push(i.getReferencedValue(), xsink))
238  return -1;
239  }
240  return 0;
241  }
242 
243  // fast merge without type conversion; performs type stripping if necessary
244  DLLLOCAL static QoreListNode* mergeWithoutTypeConversion(ReferenceHolder<QoreListNode>& rv, const qore_list_private& list) {
245  for (size_t i = 0; i < list.length; ++i) {
246  QoreValue v = list.entry[i];
247  if (!rv->priv->complexTypeInfo) {
248  v = copy_strip_complex_types(v);
249  } else {
250  v.refSelf();
251  }
252  rv->priv->pushIntern(v);
253  }
254 
255  return rv.release();
256  }
257 
258  DLLLOCAL void pushIntern(QoreValue val) {
259  getEntryReference(length) = val;
260  if (needs_scan(val)) {
261  incScanCount(1);
262  }
263  }
264 
265  DLLLOCAL size_t checkOffset(ptrdiff_t offset) {
266  if (offset < 0) {
267  offset = length + offset;
268  return offset < 0 ? 0 : offset;
269  } else if ((size_t)offset > length)
270  return length;
271 
272  return offset;
273  }
274 
275  DLLLOCAL void checkOffset(ptrdiff_t offset, ptrdiff_t len, size_t &n_offset, size_t &n_len) {
276  n_offset = checkOffset(offset);
277  if (len < 0) {
278  len = length + len - n_offset;
279  n_len = len < 0 ? 0 : len;
280  return;
281  }
282  n_len = len;
283  }
284 
285  QoreValue spliceSingle(size_t offset) {
286  assert(offset < length);
287 
288  QoreValue rv = entry[offset];
289  if (needs_scan(rv)) {
290  incScanCount(-1);
291  }
292 
293  size_t end = offset + 1;
294 
295  if (end != length) {
296 #pragma GCC diagnostic ignored "-Wclass-memaccess"
297  memmove(entry + offset, entry + end, sizeof(QoreValue) * (length - end));
298 #pragma GCC diagnostic pop
299  // zero out trailing entries
300  zeroEntries(length - 1, length);
301  } else // set last entry to 0
302  entry[end - 1] = QoreValue();
303 
304  if (length > 0) {
305  /* it is always greater than one but we need get rid with realloc's warning
306  * "....exceeds maximum object size 9223372036854775807" when optimizer somehow
307  * testing probably length bounds. Casting to size_t i.e. long unsigned int does not help
308  */
309  resize(length - 1);
310  }
311 
312  return rv;
313  }
314 
315  DLLLOCAL QoreListNode* spliceIntern(size_t offset, size_t len, bool extract) {
316  //printd(5, "spliceIntern(offset: %d, len: %d, length: %d)\n", offset, len, length);
317  size_t end;
318  if (len > (length - offset)) {
319  end = length;
320  len = length - offset;
321  } else
322  end = offset + len;
323 
324  QoreListNode* rv = extract ? getCopy() : nullptr;
325 
326  // dereference all entries that will be removed or add to return value list
327  for (size_t i = offset; i < end; i++) {
328  removeEntry(entry[i], rv);
329  }
330 
331  // move down entries if necessary
332  if (end != length) {
333 #pragma GCC diagnostic ignored "-Wclass-memaccess"
334  memmove(entry + offset, entry + end, sizeof(QoreValue) * (length - end));
335 #pragma GCC diagnostic pop
336  // zero out trailing entries
337  zeroEntries(length - len, length);
338  } else // set last entry to 0
339  entry[end - 1] = QoreValue();
340 
341  resize(length - len);
342 
343  return rv;
344  }
345 
346  DLLLOCAL QoreListNode* spliceIntern(size_t offset, size_t len, const QoreValue l, bool extract, ExceptionSink* xsink) {
347  // check type compatibility before modifying the list
348  ValueHolder holder(xsink);
349  if (l.getType() == NT_LIST) {
350  // create a new temporary list and check types first
351  const qore_list_private* sl = l.get<QoreListNode>()->priv;
352  QoreListNode* tmp;
353  holder = tmp = sl->getCopy();
354  tmp->priv->reserve(sl->length);
355  for (size_t i = 0; i < sl->length; ++i) {
356  ValueHolder eh(sl->entry[i].refSelf(), xsink);
357  if (checkVal(eh, xsink)) {
358  return nullptr;
359  }
360  tmp->priv->entry[i] = eh.release();
361  ++tmp->priv->length;
362  }
363  } else {
364  holder = l.refSelf();
365  if (checkVal(holder, xsink)) {
366  return nullptr;
367  }
368  }
369 
370  //printd(5, "spliceIntern(offset: %d, len: %d, length: %d)\n", offset, len, length);
371  size_t end;
372  if (len > (length - offset)) {
373  end = length;
374  len = length - offset;
375  } else
376  end = offset + len;
377 
378  QoreListNode* rv = extract ? getCopy() : nullptr;
379 
380  // dereference all entries that will be removed or add to return value list
381  for (size_t i = offset; i < end; i++) {
382  removeEntry(entry[i], rv);
383  }
384 
385  // get number of entries to insert
386  size_t n = l.getType() == NT_LIST ? l.get<const QoreListNode>()->size() : 1;
387  // difference
388  if (n > len) { // make bigger
389  size_t ol = length;
390  resize(length - len + n);
391  // move trailing entries forward if necessary
392  if (end != ol)
393 #pragma GCC diagnostic ignored "-Wclass-memaccess"
394  memmove(entry + (end - len + n), entry + end, sizeof(QoreValue) * (ol - end));
395 #pragma GCC diagnostic pop
396  } else if (len > n) { // make list smaller
397 #pragma GCC diagnostic ignored "-Wclass-memaccess"
398  memmove(entry + offset + n, entry + offset + len, sizeof(QoreValue) * (length - offset - n));
399 #pragma GCC diagnostic pop
400  // zero out trailing entries
401  zeroEntries(length - (len - n), length);
402  // resize list
403  resize(length - (len - n));
404  }
405 
406  // add in new entries
407  if (l.getType() != NT_LIST) {
408  entry[offset] = holder.release();
409  if (needs_scan(l))
410  incScanCount(1);
411  }
412  else {
413  qore_list_private* lst = holder->get<const QoreListNode>()->priv;
414  for (size_t i = 0; i < n; ++i) {
415  QoreValue v = lst->entry[i];
416  lst->entry[i] = QoreValue();
417  if (needs_scan(v))
418  incScanCount(1);
419  entry[offset + i] = v;
420  }
421  lst->length = 0;
422  }
423 
424  return rv;
425  }
426 
427  DLLLOCAL QoreValue& getEntryReference(size_t num) {
428  if (num >= length) {
429  resize(num + 1);
430  }
431  return entry[num];
432  }
433 
434  DLLLOCAL QoreValue getAndClear(size_t i) {
435  if (i >= length) {
436  return QoreValue();
437  }
438  QoreValue rv = entry[i];
439  entry[i] = QoreValue();
440 
441  if (needs_scan(rv)) {
442  incScanCount(-1);
443  }
444 
445  return rv;
446  }
447 
448  DLLLOCAL static QoreListNode* getPlainList(QoreListNode* l) {
449  if (!l->priv->complexTypeInfo)
450  return l;
451  // no exception is possible
452  ReferenceHolder<QoreListNode> holder(l, nullptr);
453  return l->priv->copy(true);
454  }
455 
456  DLLLOCAL QoreValue swapIntern(qore_offset_t offset, QoreValue val) {
457  assert(offset >= 0);
458  QoreValue& p = getEntryReference((size_t)offset);
459 
460  QoreValue rv = p;
461  p = val;
462 
463  return rv;
464  }
465 
466  DLLLOCAL QoreValue swap(qore_offset_t offset, QoreValue val) {
467  QoreValue& p = getEntryReference((size_t)offset);
468 
469  bool before = needs_scan(p);
470  bool after = needs_scan(val);
471  if (before) {
472  if (!after)
473  --obj_count;
474  }
475  else if (after) {
476  ++obj_count;
477  }
478 
479  QoreValue rv = p;
480  p = val;
481 
482  return rv;
483  }
484 
485  DLLLOCAL QoreValue takeExists(size_t offset) {
486  if (offset >= length) {
487  return QoreValue();
488  }
489 
490  QoreValue rv = entry[offset];
491  entry[offset].assignNothing();
492 
493  if (needs_scan(rv)) {
494  --obj_count;
495  }
496 
497  return rv;
498  }
499 
500  DLLLOCAL void reserve(size_t num) {
501  if (num < length)
502  return;
503  // make larger
504  if (num >= allocated) {
505  size_t d = num >> 2;
506  allocated = num + (d < LIST_PAD ? LIST_PAD : d);
507 #pragma GCC diagnostic ignored "-Wclass-memaccess"
508  entry = (QoreValue*)realloc(entry, sizeof(QoreValue) * allocated);
509 #pragma GCC diagnostic pop
510  }
511  }
512 
513  DLLLOCAL void resize(size_t num) {
514  if (num < length) { // make smaller
515  //entry = (QoreValue*)realloc(entry, sizeof(QoreValue*) * num);
516  length = num;
517  return;
518  }
519  // make larger
520  if (num >= length) {
521  if (num >= allocated) {
522  size_t d = num >> 2;
523  allocated = num + (d < LIST_PAD ? LIST_PAD : d);
524 #pragma GCC diagnostic ignored "-Wclass-memaccess"
525  entry = (QoreValue*)realloc(entry, sizeof(QoreValue) * allocated);
526 #pragma GCC diagnostic pop
527  }
528  zeroEntries(length, num);
529  }
530  length = num;
531  }
532 
533  DLLLOCAL void zeroEntries(size_t start, size_t end) {
534  for (size_t i = start; i < end; ++i) {
535  entry[i] = QoreValue();
536  }
537  }
538 
539  DLLLOCAL void removeEntry(QoreValue& v, QoreListNode*& rv) {
540  if (needs_scan(v)) {
541  incScanCount(-1);
542  }
543  if (!rv)
544  rv = new QoreListNode(autoTypeInfo);
545  rv->priv->pushIntern(v);
546  }
547 
548  DLLLOCAL int getLValue(size_t ind, LValueHelper& lvh, bool for_remove, ExceptionSink* xsink);
549 
550  // mergesort for controlled and interruptible sorts (stable)
551  DLLLOCAL int mergesort(const ResolvedCallReferenceNode* fr, bool ascending, ExceptionSink* xsink);
552 
553  // quicksort for controlled and interruptible sorts (unstable)
554  DLLLOCAL int qsort(const ResolvedCallReferenceNode* fr, size_t left, size_t right, bool ascending,
555  ExceptionSink* xsink);
556 
557  DLLLOCAL void incScanCount(int dt) {
558  assert(dt);
559  assert(obj_count || (dt > 0));
560  //printd(5, "qore_list_private::incScanCount() this: %p dt: %d: %d -> %d\n", this, dt, obj_count, obj_count + dt);
561  obj_count += dt;
562  }
563 
564  DLLLOCAL QoreListNode* eval(ExceptionSink* xsink);
565 
566  DLLLOCAL static void setNeedsEval(QoreListNode& l) {
567  l.needs_eval_flag = true;
568  l.value = false;
569  }
570 
571  DLLLOCAL static int parseInitComplexListInitialization(const QoreProgramLocation* loc,
572  QoreParseContext& parse_context, QoreParseListNode* args, QoreValue& new_args);
573 
574  DLLLOCAL static int parseInitListInitialization(const QoreProgramLocation* loc, QoreParseContext& parse_context,
575  QoreParseListNode* args, QoreValue& new_args, int& err);
576 
577  DLLLOCAL static int parseCheckComplexListInitialization(const QoreProgramLocation* loc,
578  const QoreTypeInfo* typeInfo, const QoreTypeInfo* expTypeInfo, const QoreValue exp,
579  const char* context_action, bool strict_check = true);
580 
581  DLLLOCAL static int parseCheckTypedAssignment(const QoreProgramLocation* loc, const QoreValue arg,
582  const QoreTypeInfo* vti, const char* context_action, bool strict_check = true);
583 
584  DLLLOCAL static QoreListNode* newComplexList(const QoreTypeInfo* typeInfo, const QoreValue args,
585  ExceptionSink* xsink);
586 
587  // caller owns any reference in "init"
588  DLLLOCAL static QoreListNode* newComplexListFromValue(const QoreTypeInfo* typeInfo, QoreValue init,
589  ExceptionSink* xsink);
590 
591  DLLLOCAL static const qore_list_private* get(const QoreListNode& l) {
592  return l.priv;
593  }
594 
595  DLLLOCAL static qore_list_private* get(QoreListNode& l) {
596  return l.priv;
597  }
598 
599  DLLLOCAL static unsigned getScanCount(const QoreListNode& l) {
600  return l.priv->obj_count;
601  }
602 
603  DLLLOCAL static void incScanCount(const QoreListNode& l, int dt) {
604  l.priv->incScanCount(dt);
605  }
606 };
607 
608 #endif
bool value
this is true for values, if false then either the type needs evaluation to produce a value or is a pa...
Definition: AbstractQoreNode.h:330
bool needs_eval_flag
if this is true then the type can be evaluated
Definition: AbstractQoreNode.h:333
For use on the stack only: iterates through elements of a const QoreListNode.
Definition: QoreListNode.h:563
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
DLLEXPORT int appendLastDescription(const char *fmt,...)
appends a formatted string to the top exception description if the desc value is a string
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
hashdecl qore_list_private * priv
this structure holds the private implementation for the type
Definition: QoreListNode.h:67
DLLEXPORT size_t size() const
returns the number of elements in the list
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
DLLLOCAL T * release()
releases the pointer to the caller
Definition: ReferenceHolder.h:83
base class for resolved call references
Definition: CallReferenceNode.h:109
holds an object and dereferences it in the destructor
Definition: QoreValue.h:476
DLLEXPORT QoreValue release()
returns a QoreValue object and leaves the current object empty; the caller owns any reference contain...
intptr_t qore_offset_t
used for offsets that could be negative
Definition: common.h:76
const qore_type_t NT_LIST
type value for QoreListNode
Definition: node_types.h:50
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:275
DLLEXPORT const QoreTypeInfo * getTypeInfo() const
returns the type of the value
DLLEXPORT AbstractQoreNode * assignNothing()
sets the value of the object to QoreNothingNode and returns any node value held previously
DLLEXPORT QoreValue refSelf() const
references the contained value if type == QV_Node, returns itself