PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/collections/ext_collections-map.h

http://github.com/facebook/hiphop-php
C Header | 459 lines | 368 code | 60 blank | 31 comment | 47 complexity | ef2b705acf7a8f483223680a00c05c25 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
  1. #ifndef incl_HPHP_EXT_COLLECTIONS_MAP_H
  2. #define incl_HPHP_EXT_COLLECTIONS_MAP_H
  3. #include "hphp/runtime/ext/collections/ext_collections.h"
  4. #include "hphp/runtime/ext/collections/hash-collection.h"
  5. #include "hphp/runtime/base/builtin-functions.h"
  6. #include "hphp/runtime/vm/native-data.h"
  7. namespace HPHP {
  8. /////////////////////////////////////////////////////////////////////////////
  9. struct BaseVector;
  10. namespace collections {
  11. struct MapIterator;
  12. void deepCopy(tv_lval);
  13. }
  14. /**
  15. * BaseMap is a hash-table implementation with int and string keys only.
  16. * It doesn't represent any PHP-land class; that job is delegated to its
  17. * c_-prefixed child classes.
  18. */
  19. struct BaseMap : HashCollection {
  20. protected:
  21. // BaseMap is an abstract class, with no additional member needing
  22. // initialization.
  23. using HashCollection::HashCollection;
  24. ~BaseMap();
  25. public:
  26. // init(), used by Map::__construct()
  27. // expects an iterable of key=>value
  28. void init(const Variant& t) {
  29. assertx(m_size == 0);
  30. addAllImpl(t);
  31. }
  32. // addAllPairs(), used by Map::addAll()
  33. // expects an iterable of Pair objects
  34. void addAllPairs(const Variant& iterable);
  35. TypedValue* at(int64_t key) const { return atImpl<true>(key); }
  36. TypedValue* at(StringData* key) const { return atImpl<true>(key); }
  37. TypedValue* get(int64_t key) const { return atImpl<false>(key); }
  38. TypedValue* get(StringData* key) const { return atImpl<false>(key); }
  39. Variant firstValue();
  40. Variant firstKey();
  41. Variant lastValue();
  42. Variant lastKey();
  43. template<class TMap>
  44. typename std::enable_if<
  45. std::is_base_of<BaseMap, TMap>::value, TMap*>::type
  46. static Clone(ObjectData* obj);
  47. /*
  48. * Append `v' to the Map and incref it if it's refcounted.
  49. */
  50. void add(TypedValue v);
  51. void add(const Variant& v) { add(*v.asTypedValue()); }
  52. /*
  53. * Add `k' => `v' to the Map, increffing each if it's refcounted.
  54. */
  55. void set(int64_t k, TypedValue v);
  56. void set(StringData* k, TypedValue v);
  57. void set(int64_t k, const Variant& v) { set(k, *v.asTypedValue()); }
  58. void set(StringData* k, const Variant& v) { set(k, *v.asTypedValue()); }
  59. void set(TypedValue k, TypedValue v) {
  60. if (k.m_type == KindOfInt64) {
  61. set(k.m_data.num, v);
  62. } else if (isStringType(k.m_type)) {
  63. set(k.m_data.pstr, v);
  64. } else {
  65. throwBadKeyType();
  66. }
  67. }
  68. void set(const Variant& k, const Variant& v) {
  69. set(*k.asTypedValue(), *v.asTypedValue());
  70. }
  71. /*
  72. * Add `k` => `v` to the Map without inc-ref-ing the value.
  73. */
  74. void setMove(int64_t k, TypedValue v);
  75. void setMove(StringData* k, TypedValue v);
  76. Variant pop();
  77. Variant popFront();
  78. Array toPHPArray();
  79. public:
  80. template <IntishCast intishCast = IntishCast::None>
  81. static Array ToArray(const ObjectData* obj) {
  82. check_collection_cast_to_array();
  83. return const_cast<BaseMap*>(
  84. static_cast<const BaseMap*>(obj)
  85. )->toPHPArrayImpl<intishCast>();
  86. }
  87. static bool ToBool(const ObjectData* obj);
  88. template <bool throwOnMiss>
  89. static TypedValue* OffsetAt(ObjectData* obj, const TypedValue* key) {
  90. auto map = static_cast<BaseMap*>(obj);
  91. if (key->m_type == KindOfInt64) {
  92. return throwOnMiss ? map->at(key->m_data.num)
  93. : map->get(key->m_data.num);
  94. }
  95. if (isStringType(key->m_type)) {
  96. return throwOnMiss ? map->at(key->m_data.pstr)
  97. : map->get(key->m_data.pstr);
  98. }
  99. throwBadKeyType();
  100. return nullptr;
  101. }
  102. static void OffsetSet(ObjectData* obj, const TypedValue* key,
  103. const TypedValue* val);
  104. static bool OffsetIsset(ObjectData* obj, const TypedValue* key);
  105. static bool OffsetContains(ObjectData* obj, const TypedValue* key);
  106. static void OffsetUnset(ObjectData* obj, const TypedValue* key);
  107. static bool Equals(const ObjectData* obj1, const ObjectData* obj2);
  108. [[noreturn]] static void throwBadKeyType();
  109. protected:
  110. Variant php_at(const Variant& key) const {
  111. if (key.isInteger()) {
  112. return tvAsCVarRef(atImpl<true>(key.toInt64()));
  113. }
  114. if (key.isString()) {
  115. return tvAsCVarRef(atImpl<true>(key.getStringData()));
  116. }
  117. throwBadKeyType();
  118. }
  119. bool php_containsKey(const Variant& key) const {
  120. DataType t = key.getType();
  121. if (t == KindOfInt64) {
  122. return contains(key.toInt64());
  123. }
  124. if (isStringType(t)) {
  125. return contains(key.getStringData());
  126. }
  127. BaseMap::throwBadKeyType();
  128. }
  129. Variant php_get(const Variant& key) const {
  130. TypedValue *tv;
  131. if (key.isInteger()) {
  132. tv = atImpl<false>(key.toInt64());
  133. } else if (key.isString()) {
  134. tv = atImpl<false>(key.getStringData());
  135. } else {
  136. throwBadKeyType();
  137. }
  138. if (tv) return tvAsCVarRef(tv);
  139. return init_null_variant;
  140. }
  141. template<bool throwOnError>
  142. TypedValue* atImpl(int64_t key) const {
  143. auto p = find(key, hash_int64(key));
  144. if (UNLIKELY(p == Empty)) {
  145. if (throwOnError) {
  146. collections::throwUndef(key);
  147. } else {
  148. return nullptr;
  149. }
  150. }
  151. return const_cast<TypedValue*>(
  152. static_cast<const TypedValue*>(&(data()[p].data))
  153. );
  154. }
  155. template<bool throwOnError>
  156. TypedValue* atImpl(StringData* key) const {
  157. auto p = find(key, key->hash());
  158. if (UNLIKELY(p == Empty)) {
  159. if (throwOnError) {
  160. collections::throwUndef(key);
  161. } else {
  162. return nullptr;
  163. }
  164. }
  165. return const_cast<TypedValue*>(
  166. static_cast<const TypedValue*>(&(data()[p].data))
  167. );
  168. }
  169. Object getIterator();
  170. void addAllImpl(const Variant& iterable);
  171. void setAllImpl(const Variant& iterable);
  172. // Set `k` to `v` in the Map. Do not inc-ref `v`. Do not check for mutation.
  173. void setImpl(int64_t k, TypedValue v);
  174. void setImpl(StringData* k, TypedValue v);
  175. // setRaw() assigns a value to the specified key in this Map, but doesn't
  176. // check for an immutable buffer, so it's only safe to use in some cases.
  177. // If you're not sure, use set() instead.
  178. void setRaw(int64_t k, TypedValue v);
  179. void setRaw(StringData* key, TypedValue v);
  180. void setRaw(int64_t k, const Variant& v) { setRaw(k, *v.asTypedValue()); }
  181. void setRaw(StringData* k, const Variant& v) { setRaw(k, *v.asTypedValue()); }
  182. void setRaw(TypedValue k, TypedValue v) {
  183. if (k.m_type == KindOfInt64) {
  184. setRaw(k.m_data.num, v);
  185. } else if (isStringType(k.m_type)) {
  186. setRaw(k.m_data.pstr, v);
  187. } else {
  188. throwBadKeyType();
  189. }
  190. }
  191. void setRaw(const Variant& k, const Variant& v) {
  192. setRaw(*k.asTypedValue(), *v.asTypedValue());
  193. }
  194. template<class TMap>
  195. typename std::enable_if<
  196. std::is_base_of<BaseMap, TMap>::value, Object>::type
  197. php_differenceByKey(const Variant& it);
  198. template<class TMap>
  199. typename std::enable_if<
  200. std::is_base_of<BaseMap, TMap>::value, Object>::type
  201. php_zip(const Variant& iterable);
  202. template<class TMap>
  203. typename std::enable_if<
  204. std::is_base_of<BaseMap, TMap>::value, Object>::type
  205. php_take(const Variant& n);
  206. template<class TMap>
  207. typename std::enable_if<
  208. std::is_base_of<BaseMap, TMap>::value, Object>::type
  209. php_skip(const Variant& n);
  210. template<class TMap>
  211. typename std::enable_if<
  212. std::is_base_of<BaseMap, TMap>::value, Object>::type
  213. php_slice(const Variant& start, const Variant& len);
  214. template<class TVector>
  215. typename std::enable_if<
  216. std::is_base_of<BaseVector, TVector>::value, Object>::type
  217. php_concat(const Variant& iterable);
  218. template<class TMap>
  219. static typename std::enable_if<
  220. std::is_base_of<BaseMap, TMap>::value, Object>::type
  221. FromItems(const Class*, const Variant& iterable);
  222. template<class TMap>
  223. static typename std::enable_if<
  224. std::is_base_of<BaseMap, TMap>::value, Object>::type
  225. FromArray(const Class*, const Variant& arr);
  226. template<class TVector>
  227. Object php_values() {
  228. auto target = req::make<TVector>();
  229. int64_t sz = m_size;
  230. target->reserve(sz);
  231. assertx(target->canMutateBuffer());
  232. target->setSize(sz);
  233. int64_t out = 0;
  234. auto* eLimit = elmLimit();
  235. for (auto* e = firstElm(); e != eLimit; e = nextElm(e, eLimit), ++out) {
  236. tvDup(e->data, target->dataAt(out));
  237. }
  238. return Object{std::move(target)};
  239. }
  240. template<class TVector>
  241. Object php_keys() {
  242. auto vec = req::make<TVector>();
  243. vec->reserve(m_size);
  244. assertx(vec->canMutateBuffer());
  245. auto* e = firstElm();
  246. auto* eLimit = elmLimit();
  247. int64_t j = 0;
  248. for (; e != eLimit; e = nextElm(e, eLimit), vec->incSize(), ++j) {
  249. if (e->hasIntKey()) {
  250. tvCopy(make_tv<KindOfInt64>(e->ikey), vec->dataAt(j));
  251. } else {
  252. assertx(e->hasStrKey());
  253. tvDup(make_tv<KindOfString>(e->skey), vec->dataAt(j));
  254. }
  255. }
  256. return Object{std::move(vec)};
  257. }
  258. private:
  259. friend void collections::deepCopy(tv_lval);
  260. friend struct collections::CollectionsExtension;
  261. friend struct collections::MapIterator;
  262. friend struct c_Vector;
  263. friend struct c_Map;
  264. friend struct c_ImmMap;
  265. friend struct c_AwaitAllWaitHandle;
  266. friend struct c_GenMapWaitHandle;
  267. static void compileTimeAssertions() {
  268. // For performance, all native collection classes have their m_size field
  269. // at the same offset.
  270. static_assert(offsetof(BaseMap, m_size)
  271. == collections::FAST_SIZE_OFFSET, "");
  272. }
  273. };
  274. /////////////////////////////////////////////////////////////////////////////
  275. struct c_Map : BaseMap {
  276. DECLARE_COLLECTIONS_CLASS(Map);
  277. explicit c_Map()
  278. : BaseMap(c_Map::classof(), HeaderKind::Map) { }
  279. explicit c_Map(ArrayData* arr)
  280. : BaseMap(c_Map::classof(), HeaderKind::Map, arr) { }
  281. explicit c_Map(uint32_t cap)
  282. : BaseMap(c_Map::classof(), HeaderKind::Map, cap) { }
  283. void setAll(const Variant& t) {
  284. setAllImpl(t);
  285. }
  286. void clear();
  287. static c_Map* Clone(ObjectData* obj);
  288. Object getImmutableCopy();
  289. protected:
  290. friend struct collections::CollectionsExtension;
  291. Object php_add(const Variant& pair) {
  292. add(pair);
  293. return Object{this};
  294. }
  295. Object php_addAll(const Variant& it) {
  296. addAllPairs(it);
  297. return Object{this};
  298. }
  299. Object php_clear() {
  300. clear();
  301. return Object{this};
  302. }
  303. Object php_removeKey(const Variant& key) {
  304. DataType t = key.getType();
  305. if (t == KindOfInt64) {
  306. remove(key.toInt64());
  307. } else if (isStringType(t)) {
  308. remove(key.getStringData());
  309. } else {
  310. throwBadKeyType();
  311. }
  312. return Object{this};
  313. }
  314. void php_reserve(int64_t cap) {
  315. if (cap < 0) {
  316. SystemLib::throwInvalidArgumentExceptionObject(
  317. "Parameter sz must be a non-negative integer"
  318. );
  319. }
  320. reserve(cap);
  321. }
  322. Object php_set(const Variant& key, const Variant& value) {
  323. set(key, value);
  324. return Object{this};
  325. }
  326. Object php_setAll(const Variant& it) {
  327. setAll(it);
  328. return Object{this};
  329. }
  330. };
  331. /////////////////////////////////////////////////////////////////////////////
  332. struct c_ImmMap : BaseMap {
  333. DECLARE_COLLECTIONS_CLASS(ImmMap)
  334. public:
  335. explicit c_ImmMap()
  336. : BaseMap(c_ImmMap::classof(), HeaderKind::ImmMap) { }
  337. explicit c_ImmMap(ArrayData* arr)
  338. : BaseMap(c_ImmMap::classof(), HeaderKind::ImmMap, arr) { }
  339. explicit c_ImmMap(uint32_t cap)
  340. : BaseMap(c_ImmMap::classof(), HeaderKind::ImmMap, cap) { }
  341. static c_ImmMap* Clone(ObjectData* obj);
  342. public:
  343. friend struct BaseMap;
  344. friend struct c_Map;
  345. };
  346. namespace collections {
  347. /////////////////////////////////////////////////////////////////////////////
  348. extern const StaticString
  349. s_MapIterator;
  350. struct MapIterator {
  351. MapIterator() {}
  352. MapIterator(const MapIterator& src) = delete;
  353. MapIterator& operator=(const MapIterator& src) {
  354. m_obj = src.m_obj;
  355. m_pos = src.m_pos;
  356. return *this;
  357. }
  358. ~MapIterator() {}
  359. static Object newInstance() {
  360. static Class* cls = Unit::lookupClass(s_MapIterator.get());
  361. assertx(cls);
  362. return Object{cls};
  363. }
  364. void setMap(BaseMap* mp) {
  365. m_obj = mp;
  366. m_pos = mp->iter_begin();
  367. }
  368. Variant current() const {
  369. auto const mp = m_obj.get();
  370. if (!mp->iter_valid(m_pos)) {
  371. throw_iterator_not_valid();
  372. }
  373. return tvAsCVarRef(mp->iter_value(m_pos));
  374. }
  375. Variant key() const {
  376. auto const mp = m_obj.get();
  377. if (!mp->iter_valid(m_pos)) {
  378. throw_iterator_not_valid();
  379. }
  380. return mp->iter_key(m_pos);
  381. }
  382. bool valid() const {
  383. return m_obj->iter_valid(m_pos);
  384. }
  385. void next() {
  386. auto const mp = m_obj.get();
  387. m_pos = mp->iter_next(m_pos);
  388. }
  389. void rewind() {
  390. auto const mp = m_obj.get();
  391. m_pos = mp->iter_begin();
  392. }
  393. private:
  394. req::ptr<BaseMap> m_obj;
  395. uint32_t m_pos{0};
  396. };
  397. /////////////////////////////////////////////////////////////////////////////
  398. }}
  399. #endif