y_hashmap_impl.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. Legal:
  3. Version: MPL 1.1
  4. The contents of this file are subject to the Mozilla Public License Version
  5. 1.1 the "License"; you may not use this file except in compliance with
  6. the License. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/
  8. Software distributed under the License is distributed on an "AS IS" basis,
  9. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. for the specific language governing rights and limitations under the
  11. License.
  12. The Original Code is the YSI framework.
  13. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  14. Portions created by the Initial Developer are Copyright C 2011
  15. the Initial Developer. All Rights Reserved.
  16. Contributors:
  17. Y_Less
  18. koolk
  19. JoeBullet/Google63
  20. g_aSlice/Slice
  21. Misiur
  22. samphunter
  23. tianmeta
  24. maddinat0r
  25. spacemud
  26. Crayder
  27. Dayvison
  28. Ahmad45123
  29. Zeex
  30. irinel1996
  31. Yiin-
  32. Chaprnks
  33. Konstantinos
  34. Masterchen09
  35. Southclaws
  36. PatchwerkQWER
  37. m0k1
  38. paulommu
  39. udan111
  40. Thanks:
  41. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  42. ZeeX - Very productive conversations.
  43. koolk - IsPlayerinAreaEx code.
  44. TheAlpha - Danish translation.
  45. breadfish - German translation.
  46. Fireburn - Dutch translation.
  47. yom - French translation.
  48. 50p - Polish translation.
  49. Zamaroht - Spanish translation.
  50. Los - Portuguese translation.
  51. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  52. me to strive to better.
  53. Pixels^ - Running XScripters where the idea was born.
  54. Matite - Pestering me to release it and using it.
  55. Very special thanks to:
  56. Thiadmer - PAWN, whose limits continue to amaze me!
  57. Kye/Kalcor - SA:MP.
  58. SA:MP Team past, present and future - SA:MP.
  59. Optional plugins:
  60. Gamer_Z - GPS.
  61. Incognito - Streamer.
  62. Me - sscanf2, fixes2, Whirlpool.
  63. */
  64. #define HASH_MAP_SIZE (256)
  65. #define HASH_MAP_PTR (HASH_MAP_SIZE)
  66. #define HASH_MAP_SIZE_1 (HASH_MAP_SIZE + 1)
  67. #define HASH_MAP_SIZE_2 (HASH_MAP_SIZE + 2)
  68. #define HASH_MAP_SIZE_3 (HASH_MAP_SIZE + 3)
  69. #define HASH_MAP_UNUSED (HASH_MAP_SIZE + 4)
  70. #define HashMap:%0<%1> %0[HASH_MAP_SIZE + 5]
  71. #define HASH_MAP_DIR_LEFT (4)
  72. #define HASH_MAP_DIR_RIGHT (8)
  73. // There are three pieces of hash map data associated with each stored string.
  74. // In byte offsets, these are:
  75. //
  76. // 0 - The hash of the stored string.
  77. // 4 - The left pointer for the binary tree.
  78. // 8 - The right pointer for the binary tree.
  79. //
  80. #define HASH_MAP_DATA (3)
  81. /*-------------------------------------------------------------------------*//**
  82. * <param name="str">String to hash.</param>
  83. * <param name="hash">Desination of the hash.</param>
  84. * <remarks>
  85. * Quickly hashes the string using Bernstein. Caters for both packed and
  86. * unpacked strings.
  87. * </remarks>
  88. *//*------------------------------------------------------------------------**/
  89. P:D(HashMap_Hash(str[],&hash));
  90. #define HashMap_Hash(%0,%1) (%1=YHash(%0))
  91. /*-------------------------------------------------------------------------*//**
  92. * <param name="m">Hash map to initialise.</param>
  93. * <param name="target">Array to point in to.</param>
  94. * <param name="slot">Second dimension slot of the hashed data.</param>
  95. * <remarks>
  96. * Finds the location of the hash map linked list data in the passed array data
  97. * and uses that to read the data through pointers subsequently. It doesn't
  98. * matter WHERE in the enum the hash map data is, and if its not there you'll
  99. * get an error, or at least a warning.
  100. * </remarks>
  101. *//*------------------------------------------------------------------------**/
  102. P:D(HashMap_Init(HashMap:m<>, target[][], slot));
  103. /*-------------------------------------------------------------------------*//**
  104. * <param name="m">Hash map to initialise.</param>
  105. * <param name="target">Address of the hashmap data.</param>
  106. * <param name="size1">Number of entries.</param>
  107. * <param name="size2">Total Size of each entry IN BYTES.</param>
  108. * <param name="t2">Address of the name AND data start.</param>
  109. * <remarks>
  110. * Finds the location of the hash map linked list data in the passed array data
  111. * and uses that to read the data through pointers subsequently. It doesn't
  112. * matter WHERE in the enum the hash map data is, and if its not there you'll
  113. * get an error, or at least a warning.
  114. * </remarks>
  115. *//*------------------------------------------------------------------------**/
  116. stock _HashMap_Init(HashMap:m<>, &target, size1, size2, &t2)
  117. {
  118. static
  119. HashMap:sInit<> = {0, ...};
  120. m = sInit;
  121. new
  122. ptr;
  123. // Save the pointer.
  124. #emit LOAD.S.pri target
  125. #emit STOR.S.pri ptr
  126. m[HASH_MAP_UNUSED] = ptr,
  127. m[HASH_MAP_PTR] = ptr,
  128. // Store the number of elements in the array.
  129. m[HASH_MAP_SIZE_1] = size1,
  130. // Store the size of each element.
  131. m[HASH_MAP_SIZE_2] = size2;
  132. // Store the size of "_E_HASH_MAP_NAME" in bytes.
  133. #emit LOAD.S.pri target
  134. #emit LOAD.S.alt t2
  135. #emit SUB
  136. #emit STOR.S.pri ptr
  137. m[HASH_MAP_SIZE_3] = ptr;
  138. // Using the hash for the list (in "target"), pointing to the hash of the
  139. // next element; initialise the whole hash map in to a linked list of unused
  140. // elements.
  141. while (--size1)
  142. {
  143. // Increment "target" by "size2".
  144. #emit LOAD.S.alt target
  145. #emit LOAD.S.pri size2
  146. #emit ADD
  147. #emit STOR.I
  148. #emit STOR.S.pri target
  149. }
  150. // End the list.
  151. target = 0;
  152. }
  153. // Uses "%2 - %2" to make a tagged 0 without knowing the tag.
  154. #define HashMap_Init(%0,%1,%2) _HashMap_Init(%0, %1[0][(%2)], sizeof (%1), sizeof (%1[]) * cellbytes, %1[0][(%2) - (%2)])
  155. /*-------------------------------------------------------------------------*//**
  156. * <param name="m">Hash map to get a free slot in.</param>
  157. * <returns>
  158. * The index of a currently unused slot.
  159. * </returns>
  160. * <remarks>
  161. * This assumes that you correct use <c>GetUnused</c> and <c>AddUnused</c> in
  162. * pairs. If your code has a different way of tracking unused slots, don't
  163. * try and mix that with this!
  164. * </remarks>
  165. *//*------------------------------------------------------------------------**/
  166. stock HashMap_GetUnused(HashMap:m<>)
  167. {
  168. new
  169. ptr = m[HASH_MAP_UNUSED];
  170. if (ptr)
  171. {
  172. // Get the next slot.
  173. #emit LOAD.S.pri m
  174. #emit ADD.C 1040 // (256 + 4) * 4
  175. #emit MOVE.alt
  176. #emit LREF.S.pri ptr
  177. #emit STOR.I
  178. return (ptr - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2];
  179. }
  180. return -1;
  181. }
  182. /*-------------------------------------------------------------------------*//**
  183. * <param name="m">Hash map to get a free slot in.</param>
  184. * <returns>
  185. * The index of a currently unused slot.
  186. * </returns>
  187. * <remarks>
  188. * This assumes that you correct use <c>GetUnused</c> and <c>AddUnused</c> in
  189. * pairs. If your code has a different way of tracking unused slots, don't
  190. * try and mix that with this!
  191. * </remarks>
  192. *//*------------------------------------------------------------------------**/
  193. stock HashMap_AddUnused(HashMap:m<>, value)
  194. {
  195. if (0 <= value < m[HASH_MAP_SIZE_1])
  196. {
  197. // Convert the index to a pointer.
  198. value = value * m[HASH_MAP_SIZE_2] + m[HASH_MAP_PTR];
  199. #emit LOAD.S.pri m
  200. #emit ADD.C 1040 // (256 + 4) * 4
  201. #emit MOVE.alt
  202. // Store the current value of "m[HASH_MAP_UNUSED]" in "*value".
  203. #emit LOAD.I
  204. #emit SREF.S.pri value
  205. // Set "m[HASH_MAP_UNUSED]" to "value"
  206. #emit LOAD.S.pri value
  207. #emit STOR.I
  208. }
  209. }
  210. /*-------------------------------------------------------------------------*//**
  211. * <param name="str">String to get the size of.</param>
  212. * <returns>
  213. * The number of BYTES this string takes up including the NULL.
  214. * </returns>
  215. * <remarks>
  216. * Caters for both packed and unpacked strings. The weirdness is basically
  217. * just: <code>ispacked(str) ? (* 1) : (* 4)</code>.
  218. * </remarks>
  219. *//*------------------------------------------------------------------------**/
  220. P:D(HashMap_ByteLen(str[]));
  221. #define HashMap_ByteLen(%0) ((strlen((%0)) + 1) << (2 * _:!ispacked((%0))))
  222. /*-------------------------------------------------------------------------*//**
  223. * <param name="ignorecase"></param>
  224. * <param name="hash"></param>
  225. * <param name="m"></param>
  226. * <param name="str"></param>
  227. * <param name="value">More like the target slot.</param>
  228. * <remarks>
  229. * Adds a value to the given hash map under the given string key.
  230. *
  231. * Actually more like adding an index, not a value...
  232. * </remarks>
  233. *//*------------------------------------------------------------------------**/
  234. stock bool:HashMap_AddWithHash(HashMap:m<>, const str[], hash, value, bool:ignorecase = false)
  235. {
  236. P:3("HashMap_Add called: %d <= %d < %d", 0, value, m[HASH_MAP_SIZE_1]);
  237. if (0 <= value < m[HASH_MAP_SIZE_1])
  238. {
  239. // Check if the index is already in a hash map. Doesn't work if the
  240. // entry is a leaf, since it will be a member, but also have both
  241. // pointers as 0... I don't think there is a nice way to do this.
  242. // if (AMX_Read(ptr + 4) || AMX_Read(ptr + 8)) return false;
  243. static
  244. ptr,
  245. res;
  246. // Add this entry to the correct binary tree.
  247. P:5("HashMap_Add: mask = %d", hash & 0xFF);
  248. new
  249. prev = ref(m[hash & 0xFF]),
  250. next;
  251. for ( ; ; )
  252. {
  253. #emit LREF.S.pri prev
  254. #emit STOR.S.pri next
  255. if (!next)
  256. break;
  257. {}
  258. #emit LREF.S.pri next
  259. #emit STOR.pri res
  260. if (hash == res)
  261. {
  262. // It doesn't matter which way we go on matching hashes, as long
  263. // as the direction is consistent.
  264. #emit PUSH.C 0x7FFFFFFF
  265. #emit PUSH.S ignorecase
  266. #emit PUSH.S str
  267. #emit LOAD.S.pri m
  268. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  269. #emit LOAD.I
  270. #emit LOAD.S.alt next
  271. #emit SUB.alt
  272. #emit PUSH.pri
  273. #emit PUSH.C 16
  274. #emit SYSREQ.C strcmp
  275. #emit STACK 20
  276. #emit STOR.pri ptr
  277. if (ptr == 0)
  278. {
  279. P:E("Trying to add two copies of a string to a hashmap: \"%s\", %d", str, value);
  280. return false;
  281. }
  282. else if (ptr < 0) // Lower, move left.
  283. prev = next + HASH_MAP_DIR_LEFT;
  284. else // Higher, move right.
  285. prev = next + HASH_MAP_DIR_RIGHT;
  286. }
  287. else if (hash < res) // Lower, move left.
  288. prev = next + HASH_MAP_DIR_LEFT;
  289. else // Higher, move right.
  290. prev = next + HASH_MAP_DIR_RIGHT;
  291. }
  292. P:6("HashMap_Add: used = %d", AMX_Read(m[HASH_MAP_PTR] + value * m[HASH_MAP_SIZE_2]));
  293. // Get the address of this structure.
  294. ptr = m[HASH_MAP_PTR] + value * m[HASH_MAP_SIZE_2],
  295. // Copy the hashed value.
  296. AMX_Write(ptr, hash),
  297. AMX_Write(ptr + HASH_MAP_DIR_LEFT, 0),
  298. AMX_Write(ptr + HASH_MAP_DIR_RIGHT, 0),
  299. // Add this hash to the hash list.
  300. AMX_Write(prev, ptr),
  301. // Get the hashed string destination size, and copy the string.
  302. next = m[HASH_MAP_SIZE_3],
  303. rawMemcpy(ptr - next, ref(str), next);
  304. return true;
  305. }
  306. return false;
  307. }
  308. /*-------------------------------------------------------------------------*//**
  309. * <param name="ignorecase"></param>
  310. * <param name="m"></param>
  311. * <param name="str"></param>
  312. * <param name="value">More like the target slot.</param>
  313. * <remarks>
  314. * Adds a value to the given hash map under the given string key.
  315. *
  316. * Actually more like adding an index, not a value...
  317. * </remarks>
  318. *//*------------------------------------------------------------------------**/
  319. stock bool:HashMap_Add(HashMap:m<>, const str[], value, bool:ignorecase = false)
  320. {
  321. return HashMap_AddWithHash(m, str, YHash(str, .sensitive = !ignorecase), value, ignorecase);
  322. }
  323. /*-------------------------------------------------------------------------*//**
  324. * <param name="m">The hash map to search.</param>
  325. * <param name="str">The key to find.</param>
  326. * <returns>
  327. * The value associated with this key in the given hash map.
  328. * </returns>
  329. *//*------------------------------------------------------------------------**/
  330. stock HashMap_Get(HashMap:m<>, const str[], bool:ignorecase = false)
  331. {
  332. return HashMap_GetWithHash(m, str, YHash(str, .sensitive = !ignorecase), ignorecase);
  333. }
  334. /*-------------------------------------------------------------------------*//**
  335. * <param name="m">The hash map to search.</param>
  336. * <param name="str">The key to find.</param>
  337. * <param name="hash">The hashed key.</param>
  338. * <returns>
  339. * The value associated with this key in the given hash map.
  340. * </returns>
  341. *//*------------------------------------------------------------------------**/
  342. stock HashMap_GetWithHash(HashMap:m<>, const str[], hash, bool:ignorecase = false)
  343. {
  344. P:3("HashMap_Get called: mask = %d (%d)", hash, hash & 0xFF);
  345. new
  346. prev = ref(m[hash & 0xFF]),
  347. next;
  348. static
  349. res;
  350. for ( ; ; )
  351. {
  352. #emit LREF.S.pri prev
  353. #emit STOR.S.pri next
  354. if (!next)
  355. break;
  356. {}
  357. #emit LREF.S.pri next
  358. #emit STOR.pri res
  359. if (hash == res)
  360. {
  361. // It doesn't matter which way we go on matching hashes, as long
  362. // as the direction is consistent.
  363. #emit PUSH.C 0x7FFFFFFF
  364. #emit PUSH.S ignorecase
  365. #emit PUSH.S str
  366. #emit LOAD.S.pri m
  367. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  368. #emit LOAD.I
  369. #emit LOAD.S.alt next
  370. #emit SUB.alt
  371. #emit PUSH.pri
  372. #emit PUSH.C 16
  373. #emit SYSREQ.C strcmp
  374. #emit STACK 20
  375. #emit STOR.pri res
  376. if (res == 0)
  377. {
  378. return (next - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2];
  379. }
  380. else if (res < 0) // Lower, move left.
  381. prev = next + HASH_MAP_DIR_LEFT;
  382. else // Higher, move right.
  383. prev = next + HASH_MAP_DIR_RIGHT;
  384. }
  385. else if (hash < res) // Lower, move left.
  386. prev = next + HASH_MAP_DIR_LEFT;
  387. else // Higher, move right.
  388. prev = next + HASH_MAP_DIR_RIGHT;
  389. }
  390. return -1;
  391. }
  392. /*-------------------------------------------------------------------------*//**
  393. * <param name="m">The hash map to modify.</param>
  394. * <param name="str">The key to remove from the hash map.</param>
  395. * <remarks>
  396. * Removes a given key and its associated value from the given hash map (if it
  397. * can be found in the map in the first place).
  398. * </remarks>
  399. *//*------------------------------------------------------------------------**/
  400. stock bool:HashMap_RemoveKeyWithHash(HashMap:m<>, const str[], hash, bool:ignorecase = false)
  401. {
  402. // First, find the key and it's parent.
  403. new
  404. prev = ref(m[hash & 0xFF]),
  405. next;
  406. static
  407. res;
  408. // First, find this key in the hashmap.
  409. for ( ; ; )
  410. {
  411. #emit LREF.S.pri prev
  412. #emit STOR.S.pri next
  413. if (!next)
  414. return false;
  415. {}
  416. #emit LREF.S.pri next
  417. #emit STOR.pri res
  418. if (hash == res)
  419. {
  420. // It doesn't matter which way we go on matching hashes, as long
  421. // as the direction is consistent.
  422. #emit PUSH.C 0x7FFFFFFF
  423. #emit PUSH.S ignorecase
  424. #emit PUSH.S str
  425. #emit LOAD.S.pri m
  426. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  427. #emit LOAD.I
  428. #emit LOAD.S.alt next
  429. #emit SUB.alt
  430. #emit PUSH.pri
  431. #emit PUSH.C 16
  432. #emit SYSREQ.C strcmp
  433. #emit STACK 20
  434. #emit STOR.pri res
  435. if (res == 0)
  436. {
  437. break;
  438. }
  439. else if (res < 0) // Lower, move left.
  440. prev = next + HASH_MAP_DIR_LEFT;
  441. else // Higher, move right.
  442. prev = next + HASH_MAP_DIR_RIGHT;
  443. }
  444. else if (hash < res) // Lower, move left.
  445. prev = next + HASH_MAP_DIR_LEFT;
  446. else // Higher, move right.
  447. prev = next + HASH_MAP_DIR_RIGHT;
  448. }
  449. // The LEFT/RIGHT apparent swap below is correct. We want the right-most
  450. // value on the left branch, and vice-versa.
  451. new
  452. left = AMX_Read(next + HASH_MAP_DIR_LEFT),
  453. right = AMX_Read(next + HASH_MAP_DIR_RIGHT);
  454. // Find an empty branch, or the smallest branch.
  455. if (!left)
  456. {
  457. if (!right)
  458. AMX_Write(prev, 0);
  459. else
  460. AMX_Write(prev, right);
  461. }
  462. else if (!right)
  463. {
  464. AMX_Write(prev, left);
  465. }
  466. else
  467. {
  468. new
  469. lHeight = 1,
  470. rHeight = 1,
  471. lParent = 0,
  472. rParent = 0,
  473. lVal = HashMap_GetBranchEnd(left, lHeight, lParent, HASH_MAP_DIR_RIGHT),
  474. rVal = HashMap_GetBranchEnd(right, rHeight, rParent, HASH_MAP_DIR_LEFT);
  475. if (lHeight < rHeight)
  476. {
  477. // Reduce the right (larger) branch. If this branch is bigger, and
  478. // no branch is empty, then this branch MUST be at least two nodes
  479. // high, and so MUST have a parent.
  480. AMX_Write(prev, rVal),
  481. AMX_Write(rParent + HASH_MAP_DIR_LEFT, AMX_Read(rVal + HASH_MAP_DIR_RIGHT)),
  482. AMX_Write(rVal + HASH_MAP_DIR_RIGHT, right),
  483. AMX_Write(rVal + HASH_MAP_DIR_LEFT, left);
  484. }
  485. else
  486. {
  487. // Reduce the left (larger) branch. This branch MAY NOT have a
  488. // parent, if both branches are equal at one node high.
  489. AMX_Write(prev, lVal);
  490. if (lParent)
  491. {
  492. AMX_Write(lParent + HASH_MAP_DIR_RIGHT, AMX_Read(lVal + HASH_MAP_DIR_LEFT)),
  493. AMX_Write(lVal + HASH_MAP_DIR_RIGHT, right),
  494. AMX_Write(lVal + HASH_MAP_DIR_LEFT, left);
  495. }
  496. else
  497. {
  498. AMX_Write(lVal + HASH_MAP_DIR_RIGHT, right),
  499. AMX_Write(lVal + HASH_MAP_DIR_LEFT, 0);
  500. }
  501. }
  502. }
  503. // Clear the removed node's pointers and string.
  504. AMX_Write(next + HASH_MAP_DIR_LEFT, 0),
  505. AMX_Write(next + HASH_MAP_DIR_RIGHT, 0),
  506. AMX_Write(next - m[HASH_MAP_SIZE_3], 0);
  507. return true;
  508. }
  509. stock bool:HashMap_RemoveKey(HashMap:m<>, const str[], bool:ignorecase = false)
  510. {
  511. return HashMap_RemoveKeyWithHash(m, str, YHash(str, .sensitive = !ignorecase), ignorecase);
  512. }
  513. static stock HashMap_GetBranchEnd(cur, &height, &parent, dir)
  514. {
  515. // Find the node as close in (hash) value to the current node as possible.
  516. static
  517. next;
  518. while ((next = AMX_Read(cur + dir)))
  519. {
  520. ++height,
  521. parent = cur,
  522. cur = next;
  523. }
  524. return cur;
  525. }
  526. /*-------------------------------------------------------------------------*//**
  527. * <param name="m">Hash map to modify.</param>
  528. * <param name="value">Value to remove.</param>
  529. * <remarks>
  530. * Removes a value from the hash map. First it gets the string key for the
  531. * value, then removes that (to update associated linked lists correctly).
  532. * </remarks>
  533. *//*------------------------------------------------------------------------**/
  534. stock bool:HashMap_RemoveValue(HashMap:m<>, value)
  535. {
  536. if (0 <= value < m[HASH_MAP_SIZE_1])
  537. {
  538. static
  539. sString[128 char];
  540. new
  541. size = m[HASH_MAP_SIZE_3];
  542. rawMemcpy(ref(sString), m[HASH_MAP_PTR] + value * m[HASH_MAP_SIZE_2] - size, min(size, sizeof (sString) * 4));
  543. return HashMap_RemoveKey(m, sString);
  544. }
  545. return false;
  546. }
  547. /*-------------------------------------------------------------------------*//**
  548. * <param name="m">The hash map to modify.</param>
  549. * <param name="str">The key to modify.</param>
  550. * <param name="value">The new value for the given key.</param>
  551. * <remarks>
  552. * If this key is already in the hash map it is removed, and then the new value
  553. * is added in its place. If the string already exists, its associated data is
  554. * removed. If the value already exists, it is removed as well.
  555. * </remarks>
  556. *//*------------------------------------------------------------------------**/
  557. stock HashMap_Set(HashMap:m<>, const str[], const value)
  558. {
  559. return
  560. HashMap_RemoveKey(m, str),
  561. HashMap_RemoveValue(m, value),
  562. HashMap_Add(m, str, value);
  563. }