Qore Programming Language  0.9.16
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 - 2019 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  }
270  else if ((size_t)offset > length)
271  return length;
272 
273  return offset;
274  }
275 
276  DLLLOCAL void checkOffset(ptrdiff_t offset, ptrdiff_t len, size_t &n_offset, size_t &n_len) {
277  n_offset = checkOffset(offset);
278  if (len < 0) {
279  len = length + len - n_offset;
280  n_len = len < 0 ? 0 : len;
281  return;
282  }
283  n_len = len;
284  }
285 
286  QoreValue spliceSingle(size_t offset) {
287  assert(offset < length);
288 
289  QoreValue rv = entry[offset];
290  if (needs_scan(rv)) {
291  incScanCount(-1);
292  }
293 
294  size_t end = offset + 1;
295 
296  if (end != length) {
297  memmove(entry + offset, entry + end, sizeof(QoreValue) * (length - end));
298  // zero out trailing entries
299  zeroEntries(length - 1, length);
300  }
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  }
322  else
323  end = offset + len;
324 
325  QoreListNode* rv = extract ? getCopy() : nullptr;
326 
327  // dereference all entries that will be removed or add to return value list
328  for (size_t i = offset; i < end; i++) {
329  removeEntry(entry[i], rv);
330  }
331 
332  // move down entries if necessary
333  if (end != length) {
334  memmove(entry + offset, entry + end, sizeof(QoreValue) * (length - end));
335  // zero out trailing entries
336  zeroEntries(length - len, length);
337  }
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  }
364  else {
365  holder = l.refSelf();
366  if (checkVal(holder, xsink)) {
367  return nullptr;
368  }
369  }
370 
371  //printd(5, "spliceIntern(offset: %d, len: %d, length: %d)\n", offset, len, length);
372  size_t end;
373  if (len > (length - offset)) {
374  end = length;
375  len = length - offset;
376  }
377  else
378  end = offset + len;
379 
380  QoreListNode* rv = extract ? getCopy() : nullptr;
381 
382  // dereference all entries that will be removed or add to return value list
383  for (size_t i = offset; i < end; i++) {
384  removeEntry(entry[i], rv);
385  }
386 
387  // get number of entries to insert
388  size_t n = l.getType() == NT_LIST ? l.get<const QoreListNode>()->size() : 1;
389  // difference
390  if (n > len) { // make bigger
391  size_t ol = length;
392  resize(length - len + n);
393  // move trailing entries forward if necessary
394  if (end != ol)
395  memmove(entry + (end - len + n), entry + end, sizeof(QoreValue) * (ol - end));
396  }
397  else if (len > n) { // make list smaller
398  memmove(entry + offset + n, entry + offset + len, sizeof(QoreValue) * (length - offset - n));
399  // zero out trailing entries
400  zeroEntries(length - (len - n), length);
401  // resize list
402  resize(length - (len - n));
403  }
404 
405  // add in new entries
406  if (l.getType() != NT_LIST) {
407  entry[offset] = holder.release();
408  if (needs_scan(l))
409  incScanCount(1);
410  }
411  else {
412  qore_list_private* lst = holder->get<const QoreListNode>()->priv;
413  for (size_t i = 0; i < n; ++i) {
414  QoreValue v = lst->entry[i];
415  lst->entry[i] = QoreValue();
416  if (needs_scan(v))
417  incScanCount(1);
418  entry[offset + i] = v;
419  }
420  lst->length = 0;
421  }
422 
423  return rv;
424  }
425 
426  DLLLOCAL QoreValue& getEntryReference(size_t num) {
427  if (num >= length) {
428  resize(num + 1);
429  }
430  return entry[num];
431  }
432 
433  DLLLOCAL QoreValue getAndClear(size_t i) {
434  if (i >= length) {
435  return QoreValue();
436  }
437  QoreValue rv = entry[i];
438  entry[i] = QoreValue();
439 
440  if (needs_scan(rv)) {
441  incScanCount(-1);
442  }
443 
444  return rv;
445  }
446 
447  DLLLOCAL static QoreListNode* getPlainList(QoreListNode* l) {
448  if (!l->priv->complexTypeInfo)
449  return l;
450  // no exception is possible
451  ReferenceHolder<QoreListNode> holder(l, nullptr);
452  return l->priv->copy(true);
453  }
454 
455  DLLLOCAL QoreValue swapIntern(qore_offset_t offset, QoreValue val) {
456  assert(offset >= 0);
457  QoreValue& p = getEntryReference((size_t)offset);
458 
459  QoreValue rv = p;
460  p = val;
461 
462  return rv;
463  }
464 
465  DLLLOCAL QoreValue swap(qore_offset_t offset, QoreValue val) {
466  QoreValue& p = getEntryReference((size_t)offset);
467 
468  bool before = needs_scan(p);
469  bool after = needs_scan(val);
470  if (before) {
471  if (!after)
472  --obj_count;
473  }
474  else if (after) {
475  ++obj_count;
476  }
477 
478  QoreValue rv = p;
479  p = val;
480 
481  return rv;
482  }
483 
484  DLLLOCAL QoreValue takeExists(size_t offset) {
485  if (offset >= length) {
486  return QoreValue();
487  }
488 
489  QoreValue rv = entry[offset];
490  entry[offset].assignNothing();
491 
492  if (needs_scan(rv)) {
493  --obj_count;
494  }
495 
496  return rv;
497  }
498 
499  DLLLOCAL void reserve(size_t num) {
500  if (num < length)
501  return;
502  // make larger
503  if (num >= allocated) {
504  size_t d = num >> 2;
505  allocated = num + (d < LIST_PAD ? LIST_PAD : d);
506  entry = (QoreValue*)realloc(entry, sizeof(QoreValue) * allocated);
507  }
508  }
509 
510  DLLLOCAL void resize(size_t num) {
511  if (num < length) { // make smaller
512  //entry = (QoreValue*)realloc(entry, sizeof(QoreValue*) * num);
513  length = num;
514  return;
515  }
516  // make larger
517  if (num >= length) {
518  if (num >= allocated) {
519  size_t d = num >> 2;
520  allocated = num + (d < LIST_PAD ? LIST_PAD : d);
521  entry = (QoreValue*)realloc(entry, sizeof(QoreValue) * allocated);
522  }
523  zeroEntries(length, num);
524  }
525  length = num;
526  }
527 
528  DLLLOCAL void zeroEntries(size_t start, size_t end) {
529  for (size_t i = start; i < end; ++i) {
530  entry[i] = QoreValue();
531  }
532  }
533 
534  DLLLOCAL void removeEntry(QoreValue& v, QoreListNode*& rv) {
535  if (needs_scan(v)) {
536  incScanCount(-1);
537  }
538  if (!rv)
539  rv = new QoreListNode(autoTypeInfo);
540  rv->priv->pushIntern(v);
541  }
542 
543  DLLLOCAL int getLValue(size_t ind, LValueHelper& lvh, bool for_remove, ExceptionSink* xsink);
544 
545  // mergesort for controlled and interruptible sorts (stable)
546  DLLLOCAL int mergesort(const ResolvedCallReferenceNode* fr, bool ascending, ExceptionSink* xsink);
547 
548  // quicksort for controlled and interruptible sorts (unstable)
549  DLLLOCAL int qsort(const ResolvedCallReferenceNode* fr, size_t left, size_t right, bool ascending, ExceptionSink* xsink);
550 
551  DLLLOCAL void incScanCount(int dt) {
552  assert(dt);
553  assert(obj_count || (dt > 0));
554  //printd(5, "qore_list_private::incScanCount() this: %p dt: %d: %d -> %d\n", this, dt, obj_count, obj_count + dt);
555  obj_count += dt;
556  }
557 
558  DLLLOCAL QoreListNode* eval(ExceptionSink* xsink);
559 
560  DLLLOCAL static void setNeedsEval(QoreListNode& l) {
561  l.needs_eval_flag = true;
562  l.value = false;
563  }
564 
565  DLLLOCAL static int parseInitComplexListInitialization(const QoreProgramLocation* loc, LocalVar *oflag, int pflag, QoreParseListNode* args, QoreValue& new_args, const QoreTypeInfo* vti);
566 
567  DLLLOCAL static int parseInitListInitialization(const QoreProgramLocation* loc, LocalVar *oflag, int pflag, int& lvids, QoreParseListNode* args, QoreValue& new_args, const QoreTypeInfo*& argTypeInfo);
568 
569  DLLLOCAL static void parseCheckComplexListInitialization(const QoreProgramLocation* loc, const QoreTypeInfo* typeInfo, const QoreTypeInfo* expTypeInfo, const QoreValue exp, const char* context_action, bool strict_check = true);
570 
571  DLLLOCAL static void parseCheckTypedAssignment(const QoreProgramLocation* loc, const QoreValue arg, const QoreTypeInfo* vti, const char* context_action, bool strict_check = true);
572 
573  DLLLOCAL static QoreListNode* newComplexList(const QoreTypeInfo* typeInfo, const QoreValue args, ExceptionSink* xsink);
574 
575  // caller owns any reference in "init"
576  DLLLOCAL static QoreListNode* newComplexListFromValue(const QoreTypeInfo* typeInfo, QoreValue init, ExceptionSink* xsink);
577 
578  DLLLOCAL static const qore_list_private* get(const QoreListNode& l) {
579  return l.priv;
580  }
581 
582  DLLLOCAL static qore_list_private* get(QoreListNode& l) {
583  return l.priv;
584  }
585 
586  DLLLOCAL static unsigned getScanCount(const QoreListNode& l) {
587  return l.priv->obj_count;
588  }
589 
590  DLLLOCAL static void incScanCount(const QoreListNode& l, int dt) {
591  l.priv->incScanCount(dt);
592  }
593 };
594 
595 #endif
ConstListIterator
For use on the stack only: iterates through elements of a const QoreListNode.
Definition: QoreListNode.h:564
qore_offset_t
intptr_t qore_offset_t
used for offsets that could be negative
Definition: common.h:76
AbstractQoreNode::value
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:324
QoreValue::assignNothing
DLLEXPORT AbstractQoreNode * assignNothing()
sets the value of the object to QoreNothingNode and returns any node value held previously
QoreListNode::size
DLLEXPORT size_t size() const
returns the number of elements in the list
QoreValue
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
ReferenceHolder::release
DLLLOCAL T * release()
releases the pointer to the caller
Definition: ReferenceHolder.h:83
QoreListNode::priv
hashdecl qore_list_private * priv
this structure holds the private implementation for the type
Definition: QoreListNode.h:67
ReferenceHolder< QoreListNode >
QoreListNode
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
ExceptionSink::appendLastDescription
DLLEXPORT int appendLastDescription(const char *fmt,...)
appends a formatted string to the top exception description if the desc value is a string
QoreString
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:81
QoreValue::refSelf
DLLEXPORT QoreValue refSelf() const
references the contained value if type == QV_Node, returns itself
AbstractQoreNode::needs_eval_flag
bool needs_eval_flag
if this is true then the type can be evaluated
Definition: AbstractQoreNode.h:327
QoreString::concat
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
ExceptionSink
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
QoreValue::getTypeInfo
const DLLEXPORT QoreTypeInfo * getTypeInfo() const
returns the type of the value
ValueHolder::release
DLLEXPORT QoreValue release()
returns a QoreValue object and leaves the current object empty; the caller owns any reference contain...
NT_LIST
const qore_type_t NT_LIST
type value for QoreListNode
Definition: node_types.h:50
ValueHolder
holds an object and dereferences it in the destructor
Definition: QoreValue.h:452
ResolvedCallReferenceNode
base class for resolved call references
Definition: CallReferenceNode.h:105
NT_HASH
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51