zend_hash.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
  11. | If you did not receive a copy of the Zend license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@zend.com so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andi Gutmans <andi@zend.com> |
  16. | Zeev Suraski <zeev@zend.com> |
  17. +----------------------------------------------------------------------+
  18. */
  19. /* $Id$ */
  20. #include "zend.h"
  21. #include "zend_globals.h"
  22. #define CONNECT_TO_BUCKET_DLLIST(element, list_head) \
  23. (element)->pNext = (list_head); \
  24. (element)->pLast = NULL; \
  25. if ((element)->pNext) { \
  26. (element)->pNext->pLast = (element); \
  27. }
  28. #define CONNECT_TO_GLOBAL_DLLIST_EX(element, ht, last, next)\
  29. (element)->pListLast = (last); \
  30. (element)->pListNext = (next); \
  31. if ((last) != NULL) { \
  32. (last)->pListNext = (element); \
  33. } else { \
  34. (ht)->pListHead = (element); \
  35. } \
  36. if ((next) != NULL) { \
  37. (next)->pListLast = (element); \
  38. } else { \
  39. (ht)->pListTail = (element); \
  40. } \
  41. #define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
  42. CONNECT_TO_GLOBAL_DLLIST_EX(element, ht, (ht)->pListTail, (Bucket *) NULL); \
  43. if ((ht)->pInternalPointer == NULL) { \
  44. (ht)->pInternalPointer = (element); \
  45. }
  46. #if ZEND_DEBUG
  47. #define HT_OK 0
  48. #define HT_IS_DESTROYING 1
  49. #define HT_DESTROYED 2
  50. #define HT_CLEANING 3
  51. static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
  52. {
  53. if (ht->inconsistent==HT_OK) {
  54. return;
  55. }
  56. switch (ht->inconsistent) {
  57. case HT_IS_DESTROYING:
  58. zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
  59. break;
  60. case HT_DESTROYED:
  61. zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
  62. break;
  63. case HT_CLEANING:
  64. zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
  65. break;
  66. default:
  67. zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
  68. break;
  69. }
  70. zend_bailout();
  71. }
  72. #define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
  73. #define SET_INCONSISTENT(n) ht->inconsistent = n;
  74. #else
  75. #define IS_CONSISTENT(a)
  76. #define SET_INCONSISTENT(n)
  77. #endif
  78. #define HASH_PROTECT_RECURSION(ht) \
  79. if ((ht)->bApplyProtection) { \
  80. if ((ht)->nApplyCount++ >= 3) { \
  81. zend_error(E_ERROR, "Nesting level too deep - recursive dependency?"); \
  82. } \
  83. }
  84. #define HASH_UNPROTECT_RECURSION(ht) \
  85. if ((ht)->bApplyProtection) { \
  86. (ht)->nApplyCount--; \
  87. }
  88. #define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
  89. if ((ht)->nNumOfElements > (ht)->nTableSize) { \
  90. zend_hash_do_resize(ht); \
  91. }
  92. static void zend_hash_do_resize(HashTable *ht);
  93. ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
  94. {
  95. return zend_inline_hash_func(arKey, nKeyLength);
  96. }
  97. #define UPDATE_DATA(ht, p, pData, nDataSize) \
  98. if (nDataSize == sizeof(void*)) { \
  99. if ((p)->pData != &(p)->pDataPtr) { \
  100. pefree_rel((p)->pData, (ht)->persistent); \
  101. } \
  102. memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
  103. (p)->pData = &(p)->pDataPtr; \
  104. } else { \
  105. if ((p)->pData == &(p)->pDataPtr) { \
  106. (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent); \
  107. (p)->pDataPtr=NULL; \
  108. } else { \
  109. (p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent); \
  110. /* (p)->pDataPtr is already NULL so no need to initialize it */ \
  111. } \
  112. memcpy((p)->pData, pData, nDataSize); \
  113. }
  114. #define INIT_DATA(ht, p, _pData, nDataSize); \
  115. if (nDataSize == sizeof(void*)) { \
  116. memcpy(&(p)->pDataPtr, (_pData), sizeof(void *)); \
  117. (p)->pData = &(p)->pDataPtr; \
  118. } else { \
  119. (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
  120. memcpy((p)->pData, (_pData), nDataSize); \
  121. (p)->pDataPtr=NULL; \
  122. }
  123. #define CHECK_INIT(ht) do { \
  124. if (UNEXPECTED((ht)->nTableMask == 0)) { \
  125. (ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent); \
  126. (ht)->nTableMask = (ht)->nTableSize - 1; \
  127. } \
  128. } while (0)
  129. static const Bucket *uninitialized_bucket = NULL;
  130. static zend_always_inline void i_zend_hash_bucket_delete(HashTable *ht, Bucket *p)
  131. {
  132. #ifdef ZEND_SIGNALS
  133. TSRMLS_FETCH();
  134. #endif
  135. HANDLE_BLOCK_INTERRUPTIONS();
  136. if (p->pLast) {
  137. p->pLast->pNext = p->pNext;
  138. } else {
  139. ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
  140. }
  141. if (p->pNext) {
  142. p->pNext->pLast = p->pLast;
  143. }
  144. if (p->pListLast != NULL) {
  145. p->pListLast->pListNext = p->pListNext;
  146. } else {
  147. /* Deleting the head of the list */
  148. ht->pListHead = p->pListNext;
  149. }
  150. if (p->pListNext != NULL) {
  151. p->pListNext->pListLast = p->pListLast;
  152. } else {
  153. /* Deleting the tail of the list */
  154. ht->pListTail = p->pListLast;
  155. }
  156. if (ht->pInternalPointer == p) {
  157. ht->pInternalPointer = p->pListNext;
  158. }
  159. ht->nNumOfElements--;
  160. if (ht->pDestructor) {
  161. ht->pDestructor(p->pData);
  162. }
  163. if (p->pData != &p->pDataPtr) {
  164. pefree(p->pData, ht->persistent);
  165. }
  166. pefree(p, ht->persistent);
  167. HANDLE_UNBLOCK_INTERRUPTIONS();
  168. }
  169. static void zend_hash_bucket_delete(HashTable *ht, Bucket *p) {
  170. i_zend_hash_bucket_delete(ht, p);
  171. }
  172. ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
  173. {
  174. uint i = 3;
  175. SET_INCONSISTENT(HT_OK);
  176. if (nSize >= 0x80000000) {
  177. /* prevent overflow */
  178. ht->nTableSize = 0x80000000;
  179. } else {
  180. while ((1U << i) < nSize) {
  181. i++;
  182. }
  183. ht->nTableSize = 1 << i;
  184. }
  185. ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
  186. ht->pDestructor = pDestructor;
  187. ht->arBuckets = (Bucket**)&uninitialized_bucket;
  188. ht->pListHead = NULL;
  189. ht->pListTail = NULL;
  190. ht->nNumOfElements = 0;
  191. ht->nNextFreeElement = 0;
  192. ht->pInternalPointer = NULL;
  193. ht->persistent = persistent;
  194. ht->nApplyCount = 0;
  195. ht->bApplyProtection = 1;
  196. return SUCCESS;
  197. }
  198. ZEND_API int _zend_hash_init_ex(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
  199. {
  200. int retval = _zend_hash_init(ht, nSize, pDestructor, persistent ZEND_FILE_LINE_CC);
  201. ht->bApplyProtection = bApplyProtection;
  202. return retval;
  203. }
  204. ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
  205. {
  206. ht->bApplyProtection = bApplyProtection;
  207. }
  208. ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
  209. {
  210. ulong h;
  211. uint nIndex;
  212. Bucket *p;
  213. #ifdef ZEND_SIGNALS
  214. TSRMLS_FETCH();
  215. #endif
  216. IS_CONSISTENT(ht);
  217. ZEND_ASSERT(nKeyLength != 0);
  218. CHECK_INIT(ht);
  219. h = zend_inline_hash_func(arKey, nKeyLength);
  220. nIndex = h & ht->nTableMask;
  221. p = ht->arBuckets[nIndex];
  222. while (p != NULL) {
  223. if (p->arKey == arKey ||
  224. ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
  225. if (flag & HASH_ADD) {
  226. return FAILURE;
  227. }
  228. ZEND_ASSERT(p->pData != pData);
  229. HANDLE_BLOCK_INTERRUPTIONS();
  230. if (ht->pDestructor) {
  231. ht->pDestructor(p->pData);
  232. }
  233. UPDATE_DATA(ht, p, pData, nDataSize);
  234. if (pDest) {
  235. *pDest = p->pData;
  236. }
  237. HANDLE_UNBLOCK_INTERRUPTIONS();
  238. return SUCCESS;
  239. }
  240. p = p->pNext;
  241. }
  242. if (IS_INTERNED(arKey)) {
  243. p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
  244. p->arKey = arKey;
  245. } else {
  246. p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
  247. p->arKey = (const char*)(p + 1);
  248. memcpy((char*)p->arKey, arKey, nKeyLength);
  249. }
  250. p->nKeyLength = nKeyLength;
  251. INIT_DATA(ht, p, pData, nDataSize);
  252. p->h = h;
  253. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  254. if (pDest) {
  255. *pDest = p->pData;
  256. }
  257. HANDLE_BLOCK_INTERRUPTIONS();
  258. CONNECT_TO_GLOBAL_DLLIST(p, ht);
  259. ht->arBuckets[nIndex] = p;
  260. HANDLE_UNBLOCK_INTERRUPTIONS();
  261. ht->nNumOfElements++;
  262. ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
  263. return SUCCESS;
  264. }
  265. ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
  266. {
  267. uint nIndex;
  268. Bucket *p;
  269. #ifdef ZEND_SIGNALS
  270. TSRMLS_FETCH();
  271. #endif
  272. IS_CONSISTENT(ht);
  273. ZEND_ASSERT(nKeyLength != 0);
  274. CHECK_INIT(ht);
  275. nIndex = h & ht->nTableMask;
  276. p = ht->arBuckets[nIndex];
  277. while (p != NULL) {
  278. if (p->arKey == arKey ||
  279. ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
  280. if (flag & HASH_ADD) {
  281. return FAILURE;
  282. }
  283. ZEND_ASSERT(p->pData != pData);
  284. HANDLE_BLOCK_INTERRUPTIONS();
  285. if (ht->pDestructor) {
  286. ht->pDestructor(p->pData);
  287. }
  288. UPDATE_DATA(ht, p, pData, nDataSize);
  289. if (pDest) {
  290. *pDest = p->pData;
  291. }
  292. HANDLE_UNBLOCK_INTERRUPTIONS();
  293. return SUCCESS;
  294. }
  295. p = p->pNext;
  296. }
  297. if (IS_INTERNED(arKey)) {
  298. p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
  299. p->arKey = arKey;
  300. } else {
  301. p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
  302. p->arKey = (const char*)(p + 1);
  303. memcpy((char*)p->arKey, arKey, nKeyLength);
  304. }
  305. p->nKeyLength = nKeyLength;
  306. INIT_DATA(ht, p, pData, nDataSize);
  307. p->h = h;
  308. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  309. if (pDest) {
  310. *pDest = p->pData;
  311. }
  312. HANDLE_BLOCK_INTERRUPTIONS();
  313. ht->arBuckets[nIndex] = p;
  314. CONNECT_TO_GLOBAL_DLLIST(p, ht);
  315. HANDLE_UNBLOCK_INTERRUPTIONS();
  316. ht->nNumOfElements++;
  317. ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */
  318. return SUCCESS;
  319. }
  320. ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength)
  321. {
  322. void *dummy = (void *) 1;
  323. return zend_hash_add(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
  324. }
  325. ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
  326. {
  327. uint nIndex;
  328. Bucket *p;
  329. #ifdef ZEND_SIGNALS
  330. TSRMLS_FETCH();
  331. #endif
  332. IS_CONSISTENT(ht);
  333. CHECK_INIT(ht);
  334. if (flag & HASH_NEXT_INSERT) {
  335. h = ht->nNextFreeElement;
  336. }
  337. nIndex = h & ht->nTableMask;
  338. p = ht->arBuckets[nIndex];
  339. while (p != NULL) {
  340. if ((p->nKeyLength == 0) && (p->h == h)) {
  341. if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) {
  342. return FAILURE;
  343. }
  344. ZEND_ASSERT(p->pData != pData);
  345. HANDLE_BLOCK_INTERRUPTIONS();
  346. if (ht->pDestructor) {
  347. ht->pDestructor(p->pData);
  348. }
  349. UPDATE_DATA(ht, p, pData, nDataSize);
  350. HANDLE_UNBLOCK_INTERRUPTIONS();
  351. if (pDest) {
  352. *pDest = p->pData;
  353. }
  354. return SUCCESS;
  355. }
  356. p = p->pNext;
  357. }
  358. p = (Bucket *) pemalloc_rel(sizeof(Bucket), ht->persistent);
  359. p->arKey = NULL;
  360. p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
  361. p->h = h;
  362. INIT_DATA(ht, p, pData, nDataSize);
  363. if (pDest) {
  364. *pDest = p->pData;
  365. }
  366. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  367. HANDLE_BLOCK_INTERRUPTIONS();
  368. ht->arBuckets[nIndex] = p;
  369. CONNECT_TO_GLOBAL_DLLIST(p, ht);
  370. HANDLE_UNBLOCK_INTERRUPTIONS();
  371. if ((long)h >= (long)ht->nNextFreeElement) {
  372. ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
  373. }
  374. ht->nNumOfElements++;
  375. ZEND_HASH_IF_FULL_DO_RESIZE(ht);
  376. return SUCCESS;
  377. }
  378. static void zend_hash_do_resize(HashTable *ht)
  379. {
  380. Bucket **t;
  381. #ifdef ZEND_SIGNALS
  382. TSRMLS_FETCH();
  383. #endif
  384. IS_CONSISTENT(ht);
  385. if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */
  386. t = (Bucket **) perealloc(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
  387. HANDLE_BLOCK_INTERRUPTIONS();
  388. ht->arBuckets = t;
  389. ht->nTableSize = (ht->nTableSize << 1);
  390. ht->nTableMask = ht->nTableSize - 1;
  391. zend_hash_rehash(ht);
  392. HANDLE_UNBLOCK_INTERRUPTIONS();
  393. }
  394. }
  395. ZEND_API int zend_hash_rehash(HashTable *ht)
  396. {
  397. Bucket *p;
  398. uint nIndex;
  399. IS_CONSISTENT(ht);
  400. if (UNEXPECTED(ht->nNumOfElements == 0)) {
  401. return SUCCESS;
  402. }
  403. memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
  404. for (p = ht->pListHead; p != NULL; p = p->pListNext) {
  405. nIndex = p->h & ht->nTableMask;
  406. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  407. ht->arBuckets[nIndex] = p;
  408. }
  409. return SUCCESS;
  410. }
  411. ZEND_API void zend_hash_reindex(HashTable *ht, zend_bool only_integer_keys) {
  412. Bucket *p;
  413. uint nIndex;
  414. ulong offset = 0;
  415. IS_CONSISTENT(ht);
  416. if (UNEXPECTED(ht->nNumOfElements == 0)) {
  417. ht->nNextFreeElement = 0;
  418. return;
  419. }
  420. memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
  421. for (p = ht->pListHead; p != NULL; p = p->pListNext) {
  422. if (!only_integer_keys || p->nKeyLength == 0) {
  423. p->h = offset++;
  424. p->nKeyLength = 0;
  425. }
  426. nIndex = p->h & ht->nTableMask;
  427. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
  428. ht->arBuckets[nIndex] = p;
  429. }
  430. ht->nNextFreeElement = offset;
  431. }
  432. ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
  433. {
  434. uint nIndex;
  435. Bucket *p;
  436. IS_CONSISTENT(ht);
  437. if (flag == HASH_DEL_KEY) {
  438. h = zend_inline_hash_func(arKey, nKeyLength);
  439. }
  440. nIndex = h & ht->nTableMask;
  441. p = ht->arBuckets[nIndex];
  442. while (p != NULL) {
  443. if ((p->h == h)
  444. && (p->nKeyLength == nKeyLength)
  445. && ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */
  446. || !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */
  447. i_zend_hash_bucket_delete(ht, p);
  448. return SUCCESS;
  449. }
  450. p = p->pNext;
  451. }
  452. return FAILURE;
  453. }
  454. ZEND_API void zend_hash_destroy(HashTable *ht)
  455. {
  456. Bucket *p, *q;
  457. IS_CONSISTENT(ht);
  458. SET_INCONSISTENT(HT_IS_DESTROYING);
  459. p = ht->pListHead;
  460. while (p != NULL) {
  461. q = p;
  462. p = p->pListNext;
  463. if (ht->pDestructor) {
  464. ht->pDestructor(q->pData);
  465. }
  466. if (q->pData != &q->pDataPtr) {
  467. pefree(q->pData, ht->persistent);
  468. }
  469. pefree(q, ht->persistent);
  470. }
  471. if (ht->nTableMask) {
  472. pefree(ht->arBuckets, ht->persistent);
  473. }
  474. SET_INCONSISTENT(HT_DESTROYED);
  475. }
  476. ZEND_API void zend_hash_clean(HashTable *ht)
  477. {
  478. Bucket *p, *q;
  479. IS_CONSISTENT(ht);
  480. p = ht->pListHead;
  481. if (ht->nTableMask) {
  482. memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
  483. }
  484. ht->pListHead = NULL;
  485. ht->pListTail = NULL;
  486. ht->nNumOfElements = 0;
  487. ht->nNextFreeElement = 0;
  488. ht->pInternalPointer = NULL;
  489. while (p != NULL) {
  490. q = p;
  491. p = p->pListNext;
  492. if (ht->pDestructor) {
  493. ht->pDestructor(q->pData);
  494. }
  495. if (q->pData != &q->pDataPtr) {
  496. pefree(q->pData, ht->persistent);
  497. }
  498. pefree(q, ht->persistent);
  499. }
  500. }
  501. ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
  502. {
  503. IS_CONSISTENT(ht);
  504. while (ht->pListHead != NULL) {
  505. zend_hash_bucket_delete(ht, ht->pListHead);
  506. }
  507. if (ht->nTableMask) {
  508. pefree(ht->arBuckets, ht->persistent);
  509. }
  510. SET_INCONSISTENT(HT_DESTROYED);
  511. }
  512. ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
  513. {
  514. IS_CONSISTENT(ht);
  515. while (ht->pListTail != NULL) {
  516. zend_hash_bucket_delete(ht, ht->pListTail);
  517. }
  518. if (ht->nTableMask) {
  519. pefree(ht->arBuckets, ht->persistent);
  520. }
  521. SET_INCONSISTENT(HT_DESTROYED);
  522. }
  523. /* This is used to recurse elements and selectively delete certain entries
  524. * from a hashtable. apply_func() receives the data and decides if the entry
  525. * should be deleted or recursion should be stopped. The following three
  526. * return codes are possible:
  527. * ZEND_HASH_APPLY_KEEP - continue
  528. * ZEND_HASH_APPLY_STOP - stop iteration
  529. * ZEND_HASH_APPLY_REMOVE - delete the element, combineable with the former
  530. */
  531. ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
  532. {
  533. Bucket *p;
  534. IS_CONSISTENT(ht);
  535. HASH_PROTECT_RECURSION(ht);
  536. p = ht->pListHead;
  537. while (p != NULL) {
  538. int result = apply_func(p->pData TSRMLS_CC);
  539. Bucket *p_next = p->pListNext;
  540. if (result & ZEND_HASH_APPLY_REMOVE) {
  541. zend_hash_bucket_delete(ht, p);
  542. }
  543. p = p_next;
  544. if (result & ZEND_HASH_APPLY_STOP) {
  545. break;
  546. }
  547. }
  548. HASH_UNPROTECT_RECURSION(ht);
  549. }
  550. ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
  551. {
  552. Bucket *p;
  553. IS_CONSISTENT(ht);
  554. HASH_PROTECT_RECURSION(ht);
  555. p = ht->pListHead;
  556. while (p != NULL) {
  557. int result = apply_func(p->pData, argument TSRMLS_CC);
  558. Bucket *p_next = p->pListNext;
  559. if (result & ZEND_HASH_APPLY_REMOVE) {
  560. zend_hash_bucket_delete(ht, p);
  561. }
  562. p = p_next;
  563. if (result & ZEND_HASH_APPLY_STOP) {
  564. break;
  565. }
  566. }
  567. HASH_UNPROTECT_RECURSION(ht);
  568. }
  569. ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, ...)
  570. {
  571. Bucket *p;
  572. va_list args;
  573. zend_hash_key hash_key;
  574. IS_CONSISTENT(ht);
  575. HASH_PROTECT_RECURSION(ht);
  576. p = ht->pListHead;
  577. while (p != NULL) {
  578. int result;
  579. Bucket *p_next;
  580. va_start(args, num_args);
  581. hash_key.arKey = p->arKey;
  582. hash_key.nKeyLength = p->nKeyLength;
  583. hash_key.h = p->h;
  584. result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key);
  585. p_next = p->pListNext;
  586. if (result & ZEND_HASH_APPLY_REMOVE) {
  587. zend_hash_bucket_delete(ht, p);
  588. }
  589. p = p_next;
  590. if (result & ZEND_HASH_APPLY_STOP) {
  591. va_end(args);
  592. break;
  593. }
  594. va_end(args);
  595. }
  596. HASH_UNPROTECT_RECURSION(ht);
  597. }
  598. ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
  599. {
  600. Bucket *p;
  601. IS_CONSISTENT(ht);
  602. HASH_PROTECT_RECURSION(ht);
  603. p = ht->pListTail;
  604. while (p != NULL) {
  605. int result = apply_func(p->pData TSRMLS_CC);
  606. Bucket *p_last = p->pListLast;
  607. if (result & ZEND_HASH_APPLY_REMOVE) {
  608. zend_hash_bucket_delete(ht, p);
  609. }
  610. p = p_last;
  611. if (result & ZEND_HASH_APPLY_STOP) {
  612. break;
  613. }
  614. }
  615. HASH_UNPROTECT_RECURSION(ht);
  616. }
  617. ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
  618. {
  619. Bucket *p;
  620. void *new_entry;
  621. zend_bool setTargetPointer;
  622. IS_CONSISTENT(source);
  623. IS_CONSISTENT(target);
  624. setTargetPointer = !target->pInternalPointer;
  625. p = source->pListHead;
  626. while (p) {
  627. if (setTargetPointer && source->pInternalPointer == p) {
  628. target->pInternalPointer = NULL;
  629. }
  630. if (p->nKeyLength) {
  631. zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &new_entry);
  632. } else {
  633. zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
  634. }
  635. if (pCopyConstructor) {
  636. pCopyConstructor(new_entry);
  637. }
  638. p = p->pListNext;
  639. }
  640. if (!target->pInternalPointer) {
  641. target->pInternalPointer = target->pListHead;
  642. }
  643. }
  644. ZEND_API void _zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite ZEND_FILE_LINE_DC)
  645. {
  646. Bucket *p;
  647. void *t;
  648. int mode = (overwrite?HASH_UPDATE:HASH_ADD);
  649. IS_CONSISTENT(source);
  650. IS_CONSISTENT(target);
  651. p = source->pListHead;
  652. while (p) {
  653. if (p->nKeyLength>0) {
  654. if (_zend_hash_quick_add_or_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t, mode ZEND_FILE_LINE_RELAY_CC)==SUCCESS && pCopyConstructor) {
  655. pCopyConstructor(t);
  656. }
  657. } else {
  658. if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
  659. pCopyConstructor(t);
  660. }
  661. }
  662. p = p->pListNext;
  663. }
  664. target->pInternalPointer = target->pListHead;
  665. }
  666. static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
  667. {
  668. zend_hash_key hash_key;
  669. hash_key.arKey = p->arKey;
  670. hash_key.nKeyLength = p->nKeyLength;
  671. hash_key.h = p->h;
  672. return merge_checker_func(target, source_data, &hash_key, pParam);
  673. }
  674. ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam)
  675. {
  676. Bucket *p;
  677. void *t;
  678. IS_CONSISTENT(source);
  679. IS_CONSISTENT(target);
  680. p = source->pListHead;
  681. while (p) {
  682. if (zend_hash_replace_checker_wrapper(target, p->pData, p, pParam, pMergeSource)) {
  683. if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
  684. pCopyConstructor(t);
  685. }
  686. }
  687. p = p->pListNext;
  688. }
  689. target->pInternalPointer = target->pListHead;
  690. }
  691. /* Returns SUCCESS if found and FAILURE if not. The pointer to the
  692. * data is returned in pData. The reason is that there's no reason
  693. * someone using the hash table might not want to have NULL data
  694. */
  695. ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
  696. {
  697. ulong h;
  698. uint nIndex;
  699. Bucket *p;
  700. IS_CONSISTENT(ht);
  701. h = zend_inline_hash_func(arKey, nKeyLength);
  702. nIndex = h & ht->nTableMask;
  703. p = ht->arBuckets[nIndex];
  704. while (p != NULL) {
  705. if (p->arKey == arKey ||
  706. ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
  707. *pData = p->pData;
  708. return SUCCESS;
  709. }
  710. p = p->pNext;
  711. }
  712. return FAILURE;
  713. }
  714. ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
  715. {
  716. uint nIndex;
  717. Bucket *p;
  718. ZEND_ASSERT(nKeyLength != 0);
  719. IS_CONSISTENT(ht);
  720. nIndex = h & ht->nTableMask;
  721. p = ht->arBuckets[nIndex];
  722. while (p != NULL) {
  723. if (p->arKey == arKey ||
  724. ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
  725. *pData = p->pData;
  726. return SUCCESS;
  727. }
  728. p = p->pNext;
  729. }
  730. return FAILURE;
  731. }
  732. ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
  733. {
  734. ulong h;
  735. uint nIndex;
  736. Bucket *p;
  737. IS_CONSISTENT(ht);
  738. h = zend_inline_hash_func(arKey, nKeyLength);
  739. nIndex = h & ht->nTableMask;
  740. p = ht->arBuckets[nIndex];
  741. while (p != NULL) {
  742. if (p->arKey == arKey ||
  743. ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
  744. return 1;
  745. }
  746. p = p->pNext;
  747. }
  748. return 0;
  749. }
  750. ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
  751. {
  752. uint nIndex;
  753. Bucket *p;
  754. ZEND_ASSERT(nKeyLength != 0);
  755. IS_CONSISTENT(ht);
  756. nIndex = h & ht->nTableMask;
  757. p = ht->arBuckets[nIndex];
  758. while (p != NULL) {
  759. if (p->arKey == arKey ||
  760. ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
  761. return 1;
  762. }
  763. p = p->pNext;
  764. }
  765. return 0;
  766. }
  767. ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
  768. {
  769. uint nIndex;
  770. Bucket *p;
  771. IS_CONSISTENT(ht);
  772. nIndex = h & ht->nTableMask;
  773. p = ht->arBuckets[nIndex];
  774. while (p != NULL) {
  775. if ((p->h == h) && (p->nKeyLength == 0)) {
  776. *pData = p->pData;
  777. return SUCCESS;
  778. }
  779. p = p->pNext;
  780. }
  781. return FAILURE;
  782. }
  783. ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
  784. {
  785. uint nIndex;
  786. Bucket *p;
  787. IS_CONSISTENT(ht);
  788. nIndex = h & ht->nTableMask;
  789. p = ht->arBuckets[nIndex];
  790. while (p != NULL) {
  791. if ((p->h == h) && (p->nKeyLength == 0)) {
  792. return 1;
  793. }
  794. p = p->pNext;
  795. }
  796. return 0;
  797. }
  798. ZEND_API int zend_hash_num_elements(const HashTable *ht)
  799. {
  800. IS_CONSISTENT(ht);
  801. return ht->nNumOfElements;
  802. }
  803. ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
  804. {
  805. ptr->pos = ht->pInternalPointer;
  806. if (ht->pInternalPointer) {
  807. ptr->h = ht->pInternalPointer->h;
  808. return 1;
  809. } else {
  810. ptr->h = 0;
  811. return 0;
  812. }
  813. }
  814. ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
  815. {
  816. if (ptr->pos == NULL) {
  817. ht->pInternalPointer = NULL;
  818. } else if (ht->pInternalPointer != ptr->pos) {
  819. Bucket *p;
  820. IS_CONSISTENT(ht);
  821. p = ht->arBuckets[ptr->h & ht->nTableMask];
  822. while (p != NULL) {
  823. if (p == ptr->pos) {
  824. ht->pInternalPointer = p;
  825. return 1;
  826. }
  827. p = p->pNext;
  828. }
  829. return 0;
  830. }
  831. return 1;
  832. }
  833. ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
  834. {
  835. IS_CONSISTENT(ht);
  836. if (pos)
  837. *pos = ht->pListHead;
  838. else
  839. ht->pInternalPointer = ht->pListHead;
  840. }
  841. /* This function will be extremely optimized by remembering
  842. * the end of the list
  843. */
  844. ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
  845. {
  846. IS_CONSISTENT(ht);
  847. if (pos)
  848. *pos = ht->pListTail;
  849. else
  850. ht->pInternalPointer = ht->pListTail;
  851. }
  852. ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
  853. {
  854. HashPosition *current = pos ? pos : &ht->pInternalPointer;
  855. IS_CONSISTENT(ht);
  856. if (*current) {
  857. *current = (*current)->pListNext;
  858. return SUCCESS;
  859. } else
  860. return FAILURE;
  861. }
  862. ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
  863. {
  864. HashPosition *current = pos ? pos : &ht->pInternalPointer;
  865. IS_CONSISTENT(ht);
  866. if (*current) {
  867. *current = (*current)->pListLast;
  868. return SUCCESS;
  869. } else
  870. return FAILURE;
  871. }
  872. /* This function should be made binary safe */
  873. ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index, uint *str_length, ulong *num_index, zend_bool duplicate, HashPosition *pos)
  874. {
  875. Bucket *p;
  876. p = pos ? (*pos) : ht->pInternalPointer;
  877. IS_CONSISTENT(ht);
  878. if (p) {
  879. if (p->nKeyLength) {
  880. if (duplicate) {
  881. *str_index = estrndup(p->arKey, p->nKeyLength - 1);
  882. } else {
  883. *str_index = (char*)p->arKey;
  884. }
  885. if (str_length) {
  886. *str_length = p->nKeyLength;
  887. }
  888. return HASH_KEY_IS_STRING;
  889. } else {
  890. *num_index = p->h;
  891. return HASH_KEY_IS_LONG;
  892. }
  893. }
  894. return HASH_KEY_NON_EXISTENT;
  895. }
  896. ZEND_API void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) {
  897. Bucket *p;
  898. IS_CONSISTENT(ht);
  899. p = pos ? (*pos) : ht->pInternalPointer;
  900. if (!p) {
  901. Z_TYPE_P(key) = IS_NULL;
  902. } else if (p->nKeyLength) {
  903. Z_TYPE_P(key) = IS_STRING;
  904. Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char*)p->arKey : estrndup(p->arKey, p->nKeyLength - 1);
  905. Z_STRLEN_P(key) = p->nKeyLength - 1;
  906. } else {
  907. Z_TYPE_P(key) = IS_LONG;
  908. Z_LVAL_P(key) = p->h;
  909. }
  910. }
  911. ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
  912. {
  913. Bucket *p;
  914. p = pos ? (*pos) : ht->pInternalPointer;
  915. IS_CONSISTENT(ht);
  916. if (p) {
  917. if (p->nKeyLength) {
  918. return HASH_KEY_IS_STRING;
  919. } else {
  920. return HASH_KEY_IS_LONG;
  921. }
  922. }
  923. return HASH_KEY_NON_EXISTENT;
  924. }
  925. ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
  926. {
  927. Bucket *p;
  928. p = pos ? (*pos) : ht->pInternalPointer;
  929. IS_CONSISTENT(ht);
  930. if (p) {
  931. *pData = p->pData;
  932. return SUCCESS;
  933. } else {
  934. return FAILURE;
  935. }
  936. }
  937. /* This function changes key of current element without changing elements'
  938. * order. If element with target key already exists, it will be deleted first.
  939. */
  940. ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const char *str_index, uint str_length, ulong num_index, int mode, HashPosition *pos)
  941. {
  942. Bucket *p, *q;
  943. ulong h;
  944. #ifdef ZEND_SIGNALS
  945. TSRMLS_FETCH();
  946. #endif
  947. p = pos ? (*pos) : ht->pInternalPointer;
  948. IS_CONSISTENT(ht);
  949. if (p) {
  950. if (key_type == HASH_KEY_IS_LONG) {
  951. str_length = 0;
  952. if (!p->nKeyLength && p->h == num_index) {
  953. return SUCCESS;
  954. }
  955. q = ht->arBuckets[num_index & ht->nTableMask];
  956. while (q != NULL) {
  957. if (!q->nKeyLength && q->h == num_index) {
  958. break;
  959. }
  960. q = q->pNext;
  961. }
  962. } else if (key_type == HASH_KEY_IS_STRING) {
  963. if (IS_INTERNED(str_index)) {
  964. h = INTERNED_HASH(str_index);
  965. } else {
  966. h = zend_inline_hash_func(str_index, str_length);
  967. }
  968. if (p->arKey == str_index ||
  969. (p->nKeyLength == str_length &&
  970. p->h == h &&
  971. memcmp(p->arKey, str_index, str_length) == 0)) {
  972. return SUCCESS;
  973. }
  974. q = ht->arBuckets[h & ht->nTableMask];
  975. while (q != NULL) {
  976. if (q->arKey == str_index ||
  977. (q->h == h && q->nKeyLength == str_length &&
  978. memcmp(q->arKey, str_index, str_length) == 0)) {
  979. break;
  980. }
  981. q = q->pNext;
  982. }
  983. } else {
  984. return FAILURE;
  985. }
  986. if (q) {
  987. if (mode != HASH_UPDATE_KEY_ANYWAY) {
  988. Bucket *r = p->pListLast;
  989. int found = HASH_UPDATE_KEY_IF_BEFORE;
  990. while (r) {
  991. if (r == q) {
  992. found = HASH_UPDATE_KEY_IF_AFTER;
  993. break;
  994. }
  995. r = r->pListLast;
  996. }
  997. if (mode & found) {
  998. /* delete current bucket */
  999. zend_hash_bucket_delete(ht, p);
  1000. return FAILURE;
  1001. }
  1002. }
  1003. /* delete another bucket with the same key */
  1004. zend_hash_bucket_delete(ht, q);
  1005. }
  1006. HANDLE_BLOCK_INTERRUPTIONS();
  1007. if (p->pNext) {
  1008. p->pNext->pLast = p->pLast;
  1009. }
  1010. if (p->pLast) {
  1011. p->pLast->pNext = p->pNext;
  1012. } else {
  1013. ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
  1014. }
  1015. if ((IS_INTERNED(p->arKey) != IS_INTERNED(str_index)) ||
  1016. (!IS_INTERNED(p->arKey) && p->nKeyLength != str_length)) {
  1017. Bucket *q;
  1018. if (IS_INTERNED(str_index)) {
  1019. q = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
  1020. } else {
  1021. q = (Bucket *) pemalloc(sizeof(Bucket) + str_length, ht->persistent);
  1022. }
  1023. q->nKeyLength = str_length;
  1024. if (p->pData == &p->pDataPtr) {
  1025. q->pData = &q->pDataPtr;
  1026. } else {
  1027. q->pData = p->pData;
  1028. }
  1029. q->pDataPtr = p->pDataPtr;
  1030. CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p->pListLast, p->pListNext);
  1031. if (ht->pInternalPointer == p) {
  1032. ht->pInternalPointer = q;
  1033. }
  1034. if (pos) {
  1035. *pos = q;
  1036. }
  1037. pefree(p, ht->persistent);
  1038. p = q;
  1039. }
  1040. if (key_type == HASH_KEY_IS_LONG) {
  1041. p->h = num_index;
  1042. if ((long)num_index >= (long)ht->nNextFreeElement) {
  1043. ht->nNextFreeElement = num_index < LONG_MAX ? num_index + 1 : LONG_MAX;
  1044. }
  1045. } else {
  1046. p->h = h;
  1047. p->nKeyLength = str_length;
  1048. if (IS_INTERNED(str_index)) {
  1049. p->arKey = str_index;
  1050. } else {
  1051. p->arKey = (const char*)(p+1);
  1052. memcpy((char*)p->arKey, str_index, str_length);
  1053. }
  1054. }
  1055. CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[p->h & ht->nTableMask]);
  1056. ht->arBuckets[p->h & ht->nTableMask] = p;
  1057. HANDLE_UNBLOCK_INTERRUPTIONS();
  1058. return SUCCESS;
  1059. } else {
  1060. return FAILURE;
  1061. }
  1062. }
  1063. /* Performs an in-place splice operation on a hashtable:
  1064. * The elements between offset and offset+length are removed and the elements in list[list_count]
  1065. * are inserted in their place. The removed elements can be optionally collected into a hashtable.
  1066. * This operation reindexes the hashtable, i.e. integer keys will be zero-based and sequential,
  1067. * while string keys stay intact. The same applies to the elements inserted into the removed HT. */
  1068. ZEND_API void _zend_hash_splice(HashTable *ht, uint nDataSize, copy_ctor_func_t pCopyConstructor, uint offset, uint length, void **list, uint list_count, HashTable *removed ZEND_FILE_LINE_DC) /* {{{ */
  1069. {
  1070. int pos;
  1071. Bucket *p;
  1072. IS_CONSISTENT(ht);
  1073. CHECK_INIT(ht);
  1074. /* Skip all elements until offset */
  1075. for (pos = 0, p = ht->pListHead; pos < offset && p; pos++, p = p->pListNext);
  1076. while (pos < offset + length && p) {
  1077. /* Copy removed element into HT, if it was specified */
  1078. if (removed != NULL) {
  1079. void *new_entry;
  1080. if (p->nKeyLength == 0) {
  1081. zend_hash_next_index_insert(removed, p->pData, sizeof(zval *), &new_entry);
  1082. } else {
  1083. zend_hash_quick_update(removed, p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval *), &new_entry);
  1084. }
  1085. if (pCopyConstructor) {
  1086. pCopyConstructor(new_entry);
  1087. }
  1088. }
  1089. /* Remove element */
  1090. {
  1091. Bucket *p_next = p->pListNext;
  1092. zend_hash_bucket_delete(ht, p);
  1093. p = p_next;
  1094. }
  1095. pos++;
  1096. }
  1097. if (list != NULL) {
  1098. int i;
  1099. for (i = 0; i < list_count; i++) {
  1100. /* Add new element only to the global linked list, not the bucket list.
  1101. * Also use key 0 for everything, as we'll reindex the hashtable anyways. */
  1102. Bucket *q = pemalloc_rel(sizeof(Bucket), ht->persistent);
  1103. q->arKey = NULL;
  1104. q->nKeyLength = 0;
  1105. q->h = 0;
  1106. INIT_DATA(ht, q, list[i], nDataSize);
  1107. CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p ? p->pListLast : ht->pListTail, p);
  1108. ht->nNumOfElements++;
  1109. if (pCopyConstructor) {
  1110. pCopyConstructor(q->pData);
  1111. }
  1112. }
  1113. ZEND_HASH_IF_FULL_DO_RESIZE(ht);
  1114. }
  1115. zend_hash_reindex(ht, 1);
  1116. }
  1117. /* }}} */
  1118. ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
  1119. compare_func_t compar, int renumber TSRMLS_DC)
  1120. {
  1121. Bucket **arTmp;
  1122. Bucket *p;
  1123. int i, j;
  1124. IS_CONSISTENT(ht);
  1125. if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
  1126. return SUCCESS;
  1127. }
  1128. arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
  1129. p = ht->pListHead;
  1130. i = 0;
  1131. while (p) {
  1132. arTmp[i] = p;
  1133. p = p->pListNext;
  1134. i++;
  1135. }
  1136. (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
  1137. HANDLE_BLOCK_INTERRUPTIONS();
  1138. ht->pListHead = arTmp[0];
  1139. ht->pListTail = NULL;
  1140. ht->pInternalPointer = ht->pListHead;
  1141. arTmp[0]->pListLast = NULL;
  1142. if (i > 1) {
  1143. arTmp[0]->pListNext = arTmp[1];
  1144. for (j = 1; j < i-1; j++) {
  1145. arTmp[j]->pListLast = arTmp[j-1];
  1146. arTmp[j]->pListNext = arTmp[j+1];
  1147. }
  1148. arTmp[j]->pListLast = arTmp[j-1];
  1149. arTmp[j]->pListNext = NULL;
  1150. } else {
  1151. arTmp[0]->pListNext = NULL;
  1152. }
  1153. ht->pListTail = arTmp[i-1];
  1154. pefree(arTmp, ht->persistent);
  1155. HANDLE_UNBLOCK_INTERRUPTIONS();
  1156. if (renumber) {
  1157. zend_hash_reindex(ht, 0);
  1158. }
  1159. return SUCCESS;
  1160. }
  1161. ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
  1162. {
  1163. Bucket *p1, *p2 = NULL;
  1164. int result;
  1165. void *pData2;
  1166. IS_CONSISTENT(ht1);
  1167. IS_CONSISTENT(ht2);
  1168. HASH_PROTECT_RECURSION(ht1);
  1169. HASH_PROTECT_RECURSION(ht2);
  1170. result = ht1->nNumOfElements - ht2->nNumOfElements;
  1171. if (result!=0) {
  1172. HASH_UNPROTECT_RECURSION(ht1);
  1173. HASH_UNPROTECT_RECURSION(ht2);
  1174. return result;
  1175. }
  1176. p1 = ht1->pListHead;
  1177. if (ordered) {
  1178. p2 = ht2->pListHead;
  1179. }
  1180. while (p1) {
  1181. if (ordered && !p2) {
  1182. HASH_UNPROTECT_RECURSION(ht1);
  1183. HASH_UNPROTECT_RECURSION(ht2);
  1184. return 1; /* That's not supposed to happen */
  1185. }
  1186. if (ordered) {
  1187. if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
  1188. if (p1->h != p2->h) {
  1189. HASH_UNPROTECT_RECURSION(ht1);
  1190. HASH_UNPROTECT_RECURSION(ht2);
  1191. return p1->h > p2->h ? 1 : -1;
  1192. }
  1193. } else { /* string indices */
  1194. result = p1->nKeyLength - p2->nKeyLength;
  1195. if (result!=0) {
  1196. HASH_UNPROTECT_RECURSION(ht1);
  1197. HASH_UNPROTECT_RECURSION(ht2);
  1198. return result;
  1199. }
  1200. result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength);
  1201. if (result!=0) {
  1202. HASH_UNPROTECT_RECURSION(ht1);
  1203. HASH_UNPROTECT_RECURSION(ht2);
  1204. return result;
  1205. }
  1206. }
  1207. pData2 = p2->pData;
  1208. } else {
  1209. if (p1->nKeyLength==0) { /* numeric index */
  1210. if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) {
  1211. HASH_UNPROTECT_RECURSION(ht1);
  1212. HASH_UNPROTECT_RECURSION(ht2);
  1213. return 1;
  1214. }
  1215. } else { /* string index */
  1216. if (zend_hash_quick_find(ht2, p1->arKey, p1->nKeyLength, p1->h, &pData2)==FAILURE) {
  1217. HASH_UNPROTECT_RECURSION(ht1);
  1218. HASH_UNPROTECT_RECURSION(ht2);
  1219. return 1;
  1220. }
  1221. }
  1222. }
  1223. result = compar(p1->pData, pData2 TSRMLS_CC);
  1224. if (result!=0) {
  1225. HASH_UNPROTECT_RECURSION(ht1);
  1226. HASH_UNPROTECT_RECURSION(ht2);
  1227. return result;
  1228. }
  1229. p1 = p1->pListNext;
  1230. if (ordered) {
  1231. p2 = p2->pListNext;
  1232. }
  1233. }
  1234. HASH_UNPROTECT_RECURSION(ht1);
  1235. HASH_UNPROTECT_RECURSION(ht2);
  1236. return 0;
  1237. }
  1238. ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
  1239. {
  1240. Bucket *p, *res;
  1241. IS_CONSISTENT(ht);
  1242. if (ht->nNumOfElements == 0 ) {
  1243. *pData=NULL;
  1244. return FAILURE;
  1245. }
  1246. res = p = ht->pListHead;
  1247. while ((p = p->pListNext)) {
  1248. if (flag) {
  1249. if (compar(&res, &p TSRMLS_CC) < 0) { /* max */
  1250. res = p;
  1251. }
  1252. } else {
  1253. if (compar(&res, &p TSRMLS_CC) > 0) { /* min */
  1254. res = p;
  1255. }
  1256. }
  1257. }
  1258. *pData = res->pData;
  1259. return SUCCESS;
  1260. }
  1261. ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
  1262. {
  1263. IS_CONSISTENT(ht);
  1264. return ht->nNextFreeElement;
  1265. }
  1266. #if ZEND_DEBUG
  1267. void zend_hash_display_pListTail(const HashTable *ht)
  1268. {
  1269. Bucket *p;
  1270. p = ht->pListTail;
  1271. while (p != NULL) {
  1272. zend_output_debug_string(0, "pListTail has key %s\n", p->arKey);
  1273. p = p->pListLast;
  1274. }
  1275. }
  1276. void zend_hash_display(const HashTable *ht)
  1277. {
  1278. Bucket *p;
  1279. uint i;
  1280. if (UNEXPECTED(ht->nNumOfElements == 0)) {
  1281. zend_output_debug_string(0, "The hash is empty");
  1282. return;
  1283. }
  1284. for (i = 0; i < ht->nTableSize; i++) {
  1285. p = ht->arBuckets[i];
  1286. while (p != NULL) {
  1287. zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
  1288. p = p->pNext;
  1289. }
  1290. }
  1291. p = ht->pListTail;
  1292. while (p != NULL) {
  1293. zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
  1294. p = p->pListLast;
  1295. }
  1296. }
  1297. #endif
  1298. /*
  1299. * Local variables:
  1300. * tab-width: 4
  1301. * c-basic-offset: 4
  1302. * indent-tabs-mode: t
  1303. * End:
  1304. */