-
Notifications
You must be signed in to change notification settings - Fork 3k
/
type-array.h
526 lines (450 loc) · 17.3 KB
/
type-array.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_ARRAY_H_
#define incl_HPHP_ARRAY_H_
#include "hphp/runtime/base/array-data.h"
#include "hphp/runtime/base/req-ptr.h"
#include "hphp/runtime/base/types.h"
#include <algorithm>
#include <vector>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// forward declaration
class ArrayIter;
class VariableUnserializer;
#define ACCESSPARAMS_DECL AccessFlags::Type flags = AccessFlags::None
/*
* Array type wrapping around ArrayData to implement reference
* counting, copy-on-write and ArrayData escalation.
*
* "Escalation" happens when an underlying ArrayData cannot handle an operation
* and instead it needs to "upgrade" itself to be a more general (but slower)
* type of ArrayData to accomplish the task. This "upgrade" is called
* escalation.
*/
class Array {
using Ptr = req::ptr<ArrayData>;
using NoIncRef = Ptr::NoIncRef;
using NonNull = Ptr::NonNull;
Ptr m_arr;
Array(ArrayData* ad, NoIncRef) : m_arr(ad, NoIncRef{}) {}
public:
/*
* Create an empty array or an array with one element. Note these are
* different than those copying constructors that also take one value.
*/
static Array Create() {
return Array(ArrayData::Create(), NoIncRef{});
}
static Array Create(const Variant& value) {
return Array(ArrayData::Create(value), NoIncRef{});
}
static Array Create(const Variant& key, const Variant& value);
public:
Array() {}
~Array();
// Take ownership of this ArrayData.
static ALWAYS_INLINE Array attach(ArrayData* ad) {
return Array(ad, NoIncRef{});
}
// Transfer ownership of our reference to this ArrayData.
ArrayData* detach() { return m_arr.detach(); }
ArrayData* get() const { return m_arr.get(); }
void reset(ArrayData* arr = nullptr) { m_arr.reset(arr); }
// Deliberately doesn't throw_null_pointer_exception as a perf
// optimization.
ArrayData* operator->() const { return m_arr.get(); }
void escalate();
// Make a copy of this array. Like the underlying ArrayData::copy operation,
// the returned Array may point to the same underlying array as the original,
// or a new one.
Array copy() const {
if (!m_arr)
return Array{};
auto new_arr = m_arr->copy();
return (new_arr != m_arr) ?
Array{new_arr, NoIncRef{}} : Array{*this};
}
/*
* Constructors. Those that take "arr" or "var" are copy constructors, taking
* array value from the parameter, and they are NOT constructing an array
* with that single value (then one should use Array::Create() functions).
*/
explicit Array(ArrayData* data) : m_arr(data) { }
/* implicit */ Array(const Array& arr) : m_arr(arr.m_arr) { }
/*
* Special constructor for use from ArrayInit that creates an Array without a
* null check and without an inc-ref.
*/
enum class ArrayInitCtor { Tag };
explicit Array(ArrayData* ad, ArrayInitCtor)
: m_arr(ad, NoIncRef{})
{}
// Move ctor
Array(Array&& src) noexcept : m_arr(std::move(src.m_arr)) { }
// Move assign
Array& operator=(Array&& src) {
m_arr = std::move(src.m_arr);
return *this;
}
/*
* Informational
*/
bool empty() const {
return !m_arr || m_arr->empty();
}
ssize_t size() const {
return m_arr ? m_arr->size() : 0;
}
ssize_t length() const {
return m_arr ? m_arr->size() : 0;
}
bool isNull() const {
return !m_arr;
}
Array values() const;
/*
* Operators
*/
Array& operator=(ArrayData* data) {
m_arr = data;
return *this;
}
Array& operator=(const Array& arr) {
m_arr = arr.m_arr;
return *this;
}
Array& operator=(const Variant& v);
Array operator+(ArrayData* data) const;
Array operator+(const Array& v) const;
Array operator+(const Variant& v) const = delete;
Array& operator+=(ArrayData* data);
Array& operator+=(const Array& v);
Array& operator+=(const Variant& v);
// Move assignment
Array& operator=(Variant&& v);
/*
* Returns the entries that have keys and/or values that are not present in
* specified array. Keys and values can be compared by user supplied
* functions and key_data or value_data will be passed into PFUNC_CMP as
* "data" parameter. Otherwise, equal() will be called for comparisons. If
* both by_key and by_value, both keys and values have to match to be
* excluded.
*/
typedef int (*PFUNC_CMP)(const Variant& v1, const Variant& v2, const void* data);
Array diff(const Variant& array, bool by_key, bool by_value,
PFUNC_CMP key_cmp_function = nullptr,
const void* key_data = nullptr,
PFUNC_CMP value_cmp_function = nullptr,
const void* value_data = nullptr) const;
/*
* Returns the entries that have keys and/or values that are present in
* specified array. Keys and values can be compared by user supplied
* functions and key_data or value_data will be passed into PFUNC_CMP as
* "data" parameter. Otherwise, equal() will be called for comparisons. If
* both by_key and by_value, both keys and values have to match to be
* included.
*/
Array intersect(const Variant& array, bool by_key, bool by_value,
PFUNC_CMP key_cmp_function = nullptr,
const void* key_data = nullptr,
PFUNC_CMP value_cmp_function = nullptr,
const void* value_data = nullptr) const;
/*
* Iterator functions. See array-iterator.h for end() and next().
*/
ArrayIter begin(const String& context = null_string) const;
/*
* Manipulations
*
* Merge: This is different from operator "+", where existing key's values
* are NOT modified. This function will actually override with new values.
* When merging a vector with a vector, new elements are always appended, and
* this is also different from operator "+", where existing numeric indices
* are not modified.
*
* Slice: Taking a slice. When "preserve_keys" is true, a vector will turn
* into numerically keyed map.
*/
Array& merge(const Array& arr);
/*
* Sorting.
*/
static int SortRegularAscending(const Variant& v1, const Variant& v2,
const void* data);
static int SortNumericAscending(const Variant& v1, const Variant& v2,
const void* data);
static int SortStringAscending(const Variant& v1, const Variant& v2,
const void* data);
static int SortStringAscendingCase(const Variant& v1, const Variant& v2,
const void* data);
static int SortLocaleStringAscending(const Variant& v1, const Variant& v2,
const void* data);
static int SortRegularDescending(const Variant& v1, const Variant& v2,
const void* data);
static int SortNumericDescending(const Variant& v1, const Variant& v2,
const void* data);
static int SortStringDescending(const Variant& v1, const Variant& v2,
const void* data);
static int SortStringDescendingCase(const Variant& v1, const Variant& v2,
const void* data);
static int SortLocaleStringDescending(const Variant& v1, const Variant& v2,
const void* data);
static int SortNaturalAscending(const Variant& v1, const Variant& v2,
const void* data);
static int SortNaturalDescending(const Variant& v1, const Variant& v2,
const void* data);
static int SortNaturalCaseAscending(const Variant& v1, const Variant& v2,
const void* data);
static int SortNaturalCaseDescending(const Variant& v1, const Variant& v2,
const void* data);
void sort(PFUNC_CMP cmp_func, bool by_key, bool renumber,
const void* data = nullptr);
/*
* Sort multiple arrays at once similar to how ORDER BY clause works in SQL.
*/
struct SortData {
Variant* original;
const Array* array;
bool by_key;
PFUNC_CMP cmp_func;
const void* data;
std::vector<ssize_t> positions;
};
static bool MultiSort(std::vector<SortData>& data, bool renumber);
static void SortImpl(std::vector<int>& indices, const Array& source,
Array::SortData& opaque,
Array::PFUNC_CMP cmp_func,
bool by_key, const void* data = nullptr);
/*
* Type conversions
*/
bool toBoolean() const { return m_arr && !m_arr->empty(); }
char toByte () const { return (m_arr && !m_arr->empty()) ? 1 : 0; }
short toInt16 () const { return (m_arr && !m_arr->empty()) ? 1 : 0; }
int toInt32 () const { return (m_arr && !m_arr->empty()) ? 1 : 0; }
int64_t toInt64 () const { return (m_arr && !m_arr->empty()) ? 1 : 0; }
double toDouble () const { return (m_arr && !m_arr->empty()) ? 1.0 : 0.0; }
String toString () const;
/*
* Comparisons
*/
bool same (const Array& v2) const;
bool same (const Object& v2) const;
bool equal(const Array& v2) const;
bool equal(const Object& v2) const;
bool less (const Array& v2, bool flip = false) const;
bool less (const Object& v2) const;
bool less (const Variant& v2) const;
bool more (const Array& v2, bool flip = true) const;
bool more (const Object& v2) const;
bool more (const Variant& v2) const;
int compare (const Array& v2, bool flip = false) const;
/*
* Offset
*/
Variant rvalAt(int key, ACCESSPARAMS_DECL) const;
Variant rvalAt(int64_t key, ACCESSPARAMS_DECL) const;
Variant rvalAt(double key, ACCESSPARAMS_DECL) const = delete;
Variant rvalAt(const String& key, ACCESSPARAMS_DECL) const;
Variant rvalAt(const Variant& key, ACCESSPARAMS_DECL) const;
/*
* To get offset for temporary usage
*/
const Variant& rvalAtRef(int key, ACCESSPARAMS_DECL) const;
const Variant& rvalAtRef(int64_t key, ACCESSPARAMS_DECL) const;
const Variant& rvalAtRef(double key, ACCESSPARAMS_DECL) const = delete;
const Variant& rvalAtRef(const Variant& key, ACCESSPARAMS_DECL) const;
const Variant& rvalAtRef(const String& key, ACCESSPARAMS_DECL) const;
const Variant operator[](int key) const;
const Variant operator[](int64_t key) const;
const Variant operator[](double key) const = delete;
const Variant operator[](const String& key) const;
const Variant operator[](const Variant& key) const;
const Variant operator[](const char*) const = delete; // use const String&
/*
* Get an lval reference to a newly created element, with the intent
* of reading or writing to it as a Cell.
*/
Variant& lvalAt();
/*
* Get an lval reference to a newly created element, with the intent
* of using binding assignment with the newly created element.
*/
Variant& lvalAtRef();
/*
* Get an lval reference to an element.
*/
Variant& lvalAt(int key, ACCESSPARAMS_DECL) {
return lvalAtImpl(key, flags);
}
Variant& lvalAt(int64_t key, ACCESSPARAMS_DECL) {
return lvalAtImpl(key, flags);
}
Variant& lvalAt(double key, ACCESSPARAMS_DECL) = delete;
Variant& lvalAt(const String& key, ACCESSPARAMS_DECL);
Variant& lvalAt(const Variant& key, ACCESSPARAMS_DECL);
/*
* Set an element to a value.
*/
void set(int key, const Variant& v) { set(int64_t(key), v); }
void set(int64_t key, const Variant& v);
void set(double key, const Variant& v) = delete;
void set(const String& key, const Variant& v, bool isKey = false);
void set(const Variant& key, const Variant& v, bool isKey = false);
/*
* Set an element to a reference.
*/
void setRef(int key, Variant& v) { setRef(int64_t(key), v); }
void setRef(int64_t key, Variant& v);
void setRef(double key, Variant& v) = delete;
void setRef(const String& key, Variant& v, bool isKey = false);
void setRef(const Variant& key, Variant& v, bool isKey = false);
void setWithRef(const Variant& key, const Variant& v, bool isKey = false);
/*
* Add an element.
*/
void add(int key, const Variant& v) { add(int64_t(key), v); }
void add(int64_t key, const Variant& v);
void add(double key, const Variant& v) = delete;
void add(const String& key, const Variant& v, bool isKey = false);
void add(const Variant& key, const Variant& v, bool isKey = false);
/*
* Membership functions.
*/
bool exists(char key) const { return existsImpl((int64_t)key); }
bool exists(short key) const { return existsImpl((int64_t)key); }
bool exists(int key) const { return existsImpl((int64_t)key); }
bool exists(int64_t key) const { return existsImpl(key); }
bool exists(double key) const = delete;
bool exists(const String& key, bool isKey = false) const;
bool exists(const Variant& key, bool isKey = false) const;
/*
* Remove an element.
*/
void remove(char key) { removeImpl((int64_t)key); }
void remove(short key) { removeImpl((int64_t)key); }
void remove(int key) { removeImpl((int64_t)key); }
void remove(int64_t key) { removeImpl(key); }
void remove(double key) = delete;
void remove(const String& key, bool isString = false);
void remove(const Variant& key);
/*
* Remove all elements.
*/
void clear() { operator=(Create()); }
/*
* Append an element.
*/
const Variant& append(const Variant& v);
const Variant& appendRef(Variant& v);
const Variant& appendWithRef(const Variant& v);
/*
* Stack/queue-like functions.
*/
Variant pop();
Variant dequeue();
void prepend(const Variant& v);
void setEvalScalar() const;
private:
Array& plusImpl(ArrayData* data);
Array& mergeImpl(ArrayData* data);
Array diffImpl(const Array& array, bool by_key, bool by_value, bool match,
PFUNC_CMP key_cmp_function, const void* key_data,
PFUNC_CMP value_cmp_function, const void* value_data) const;
template<typename T> void setImpl(const T& key, const Variant& v);
template<typename T> void setRefImpl(const T& key, Variant& v);
template<typename T> void addImpl(const T& key, const Variant& v);
template<typename T>
bool existsImpl(const T& key) const {
if (m_arr) return m_arr->exists(key);
return false;
}
template<typename T>
void removeImpl(const T& key) {
if (m_arr) {
ArrayData* escalated = m_arr->remove(key, (m_arr->hasMultipleRefs()));
if (escalated != m_arr) m_arr = Ptr::attach(escalated);
}
}
template<typename T>
Variant& lvalAtImpl(const T& key, ACCESSPARAMS_DECL) {
if (!m_arr) m_arr = Ptr::attach(ArrayData::Create());
Variant* ret = nullptr;
ArrayData* escalated = m_arr->lval(key, ret, m_arr->hasMultipleRefs());
if (escalated != m_arr) m_arr = Ptr::attach(escalated);
assert(ret);
return *ret;
}
static void compileTimeAssertions();
};
///////////////////////////////////////////////////////////////////////////////
// ArrNR
struct ArrNR {
explicit ArrNR(ArrayData* data = nullptr) {
m_px = data;
}
ArrNR(const ArrNR& a) {
m_px = a.m_px;
}
~ArrNR() {
if (debug) {
m_px = reinterpret_cast<ArrayData*>(0xdeadbeeffaceb004);
}
}
operator const Array&() const { return asArray(); }
Array& asArray() {
return *reinterpret_cast<Array*>(this); // XXX
}
const Array& asArray() const {
return const_cast<ArrNR*>(this)->asArray();
}
ArrayData* get() const { return m_px; }
protected:
ArrayData* m_px;
private:
static void compileTimeAssertions();
};
///////////////////////////////////////////////////////////////////////////////
/*
* An Array wrapper that doesn't run a destructor unless you
* explicitly ask it to.
*
* This is used for the dynPropTable in ExecutionContext, so that at
* sweep time we don't run these destructors.
*/
struct ArrayNoDtor {
/* implicit */ ArrayNoDtor(ArrayData* table) { new (&m_arr) Array(table); }
ArrayNoDtor() { new (&m_arr) Array(); }
ArrayNoDtor(ArrayNoDtor&& o) noexcept {
new (&m_arr) Array(std::move(o.m_arr));
}
~ArrayNoDtor() {}
Array& arr() { return m_arr; }
const Array& arr() const { return m_arr; }
void destroy() { m_arr.~Array(); }
private:
union { Array m_arr; };
};
///////////////////////////////////////////////////////////////////////////////
ALWAYS_INLINE Array empty_array() {
return Array::attach(staticEmptyArray());
}
///////////////////////////////////////////////////////////////////////////////
}
// nobody else needs this outside the Array decl
#undef ACCESSPARAMS_DECL
#endif // incl_HPHP_ARRAY_H_