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

/hphp/runtime/base/array-init.h

https://github.com/tstarling/hiphop-php
C Header | 448 lines | 318 code | 60 blank | 70 comment | 46 complexity | 058ff4daa2d1033fef34e7fb302a9f91 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #ifndef incl_HPHP_ARRAY_INIT_H_
  17. #define incl_HPHP_ARRAY_INIT_H_
  18. #include "hphp/runtime/base/array-data.h"
  19. #include "hphp/runtime/base/complex-types.h"
  20. #include "hphp/runtime/base/hphp-array.h"
  21. namespace HPHP {
  22. //////////////////////////////////////////////////////////////////////
  23. struct ArrayInit {
  24. enum MapInit { mapInit };
  25. explicit ArrayInit(size_t n);
  26. ArrayInit(size_t n, MapInit);
  27. ArrayInit(ArrayInit&& other)
  28. : m_data(other.m_data)
  29. #ifdef DEBUG
  30. , m_addCount(other.m_addCount)
  31. , m_expectedCount(other.m_expectedCount)
  32. #endif
  33. {
  34. other.m_data = nullptr;
  35. #ifdef DEBUG
  36. other.m_expectedCount = 0;
  37. #endif
  38. }
  39. ArrayInit(const ArrayInit&) = delete;
  40. ArrayInit& operator=(const ArrayInit&) = delete;
  41. ~ArrayInit() {
  42. // In case an exception interrupts the initialization.
  43. if (m_data) m_data->release();
  44. }
  45. ArrayInit& set(const Variant& v) {
  46. performOp([&]{ return m_data->append(v, false); });
  47. return *this;
  48. }
  49. ArrayInit& set(RefResult v) { return setRef(variant(v)); }
  50. ArrayInit& set(CVarWithRefBind v) {
  51. performOp([&]{ return m_data->appendWithRef(variant(v), false); });
  52. return *this;
  53. }
  54. ArrayInit& setRef(const Variant& v) {
  55. performOp([&]{ return m_data->appendRef(v, false); });
  56. return *this;
  57. }
  58. ArrayInit& set(int64_t name, const Variant& v, bool keyConverted = false) {
  59. performOp([&]{ return m_data->set(name, v, false); });
  60. return *this;
  61. }
  62. // set(const char*) deprecated. Use set(CStrRef) with a StaticString,
  63. // if you have a literal, or String otherwise.
  64. ArrayInit& set(const char*, const Variant& v, bool keyConverted = false) = delete;
  65. ArrayInit& set(const String& name, const Variant& v, bool keyConverted = false) {
  66. if (keyConverted) {
  67. performOp([&]{ return m_data->set(name, v, false); });
  68. } else if (!name.isNull()) {
  69. performOp([&]{ return m_data->set(name.toKey(), v, false); });
  70. }
  71. return *this;
  72. }
  73. ArrayInit& set(const Variant& name, const Variant& v, bool keyConverted = false) {
  74. if (keyConverted) {
  75. performOp([&]{ return m_data->set(name, v, false); });
  76. } else {
  77. VarNR k(name.toKey());
  78. if (!k.isNull()) {
  79. performOp([&]{ return m_data->set(k, v, false); });
  80. }
  81. }
  82. return *this;
  83. }
  84. template<typename T>
  85. ArrayInit& set(const T &name, const Variant& v, bool keyConverted = false) {
  86. if (keyConverted) {
  87. performOp([&]{ return m_data->set(name, v, false); });
  88. } else {
  89. VarNR k(Variant(name).toKey());
  90. if (!k.isNull()) {
  91. performOp([&]{ return m_data->set(k, v, false); });
  92. }
  93. }
  94. return *this;
  95. }
  96. ArrayInit& set(const String& name, RefResult v, bool keyConverted = false) {
  97. if (keyConverted) {
  98. performOp([&]{ return m_data->setRef(name, variant(v), false); });
  99. } else if (!name.isNull()) {
  100. performOp([&]{
  101. return m_data->setRef(name.toKey(), variant(v), false);
  102. });
  103. }
  104. return *this;
  105. }
  106. ArrayInit& set(const Variant& name, RefResult v, bool keyConverted = false) {
  107. if (keyConverted) {
  108. performOp([&]{ return m_data->setRef(name, variant(v), false); });
  109. } else {
  110. VarNR k(name.toKey());
  111. if (!k.isNull()) {
  112. performOp([&]{ return m_data->setRef(k, variant(v), false); });
  113. }
  114. }
  115. return *this;
  116. }
  117. template<typename T>
  118. ArrayInit& set(const T &name, RefResult v, bool keyConverted = false) {
  119. if (keyConverted) {
  120. performOp([&]{ return m_data->setRef(name, variant(v), false); });
  121. } else {
  122. VarNR k(Variant(name).toKey());
  123. if (!k.isNull()) {
  124. performOp([&]{ return m_data->setRef(k, variant(v), false); });
  125. }
  126. }
  127. return *this;
  128. }
  129. ArrayInit& add(int64_t name, const Variant& v, bool keyConverted = false) {
  130. performOp([&]{ return m_data->add(name, v, false); });
  131. return *this;
  132. }
  133. ArrayInit& add(const String& name, const Variant& v, bool keyConverted = false) {
  134. if (keyConverted) {
  135. performOp([&]{ return m_data->add(name, v, false); });
  136. } else if (!name.isNull()) {
  137. performOp([&]{ return m_data->add(name.toKey(), v, false); });
  138. }
  139. return *this;
  140. }
  141. ArrayInit& add(const Variant& name, const Variant& v, bool keyConverted = false) {
  142. if (keyConverted) {
  143. performOp([&]{ return m_data->add(name, v, false); });
  144. } else {
  145. VarNR k(name.toKey());
  146. if (!k.isNull()) {
  147. performOp([&]{ return m_data->add(k, v, false); });
  148. }
  149. }
  150. return *this;
  151. }
  152. template<typename T>
  153. ArrayInit& add(const T &name, const Variant& v, bool keyConverted = false) {
  154. if (keyConverted) {
  155. performOp([&]{ return m_data->add(name, v, false); });
  156. } else {
  157. VarNR k(Variant(name).toKey());
  158. if (!k.isNull()) {
  159. performOp([&]{ return m_data->add(k, v, false); });
  160. }
  161. }
  162. return *this;
  163. }
  164. ArrayInit& setRef(int64_t name, const Variant& v, bool keyConverted = false) {
  165. performOp([&]{ return m_data->setRef(name, v, false); });
  166. return *this;
  167. }
  168. ArrayInit& setRef(const String& name, const Variant& v, bool keyConverted = false) {
  169. if (keyConverted) {
  170. performOp([&]{ return m_data->setRef(name, v, false); });
  171. } else {
  172. performOp([&]{ return m_data->setRef(name.toKey(), v, false); });
  173. }
  174. return *this;
  175. }
  176. ArrayInit& setRef(const Variant& name, const Variant& v, bool keyConverted = false) {
  177. if (keyConverted) {
  178. performOp([&]{ return m_data->setRef(name, v, false); });
  179. } else {
  180. Variant key(name.toKey());
  181. if (!key.isNull()) {
  182. performOp([&]{ return m_data->setRef(key, v, false); });
  183. }
  184. }
  185. return *this;
  186. }
  187. template<typename T>
  188. ArrayInit& setRef(const T &name, const Variant& v, bool keyConverted = false) {
  189. if (keyConverted) {
  190. performOp([&]{ return m_data->setRef(name, v, false); });
  191. } else {
  192. VarNR key(Variant(name).toKey());
  193. if (!key.isNull()) {
  194. performOp([&]{ return m_data->setRef(key, v, false); });
  195. }
  196. }
  197. return *this;
  198. }
  199. // Prefer toArray() in new code---it can save a null check when the
  200. // compiler can't prove m_data hasn't changed.
  201. ArrayData *create() {
  202. ArrayData *ret = m_data;
  203. m_data = nullptr;
  204. assert(true || (m_expectedCount = 0)); // reset; no more adds allowed
  205. return ret;
  206. }
  207. Array toArray() {
  208. auto ptr = m_data;
  209. m_data = nullptr;
  210. assert(true || (m_expectedCount = 0)); // reset; no more adds allowed
  211. return Array(ptr, Array::ArrayInitCtor::Tag);
  212. }
  213. Variant toVariant() {
  214. auto ptr = m_data;
  215. m_data = nullptr;
  216. assert(true || (m_expectedCount = 0)); // reset; no more adds allowed
  217. return Variant(ptr, Variant::ArrayInitCtor{});
  218. }
  219. private:
  220. template<class Operation>
  221. ALWAYS_INLINE void performOp(Operation oper) {
  222. DEBUG_ONLY auto newp = oper();
  223. // Array escalation must not happen during these reserved
  224. // initializations.
  225. assert(newp == m_data);
  226. // You cannot add/set more times than you reserved with ArrayInit.
  227. assert(++m_addCount <= m_expectedCount);
  228. }
  229. private:
  230. ArrayData* m_data;
  231. #ifdef DEBUG
  232. size_t m_addCount;
  233. size_t m_expectedCount;
  234. #endif
  235. };
  236. //////////////////////////////////////////////////////////////////////
  237. /*
  238. * Initializer for a vector-shaped array.
  239. */
  240. class PackedArrayInit {
  241. public:
  242. explicit PackedArrayInit(size_t n)
  243. : m_vec(HphpArray::MakeReserve(n))
  244. #ifdef DEBUG
  245. , m_addCount(0)
  246. , m_expectedCount(n)
  247. #endif
  248. {
  249. m_vec->setRefCount(0);
  250. }
  251. PackedArrayInit(PackedArrayInit&& other)
  252. : m_vec(other.m_vec)
  253. #ifdef DEBUG
  254. , m_addCount(other.m_addCount)
  255. , m_expectedCount(other.m_expectedCount)
  256. #endif
  257. {
  258. other.m_vec = nullptr;
  259. #ifdef DEBUG
  260. other.m_expectedCount = 0;
  261. #endif
  262. }
  263. PackedArrayInit(const PackedArrayInit&) = delete;
  264. PackedArrayInit& operator=(const PackedArrayInit&) = delete;
  265. ~PackedArrayInit() {
  266. // In case an exception interrupts the initialization.
  267. if (m_vec) m_vec->release();
  268. }
  269. /*
  270. * Append a new element to the packed array.
  271. */
  272. PackedArrayInit& append(const Variant& v) {
  273. performOp([&]{ return HphpArray::AppendPacked(m_vec, v, false); });
  274. return *this;
  275. }
  276. /*
  277. * Box v if it is not already boxed, and append a new element that
  278. * is KindOfRef and points to the same RefData as v.
  279. *
  280. * Post: v.getRawType() == KindOfRef
  281. */
  282. PackedArrayInit& appendRef(const Variant& v) {
  283. performOp([&]{ return HphpArray::AppendRefPacked(m_vec, v, false); });
  284. return *this;
  285. }
  286. /*
  287. * Append v, preserving references in the way php does. That is, if
  288. * v is a KindOfRef with refcount > 1, the new element in *this will
  289. * be KindOfRef and share the same RefData. Otherwise, the new
  290. * element is split.
  291. */
  292. PackedArrayInit& appendWithRef(const Variant& v) {
  293. performOp([&]{ return HphpArray::AppendWithRefPacked(m_vec, v, false); });
  294. return *this;
  295. }
  296. Variant toVariant() {
  297. auto ptr = m_vec;
  298. m_vec = nullptr;
  299. assert(true || (m_expectedCount = 0)); // reset; no more adds allowed
  300. return Variant(ptr, Variant::ArrayInitCtor{});
  301. }
  302. Array toArray() {
  303. ArrayData* ptr = m_vec;
  304. m_vec = nullptr;
  305. assert(true || (m_expectedCount = 0)); // reset; no more adds allowed
  306. return Array(ptr, Array::ArrayInitCtor::Tag);
  307. }
  308. ArrayData *create() {
  309. auto ptr = m_vec;
  310. m_vec = nullptr;
  311. assert(true || (m_expectedCount = 0)); // reset; no more adds allowed
  312. return ptr;
  313. }
  314. private:
  315. template<class Operation>
  316. ALWAYS_INLINE void performOp(Operation oper) {
  317. DEBUG_ONLY auto newp = oper();
  318. // Array escalation must not happen during these reserved
  319. // initializations.
  320. assert(newp == m_vec);
  321. // You cannot add/set more times than you reserved with ArrayInit.
  322. assert(++m_addCount <= m_expectedCount);
  323. }
  324. private:
  325. HphpArray* m_vec;
  326. #ifdef DEBUG
  327. size_t m_addCount;
  328. size_t m_expectedCount;
  329. #endif
  330. };
  331. //////////////////////////////////////////////////////////////////////
  332. namespace make_array_detail {
  333. inline void packed_impl(PackedArrayInit&) {}
  334. template<class Val, class... Vals>
  335. void packed_impl(PackedArrayInit& init, Val&& val, Vals&&... vals) {
  336. init.append(std::forward<Val>(val));
  337. packed_impl(init, std::forward<Vals>(vals)...);
  338. }
  339. inline String init_key(const char* s) { return String(s); }
  340. inline int64_t init_key(int k) { return k; }
  341. inline int64_t init_key(int64_t k) { return k; }
  342. inline const String& init_key(const String& k) { return k; }
  343. inline void map_impl(ArrayInit&) {}
  344. template<class Key, class Val, class... KVPairs>
  345. void map_impl(ArrayInit& init, Key&& key, Val&& val, KVPairs&&... kvpairs) {
  346. init.set(init_key(std::forward<Key>(key)), std::forward<Val>(val));
  347. map_impl(init, std::forward<KVPairs>(kvpairs)...);
  348. }
  349. }
  350. /*
  351. * Helper for creating packed arrays (vector-like) that don't contain
  352. * references.
  353. *
  354. * Usage:
  355. *
  356. * auto newArray = make_packed_array(1, 2, 3, 4);
  357. *
  358. * If you need to deal with references, you currently have to use
  359. * PackedArrayInit directly.
  360. */
  361. template<class... Vals>
  362. Array make_packed_array(Vals&&... vals) {
  363. static_assert(sizeof...(vals), "use Array::Create() instead");
  364. PackedArrayInit init(sizeof...(vals));
  365. make_array_detail::packed_impl(init, std::forward<Vals>(vals)...);
  366. return init.toArray();
  367. }
  368. /*
  369. * Helper for creating map-like arrays (kMixedKind). Takes pairs of
  370. * arguments for the keys and values.
  371. *
  372. * Usage:
  373. *
  374. * auto newArray = make_map_array(keyOne, valueOne,
  375. * otherKey, otherValue);
  376. *
  377. */
  378. template<class... KVPairs>
  379. Array make_map_array(KVPairs&&... kvpairs) {
  380. static_assert(
  381. sizeof...(kvpairs) % 2 == 0, "make_map_array needs key value pairs");
  382. ArrayInit init(sizeof...(kvpairs) / 2);
  383. make_array_detail::map_impl(init, std::forward<KVPairs>(kvpairs)...);
  384. return init.toArray();
  385. }
  386. //////////////////////////////////////////////////////////////////////
  387. }
  388. #endif