Update call_user_func_array() to support collections
[hiphop-php.git] / hphp / runtime / ext / base_vector.cpp
blob04ff8b9151f91b21a2093a84fb7f3aebac1c6c30
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/base_vector.h"
19 #include "hphp/runtime/ext/ext_array.h"
20 #include "hphp/runtime/ext/ext_collections.h"
22 namespace HPHP {
23 ///////////////////////////////////////////////////////////////////////////////
25 // ConstCollection
27 bool BaseVector::isempty() {
28 return !toBoolImpl();
31 int64_t BaseVector::count() {
32 return m_size;
35 Object BaseVector::items() {
36 return SystemLib::AllocLazyIterableViewObject(this);
39 // ConstIndexAccess
41 bool BaseVector::containskey(CVarRef key) {
42 if (key.isInteger()) {
43 return contains(key.toInt64());
45 throwBadKeyType();
46 return false;
49 Variant BaseVector::at(CVarRef key) {
50 if (key.isInteger()) {
51 return tvAsCVarRef(at(key.toInt64()));
53 throwBadKeyType();
54 return uninit_null();
57 Variant BaseVector::get(CVarRef key) {
58 if (key.isInteger()) {
59 TypedValue* tv = get(key.toInt64());
60 if (tv) {
61 return tvAsCVarRef(tv);
62 } else {
63 return uninit_null();
66 throwBadKeyType();
67 return uninit_null();
70 // KeyedIterable
72 Object BaseVector::getiterator() {
73 c_VectorIterator* it = NEWOBJ(c_VectorIterator)();
74 it->m_obj = this;
75 it->m_pos = 0;
76 it->m_version = getVersion();
77 return it;
80 void BaseVector::map(BaseVector* bvec, CVarRef callback) {
81 CallCtx ctx;
82 vm_decode_function(callback, nullptr, false, ctx);
83 if (!ctx.func) {
84 Object e(SystemLib::AllocInvalidArgumentExceptionObject(
85 "Parameter must be a valid callback"));
86 throw e;
88 uint sz = m_size;
89 bvec->reserve(sz);
90 for (uint i = 0; i < sz; ++i) {
91 TypedValue* tv = &bvec->m_data[i];
92 int32_t version = m_version;
93 g_vmContext->invokeFuncFew(tv, ctx, 1, &m_data[i]);
94 if (UNLIKELY(version != m_version)) {
95 tvRefcountedDecRef(tv);
96 throw_collection_modified();
98 ++bvec->m_size;
102 void BaseVector::mapwithkey(BaseVector* bvec, CVarRef callback) {
103 CallCtx ctx;
104 vm_decode_function(callback, nullptr, false, ctx);
105 if (!ctx.func) {
106 Object e(SystemLib::AllocInvalidArgumentExceptionObject(
107 "Parameter must be a valid callback"));
108 throw e;
110 uint sz = m_size;
111 bvec->reserve(sz);
112 for (uint i = 0; i < sz; ++i) {
113 TypedValue* tv = &bvec->m_data[i];
114 int32_t version = m_version;
115 TypedValue args[2] = {
116 make_tv<KindOfInt64>(i),
117 m_data[i]
119 g_vmContext->invokeFuncFew(tv, ctx, 2, args);
120 if (UNLIKELY(version != m_version)) {
121 tvRefcountedDecRef(tv);
122 throw_collection_modified();
124 ++bvec->m_size;
128 void BaseVector::filter(BaseVector* bvec, CVarRef callback) {
129 CallCtx ctx;
130 vm_decode_function(callback, nullptr, false, ctx);
131 if (!ctx.func) {
132 Object e(SystemLib::AllocInvalidArgumentExceptionObject(
133 "Parameter must be a valid callback"));
134 throw e;
136 uint sz = m_size;
137 for (uint i = 0; i < sz; ++i) {
138 Variant ret;
139 int32_t version = m_version;
140 g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, &m_data[i]);
141 if (UNLIKELY(version != m_version)) {
142 throw_collection_modified();
144 if (ret.toBoolean()) {
145 bvec->add(&m_data[i]);
150 void BaseVector::filterwithkey(BaseVector* bvec, CVarRef callback) {
151 CallCtx ctx;
152 vm_decode_function(callback, nullptr, false, ctx);
153 if (!ctx.func) {
154 Object e(SystemLib::AllocInvalidArgumentExceptionObject(
155 "Parameter must be a valid callback"));
156 throw e;
158 uint sz = m_size;
159 for (uint i = 0; i < sz; ++i) {
160 Variant ret;
161 int32_t version = m_version;
162 TypedValue args[2] = {
163 make_tv<KindOfInt64>(i),
164 m_data[i]
166 g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 2, args);
167 if (UNLIKELY(version != m_version)) {
168 throw_collection_modified();
170 if (ret.toBoolean()) {
171 bvec->add(&m_data[i]);
176 void BaseVector::zip(BaseVector* bvec, CVarRef iterable) {
177 size_t itSize;
178 ArrayIter iter = getArrayIterHelper(iterable, itSize);
179 uint sz = m_size;
180 bvec->reserve(std::min(itSize, size_t(sz)));
181 for (uint i = 0; i < sz && iter; ++i, ++iter) {
182 Variant v = iter.second();
183 if (bvec->m_capacity <= bvec->m_size) {
184 bvec->grow();
186 c_Pair* pair = NEWOBJ(c_Pair)();
187 pair->incRefCount();
188 pair->initAdd(&m_data[i]);
189 pair->initAdd(cvarToCell(&v));
190 bvec->m_data[i].m_data.pobj = pair;
191 bvec->m_data[i].m_type = KindOfObject;
192 ++bvec->m_size;
196 void BaseVector::kvzip(BaseVector* bvec) {
197 bvec->reserve(m_size);
198 for (uint i = 0; i < m_size; ++i) {
199 c_Pair* pair = NEWOBJ(c_Pair)();
200 pair->incRefCount();
201 pair->elm0.m_type = KindOfInt64;
202 pair->elm0.m_data.num = i;
203 ++pair->m_size;
204 pair->initAdd(&m_data[i]);
205 bvec->m_data[i].m_data.pobj = pair;
206 bvec->m_data[i].m_type = KindOfObject;
207 ++bvec->m_size;
211 void BaseVector::keys(BaseVector* bvec) {
212 bvec->reserve(m_size);
213 bvec->m_size = m_size;
214 for (uint i = 0; i < m_size; ++i) {
215 bvec->m_data[i].m_data.num = i;
216 bvec->m_data[i].m_type = KindOfInt64;
220 // Others
222 void BaseVector::construct(CVarRef iterable /* = null_variant */) {
223 if (iterable.isNull()) return;
224 init(iterable);
227 Object BaseVector::lazy() {
228 return SystemLib::AllocLazyKeyedIterableViewObject(this);
231 Array BaseVector::toarray() {
232 return toArrayImpl();
235 Array BaseVector::tokeysarray() {
236 PackedArrayInit ai(m_size);
237 uint sz = m_size;
238 for (uint i = 0; i < sz; ++i) {
239 ai.append((int64_t)i);
241 return ai.toArray();
244 Array BaseVector::tovaluesarray() {
245 return toArrayImpl();
248 int64_t BaseVector::linearsearch(CVarRef search_value) {
249 uint sz = m_size;
250 for (uint i = 0; i < sz; ++i) {
251 if (same(search_value, tvAsCVarRef(&m_data[i]))) {
252 return i;
255 return -1;
258 // Non PHP-land methods.
260 bool BaseVector::OffsetIsset(ObjectData* obj, TypedValue* key) {
261 assert(key->m_type != KindOfRef);
262 auto vec = static_cast<BaseVector*>(obj);
263 TypedValue* result;
264 if (key->m_type == KindOfInt64) {
265 result = vec->get(key->m_data.num);
266 } else {
267 throwBadKeyType();
268 result = nullptr;
270 return result ? !cellIsNull(tvToCell(result)) : false;
273 bool BaseVector::OffsetEmpty(ObjectData* obj, TypedValue* key) {
274 assert(key->m_type != KindOfRef);
275 auto vec = static_cast<BaseVector*>(obj);
276 TypedValue* result;
277 if (key->m_type == KindOfInt64) {
278 result = vec->get(key->m_data.num);
279 } else {
280 throwBadKeyType();
281 result = nullptr;
283 return result ? !cellToBool(*result) : true;
286 bool BaseVector::OffsetContains(ObjectData* obj, TypedValue* key) {
287 assert(key->m_type != KindOfRef);
288 auto vec = static_cast<BaseVector*>(obj);
289 if (key->m_type == KindOfInt64) {
290 return vec->contains(key->m_data.num);
291 } else {
292 throwBadKeyType();
293 return false;
297 TypedValue* BaseVector::OffsetGet(ObjectData* obj, TypedValue* key) {
298 assert(key->m_type != KindOfRef);
299 auto vec = static_cast<BaseVector*>(obj);
300 if (key->m_type == KindOfInt64) {
301 return vec->at(key->m_data.num);
303 throwBadKeyType();
304 return nullptr;
307 bool BaseVector::Equals(const ObjectData* obj1, const ObjectData* obj2) {
308 auto bv1 = static_cast<const BaseVector*>(obj1);
309 auto bv2 = static_cast<const BaseVector*>(obj2);
311 uint sz = bv1->m_size;
312 if (sz != bv2->m_size) {
313 return false;
316 for (uint i = 0; i < sz; ++i) {
317 if (!equal(tvAsCVarRef(&bv1->m_data[i]),
318 tvAsCVarRef(&bv2->m_data[i]))) {
320 return false;
324 return true;
327 void BaseVector::Unserialize(const char* vectorType,
328 ObjectData* obj,
329 VariableUnserializer* uns,
330 int64_t sz,
331 char type) {
332 if (type != 'V') {
333 throw Exception("%s does not support the '%c' serialization "
334 "format", vectorType, type);
336 auto bvec = static_cast<BaseVector*>(obj);
337 bvec->reserve(sz);
338 for (int64_t i = 0; i < sz; ++i) {
339 auto tv = &bvec->m_data[bvec->m_size];
340 tv->m_type = KindOfNull;
341 ++bvec->m_size;
342 tvAsVariant(tv).unserialize(uns, Uns::Mode::ColValue);
346 // Helpers
348 Array BaseVector::toArrayImpl() const {
349 PackedArrayInit ai(m_size);
350 uint sz = m_size;
351 for (uint i = 0; i < sz; ++i) {
352 ai.append(tvAsCVarRef(&m_data[i]));
354 return ai.toArray();
357 void BaseVector::grow() {
358 mutate();
360 if (m_capacity) {
361 m_capacity += m_capacity;
362 } else {
363 m_capacity = 8;
365 m_data = (TypedValue*)smart_realloc(m_data, m_capacity * sizeof(TypedValue));
368 void BaseVector::reserve(int64_t sz) {
369 if (sz <= 0) return;
371 if (m_capacity < sz) {
372 ++m_version;
373 mutate();
375 m_capacity = sz;
376 m_data =
377 (TypedValue*)smart_realloc(m_data, m_capacity * sizeof(TypedValue));
381 BaseVector::BaseVector(Class* cls) : ExtObjectData(cls),
382 m_size(0), m_data(nullptr), m_capacity(0),
383 m_version(0), m_frozenCopy(nullptr) {
387 * Delegate the responsibility for freeing the buffer to the
388 * frozen copy, if it exists.
390 BaseVector::~BaseVector() {
391 if (m_frozenCopy.isNull() && m_data) {
392 for (uint i = 0; i < m_size; ++i) {
393 tvRefcountedDecRef(&m_data[i]);
396 smart_free(m_data);
397 m_data = nullptr;
401 void BaseVector::throwBadKeyType() {
402 Object e(SystemLib::AllocInvalidArgumentExceptionObject(
403 "Only integer keys may be used with Vectors"));
404 throw e;
407 void BaseVector::init(CVarRef t) {
408 size_t sz;
409 ArrayIter iter = getArrayIterHelper(t, sz);
410 if (sz) {
411 reserve(sz);
413 for (; iter; ++iter) {
414 Variant v = iter.second();
415 TypedValue* tv = cvarToCell(&v);
416 add(tv);
420 void BaseVector::cow() {
421 TypedValue* newData =
422 (TypedValue*)smart_malloc(m_capacity * sizeof(TypedValue));
424 assert(newData);
426 for (uint i = 0; i < m_size; i++) {
427 cellDup(m_data[i], newData[i]);
430 m_data = newData;
431 m_frozenCopy.reset();
434 ///////////////////////////////////////////////////////////////////////////////