333f0a76f4
- Fix the behavior of array_slice when using PHP_INT_MAX
824 linhas
22 KiB
C++
824 linhas
22 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "hphp/runtime/base/array/array_util.h"
|
|
#include "hphp/runtime/base/array/array_iterator.h"
|
|
#include "hphp/runtime/base/string_util.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/runtime_error.h"
|
|
#include "hphp/runtime/ext/ext_math.h"
|
|
#include "hphp/runtime/ext/ext_json.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// compositions
|
|
|
|
Variant ArrayUtil::CreateArray(CArrRef keys, CVarRef value) {
|
|
ArrayInit ai(keys.size());
|
|
for (ArrayIter iter(keys); iter; ++iter) {
|
|
ai.set(iter.secondRef(), value);
|
|
}
|
|
return ai.create();
|
|
}
|
|
|
|
Variant ArrayUtil::CreateArray(int start_index, int num, CVarRef value) {
|
|
if (num <= 0) {
|
|
throw_invalid_argument("num: [non-positive]");
|
|
return false;
|
|
}
|
|
|
|
Array ret;
|
|
ret.set(start_index, value);
|
|
for (int i = num - 1; i > 0; i--) {
|
|
ret.append(value);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Combine(CArrRef keys, CArrRef values) {
|
|
if (keys.size() != values.size()) {
|
|
throw_invalid_argument("keys and values not same count");
|
|
return false;
|
|
}
|
|
if (keys.empty()) {
|
|
throw_invalid_argument("keys and values empty");
|
|
return false;
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter1(keys), iter2(values); iter1; ++iter1, ++iter2) {
|
|
CVarRef v(iter2.secondRef());
|
|
ret.lvalAt(iter1.secondRef()).setWithRef(v);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Chunk(CArrRef input, int size,
|
|
bool preserve_keys /* = false */) {
|
|
if (size < 1) {
|
|
throw_invalid_argument("size: %d", size);
|
|
return uninit_null();
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
Array chunk;
|
|
int current = 0;
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
if (preserve_keys) {
|
|
chunk.addLval(iter.first(), true).setWithRef(iter.secondRef());
|
|
} else {
|
|
chunk.appendWithRef(iter.secondRef());
|
|
}
|
|
if ((++current % size) == 0) {
|
|
ret.append(chunk);
|
|
chunk.clear();
|
|
}
|
|
}
|
|
|
|
if (!chunk.empty()) {
|
|
ret.append(chunk);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Slice(CArrRef input, int offset, int64_t length,
|
|
bool preserve_keys) {
|
|
int num_in = input.size();
|
|
if (offset > num_in) {
|
|
offset = num_in;
|
|
} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
|
|
offset = 0;
|
|
}
|
|
|
|
if (length < 0) {
|
|
length = num_in - offset + length;
|
|
} else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
|
|
length = num_in - offset;
|
|
}
|
|
|
|
Array out_hash = Array::Create();
|
|
int pos = 0;
|
|
ArrayIter iter(input);
|
|
for (; pos < offset && iter; ++pos, ++iter) {}
|
|
for (; pos < offset + length && iter; ++pos, ++iter) {
|
|
Variant key(iter.first());
|
|
bool doAppend = !preserve_keys && key.isNumeric();
|
|
CVarRef v = iter.secondRef();
|
|
if (doAppend) {
|
|
out_hash.appendWithRef(v);
|
|
} else {
|
|
out_hash.addLval(key, true).setWithRef(v);
|
|
}
|
|
}
|
|
return out_hash;
|
|
}
|
|
|
|
Variant ArrayUtil::Splice(CArrRef input, int offset, int64_t length /* = 0 */,
|
|
CVarRef replacement /* = null_variant */,
|
|
Array *removed /* = NULL */) {
|
|
int num_in = input.size();
|
|
if (offset > num_in) {
|
|
offset = num_in;
|
|
} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
|
|
offset = 0;
|
|
}
|
|
|
|
if (length < 0) {
|
|
length = num_in - offset + length;
|
|
} else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
|
|
length = num_in - offset;
|
|
}
|
|
|
|
Array out_hash = Array::Create();
|
|
int pos = 0;
|
|
ArrayIter iter(input);
|
|
for (; pos < offset && iter; ++pos, ++iter) {
|
|
Variant key(iter.first());
|
|
CVarRef v = iter.secondRef();
|
|
if (key.isNumeric()) {
|
|
out_hash.appendWithRef(v);
|
|
} else {
|
|
out_hash.addLval(key, true).setWithRef(v);
|
|
}
|
|
}
|
|
|
|
for (; pos < offset + length && iter; ++pos, ++iter) {
|
|
if (removed) {
|
|
Variant key(iter.first());
|
|
CVarRef v = iter.secondRef();
|
|
if (key.isNumeric()) {
|
|
removed->appendWithRef(v);
|
|
} else {
|
|
removed->lvalAt(key, AccessFlags::Key).setWithRef(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
Array arr = replacement.toArray();
|
|
if (!arr.empty()) {
|
|
for (ArrayIter iter(arr); iter; ++iter) {
|
|
CVarRef v = iter.secondRef();
|
|
out_hash.appendWithRef(v);
|
|
}
|
|
}
|
|
|
|
for (; iter; ++iter) {
|
|
Variant key(iter.first());
|
|
CVarRef v = iter.secondRef();
|
|
if (key.isNumeric()) {
|
|
out_hash.appendWithRef(v);
|
|
} else {
|
|
out_hash.lvalAt(key, AccessFlags::Key).setWithRef(v);
|
|
}
|
|
}
|
|
|
|
return out_hash;
|
|
}
|
|
|
|
Variant ArrayUtil::Pad(CArrRef input, CVarRef pad_value, int pad_size,
|
|
bool pad_right /* = true */) {
|
|
int input_size = input.size();
|
|
if (input_size >= pad_size) {
|
|
return input;
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
if (pad_right) {
|
|
ret = input;
|
|
for (int i = input_size; i < pad_size; i++) {
|
|
ret.append(pad_value);
|
|
}
|
|
} else {
|
|
for (int i = input_size; i < pad_size; i++) {
|
|
ret.append(pad_value);
|
|
}
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
Variant key(iter.first());
|
|
if (key.isNumeric()) {
|
|
ret.appendWithRef(iter.secondRef());
|
|
} else {
|
|
ret.addLval(key, true).setWithRef(iter.secondRef());
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Range(unsigned char low, unsigned char high,
|
|
int64_t step /* = 1 */) {
|
|
if (step <= 0) {
|
|
throw_invalid_argument("step exceeds the specified range");
|
|
return false;
|
|
}
|
|
|
|
Array ret;
|
|
if (low > high) { // Negative Steps
|
|
for (; low >= high; low -= (unsigned int)step) {
|
|
ret.append(String::FromChar(low));
|
|
if (((signed int)low - step) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (high > low) { // Positive Steps
|
|
for (; low <= high; low += (unsigned int)step) {
|
|
ret.append(String::FromChar(low));
|
|
if (((signed int)low + step) > 255) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
ret.append(String::FromChar(low));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#define DOUBLE_DRIFT_FIX 0.000000000000001
|
|
|
|
Variant ArrayUtil::Range(double low, double high, double step /* = 1.0 */) {
|
|
Array ret;
|
|
if (low > high) { // Negative steps
|
|
if (low - high < step || step <= 0) {
|
|
throw_invalid_argument("step exceeds the specified range");
|
|
return false;
|
|
}
|
|
for (; low >= (high - DOUBLE_DRIFT_FIX); low -= step) {
|
|
ret.append(low);
|
|
}
|
|
} else if (high > low) { // Positive steps
|
|
if (high - low < step || step <= 0) {
|
|
throw_invalid_argument("step exceeds the specified range");
|
|
return false;
|
|
}
|
|
for (; low <= (high + DOUBLE_DRIFT_FIX); low += step) {
|
|
ret.append(low);
|
|
}
|
|
} else {
|
|
ret.append(low);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Range(double low, double high, int64_t step /* = 1 */) {
|
|
Array ret;
|
|
if (low > high) { // Negative steps
|
|
if (low - high < step || step <= 0) {
|
|
throw_invalid_argument("step exceeds the specified range");
|
|
return false;
|
|
}
|
|
for (; low >= high; low -= step) {
|
|
ret.append((int64_t)low);
|
|
}
|
|
} else if (high > low) { // Positive steps
|
|
if (high - low < step || step <= 0) {
|
|
throw_invalid_argument("step exceeds the specified range");
|
|
return false;
|
|
}
|
|
for (; low <= high; low += step) {
|
|
ret.append((int64_t)low);
|
|
}
|
|
} else {
|
|
ret.append((int64_t)low);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const StaticString s_default("(default)");
|
|
|
|
Variant ArrayUtil::FromHdf(const Hdf &hdf) {
|
|
if (hdf.firstChild().exists()) {
|
|
Array ret = Array::Create();
|
|
|
|
const char *value = hdf.get();
|
|
if (value) {
|
|
ret.set(s_default, String(value, CopyString));
|
|
}
|
|
|
|
for (Hdf child = hdf.firstChild(); child.exists(); child = child.next()) {
|
|
ret.set(String(child.getName()), FromHdf(child));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
const char *value = hdf.get("");
|
|
if (strcasecmp(value, "false") == 0 ||
|
|
strcasecmp(value, "no") == 0 ||
|
|
strcasecmp(value, "off") == 0) {
|
|
return false;
|
|
}
|
|
if (strcasecmp(value, "true") == 0 ||
|
|
strcasecmp(value, "yes") == 0 ||
|
|
strcasecmp(value, "on") == 0) {
|
|
return true;
|
|
}
|
|
|
|
int64_t lval; double dval;
|
|
int len = strlen(value);
|
|
DataType ret = is_numeric_string(value, len, &lval, &dval, 0);
|
|
switch (ret) {
|
|
case KindOfInt64: return lval;
|
|
case KindOfDouble: return dval;
|
|
default: break;
|
|
}
|
|
return String(value, len, CopyString);
|
|
}
|
|
|
|
void ArrayUtil::ToHdf(const Array &arr, Hdf &hdf) {
|
|
for (ArrayIter iter(arr); iter; ++iter) {
|
|
CVarRef value(iter.secondRef());
|
|
if (value.isArray()) {
|
|
Hdf child = hdf[iter.first().toString().data()];
|
|
ToHdf(iter.secondRef().toArray(), child);
|
|
} else if (value.isBoolean()) {
|
|
hdf[iter.first().toString().data()] =
|
|
iter.secondRef().toBoolean() ? "true" : "false";
|
|
} else {
|
|
hdf[iter.first().toString().data()] = iter.secondRef().toString().data();
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// information and calculations
|
|
|
|
DataType ArrayUtil::Sum(CArrRef input, int64_t *isum, double *dsum) {
|
|
int64_t i = 0;
|
|
ArrayIter iter(input);
|
|
for (; iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
switch (entry.getType()) {
|
|
case KindOfDouble: {
|
|
goto DOUBLE;
|
|
}
|
|
case KindOfStaticString:
|
|
case KindOfString: {
|
|
int64_t ti;
|
|
double td;
|
|
if (entry.getStringData()->isNumericWithVal(ti, td, 1) ==
|
|
KindOfInt64) {
|
|
i += ti;
|
|
break;
|
|
} else {
|
|
goto DOUBLE;
|
|
}
|
|
}
|
|
case KindOfArray:
|
|
case KindOfObject: {
|
|
break;
|
|
}
|
|
default: {
|
|
i += entry.toInt64();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*isum = i;
|
|
return KindOfInt64;
|
|
|
|
DOUBLE:
|
|
double d = i;
|
|
for (; iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
if (!entry.is(KindOfArray) && !entry.is(KindOfObject)) {
|
|
d += entry.toDouble();
|
|
}
|
|
}
|
|
*dsum = d;
|
|
return KindOfDouble;
|
|
}
|
|
|
|
DataType ArrayUtil::Product(CArrRef input, int64_t *iprod, double *dprod) {
|
|
int64_t i = 1;
|
|
ArrayIter iter(input);
|
|
for (; iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
switch (entry.getType()) {
|
|
case KindOfDouble: {
|
|
goto DOUBLE;
|
|
}
|
|
case KindOfStaticString:
|
|
case KindOfString: {
|
|
int64_t ti;
|
|
double td;
|
|
if (entry.getStringData()->isNumericWithVal(ti, td, 1) ==
|
|
KindOfInt64) {
|
|
i *= ti;
|
|
break;
|
|
} else {
|
|
goto DOUBLE;
|
|
}
|
|
}
|
|
case KindOfArray:
|
|
case KindOfObject: {
|
|
break;
|
|
}
|
|
default: {
|
|
i *= entry.toInt64();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*iprod = i;
|
|
return KindOfInt64;
|
|
|
|
DOUBLE:
|
|
double d = i;
|
|
for (; iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
if (!entry.is(KindOfArray) && !entry.is(KindOfObject)) {
|
|
d *= entry.toDouble();
|
|
}
|
|
}
|
|
*dprod = d;
|
|
return KindOfDouble;
|
|
}
|
|
|
|
Variant ArrayUtil::CountValues(CArrRef input) {
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
if (entry.isInteger() || entry.isString()) {
|
|
if (!ret.exists(entry)) {
|
|
ret.set(entry, 1);
|
|
} else {
|
|
ret.set(entry, ret[entry].toInt64() + 1);
|
|
}
|
|
} else {
|
|
raise_warning("Can only count STRING and INTEGER values!");
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Array ArrayUtil::EnsureIntKeys(CArrRef input) {
|
|
assert(!input.isNull());
|
|
if (!input.getArrayData()->isVectorData()) {
|
|
return input.values();
|
|
}
|
|
return input;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// manipulations
|
|
|
|
Variant ArrayUtil::ChangeKeyCase(CArrRef input, bool lower) {
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
Variant key(iter.first());
|
|
if (key.isString()) {
|
|
if (lower) {
|
|
ret.set(StringUtil::ToLower(key.toString()), iter.secondRef());
|
|
} else {
|
|
ret.set(StringUtil::ToUpper(key.toString()), iter.secondRef());
|
|
}
|
|
} else {
|
|
ret.set(key, iter.secondRef());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Flip(CArrRef input) {
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
CVarRef value(iter.secondRef());
|
|
if (value.isString() || value.isInteger()) {
|
|
ret.set(value, iter.first());
|
|
} else {
|
|
raise_warning("Can only flip STRING and INTEGER values!");
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Reverse(CArrRef input, bool preserve_keys /* = false */) {
|
|
if (input.empty()) {
|
|
return input;
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
for (ssize_t pos = input->iter_end(); pos != ArrayData::invalid_index;
|
|
pos = input->iter_rewind(pos)) {
|
|
Variant key(input->getKey(pos));
|
|
if (preserve_keys || key.isString()) {
|
|
ret.addLval(key, true).setWithRef(input->getValueRef(pos));
|
|
} else {
|
|
ret.appendWithRef(input->getValueRef(pos));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void php_array_data_shuffle(std::vector<ssize_t> &indices) {
|
|
int n_elems = indices.size();
|
|
if (n_elems > 1) {
|
|
int n_left = n_elems;
|
|
while (--n_left) {
|
|
int rnd_idx = f_rand(0, n_left);
|
|
if (rnd_idx != n_left) {
|
|
ssize_t temp = indices[n_left];
|
|
indices[n_left] = indices[rnd_idx];
|
|
indices[rnd_idx] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Variant ArrayUtil::Shuffle(CArrRef input) {
|
|
int count = input.size();
|
|
if (count == 0) {
|
|
return input;
|
|
}
|
|
|
|
std::vector<ssize_t> indices;
|
|
indices.reserve(count);
|
|
for (ssize_t pos = input->iter_begin(); pos != ArrayData::invalid_index;
|
|
pos = input->iter_advance(pos)) {
|
|
indices.push_back(pos);
|
|
}
|
|
php_array_data_shuffle(indices);
|
|
|
|
Array ret = Array::Create();
|
|
for (int i = 0; i < count; i++) {
|
|
ssize_t pos = indices[i];
|
|
ret.appendWithRef(input->getValueRef(pos));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::RandomKeys(CArrRef input, int num_req /* = 1 */) {
|
|
int count = input.size();
|
|
if (num_req <= 0 || num_req > count) {
|
|
raise_warning("Second argument has to be between 1 and the "
|
|
"number of elements in the array");
|
|
return uninit_null();
|
|
}
|
|
|
|
std::vector<ssize_t> indices;
|
|
indices.reserve(count);
|
|
for (ssize_t pos = input->iter_begin(); pos != ArrayData::invalid_index;
|
|
pos = input->iter_advance(pos)) {
|
|
indices.push_back(pos);
|
|
}
|
|
php_array_data_shuffle(indices);
|
|
|
|
if (num_req == 1) {
|
|
return input->getKey(indices[0]);
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
for (int i = 0; i < num_req; i++) {
|
|
ssize_t pos = indices[i];
|
|
ret.append(input->getKey(pos));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::RandomValues(CArrRef input, int num_req /* = 1 */) {
|
|
int count = input.size();
|
|
if (num_req <= 0 || num_req > count) {
|
|
return uninit_null();
|
|
}
|
|
|
|
std::vector<ssize_t> indices;
|
|
indices.reserve(count);
|
|
for (ssize_t pos = input->iter_begin(); pos != ArrayData::invalid_index;
|
|
pos = input->iter_advance(pos)) {
|
|
indices.push_back(pos);
|
|
}
|
|
php_array_data_shuffle(indices);
|
|
|
|
if (num_req == 1) {
|
|
return input->getValue(indices[0]);
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
for (int i = 0; i < num_req; i++) {
|
|
ssize_t pos = indices[i];
|
|
ret.append(input->getValueRef(pos));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Filter(CArrRef input, PFUNC_FILTER filter /* = NULL */,
|
|
const void *data /* = NULL */) {
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
CVarRef value(iter.secondRef());
|
|
if (filter ? filter(Variant(value), data) : value.toBoolean()) {
|
|
ret.addLval(iter.first(), true).setWithRef(iter.secondRef());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::StringUnique(CArrRef input) {
|
|
Array seenValues;
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
String str(entry.toString());
|
|
if (!seenValues.exists(str)) {
|
|
seenValues.set(str, 1);
|
|
ret.set(iter.first(), entry);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::NumericUnique(CArrRef input) {
|
|
std::set<double> seenValues;
|
|
Array ret = Array::Create();
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
CVarRef entry(iter.secondRef());
|
|
double value = entry.toDouble();
|
|
std::pair<std::set<double>::iterator, bool> res =
|
|
seenValues.insert(value);
|
|
if (res.second) { // it was inserted
|
|
ret.set(iter.first(), entry);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::RegularSortUnique(CArrRef input) {
|
|
/* The output of this function in PHP strictly depends on the implementation
|
|
* of the sort function and on whether values that compare as equal end
|
|
* up in contiguous positions in the sorted array (which is not really
|
|
* well-defined in case of mixed strings/numbers). To get the same result
|
|
* as in PHP we thus need to replicate the PHP algorithm more closely than
|
|
* in the other versions of array_unique.
|
|
*/
|
|
if (input.size() <= 1) return input;
|
|
|
|
Array::SortData opaque;
|
|
vector<int> indices;
|
|
Array::SortImpl(indices, input, opaque, Array::SortRegularAscending, false);
|
|
|
|
vector<bool> duplicates(indices.size(), false);
|
|
int lastIdx = indices[0];
|
|
Variant last = input->getValue(opaque.positions[lastIdx]);
|
|
for (unsigned int i = 1; i < indices.size(); ++i) {
|
|
int currentIdx = indices[i];
|
|
Variant current = input->getValue(opaque.positions[currentIdx]);
|
|
if (current.equal(last)) {
|
|
if (currentIdx > lastIdx) {
|
|
duplicates[currentIdx] = true;
|
|
continue;
|
|
}
|
|
duplicates[lastIdx] = true;
|
|
}
|
|
lastIdx = currentIdx;
|
|
last = current;
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
int i = 0;
|
|
for (ArrayIter iter(input); iter; ++iter, ++i) {
|
|
if (!duplicates[i]) ret.set(iter.first(), iter.secondRef());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// iterations
|
|
|
|
void ArrayUtil::Walk(VRefParam input, PFUNC_WALK walk_function,
|
|
const void *data, bool recursive /* = false */,
|
|
PointerSet *seen /* = NULL */,
|
|
CVarRef userdata /* = null_variant */) {
|
|
assert(walk_function);
|
|
|
|
Variant k;
|
|
Variant v;
|
|
for (MutableArrayIter iter = input->begin(&k, v); iter.advance(); ) {
|
|
if (recursive && v.is(KindOfArray)) {
|
|
assert(seen);
|
|
ArrayData *arr = v.getArrayData();
|
|
|
|
if (v.isReferenced()) {
|
|
if (seen->find((void*)arr) != seen->end()) {
|
|
raise_warning("array_walk_recursive(): recursion detected");
|
|
return;
|
|
}
|
|
seen->insert((void*)arr);
|
|
}
|
|
|
|
Walk(directRef(v), walk_function, data, recursive, seen, userdata);
|
|
if (v.isReferenced()) {
|
|
seen->erase((void*)arr);
|
|
}
|
|
} else {
|
|
walk_function(directRef(v), k, userdata, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
Variant ArrayUtil::Map(CArrRef inputs, PFUNC_MAP map_function,
|
|
const void *data) {
|
|
Array ret = Array::Create();
|
|
|
|
if (inputs.size() == 1) {
|
|
Array arr = inputs.begin().secondRef().toArray();
|
|
if (!arr.empty()) {
|
|
for (ssize_t k = arr->iter_begin(); k != ArrayData::invalid_index;
|
|
k = arr->iter_advance(k)) {
|
|
Array params;
|
|
params.append(arr->getValueRef(k));
|
|
Variant result;
|
|
if (map_function) {
|
|
result = map_function(params, data);
|
|
} else {
|
|
result = params;
|
|
}
|
|
ret.add(arr->getKey(k), result, true);
|
|
}
|
|
}
|
|
} else {
|
|
int maxlen = 0;
|
|
vector<vector<ssize_t> > positions;
|
|
positions.resize(inputs.size());
|
|
int i = 0;
|
|
for (ArrayIter iter(inputs); iter; ++iter, ++i) {
|
|
Array arr = iter.secondRef().toArray();
|
|
int count = arr.size();
|
|
if (count > maxlen) maxlen = count;
|
|
|
|
if (count > 0) {
|
|
positions[i].reserve(count);
|
|
for (ssize_t pos = arr->iter_begin(); pos != ArrayData::invalid_index;
|
|
pos = arr->iter_advance(pos)) {
|
|
positions[i].push_back(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int k = 0; k < maxlen; k++) {
|
|
Array params;
|
|
int i = 0;
|
|
for (ArrayIter iter(inputs); iter; ++iter, ++i) {
|
|
Array arr = iter.secondRef().toArray();
|
|
if (k < arr.size()) {
|
|
params.append(arr->getValueRef(positions[i][k]));
|
|
} else {
|
|
params.append(uninit_null());
|
|
}
|
|
}
|
|
|
|
Variant result;
|
|
if (map_function) {
|
|
result = map_function(params, data);
|
|
} else {
|
|
result = params;
|
|
}
|
|
|
|
ret.append(result);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Variant ArrayUtil::Reduce(CArrRef input, PFUNC_REDUCE reduce_function,
|
|
const void *data,
|
|
CVarRef initial /* = null_variant */) {
|
|
Variant result(initial);
|
|
for (ArrayIter iter(input); iter; ++iter) {
|
|
result = reduce_function(result, iter.second(), data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ArrayUtil::InitScalarArrays(Array arrs[], int nArrs,
|
|
const char *scalarArrayData,
|
|
int scalarArrayDataSize) {
|
|
int len = scalarArrayDataSize;
|
|
char *uncompressed = gzdecode(scalarArrayData, len);
|
|
if (uncompressed == nullptr) {
|
|
throw Exception("Bad scalarArrayData %p", scalarArrayData);
|
|
}
|
|
Variant v(unserialize_from_buffer(uncompressed, len));
|
|
assert(v.isArray());
|
|
Array scalarArrays = v;
|
|
assert(scalarArrays.size() == nArrs);
|
|
for (int i = 0; i < nArrs; i++) {
|
|
arrs[i] = scalarArrays[i];
|
|
arrs[i].setEvalScalar();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|