Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
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 - 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_QORELISTPRIVATE_H
33#define _QORE_QORELISTPRIVATE_H
34
35#include <cstring>
36
38
39#define LIST_PAD 15
40
41hashdecl 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 {
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:50
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
DLLLOCAL detail::QoreValueCastHelper< T >::Result get()
returns the value as the given type
Definition: QoreValue.h:214
DLLEXPORT qore_type_t getType() const
returns the type of value contained
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
a templated class to manage a reference count of an object that can throw a Qore-language exception w...
Definition: ReferenceHolder.h:52
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:487
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:276
DLLEXPORT AbstractQoreNode * assignNothing()
sets the value of the object to QoreNothingNode and returns any node value held previously
DLLEXPORT const QoreTypeInfo * getTypeInfo() const
returns the type of the value
DLLEXPORT QoreValue refSelf() const
references the contained value if type == QV_Node, returns itself