1 | /**************************************************************************/ |
2 | /* array.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "array.h" |
32 | |
33 | #include "container_type_validate.h" |
34 | #include "core/math/math_funcs.h" |
35 | #include "core/object/class_db.h" |
36 | #include "core/object/script_language.h" |
37 | #include "core/templates/hashfuncs.h" |
38 | #include "core/templates/search_array.h" |
39 | #include "core/templates/vector.h" |
40 | #include "core/variant/callable.h" |
41 | #include "core/variant/dictionary.h" |
42 | #include "core/variant/variant.h" |
43 | |
44 | class ArrayPrivate { |
45 | public: |
46 | SafeRefCount refcount; |
47 | Vector<Variant> array; |
48 | Variant *read_only = nullptr; // If enabled, a pointer is used to a temporary value that is used to return read-only values. |
49 | ContainerTypeValidate typed; |
50 | }; |
51 | |
52 | void Array::_ref(const Array &p_from) const { |
53 | ArrayPrivate *_fp = p_from._p; |
54 | |
55 | ERR_FAIL_NULL(_fp); // Should NOT happen. |
56 | |
57 | if (_fp == _p) { |
58 | return; // whatever it is, nothing to do here move along |
59 | } |
60 | |
61 | bool success = _fp->refcount.ref(); |
62 | |
63 | ERR_FAIL_COND(!success); // should really not happen either |
64 | |
65 | _unref(); |
66 | |
67 | _p = _fp; |
68 | } |
69 | |
70 | void Array::_unref() const { |
71 | if (!_p) { |
72 | return; |
73 | } |
74 | |
75 | if (_p->refcount.unref()) { |
76 | if (_p->read_only) { |
77 | memdelete(_p->read_only); |
78 | } |
79 | memdelete(_p); |
80 | } |
81 | _p = nullptr; |
82 | } |
83 | |
84 | Variant &Array::operator[](int p_idx) { |
85 | if (unlikely(_p->read_only)) { |
86 | *_p->read_only = _p->array[p_idx]; |
87 | return *_p->read_only; |
88 | } |
89 | return _p->array.write[p_idx]; |
90 | } |
91 | |
92 | const Variant &Array::operator[](int p_idx) const { |
93 | if (unlikely(_p->read_only)) { |
94 | *_p->read_only = _p->array[p_idx]; |
95 | return *_p->read_only; |
96 | } |
97 | return _p->array[p_idx]; |
98 | } |
99 | |
100 | int Array::size() const { |
101 | return _p->array.size(); |
102 | } |
103 | |
104 | bool Array::is_empty() const { |
105 | return _p->array.is_empty(); |
106 | } |
107 | |
108 | void Array::clear() { |
109 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
110 | _p->array.clear(); |
111 | } |
112 | |
113 | bool Array::operator==(const Array &p_array) const { |
114 | return recursive_equal(p_array, 0); |
115 | } |
116 | |
117 | bool Array::operator!=(const Array &p_array) const { |
118 | return !recursive_equal(p_array, 0); |
119 | } |
120 | |
121 | bool Array::recursive_equal(const Array &p_array, int recursion_count) const { |
122 | // Cheap checks |
123 | if (_p == p_array._p) { |
124 | return true; |
125 | } |
126 | const Vector<Variant> &a1 = _p->array; |
127 | const Vector<Variant> &a2 = p_array._p->array; |
128 | const int size = a1.size(); |
129 | if (size != a2.size()) { |
130 | return false; |
131 | } |
132 | |
133 | // Heavy O(n) check |
134 | if (recursion_count > MAX_RECURSION) { |
135 | ERR_PRINT("Max recursion reached" ); |
136 | return true; |
137 | } |
138 | recursion_count++; |
139 | for (int i = 0; i < size; i++) { |
140 | if (!a1[i].hash_compare(a2[i], recursion_count)) { |
141 | return false; |
142 | } |
143 | } |
144 | |
145 | return true; |
146 | } |
147 | |
148 | bool Array::operator<(const Array &p_array) const { |
149 | int a_len = size(); |
150 | int b_len = p_array.size(); |
151 | |
152 | int min_cmp = MIN(a_len, b_len); |
153 | |
154 | for (int i = 0; i < min_cmp; i++) { |
155 | if (operator[](i) < p_array[i]) { |
156 | return true; |
157 | } else if (p_array[i] < operator[](i)) { |
158 | return false; |
159 | } |
160 | } |
161 | |
162 | return a_len < b_len; |
163 | } |
164 | |
165 | bool Array::operator<=(const Array &p_array) const { |
166 | return !operator>(p_array); |
167 | } |
168 | bool Array::operator>(const Array &p_array) const { |
169 | return p_array < *this; |
170 | } |
171 | bool Array::operator>=(const Array &p_array) const { |
172 | return !operator<(p_array); |
173 | } |
174 | |
175 | uint32_t Array::hash() const { |
176 | return recursive_hash(0); |
177 | } |
178 | |
179 | uint32_t Array::recursive_hash(int recursion_count) const { |
180 | if (recursion_count > MAX_RECURSION) { |
181 | ERR_PRINT("Max recursion reached" ); |
182 | return 0; |
183 | } |
184 | |
185 | uint32_t h = hash_murmur3_one_32(Variant::ARRAY); |
186 | |
187 | recursion_count++; |
188 | for (int i = 0; i < _p->array.size(); i++) { |
189 | h = hash_murmur3_one_32(_p->array[i].recursive_hash(recursion_count), h); |
190 | } |
191 | return hash_fmix32(h); |
192 | } |
193 | |
194 | void Array::operator=(const Array &p_array) { |
195 | if (this == &p_array) { |
196 | return; |
197 | } |
198 | _ref(p_array); |
199 | } |
200 | |
201 | void Array::assign(const Array &p_array) { |
202 | const ContainerTypeValidate &typed = _p->typed; |
203 | const ContainerTypeValidate &source_typed = p_array._p->typed; |
204 | |
205 | if (typed == source_typed || typed.type == Variant::NIL || (source_typed.type == Variant::OBJECT && typed.can_reference(source_typed))) { |
206 | // from same to same or |
207 | // from anything to variants or |
208 | // from subclasses to base classes |
209 | _p->array = p_array._p->array; |
210 | return; |
211 | } |
212 | |
213 | const Variant *source = p_array._p->array.ptr(); |
214 | int size = p_array._p->array.size(); |
215 | |
216 | if ((source_typed.type == Variant::NIL && typed.type == Variant::OBJECT) || (source_typed.type == Variant::OBJECT && source_typed.can_reference(typed))) { |
217 | // from variants to objects or |
218 | // from base classes to subclasses |
219 | for (int i = 0; i < size; i++) { |
220 | const Variant &element = source[i]; |
221 | if (element.get_type() != Variant::NIL && (element.get_type() != Variant::OBJECT || !typed.validate_object(element, "assign" ))) { |
222 | ERR_FAIL_MSG(vformat(R"(Unable to convert array index %i from "%s" to "%s".)" , i, Variant::get_type_name(element.get_type()), Variant::get_type_name(typed.type))); |
223 | } |
224 | } |
225 | _p->array = p_array._p->array; |
226 | return; |
227 | } |
228 | if (typed.type == Variant::OBJECT || source_typed.type == Variant::OBJECT) { |
229 | ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)" , Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type))); |
230 | } |
231 | |
232 | Vector<Variant> array; |
233 | array.resize(size); |
234 | Variant *data = array.ptrw(); |
235 | |
236 | if (source_typed.type == Variant::NIL && typed.type != Variant::OBJECT) { |
237 | // from variants to primitives |
238 | for (int i = 0; i < size; i++) { |
239 | const Variant *value = source + i; |
240 | if (value->get_type() == typed.type) { |
241 | data[i] = *value; |
242 | continue; |
243 | } |
244 | if (!Variant::can_convert_strict(value->get_type(), typed.type)) { |
245 | ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(value->get_type()) + "' to '" + Variant::get_type_name(typed.type) + "'." ); |
246 | } |
247 | Callable::CallError ce; |
248 | Variant::construct(typed.type, data[i], &value, 1, ce); |
249 | ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)" , i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type))); |
250 | } |
251 | } else if (Variant::can_convert_strict(source_typed.type, typed.type)) { |
252 | // from primitives to different convertible primitives |
253 | for (int i = 0; i < size; i++) { |
254 | const Variant *value = source + i; |
255 | Callable::CallError ce; |
256 | Variant::construct(typed.type, data[i], &value, 1, ce); |
257 | ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)" , i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type))); |
258 | } |
259 | } else { |
260 | ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)" , Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type))); |
261 | } |
262 | |
263 | _p->array = array; |
264 | } |
265 | |
266 | void Array::push_back(const Variant &p_value) { |
267 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
268 | Variant value = p_value; |
269 | ERR_FAIL_COND(!_p->typed.validate(value, "push_back" )); |
270 | _p->array.push_back(value); |
271 | } |
272 | |
273 | void Array::append_array(const Array &p_array) { |
274 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
275 | |
276 | Vector<Variant> validated_array = p_array._p->array; |
277 | for (int i = 0; i < validated_array.size(); ++i) { |
278 | ERR_FAIL_COND(!_p->typed.validate(validated_array.write[i], "append_array" )); |
279 | } |
280 | |
281 | _p->array.append_array(validated_array); |
282 | } |
283 | |
284 | Error Array::resize(int p_new_size) { |
285 | ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state." ); |
286 | Variant::Type &variant_type = _p->typed.type; |
287 | int old_size = _p->array.size(); |
288 | Error err = _p->array.resize_zeroed(p_new_size); |
289 | if (!err && variant_type != Variant::NIL && variant_type != Variant::OBJECT) { |
290 | for (int i = old_size; i < p_new_size; i++) { |
291 | VariantInternal::initialize(&_p->array.write[i], variant_type); |
292 | } |
293 | } |
294 | return err; |
295 | } |
296 | |
297 | Error Array::insert(int p_pos, const Variant &p_value) { |
298 | ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state." ); |
299 | Variant value = p_value; |
300 | ERR_FAIL_COND_V(!_p->typed.validate(value, "insert" ), ERR_INVALID_PARAMETER); |
301 | return _p->array.insert(p_pos, value); |
302 | } |
303 | |
304 | void Array::fill(const Variant &p_value) { |
305 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
306 | Variant value = p_value; |
307 | ERR_FAIL_COND(!_p->typed.validate(value, "fill" )); |
308 | _p->array.fill(value); |
309 | } |
310 | |
311 | void Array::erase(const Variant &p_value) { |
312 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
313 | Variant value = p_value; |
314 | ERR_FAIL_COND(!_p->typed.validate(value, "erase" )); |
315 | _p->array.erase(value); |
316 | } |
317 | |
318 | Variant Array::front() const { |
319 | ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array." ); |
320 | return operator[](0); |
321 | } |
322 | |
323 | Variant Array::back() const { |
324 | ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array." ); |
325 | return operator[](_p->array.size() - 1); |
326 | } |
327 | |
328 | Variant Array::pick_random() const { |
329 | ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array." ); |
330 | return operator[](Math::rand() % _p->array.size()); |
331 | } |
332 | |
333 | int Array::find(const Variant &p_value, int p_from) const { |
334 | if (_p->array.size() == 0) { |
335 | return -1; |
336 | } |
337 | Variant value = p_value; |
338 | ERR_FAIL_COND_V(!_p->typed.validate(value, "find" ), -1); |
339 | |
340 | int ret = -1; |
341 | |
342 | if (p_from < 0 || size() == 0) { |
343 | return ret; |
344 | } |
345 | |
346 | for (int i = p_from; i < size(); i++) { |
347 | if (StringLikeVariantComparator::compare(_p->array[i], value)) { |
348 | ret = i; |
349 | break; |
350 | } |
351 | } |
352 | |
353 | return ret; |
354 | } |
355 | |
356 | int Array::rfind(const Variant &p_value, int p_from) const { |
357 | if (_p->array.size() == 0) { |
358 | return -1; |
359 | } |
360 | Variant value = p_value; |
361 | ERR_FAIL_COND_V(!_p->typed.validate(value, "rfind" ), -1); |
362 | |
363 | if (p_from < 0) { |
364 | // Relative offset from the end |
365 | p_from = _p->array.size() + p_from; |
366 | } |
367 | if (p_from < 0 || p_from >= _p->array.size()) { |
368 | // Limit to array boundaries |
369 | p_from = _p->array.size() - 1; |
370 | } |
371 | |
372 | for (int i = p_from; i >= 0; i--) { |
373 | if (StringLikeVariantComparator::compare(_p->array[i], value)) { |
374 | return i; |
375 | } |
376 | } |
377 | |
378 | return -1; |
379 | } |
380 | |
381 | int Array::count(const Variant &p_value) const { |
382 | Variant value = p_value; |
383 | ERR_FAIL_COND_V(!_p->typed.validate(value, "count" ), 0); |
384 | if (_p->array.size() == 0) { |
385 | return 0; |
386 | } |
387 | |
388 | int amount = 0; |
389 | for (int i = 0; i < _p->array.size(); i++) { |
390 | if (StringLikeVariantComparator::compare(_p->array[i], value)) { |
391 | amount++; |
392 | } |
393 | } |
394 | |
395 | return amount; |
396 | } |
397 | |
398 | bool Array::has(const Variant &p_value) const { |
399 | Variant value = p_value; |
400 | ERR_FAIL_COND_V(!_p->typed.validate(value, "use 'has'" ), false); |
401 | |
402 | return find(value) != -1; |
403 | } |
404 | |
405 | void Array::remove_at(int p_pos) { |
406 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
407 | _p->array.remove_at(p_pos); |
408 | } |
409 | |
410 | void Array::set(int p_idx, const Variant &p_value) { |
411 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
412 | Variant value = p_value; |
413 | ERR_FAIL_COND(!_p->typed.validate(value, "set" )); |
414 | |
415 | operator[](p_idx) = value; |
416 | } |
417 | |
418 | const Variant &Array::get(int p_idx) const { |
419 | return operator[](p_idx); |
420 | } |
421 | |
422 | Array Array::duplicate(bool p_deep) const { |
423 | return recursive_duplicate(p_deep, 0); |
424 | } |
425 | |
426 | Array Array::recursive_duplicate(bool p_deep, int recursion_count) const { |
427 | Array new_arr; |
428 | new_arr._p->typed = _p->typed; |
429 | |
430 | if (recursion_count > MAX_RECURSION) { |
431 | ERR_PRINT("Max recursion reached" ); |
432 | return new_arr; |
433 | } |
434 | |
435 | if (p_deep) { |
436 | recursion_count++; |
437 | int element_count = size(); |
438 | new_arr.resize(element_count); |
439 | for (int i = 0; i < element_count; i++) { |
440 | new_arr[i] = get(i).recursive_duplicate(true, recursion_count); |
441 | } |
442 | } else { |
443 | new_arr._p->array = _p->array; |
444 | } |
445 | |
446 | return new_arr; |
447 | } |
448 | |
449 | Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { |
450 | Array result; |
451 | result._p->typed = _p->typed; |
452 | |
453 | ERR_FAIL_COND_V_MSG(p_step == 0, result, "Slice step cannot be zero." ); |
454 | |
455 | const int s = size(); |
456 | |
457 | if (s == 0 || (p_begin < -s && p_step < 0) || (p_begin >= s && p_step > 0)) { |
458 | return result; |
459 | } |
460 | |
461 | int begin = CLAMP(p_begin, -s, s - 1); |
462 | if (begin < 0) { |
463 | begin += s; |
464 | } |
465 | int end = CLAMP(p_end, -s - 1, s); |
466 | if (end < 0) { |
467 | end += s; |
468 | } |
469 | |
470 | ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice step is positive, but bounds are decreasing." ); |
471 | ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice step is negative, but bounds are increasing." ); |
472 | |
473 | int result_size = (end - begin) / p_step + (((end - begin) % p_step != 0) ? 1 : 0); |
474 | result.resize(result_size); |
475 | |
476 | for (int src_idx = begin, dest_idx = 0; dest_idx < result_size; ++dest_idx) { |
477 | result[dest_idx] = p_deep ? get(src_idx).duplicate(true) : get(src_idx); |
478 | src_idx += p_step; |
479 | } |
480 | |
481 | return result; |
482 | } |
483 | |
484 | Array Array::filter(const Callable &p_callable) const { |
485 | Array new_arr; |
486 | new_arr.resize(size()); |
487 | new_arr._p->typed = _p->typed; |
488 | int accepted_count = 0; |
489 | |
490 | const Variant *argptrs[1]; |
491 | for (int i = 0; i < size(); i++) { |
492 | argptrs[0] = &get(i); |
493 | |
494 | Variant result; |
495 | Callable::CallError ce; |
496 | p_callable.callp(argptrs, 1, result, ce); |
497 | if (ce.error != Callable::CallError::CALL_OK) { |
498 | ERR_FAIL_V_MSG(Array(), "Error calling method from 'filter': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); |
499 | } |
500 | |
501 | if (result.operator bool()) { |
502 | new_arr[accepted_count] = get(i); |
503 | accepted_count++; |
504 | } |
505 | } |
506 | |
507 | new_arr.resize(accepted_count); |
508 | |
509 | return new_arr; |
510 | } |
511 | |
512 | Array Array::map(const Callable &p_callable) const { |
513 | Array new_arr; |
514 | new_arr.resize(size()); |
515 | |
516 | const Variant *argptrs[1]; |
517 | for (int i = 0; i < size(); i++) { |
518 | argptrs[0] = &get(i); |
519 | |
520 | Variant result; |
521 | Callable::CallError ce; |
522 | p_callable.callp(argptrs, 1, result, ce); |
523 | if (ce.error != Callable::CallError::CALL_OK) { |
524 | ERR_FAIL_V_MSG(Array(), "Error calling method from 'map': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); |
525 | } |
526 | |
527 | new_arr[i] = result; |
528 | } |
529 | |
530 | return new_arr; |
531 | } |
532 | |
533 | Variant Array::reduce(const Callable &p_callable, const Variant &p_accum) const { |
534 | int start = 0; |
535 | Variant ret = p_accum; |
536 | if (ret == Variant() && size() > 0) { |
537 | ret = front(); |
538 | start = 1; |
539 | } |
540 | |
541 | const Variant *argptrs[2]; |
542 | for (int i = start; i < size(); i++) { |
543 | argptrs[0] = &ret; |
544 | argptrs[1] = &get(i); |
545 | |
546 | Variant result; |
547 | Callable::CallError ce; |
548 | p_callable.callp(argptrs, 2, result, ce); |
549 | if (ce.error != Callable::CallError::CALL_OK) { |
550 | ERR_FAIL_V_MSG(Variant(), "Error calling method from 'reduce': " + Variant::get_callable_error_text(p_callable, argptrs, 2, ce)); |
551 | } |
552 | ret = result; |
553 | } |
554 | |
555 | return ret; |
556 | } |
557 | |
558 | bool Array::any(const Callable &p_callable) const { |
559 | const Variant *argptrs[1]; |
560 | for (int i = 0; i < size(); i++) { |
561 | argptrs[0] = &get(i); |
562 | |
563 | Variant result; |
564 | Callable::CallError ce; |
565 | p_callable.callp(argptrs, 1, result, ce); |
566 | if (ce.error != Callable::CallError::CALL_OK) { |
567 | ERR_FAIL_V_MSG(false, "Error calling method from 'any': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); |
568 | } |
569 | |
570 | if (result.operator bool()) { |
571 | // Return as early as possible when one of the conditions is `true`. |
572 | // This improves performance compared to relying on `filter(...).size() >= 1`. |
573 | return true; |
574 | } |
575 | } |
576 | |
577 | return false; |
578 | } |
579 | |
580 | bool Array::all(const Callable &p_callable) const { |
581 | const Variant *argptrs[1]; |
582 | for (int i = 0; i < size(); i++) { |
583 | argptrs[0] = &get(i); |
584 | |
585 | Variant result; |
586 | Callable::CallError ce; |
587 | p_callable.callp(argptrs, 1, result, ce); |
588 | if (ce.error != Callable::CallError::CALL_OK) { |
589 | ERR_FAIL_V_MSG(false, "Error calling method from 'all': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); |
590 | } |
591 | |
592 | if (!(result.operator bool())) { |
593 | // Return as early as possible when one of the inverted conditions is `false`. |
594 | // This improves performance compared to relying on `filter(...).size() >= array_size().`. |
595 | return false; |
596 | } |
597 | } |
598 | |
599 | return true; |
600 | } |
601 | |
602 | struct _ArrayVariantSort { |
603 | _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { |
604 | bool valid = false; |
605 | Variant res; |
606 | Variant::evaluate(Variant::OP_LESS, p_l, p_r, res, valid); |
607 | if (!valid) { |
608 | res = false; |
609 | } |
610 | return res; |
611 | } |
612 | }; |
613 | |
614 | void Array::sort() { |
615 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
616 | _p->array.sort_custom<_ArrayVariantSort>(); |
617 | } |
618 | |
619 | void Array::sort_custom(const Callable &p_callable) { |
620 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
621 | _p->array.sort_custom<CallableComparator, true>(p_callable); |
622 | } |
623 | |
624 | void Array::shuffle() { |
625 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
626 | const int n = _p->array.size(); |
627 | if (n < 2) { |
628 | return; |
629 | } |
630 | Variant *data = _p->array.ptrw(); |
631 | for (int i = n - 1; i >= 1; i--) { |
632 | const int j = Math::rand() % (i + 1); |
633 | const Variant tmp = data[j]; |
634 | data[j] = data[i]; |
635 | data[i] = tmp; |
636 | } |
637 | } |
638 | |
639 | int Array::bsearch(const Variant &p_value, bool p_before) const { |
640 | Variant value = p_value; |
641 | ERR_FAIL_COND_V(!_p->typed.validate(value, "binary search" ), -1); |
642 | SearchArray<Variant, _ArrayVariantSort> avs; |
643 | return avs.bisect(_p->array.ptrw(), _p->array.size(), value, p_before); |
644 | } |
645 | |
646 | int Array::bsearch_custom(const Variant &p_value, const Callable &p_callable, bool p_before) const { |
647 | Variant value = p_value; |
648 | ERR_FAIL_COND_V(!_p->typed.validate(value, "custom binary search" ), -1); |
649 | |
650 | return _p->array.bsearch_custom<CallableComparator>(value, p_before, p_callable); |
651 | } |
652 | |
653 | void Array::reverse() { |
654 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
655 | _p->array.reverse(); |
656 | } |
657 | |
658 | void Array::push_front(const Variant &p_value) { |
659 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
660 | Variant value = p_value; |
661 | ERR_FAIL_COND(!_p->typed.validate(value, "push_front" )); |
662 | _p->array.insert(0, value); |
663 | } |
664 | |
665 | Variant Array::pop_back() { |
666 | ERR_FAIL_COND_V_MSG(_p->read_only, Variant(), "Array is in read-only state." ); |
667 | if (!_p->array.is_empty()) { |
668 | const int n = _p->array.size() - 1; |
669 | const Variant ret = _p->array.get(n); |
670 | _p->array.resize(n); |
671 | return ret; |
672 | } |
673 | return Variant(); |
674 | } |
675 | |
676 | Variant Array::pop_front() { |
677 | ERR_FAIL_COND_V_MSG(_p->read_only, Variant(), "Array is in read-only state." ); |
678 | if (!_p->array.is_empty()) { |
679 | const Variant ret = _p->array.get(0); |
680 | _p->array.remove_at(0); |
681 | return ret; |
682 | } |
683 | return Variant(); |
684 | } |
685 | |
686 | Variant Array::pop_at(int p_pos) { |
687 | ERR_FAIL_COND_V_MSG(_p->read_only, Variant(), "Array is in read-only state." ); |
688 | if (_p->array.is_empty()) { |
689 | // Return `null` without printing an error to mimic `pop_back()` and `pop_front()` behavior. |
690 | return Variant(); |
691 | } |
692 | |
693 | if (p_pos < 0) { |
694 | // Relative offset from the end |
695 | p_pos = _p->array.size() + p_pos; |
696 | } |
697 | |
698 | ERR_FAIL_INDEX_V_MSG( |
699 | p_pos, |
700 | _p->array.size(), |
701 | Variant(), |
702 | vformat( |
703 | "The calculated index %s is out of bounds (the array has %s elements). Leaving the array untouched and returning `null`." , |
704 | p_pos, |
705 | _p->array.size())); |
706 | |
707 | const Variant ret = _p->array.get(p_pos); |
708 | _p->array.remove_at(p_pos); |
709 | return ret; |
710 | } |
711 | |
712 | Variant Array::min() const { |
713 | Variant minval; |
714 | for (int i = 0; i < size(); i++) { |
715 | if (i == 0) { |
716 | minval = get(i); |
717 | } else { |
718 | bool valid; |
719 | Variant ret; |
720 | Variant test = get(i); |
721 | Variant::evaluate(Variant::OP_LESS, test, minval, ret, valid); |
722 | if (!valid) { |
723 | return Variant(); //not a valid comparison |
724 | } |
725 | if (bool(ret)) { |
726 | //is less |
727 | minval = test; |
728 | } |
729 | } |
730 | } |
731 | return minval; |
732 | } |
733 | |
734 | Variant Array::max() const { |
735 | Variant maxval; |
736 | for (int i = 0; i < size(); i++) { |
737 | if (i == 0) { |
738 | maxval = get(i); |
739 | } else { |
740 | bool valid; |
741 | Variant ret; |
742 | Variant test = get(i); |
743 | Variant::evaluate(Variant::OP_GREATER, test, maxval, ret, valid); |
744 | if (!valid) { |
745 | return Variant(); //not a valid comparison |
746 | } |
747 | if (bool(ret)) { |
748 | //is less |
749 | maxval = test; |
750 | } |
751 | } |
752 | } |
753 | return maxval; |
754 | } |
755 | |
756 | const void *Array::id() const { |
757 | return _p; |
758 | } |
759 | |
760 | Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_name, const Variant &p_script) { |
761 | _p = memnew(ArrayPrivate); |
762 | _p->refcount.init(); |
763 | set_typed(p_type, p_class_name, p_script); |
764 | assign(p_from); |
765 | } |
766 | |
767 | void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) { |
768 | ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state." ); |
769 | ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty." ); |
770 | ERR_FAIL_COND_MSG(_p->refcount.get() > 1, "Type can only be set when array has no more than one user." ); |
771 | ERR_FAIL_COND_MSG(_p->typed.type != Variant::NIL, "Type can only be set once." ); |
772 | ERR_FAIL_COND_MSG(p_class_name != StringName() && p_type != Variant::OBJECT, "Class names can only be set for type OBJECT" ); |
773 | Ref<Script> script = p_script; |
774 | ERR_FAIL_COND_MSG(script.is_valid() && p_class_name == StringName(), "Script class can only be set together with base class name" ); |
775 | |
776 | _p->typed.type = Variant::Type(p_type); |
777 | _p->typed.class_name = p_class_name; |
778 | _p->typed.script = script; |
779 | _p->typed.where = "TypedArray" ; |
780 | } |
781 | |
782 | bool Array::is_typed() const { |
783 | return _p->typed.type != Variant::NIL; |
784 | } |
785 | |
786 | bool Array::is_same_typed(const Array &p_other) const { |
787 | return _p->typed == p_other._p->typed; |
788 | } |
789 | |
790 | uint32_t Array::get_typed_builtin() const { |
791 | return _p->typed.type; |
792 | } |
793 | |
794 | StringName Array::get_typed_class_name() const { |
795 | return _p->typed.class_name; |
796 | } |
797 | |
798 | Variant Array::get_typed_script() const { |
799 | return _p->typed.script; |
800 | } |
801 | |
802 | void Array::make_read_only() { |
803 | if (_p->read_only == nullptr) { |
804 | _p->read_only = memnew(Variant); |
805 | } |
806 | } |
807 | |
808 | bool Array::is_read_only() const { |
809 | return _p->read_only != nullptr; |
810 | } |
811 | |
812 | Array::Array(const Array &p_from) { |
813 | _p = nullptr; |
814 | _ref(p_from); |
815 | } |
816 | |
817 | Array::Array() { |
818 | _p = memnew(ArrayPrivate); |
819 | _p->refcount.init(); |
820 | } |
821 | |
822 | Array::~Array() { |
823 | _unref(); |
824 | } |
825 | |