1 #include "hphp/runtime/ext/collections/ext_collections-set.h"
3 #include "hphp/runtime/ext/collections/ext_collections.h"
4 #include "hphp/runtime/ext/collections/ext_collections-map.h"
5 #include "hphp/runtime/ext/collections/ext_collections-pair.h"
6 #include "hphp/runtime/ext/collections/ext_collections-vector.h"
7 #include "hphp/runtime/ext/collections/hash-collection.h"
8 #include "hphp/runtime/base/container-functions.h"
9 #include "hphp/runtime/base/execution-context.h"
10 #include "hphp/runtime/base/mixed-array.h"
11 #include "hphp/runtime/vm/vm-regs.h"
14 /////////////////////////////////////////////////////////////////////////////
17 Class
* c_ImmSet::s_cls
;
20 bool invokeAndCastToBool(const CallCtx
& ctx
, int argc
,
21 const TypedValue
* argv
) {
22 auto ret
= Variant::attach(
23 g_context
->invokeFuncFew(ctx
, argc
, argv
)
25 return ret
.toBoolean();
28 /////////////////////////////////////////////////////////////////////////////
31 void BaseSet::addAllKeysOf(const Cell container
) {
32 assert(isContainer(container
));
34 decltype(cap()) oldCap
= 0;
37 [&](ArrayData
* adata
) {
38 auto sz
= adata
->size();
41 oldCap
= cap(); // assume minimal collisions
47 [this](Cell k
, TypedValue
/*v*/) { addRaw(k
); },
48 [this](ObjectData
* coll
) {
49 if (!m_size
&& coll
->collectionType() == CollectionType::Set
) {
50 auto hc
= static_cast<HashCollection
*>(coll
);
51 replaceArray(hc
->arrayData());
54 if (coll
->collectionType() == CollectionType::Pair
) {
61 throw_invalid_collection_parameter();
63 // ... and shrink back if that was incorrect
64 if (oldCap
) shrinkIfCapacityTooHigh(oldCap
);
67 void BaseSet::addAll(const Variant
& t
) {
68 if (t
.isNull()) { return; } // nothing to do
70 decltype(cap()) oldCap
= 0;
73 [&](ArrayData
* adata
) {
74 auto sz
= adata
->size();
77 oldCap
= cap(); // assume minimal collisions
83 [this](TypedValue v
) {
86 [this](ObjectData
* coll
) {
87 if (!m_size
&& coll
->collectionType() == CollectionType::Set
) {
88 auto hc
= static_cast<HashCollection
*>(coll
);
89 replaceArray(hc
->arrayData());
92 if (coll
->collectionType() == CollectionType::Pair
) {
97 [this](const TypedValue
* value
) {
98 add(tvAsCVarRef(value
));
102 throw_invalid_collection_parameter();
104 // ... and shrink back if that was incorrect
105 if (oldCap
) shrinkIfCapacityTooHigh(oldCap
);
110 void BaseSet::addImpl(int64_t k
) {
114 auto h
= hash_int64(k
);
115 auto p
= findForInsert(k
, h
);
116 assert(MixedArray::isValidIns(p
));
117 if (MixedArray::isValidPos(*p
)) {
118 // When there is a conflict, the add() API is supposed to replace the
119 // existing element with the new element in place. However since Sets
120 // currently only support integer and string elements, there is no way
121 // user code can really tell whether the existing element was replaced
122 // so for efficiency we do nothing.
125 if (UNLIKELY(isFull())) {
127 p
= findForInsert(k
, h
);
129 auto& e
= allocElm(p
);
131 e
.data
.m_type
= KindOfInt64
;
132 e
.data
.m_data
.num
= k
;
141 void BaseSet::addImpl(StringData
*key
) {
145 strhash_t h
= key
->hash();
146 auto p
= findForInsert(key
, h
);
147 assert(MixedArray::isValidIns(p
));
148 if (MixedArray::isValidPos(*p
)) {
151 if (UNLIKELY(isFull())) {
153 p
= findForInsert(key
, h
);
155 auto& e
= allocElm(p
);
156 // This increments the string's refcount twice, once for
157 // the key and once for the value
159 cellDup(make_tv
<KindOfString
>(key
), e
.data
);
165 void BaseSet::addRaw(int64_t k
) {
169 void BaseSet::addRaw(StringData
*key
) {
173 void BaseSet::add(int64_t k
) {
177 void BaseSet::add(StringData
*key
) {
181 void BaseSet::addFront(int64_t k
) {
183 auto h
= hash_int64(k
);
184 auto p
= findForInsert(k
, h
);
185 assert(MixedArray::isValidIns(p
));
186 if (MixedArray::isValidPos(*p
)) {
187 // When there is a conflict, the addFront() API is supposed to replace
188 // the existing element with the new element in place. However since
189 // Sets currently only support integer and string elements, there is
190 // no way user code can really tell whether the existing element was
191 // replaced so for efficiency we do nothing.
194 if (UNLIKELY(isFull())) {
196 p
= findForInsert(k
, h
);
198 auto& e
= allocElmFront(p
);
200 e
.data
.m_type
= KindOfInt64
;
201 e
.data
.m_data
.num
= k
;
206 void BaseSet::addFront(StringData
*key
) {
208 strhash_t h
= key
->hash();
209 auto p
= findForInsert(key
, h
);
210 assert(MixedArray::isValidIns(p
));
211 if (MixedArray::isValidPos(*p
)) {
214 if (UNLIKELY(isFull())) {
216 p
= findForInsert(key
, h
);
218 auto& e
= allocElmFront(p
);
219 // This increments the string's refcount twice, once for
220 // the key and once for the value
222 cellDup(make_tv
<KindOfString
>(key
), e
.data
);
226 Variant
BaseSet::pop() {
227 if (UNLIKELY(m_size
== 0)) {
228 SystemLib::throwInvalidOperationExceptionObject("Cannot pop empty Set");
231 auto e
= elmLimit() - 1;
234 if (!isTombstone(e
)) break;
236 Variant ret
= tvAsCVarRef(&e
->data
);
238 auto ei
= e
->hasIntKey() ? findForRemove(e
->ikey
, h
) :
239 findForRemove(e
->skey
, h
);
244 Variant
BaseSet::popFront() {
245 if (UNLIKELY(m_size
== 0)) {
246 SystemLib::throwInvalidOperationExceptionObject("Cannot pop empty Set");
251 assert(e
!= elmLimit());
252 if (!isTombstone(e
)) break;
254 Variant ret
= tvAsCVarRef(&e
->data
);
256 auto ei
= e
->hasIntKey() ? findForRemove(e
->ikey
, h
) :
257 findForRemove(e
->skey
, h
);
262 Variant
BaseSet::firstValue() {
263 if (!m_size
) return init_null();
265 assert(e
!= elmLimit());
266 return tvAsCVarRef(&e
->data
);
269 Variant
BaseSet::lastValue() {
270 if (!m_size
) return init_null();
271 // TODO Task# 4281431: If nthElmPos(n) is optimized to
272 // walk backward from the end when n > m_size/2, then
273 // we could use that here instead of having to use a
274 // manual while loop.
275 uint32_t pos
= posLimit() - 1;
276 while (isTombstone(pos
)) {
280 return tvAsCVarRef(&data()[pos
].data
);
283 void BaseSet::throwNoMutableIndexAccess() {
284 SystemLib::throwInvalidOperationExceptionObject(
285 "[] operator cannot be used to modify elements of a Set");
288 Array
BaseSet::ToArray(const ObjectData
* obj
) {
289 check_collection_cast_to_array();
290 return const_cast<BaseSet
*>(static_cast<const BaseSet
*>(obj
))->toArray();
293 bool BaseSet::ToBool(const ObjectData
* obj
) {
294 return static_cast<const BaseSet
*>(obj
)->toBoolImpl();
297 // This function will create a immutable copy of this Set (if it doesn't
298 // already exist) and then return it
299 Object
c_Set::getImmutableCopy() {
300 if (m_immCopy
.isNull()) {
301 auto set
= req::make
<c_ImmSet
>();
302 set
->m_size
= m_size
;
303 set
->m_version
= m_version
;
305 m_immCopy
= std::move(set
);
306 arrayData()->incRefCount();
308 assert(!m_immCopy
.isNull());
309 assert(data() == static_cast<c_ImmSet
*>(m_immCopy
.get())->data());
310 assert(arrayData()->hasMultipleRefs());
314 bool BaseSet::Equals(const ObjectData
* obj1
, const ObjectData
* obj2
) {
315 auto st1
= static_cast<const BaseSet
*>(obj1
);
316 auto st2
= static_cast<const BaseSet
*>(obj2
);
317 return MixedArray::DictEqual(st1
->arrayData(), st2
->arrayData());
320 BaseSet::~BaseSet() {
321 auto const mixed
= MixedArray::asMixed(arrayData());
322 // Avoid indirect call, as we know it is a MixedArray
323 if (mixed
->decReleaseCheck()) MixedArray::ReleaseDict(mixed
);
326 void BaseSet::throwBadValueType() {
327 SystemLib::throwInvalidArgumentExceptionObject(
328 "Only integer values and string values may be used with Sets");
332 typename
std::enable_if
<
333 std::is_base_of
<BaseSet
, TSet
>::value
, TSet
*>::type
334 BaseSet::Clone(ObjectData
* obj
) {
335 auto thiz
= static_cast<TSet
*>(obj
);
336 auto target
= req::make
<TSet
>();
338 return target
.detach();
340 thiz
->arrayData()->incRefCount();
341 target
->m_size
= thiz
->m_size
;
342 target
->m_arr
= thiz
->m_arr
;
343 return target
.detach();
346 bool BaseSet::OffsetIsset(ObjectData
* obj
, const TypedValue
* key
) {
347 assert(key
->m_type
!= KindOfRef
);
348 auto set
= static_cast<BaseSet
*>(obj
);
349 if (key
->m_type
== KindOfInt64
) {
350 return set
->contains(key
->m_data
.num
);
352 if (isStringType(key
->m_type
)) {
353 return set
->contains(key
->m_data
.pstr
);
359 bool BaseSet::OffsetEmpty(ObjectData
* obj
, const TypedValue
* key
) {
360 assert(key
->m_type
!= KindOfRef
);
361 auto set
= static_cast<BaseSet
*>(obj
);
362 if (key
->m_type
== KindOfInt64
) {
363 return set
->contains(key
->m_data
.num
) ? !cellToBool(*key
) : true;
365 if (isStringType(key
->m_type
)) {
366 return set
->contains(key
->m_data
.pstr
) ? !cellToBool(*key
) : true;
372 bool BaseSet::OffsetContains(ObjectData
* obj
, const TypedValue
* key
) {
373 assert(key
->m_type
!= KindOfRef
);
374 auto set
= static_cast<BaseSet
*>(obj
);
375 if (key
->m_type
== KindOfInt64
) {
376 return set
->contains(key
->m_data
.num
);
378 if (isStringType(key
->m_type
)) {
379 return set
->contains(key
->m_data
.pstr
);
385 void BaseSet::OffsetUnset(ObjectData
* obj
, const TypedValue
* key
) {
386 assert(key
->m_type
!= KindOfRef
);
387 auto set
= static_cast<BaseSet
*>(obj
);
388 if (key
->m_type
== KindOfInt64
) {
389 set
->remove(key
->m_data
.num
);
392 if (isStringType(key
->m_type
)) {
393 set
->remove(key
->m_data
.pstr
);
399 template<typename TSet
, bool useKey
>
400 typename
std::enable_if
<
401 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
402 BaseSet::php_map(const Variant
& callback
) {
405 vm_decode_function(callback
, nullptr, false, ctx
);
407 SystemLib::throwInvalidArgumentExceptionObject(
408 "Parameter must be a valid callback");
410 auto set
= req::make
<TSet
>();
411 if (!m_size
) return Object
{std::move(set
)};
412 assert(posLimit() != 0);
413 assert(set
->arrayData() == staticEmptyDictArrayAsMixed());
414 auto oldCap
= set
->cap();
415 set
->reserve(posLimit()); // presume minimum collisions ...
416 assert(set
->canMutateBuffer());
417 constexpr int64_t argc
= useKey
? 2 : 1;
418 TypedValue argv
[argc
];
419 for (ssize_t pos
= iter_begin(); iter_valid(pos
); pos
= iter_next(pos
)) {
420 auto e
= iter_elm(pos
);
421 int32_t pVer
= m_version
;
425 argv
[argc
-1] = e
->data
;
426 auto cbRet
= Variant::attach(
427 g_context
->invokeFuncFew(ctx
, argc
, argv
)
429 if (UNLIKELY(m_version
!= pVer
)) throw_collection_modified();
430 set
->addRaw(*cbRet
.asTypedValue());
432 // ... and shrink back if that was incorrect
433 set
->shrinkIfCapacityTooHigh(oldCap
);
434 return Object
{std::move(set
)};
437 template<typename TSet
, bool useKey
>
438 typename
std::enable_if
<
439 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
440 BaseSet::php_filter(const Variant
& callback
) {
443 vm_decode_function(callback
, nullptr, false, ctx
);
445 SystemLib::throwInvalidArgumentExceptionObject(
446 "Parameter must be a valid callback");
448 auto set
= req::make
<TSet
>();
449 if (!m_size
) return Object(std::move(set
));
450 // we don't reserve(), because we don't know how selective callback will be
452 int32_t version
= m_version
;
453 constexpr int64_t argc
= useKey
? 2 : 1;
454 TypedValue argv
[argc
];
455 for (ssize_t pos
= iter_begin(); iter_valid(pos
); pos
= iter_next(pos
)) {
456 auto e
= iter_elm(pos
);
460 argv
[argc
-1] = e
->data
;
461 bool b
= invokeAndCastToBool(ctx
, argc
, argv
);
462 if (UNLIKELY(version
!= m_version
)) {
463 throw_collection_modified();
467 if (e
->hasIntKey()) {
468 set
->addRaw(e
->data
.m_data
.num
);
470 assert(e
->hasStrKey());
471 set
->addRaw(e
->data
.m_data
.pstr
);
474 return Object(std::move(set
));
478 typename
std::enable_if
<
479 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
480 BaseSet::php_zip(const Variant
& iterable
) {
482 ArrayIter iter
= getArrayIterHelper(iterable
, sz
);
483 if (m_size
&& iter
) {
484 // At present, BaseSets only support int values and string values,
485 // so if this BaseSet is non empty and the iterable is non empty
486 // the zip operation will always fail
489 return Object(req::make
<TSet
>());
492 template<bool useKey
>
493 Object
BaseSet::php_retain(const Variant
& callback
) {
495 vm_decode_function(callback
, nullptr, false, ctx
);
497 SystemLib::throwInvalidArgumentExceptionObject(
498 "Parameter must be a valid callback");
501 if (!size
) { return Object
{this}; }
502 constexpr int64_t argc
= useKey
? 2 : 1;
503 TypedValue argv
[argc
];
504 for (ssize_t pos
= iter_begin(); iter_valid(pos
); pos
= iter_next(pos
)) {
505 int32_t version
= m_version
;
506 auto e
= iter_elm(pos
);
510 argv
[argc
-1] = e
->data
;
511 bool b
= invokeAndCastToBool(ctx
, argc
, argv
);
512 if (UNLIKELY(version
!= m_version
)) {
513 throw_collection_modified();
520 auto pp
= e
->hasIntKey() ? findForRemove(e
->ikey
, h
) :
521 findForRemove(e
->skey
, h
);
523 if (UNLIKELY(version
!= m_version
)) {
524 throw_collection_modified();
527 assert(m_size
<= size
);
528 compactOrShrinkIfDensityTooLow();
533 typename
std::enable_if
<
534 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
535 BaseSet::php_take(const Variant
& n
) {
536 if (!n
.isInteger()) {
537 SystemLib::throwInvalidArgumentExceptionObject(
538 "Parameter n must be an integer");
540 int64_t len
= n
.toInt64();
541 if (len
>= int64_t(m_size
)) {
542 // We know the result Set will simply be a copy of this Set,
543 // so we can just call Clone() and return early here.
544 return Object::attach(TSet::Clone(this));
546 auto set
= req::make
<TSet
>();
548 // We know the resulting Set will be empty, so we can return
550 return Object
{std::move(set
)};
552 size_t sz
= size_t(len
);
555 set
->setPosLimit(sz
);
556 auto table
= set
->hashTab();
557 auto mask
= set
->tableMask();
558 for (uint32_t frPos
= 0, toPos
= 0; toPos
< sz
; ++toPos
, ++frPos
) {
559 frPos
= skipTombstonesNoBoundsCheck(frPos
);
560 auto& toE
= set
->data()[toPos
];
561 dupElm(data()[frPos
], toE
);
562 *findForNewInsert(table
, mask
, toE
.probe()) = toPos
;
563 if (toE
.hasIntKey()) {
564 set
->updateNextKI(toE
.ikey
);
566 assert(toE
.hasStrKey());
569 return Object
{std::move(set
)};
573 typename
std::enable_if
<
574 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
575 BaseSet::php_takeWhile(const Variant
& fn
) {
577 vm_decode_function(fn
, nullptr, false, ctx
);
579 SystemLib::throwInvalidArgumentExceptionObject(
580 "Parameter must be a valid callback");
582 auto set
= req::make
<TSet
>();
583 if (!m_size
) return Object(std::move(set
));
585 int32_t version UNUSED
;
586 if (std::is_same
<c_Set
, TSet
>::value
) {
589 uint32_t used
= posLimit();
590 for (uint32_t i
= 0; i
< used
; ++i
) {
591 if (isTombstone(i
)) continue;
593 bool b
= invokeAndCastToBool(ctx
, 1, &e
->data
);
594 if (std::is_same
<c_Set
, TSet
>::value
) {
595 if (UNLIKELY(version
!= m_version
)) {
596 throw_collection_modified();
601 if (e
->hasIntKey()) {
602 set
->addRaw(e
->data
.m_data
.num
);
604 assert(e
->hasStrKey());
605 set
->addRaw(e
->data
.m_data
.pstr
);
608 return Object(std::move(set
));
612 typename
std::enable_if
<
613 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
614 BaseSet::php_skip(const Variant
& n
) {
615 if (!n
.isInteger()) {
616 SystemLib::throwInvalidArgumentExceptionObject(
617 "Parameter n must be an integer");
619 int64_t len
= n
.toInt64();
621 // We know the resulting Set will simply be a copy of this Set,
622 // so we can just call Clone() and return early here.
623 return Object::attach(TSet::Clone(this));
625 auto set
= req::make
<TSet
>();
627 // We know the resulting Set will be empty, so we can return
629 return Object
{std::move(set
)};
631 size_t sz
= size_t(m_size
) - size_t(len
);
635 set
->setPosLimit(sz
);
636 uint32_t frPos
= nthElmPos(len
);
637 auto table
= set
->hashTab();
638 auto mask
= set
->tableMask();
639 for (uint32_t toPos
= 0; toPos
< sz
; ++toPos
, ++frPos
) {
640 while (isTombstone(frPos
)) {
641 assert(frPos
+ 1 < posLimit());
644 auto& toE
= set
->data()[toPos
];
645 dupElm(data()[frPos
], toE
);
646 *findForNewInsert(table
, mask
, toE
.probe()) = toPos
;
647 if (toE
.hasIntKey()) {
648 set
->updateNextKI(toE
.ikey
);
650 assert(toE
.hasStrKey());
653 return Object
{std::move(set
)};
657 typename
std::enable_if
<
658 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
659 BaseSet::php_skipWhile(const Variant
& fn
) {
661 vm_decode_function(fn
, nullptr, false, ctx
);
663 SystemLib::throwInvalidArgumentExceptionObject(
664 "Parameter must be a valid callback");
666 auto set
= req::make
<TSet
>();
667 if (!m_size
) return Object(std::move(set
));
668 // we don't reserve(), because we don't know how selective fn will be
670 int32_t version UNUSED
;
671 if (std::is_same
<c_Set
, TSet
>::value
) {
674 uint32_t used
= posLimit();
676 for (; i
< used
; ++i
) {
677 if (isTombstone(i
)) continue;
679 bool b
= invokeAndCastToBool(ctx
, 1, &e
.data
);
680 if (std::is_same
<c_Set
, TSet
>::value
) {
681 if (UNLIKELY(version
!= m_version
)) {
682 throw_collection_modified();
687 for (; i
< used
; ++i
) {
688 if (isTombstone(i
)) continue;
691 set
->addRaw(e
.data
.m_data
.num
);
693 assert(e
.hasStrKey());
694 set
->addRaw(e
.data
.m_data
.pstr
);
697 return Object(std::move(set
));
701 typename
std::enable_if
<
702 std::is_base_of
<BaseSet
, TSet
>::value
, Object
>::type
703 BaseSet::php_slice(const Variant
& start
, const Variant
& len
) {
706 if (!start
.isInteger() || (istart
= start
.toInt64()) < 0) {
707 SystemLib::throwInvalidArgumentExceptionObject(
708 "Parameter start must be a non-negative integer");
710 if (!len
.isInteger() || (ilen
= len
.toInt64()) < 0) {
711 SystemLib::throwInvalidArgumentExceptionObject(
712 "Parameter len must be a non-negative integer");
714 size_t skipAmt
= std::min
<size_t>(istart
, m_size
);
715 size_t sz
= std::min
<size_t>(ilen
, size_t(m_size
) - skipAmt
);
716 auto set
= req::make
<TSet
>();
719 set
->setPosLimit(sz
);
720 uint32_t frPos
= nthElmPos(skipAmt
);
721 auto table
= set
->hashTab();
722 auto mask
= set
->tableMask();
723 for (uint32_t toPos
= 0; toPos
< sz
; ++toPos
, ++frPos
) {
724 frPos
= skipTombstonesNoBoundsCheck(frPos
);
725 auto& toE
= set
->data()[toPos
];
726 dupElm(data()[frPos
], toE
);
727 *findForNewInsert(table
, mask
, toE
.probe()) = toPos
;
728 if (toE
.hasIntKey()) {
729 set
->updateNextKI(toE
.ikey
);
731 assert(toE
.hasStrKey());
734 return Object
{std::move(set
)};
737 template<class TVector
>
738 typename
std::enable_if
<
739 std::is_base_of
<BaseVector
, TVector
>::value
, Object
>::type
740 BaseSet::php_concat(const Variant
& iterable
) {
742 ArrayIter iter
= getArrayIterHelper(iterable
, itSize
);
743 auto vec
= req::make
<TVector
>();
744 uint32_t sz
= m_size
;
745 vec
->reserve((size_t)sz
+ itSize
);
746 assert(vec
->canMutateBuffer());
749 uint32_t used
= posLimit();
750 for (uint32_t i
= 0, j
= 0; i
< used
; ++i
) {
751 if (isTombstone(i
)) {
754 cellDup(data()[i
].data
, vec
->data()[j
]);
757 for (; iter
; ++iter
) {
758 vec
->addRaw(iter
.second());
760 return Object
{std::move(vec
)};
763 Object
BaseSet::getIterator() {
764 auto iter
= collections::SetIterator::newInstance();
765 Native::data
<collections::SetIterator
>(iter
)->setSet(this);
769 ///////////////////////////////////////////////////////////////////////////////
772 void c_Set::clear() {
775 decRefArr(arrayData());
776 m_arr
= staticEmptyDictArrayAsMixed();
780 c_Set
* c_Set::Clone(ObjectData
* obj
) {
781 return BaseSet::Clone
<c_Set
>(obj
);
784 ///////////////////////////////////////////////////////////////////////////////
787 c_ImmSet
* c_ImmSet::Clone(ObjectData
* obj
) {
788 return BaseSet::Clone
<c_ImmSet
>(obj
);
791 namespace collections
{
792 /////////////////////////////////////////////////////////////////////////////
796 s_HH_ImmSet("HH\\ImmSet"),
797 s_SetIterator("SetIterator");
799 /////////////////////////////////////////////////////////////////////////////
802 static Variant
HHVM_METHOD(SetIterator
, current
) {
803 return Native::data
<SetIterator
>(this_
)->current();
806 static Variant
HHVM_METHOD(SetIterator
, key
) {
807 return Native::data
<SetIterator
>(this_
)->key();
810 static bool HHVM_METHOD(SetIterator
, valid
) {
811 return Native::data
<SetIterator
>(this_
)->valid();
814 static void HHVM_METHOD(SetIterator
, next
) {
815 Native::data
<SetIterator
>(this_
)->next();
818 static void HHVM_METHOD(SetIterator
, rewind
) {
819 Native::data
<SetIterator
>(this_
)->rewind();
822 /////////////////////////////////////////////////////////////////////////////
824 void CollectionsExtension::initSet() {
825 HHVM_ME(SetIterator
, current
);
826 HHVM_ME(SetIterator
, key
);
827 HHVM_ME(SetIterator
, valid
);
828 HHVM_ME(SetIterator
, next
);
829 HHVM_ME(SetIterator
, rewind
);
831 Native::registerNativeDataInfo
<SetIterator
>(
833 Native::NDIFlags::NO_SWEEP
836 #define BASE_ME(mn, impl) \
837 HHVM_NAMED_ME(HH\\Set, mn, impl); \
838 HHVM_NAMED_ME(HH\\ImmSet, mn, impl);
839 BASE_ME(__construct
, &BaseSet::init
);
840 BASE_ME(count
, &BaseSet::size
);
841 BASE_ME(contains
, &BaseSet::php_contains
);
842 BASE_ME(toArray
, &BaseSet::toArray
);
843 BASE_ME(toKeysArray
, &BaseSet::toKeysArray
);
844 BASE_ME(toValuesArray
, &BaseSet::toValuesArray
);
845 BASE_ME(getIterator
, &BaseSet::getIterator
);
846 BASE_ME(firstValue
, &BaseSet::firstValue
);
847 BASE_ME(lastValue
, &BaseSet::lastValue
);
850 #define TMPL_STATIC_ME(mn) \
851 HHVM_NAMED_STATIC_ME(HH\\Set, mn, BaseSet::mn<c_Set>); \
852 HHVM_NAMED_STATIC_ME(HH\\ImmSet, mn, BaseSet::mn<c_ImmSet>);
853 TMPL_STATIC_ME(fromItems
);
854 TMPL_STATIC_ME(fromKeysOf
);
855 TMPL_STATIC_ME(fromArray
);
856 #undef TMPL_STATIC_ME
858 #define TMPL_ME(mn, impl, ret) \
859 HHVM_NAMED_ME(HH\\Set, mn, impl<c_##ret>); \
860 HHVM_NAMED_ME(HH\\ImmSet, mn, impl<c_Imm##ret>);
861 TMPL_ME(zip
, &BaseSet::php_zip
, Set
);
862 TMPL_ME(take
, &BaseSet::php_take
, Set
);
863 TMPL_ME(takeWhile
, &BaseSet::php_takeWhile
, Set
);
864 TMPL_ME(skip
, &BaseSet::php_skip
, Set
);
865 TMPL_ME(skipWhile
, &BaseSet::php_skipWhile
, Set
);
866 TMPL_ME(slice
, &BaseSet::php_slice
, Set
);
867 TMPL_ME(values
, &BaseSet::php_values
, Vector
);
868 TMPL_ME(concat
, &BaseSet::php_concat
, Vector
);
871 auto const m
= &BaseSet::php_map
<c_Set
, false>;
872 auto const mk
= &BaseSet::php_map
<c_Set
, true>;
873 auto const immm
= &BaseSet::php_map
<c_ImmSet
, false>;
874 auto const immmk
= &BaseSet::php_map
<c_ImmSet
, true>;
875 HHVM_NAMED_ME(HH
\\Set
, map
, m
);
876 HHVM_NAMED_ME(HH
\\Set
, mapWithKey
, mk
);
877 HHVM_NAMED_ME(HH
\\ImmSet
, map
, immm
);
878 HHVM_NAMED_ME(HH
\\ImmSet
, mapWithKey
, immmk
);
880 auto const f
= &BaseSet::php_filter
<c_Set
, false>;
881 auto const fk
= &BaseSet::php_filter
<c_Set
, true>;
882 auto const immf
= &BaseSet::php_filter
<c_ImmSet
, false>;
883 auto const immfk
= &BaseSet::php_filter
<c_ImmSet
, true>;
884 HHVM_NAMED_ME(HH
\\Set
, filter
, f
);
885 HHVM_NAMED_ME(HH
\\Set
, filterWithKey
, fk
);
886 HHVM_NAMED_ME(HH
\\ImmSet
, filter
, immf
);
887 HHVM_NAMED_ME(HH
\\ImmSet
, filterWithKey
, immfk
);
889 HHVM_NAMED_ME(HH
\\Set
, toVector
, materialize
<c_Vector
>);
890 HHVM_NAMED_ME(HH
\\Set
, toImmVector
, materialize
<c_ImmVector
>);
891 HHVM_NAMED_ME(HH
\\Set
, toMap
, materialize
<c_Map
>);
892 HHVM_NAMED_ME(HH
\\Set
, toImmMap
, materialize
<c_ImmMap
>);
893 HHVM_NAMED_ME(HH
\\ImmSet
, toVector
, materialize
<c_Vector
>);
894 HHVM_NAMED_ME(HH
\\ImmSet
, toImmVector
, materialize
<c_ImmVector
>);
895 HHVM_NAMED_ME(HH
\\ImmSet
, toMap
, materialize
<c_Map
>);
896 HHVM_NAMED_ME(HH
\\ImmSet
, toImmMap
, materialize
<c_ImmMap
>);
897 HHVM_NAMED_ME(HH
\\ImmSet
, toSet
, materialize
<c_Set
>);
899 HHVM_NAMED_ME(HH
\\Set
, add
, &c_Set::php_add
);
900 HHVM_NAMED_ME(HH
\\Set
, addAll
, &c_Set::php_addAll
);
901 HHVM_NAMED_ME(HH
\\Set
, addAllKeysOf
, &c_Set::php_addAllKeysOf
);
902 HHVM_NAMED_ME(HH
\\Set
, clear
, &c_Set::php_clear
);
903 HHVM_NAMED_ME(HH
\\Set
, remove
, &c_Set::php_remove
);
904 HHVM_NAMED_ME(HH
\\Set
, removeAll
, &c_Set::php_removeAll
);
905 HHVM_NAMED_ME(HH
\\Set
, reserve
, &c_Set::php_reserve
);
906 HHVM_NAMED_ME(HH
\\Set
, retain
, &c_Set::php_retain
<false>);
907 HHVM_NAMED_ME(HH
\\Set
, retainWithKey
, &c_Set::php_retain
<true>);
908 HHVM_NAMED_ME(HH
\\Set
, toImmSet
, &c_Set::getImmutableCopy
);
910 loadSystemlib("collections-set");
912 c_Set::s_cls
= Unit::lookupClass(s_HH_Set
.get());
913 assertx(c_Set::s_cls
);
914 finishClass
<c_Set
>();
916 c_ImmSet::s_cls
= Unit::lookupClass(s_HH_ImmSet
.get());
917 assertx(c_ImmSet::s_cls
);
918 finishClass
<c_ImmSet
>();
921 /////////////////////////////////////////////////////////////////////////////