impl.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /**--------------------------------------------------------------------------**\
  2. ====================================
  3. y_hashmap - Link strings to values
  4. ====================================
  5. Description:
  6. Maps string indexes to integer indexes. Uses a fast hash to get an array
  7. slot, then a linked list to resolve collisions.
  8. Legal:
  9. Version: MPL 1.1
  10. The contents of this file are subject to the Mozilla Public License Version
  11. 1.1 (the "License"); you may not use this file except in compliance with
  12. the License. You may obtain a copy of the License at
  13. http://www.mozilla.org/MPL/
  14. Software distributed under the License is distributed on an "AS IS" basis,
  15. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  16. for the specific language governing rights and limitations under the
  17. License.
  18. The Original Code is the YSI hashmap include.
  19. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  20. Portions created by the Initial Developer are Copyright (C) 2011
  21. the Initial Developer. All Rights Reserved.
  22. Contributors:
  23. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  24. Thanks:
  25. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  26. ZeeX - Very productive conversations.
  27. koolk - IsPlayerinAreaEx code.
  28. TheAlpha - Danish translation.
  29. breadfish - German translation.
  30. Fireburn - Dutch translation.
  31. yom - French translation.
  32. 50p - Polish translation.
  33. Zamaroht - Spanish translation.
  34. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  35. for me to strive to better.
  36. Pixels^ - Running XScripters where the idea was born.
  37. Matite - Pestering me to release it and using it.
  38. Very special thanks to:
  39. Thiadmer - PAWN, whose limits continue to amaze me!
  40. Kye/Kalcor - SA:MP.
  41. SA:MP Team past, present and future - SA:MP.
  42. Version:
  43. 1.0
  44. Changelog:
  45. 23/06/13:
  46. First version.
  47. Functions:
  48. stock:
  49. HashMap_Init - Associate a hash map with an array.
  50. HashMap_Add - Add a value under a given string.
  51. HashMap_Get - Get a value from a string.
  52. HashMap_RemoveKey - Remove a string and its value from a hash map.
  53. HashMap_RemoveValue - Remove a value from a hash map.
  54. HashMap_Set - Change the value associated with a key.
  55. Definitions:
  56. HASH_MAP_DATA - What should be added to enums to be hash map referenced.
  57. HashMap - Declare a new hash map.
  58. \**--------------------------------------------------------------------------**/
  59. #define HASH_MAP_SIZE (256)
  60. #define HASH_MAP_PTR (HASH_MAP_SIZE)
  61. #define HASH_MAP_SIZE_1 (HASH_MAP_SIZE + 1)
  62. #define HASH_MAP_SIZE_2 (HASH_MAP_SIZE + 2)
  63. #define HASH_MAP_SIZE_3 (HASH_MAP_SIZE + 3)
  64. #define HashMap:%0<%1> %0[HASH_MAP_SIZE + 4]
  65. #define HASH_MAP_DATA (2)
  66. /**--------------------------------------------------------------------------**\
  67. <summary>HashMap_Hash</summary>
  68. <param name="str[]">String to hash.</param>
  69. <param name="&hash">Desination of the hash.</param>
  70. <returns>
  71. -
  72. </returns>
  73. <remarks>
  74. Quickly hashes the string using Bernstein. Caters for both packed and
  75. unpacked strings.
  76. </remarks>
  77. \**--------------------------------------------------------------------------**/
  78. #define HashMap_Hash(%0,%1) (%1=YHash(%0))
  79. /**--------------------------------------------------------------------------**\
  80. <summary>HashMap_Init</summary>
  81. <param name="HashMap:m<>">Hash map to initialise.</param>
  82. <param name="&target">Address of the hashmap data.</param>
  83. <param name="size1">Number of entries.</param>
  84. <param name="size2">Total Size of each entry IN BYTES.</param>
  85. <param name="&t2">Address of the name AND data start.</param>
  86. <returns>
  87. -
  88. </returns>
  89. <remarks>
  90. Finds the location of the hash map linked list data in the passed array data
  91. and uses that to read the data through pointers subsequently. It doesn't
  92. matter WHERE in the enum the hash map data is, and if its not there you'll
  93. get an error, or at least a warning.
  94. </remarks>
  95. \**--------------------------------------------------------------------------**/
  96. stock _HashMap_Init(HashMap:m<>, &target, size1, size2, &t2)
  97. {
  98. static
  99. HashMap:sInit<> = {-1, ...};
  100. m = sInit;
  101. new
  102. ptr;
  103. // Save the pointer.
  104. #emit LOAD.S.pri target
  105. #emit STOR.S.pri ptr
  106. m[HASH_MAP_PTR] = ptr,
  107. // Store the number of elements in the array.
  108. m[HASH_MAP_SIZE_1] = size1,
  109. // Store the size of each element.
  110. m[HASH_MAP_SIZE_2] = size2;
  111. // Set up the main array.
  112. while (size1--)
  113. {
  114. AMX_Write(ptr, -1),
  115. ptr += size2;
  116. }
  117. #emit LOAD.S.pri target
  118. #emit LOAD.S.alt t2
  119. #emit SUB
  120. #emit STOR.S.pri ptr
  121. // Store the size of "_E_HASH_MAP_NAME" in bytes.
  122. m[HASH_MAP_SIZE_3] = ptr;
  123. }
  124. // Uses "%2 - %2" to make a tagged 0 without knowing the tag.
  125. #define HashMap_Init(%0,%1,%2) _HashMap_Init(%0, %1[0][(%2)], sizeof (%1), sizeof (%1[]) * 4, %1[0][(%2) - (%2)])
  126. /**--------------------------------------------------------------------------**\
  127. <summary>HashMap_ByteLen</summary>
  128. <param name="str[]">String to get the size of.</param>
  129. <returns>
  130. The number of BYTES this string takes up including the NULL.
  131. </returns>
  132. <remarks>
  133. Caters for both packed and unpacked strings. The weirdness is basically
  134. just: "ispacked(str) ? (* 1) : (* 4)".
  135. </remarks>
  136. \**--------------------------------------------------------------------------**/
  137. #define HashMap_ByteLen(%0) ((strlen((%0)) + 1) << (2 * _:!ispacked((%0))))
  138. /**--------------------------------------------------------------------------**\
  139. <summary>HashMap_Add</summary>
  140. <param name="HashMap:m<>"></param>
  141. <param name="const str[]"></param>
  142. <param name="const value"></param>
  143. <returns>
  144. -
  145. </returns>
  146. <remarks>
  147. Adds a value to the given hash map under the given string key.
  148. </remarks>
  149. \**--------------------------------------------------------------------------**/
  150. stock bool:HashMap_Add(HashMap:m<>, const str[], const value)
  151. {
  152. P:3("HashMap_Add called: %d <= %d < %d", 0, value, m[HASH_MAP_SIZE_1]);
  153. if (0 <= value < m[HASH_MAP_SIZE_1])
  154. {
  155. new
  156. ptr = m[HASH_MAP_PTR] + value * m[HASH_MAP_SIZE_2];
  157. P:6("HashMap_Add: used = %d", AMX_Read(ptr));
  158. if (AMX_Read(ptr) != -1) return false;
  159. static
  160. hash,
  161. mask;
  162. HashMap_Hash(str, hash);
  163. P:5("HashMap_Add: mask = %d", hash & 0xFF);
  164. mask = hash & 0xFF,
  165. // Add this hash to the hash list.
  166. AMX_Write(ptr, m[mask]),
  167. m[mask] = ptr,
  168. // Get the hashed string destination size.
  169. mask = m[HASH_MAP_SIZE_3],
  170. rawMemcpy(ptr - mask, ref(str), mask),
  171. // Copy the hashed value.
  172. AMX_Write(ptr + 4, hash);
  173. return true;
  174. }
  175. return false;
  176. }
  177. /**--------------------------------------------------------------------------**\
  178. <summary>HashMap_Get</summary>
  179. <param name="HashMap:m<>">The hash map to search.</param>
  180. <param name="const str[]">The key to find.</param>
  181. <returns>
  182. The value associated with this key in the given hash map.
  183. </returns>
  184. <remarks>
  185. -
  186. </remarks>
  187. \**--------------------------------------------------------------------------**/
  188. stock HashMap_Get(HashMap:m<>, const str[], bool:ignorecase = false)
  189. {
  190. static
  191. hash,
  192. res;
  193. HashMap_Hash(str, hash);
  194. res = hash & 0xFF;
  195. P:3("HashMap_Get called: \"%s\" mask = %d", str, res);
  196. for (new ptr = m[res]; ptr != -1; )
  197. {
  198. #emit LOAD.S.pri ptr
  199. #emit ADD.C 4
  200. #emit LOAD.I
  201. #emit STOR.pri res
  202. if (res == hash)
  203. {
  204. P:6("HashMap_Get: Candidate %d: %d == %d", (ptr - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2], AMX_Read(ptr + 4), hash);
  205. // Maybe collisions.
  206. #emit PUSH.C 0x7FFFFFFF
  207. #emit PUSH.S ignorecase
  208. #emit PUSH.S str
  209. #emit LOAD.S.pri m
  210. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  211. #emit LOAD.I
  212. #emit LOAD.S.alt ptr
  213. #emit SUB.alt
  214. #emit PUSH.pri
  215. #emit PUSH.C 16
  216. #emit SYSREQ.C strcmp
  217. #emit STACK 20
  218. #emit STOR.pri res
  219. /*new dest[32];
  220. static const sss[] = "str = %s";
  221. // printf
  222. #emit LOAD.S.pri m
  223. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  224. #emit LOAD.I
  225. #emit LOAD.S.alt ptr
  226. #emit SUB.alt
  227. #emit PUSH.pri
  228. #emit PUSH.C sss
  229. #emit PUSH.C 8
  230. #emit SYSREQ.C printf
  231. #emit STACK 12
  232. // strunpack
  233. #emit PUSH.C 32
  234. #emit LOAD.S.pri m
  235. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  236. #emit LOAD.I
  237. #emit LOAD.S.alt ptr
  238. #emit SUB.alt
  239. #emit PUSH.pri
  240. #emit ADDR.pri dest
  241. #emit PUSH.pri
  242. #emit PUSH.C 12
  243. #emit SYSREQ.C strunpack
  244. #emit STACK 16
  245. printf("%s = %d", dest, res);*/
  246. if (res == 0) return (ptr - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2];
  247. }
  248. {} // Zero-cost bug fix.
  249. #emit LREF.S.pri ptr
  250. #emit STOR.S.pri ptr
  251. }
  252. return -1;
  253. }
  254. /**--------------------------------------------------------------------------**\
  255. <summary>HashMap_GetWithHash</summary>
  256. <param name="HashMap:m<>">The hash map to search.</param>
  257. <param name="const str[]">The key to find.</param>
  258. <param name="hash">The hashed key.</param>
  259. <returns>
  260. The value associated with this key in the given hash map.
  261. </returns>
  262. <remarks>
  263. -
  264. </remarks>
  265. \**--------------------------------------------------------------------------**/
  266. stock HashMap_GetWithHash(HashMap:m<>, const str[], hash, bool:ignorecase = false)
  267. {
  268. static
  269. res;
  270. res = hash & 0xFF;
  271. P:3("HashMap_Get called: mask = %d", res);
  272. for (new ptr = m[res]; ptr != -1; )
  273. {
  274. #emit LOAD.S.pri ptr
  275. #emit ADD.C 4
  276. #emit LOAD.I
  277. #emit STOR.pri res
  278. if (res == hash)
  279. {
  280. P:6("HashMap_Get: Candidate %d: %d == %d", (ptr - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2], AMX_Read(ptr + 4), hash);
  281. // Maybe collisions.
  282. #emit PUSH.C 0x7FFFFFFF
  283. #emit PUSH.S ignorecase
  284. #emit PUSH.S str
  285. #emit LOAD.S.pri m
  286. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  287. #emit LOAD.I
  288. #emit LOAD.S.alt ptr
  289. #emit SUB.alt
  290. #emit PUSH.pri
  291. #emit PUSH.C 16
  292. #emit SYSREQ.C strcmp
  293. #emit STACK 20
  294. #emit STOR.pri res
  295. if (res == 0) return (ptr - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2];
  296. }
  297. {} // Zero-cost bug fix.
  298. #emit LREF.S.pri ptr
  299. #emit STOR.S.pri ptr
  300. }
  301. return -1;
  302. }
  303. /**--------------------------------------------------------------------------**\
  304. <summary>HashMap_RemoveKey</summary>
  305. <param name="HashMap:m<>">The hash map to modify.</param>
  306. <param name="const str[]">The key to remove from the hash map.</param>
  307. <returns>
  308. -
  309. </returns>
  310. <remarks>
  311. Removes a given key and its associated value from the given hash map (if it
  312. can be found in the map in the first place).
  313. </remarks>
  314. \**--------------------------------------------------------------------------**/
  315. stock bool:HashMap_RemoveKey(HashMap:m<>, const str[])
  316. {
  317. static
  318. hash,
  319. res,
  320. prev;
  321. HashMap_Hash(str, hash);
  322. res = hash & 0xFF,
  323. prev = ref(m[res]);
  324. P:3("HashMap_RemoveKey called: mask = %d", res);
  325. for (new ptr = AMX_Read(prev); ptr != -1; )
  326. {
  327. #emit LOAD.S.pri ptr
  328. #emit ADD.C 4
  329. #emit LOAD.I
  330. #emit STOR.pri res
  331. if (res == hash)
  332. {
  333. P:6("HashMap_RemoveKey: Candidate %d: %d == %d", (ptr - m[HASH_MAP_PTR]) / m[HASH_MAP_SIZE_2], AMX_Read(ptr + 4), hash);
  334. // Maybe collisions.
  335. #emit PUSH.C 0x7FFFFFFF
  336. #emit PUSH.C 0
  337. #emit PUSH.S str
  338. #emit LOAD.S.pri m
  339. #emit ADD.C 1036 // 256 * 4 + 3 * 4
  340. #emit LOAD.I
  341. #emit LOAD.S.alt ptr
  342. #emit SUB.alt
  343. #emit PUSH.pri
  344. #emit PUSH.C 16
  345. #emit SYSREQ.C strcmp
  346. #emit STACK 20
  347. #emit STOR.pri res
  348. if (res == 0)
  349. {
  350. // Remove this from the linked list.
  351. AMX_Write(prev, AMX_Read(ptr)),
  352. AMX_Write(ptr, -1),
  353. AMX_Write(ptr - m[HASH_MAP_SIZE_3], 0);
  354. return true;
  355. }
  356. }
  357. prev = ptr;
  358. #emit LREF.S.pri ptr
  359. #emit STOR.S.pri ptr
  360. }
  361. return false;
  362. }
  363. /**--------------------------------------------------------------------------**\
  364. <summary>HashMap_RemoveValue</summary>
  365. <param name="HashMap:m<>">Hash map to modify.</param>
  366. <param name="value">Value to remove.</param>
  367. <returns>
  368. -
  369. </returns>
  370. <remarks>
  371. Removes a value from the hash map. First it gets the string key for the
  372. value, then removes that (to update associated linked lists correctly).
  373. </remarks>
  374. \**--------------------------------------------------------------------------**/
  375. stock bool:HashMap_RemoveValue(HashMap:m<>, value)
  376. {
  377. if (0 <= value < m[HASH_MAP_SIZE_1])
  378. {
  379. static
  380. sString[128 char];
  381. new
  382. size = m[HASH_MAP_SIZE_3];
  383. rawMemcpy(ref(sString), m[HASH_MAP_PTR] + value * m[HASH_MAP_SIZE_2] - size, min(size, sizeof (sString) * 4));
  384. return HashMap_RemoveKey(m, sString);
  385. }
  386. return false;
  387. }
  388. /**--------------------------------------------------------------------------**\
  389. <summary>HashMap_Set</summary>
  390. <param name="HashMap:m<>">The hash map to modify.</param>
  391. <param name="const str[]">The key to modify.</param>
  392. <param name="const value">The new value for the given key.</param>
  393. <returns>
  394. -
  395. </returns>
  396. <remarks>
  397. If this key is already in the hash map it is removed, and then the new value
  398. is added in its place. If the string already exists, its associated data is
  399. removed. If the value already exists, it is removed as well.
  400. </remarks>
  401. \**--------------------------------------------------------------------------**/
  402. stock HashMap_Set(HashMap:m<>, const str[], const value)
  403. {
  404. return
  405. HashMap_RemoveKey(m, str),
  406. HashMap_RemoveValue(m, value),
  407. HashMap_Add(m, str, value);
  408. }