FieldLookup.hx 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. * Copyright (C)2005-2017 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package cs.internal;
  23. @:native('haxe.lang.FieldHashConflict')
  24. @:final @:nativeGen
  25. @:keep class FieldHashConflict {
  26. @:readOnly public var hash(default,never):Int;
  27. @:readOnly public var name(default,never):String;
  28. public var value:Dynamic;
  29. public var next:FieldHashConflict;
  30. public function new(hash, name, value, next) {
  31. untyped this.hash = hash;
  32. untyped this.name = name;
  33. this.value = value;
  34. this.next = next;
  35. }
  36. }
  37. @:native('haxe.lang.FieldLookup')
  38. @:final @:nativeGen
  39. @:classCode("#pragma warning disable 628\n")
  40. @:keep @:static class FieldLookup
  41. {
  42. @:protected private static var fieldIds:cs.NativeArray<Int>;
  43. @:protected private static var fields:cs.NativeArray<String>;
  44. @:protected private static var length:Int;
  45. static function __init__()
  46. {
  47. length = fieldIds.Length;
  48. }
  49. private static function addFields(nids:cs.NativeArray<Int>, nfields:cs.NativeArray<String>):Void
  50. {
  51. // first see if we need to add anything
  52. var cids = fieldIds,
  53. cfields = fields;
  54. var nlen = nids.Length;
  55. var clen = length;
  56. if (nfields.Length != nlen) throw 'Different fields length: $nlen and ${nfields.Length}';
  57. //TODO optimize
  58. var needsChange = false;
  59. for (i in nids)
  60. {
  61. if (findHash(i, cids, clen) < 0)
  62. {
  63. needsChange = true;
  64. break;
  65. }
  66. }
  67. // if we do, lock and merge
  68. if (needsChange)
  69. {
  70. cs.Lib.lock(FieldLookup, {
  71. // trace(cs.Lib.array(nids), cs.Lib.array(cids));
  72. var ansIds = new cs.NativeArray(clen + nlen),
  73. ansFields = new cs.NativeArray(clen + nlen);
  74. var ci = 0, ni = 0, ansi = 0;
  75. while (ci < clen && ni < nlen)
  76. {
  77. if (cids[ci] < nids[ni])
  78. {
  79. ansIds[ansi] = cids[ci];
  80. ansFields[ansi] = cfields[ci];
  81. ++ci;
  82. } else {
  83. ansIds[ansi] = nids[ni];
  84. ansFields[ansi] = nfields[ni];
  85. ++ni;
  86. }
  87. ++ansi;
  88. }
  89. if (ci < clen)
  90. {
  91. cs.system.Array.Copy(cids, ci, ansIds, ansi, clen - ci);
  92. cs.system.Array.Copy(cfields, ci, ansFields, ansi, clen - ci);
  93. ansi += clen - ci;
  94. }
  95. if (ni < nlen)
  96. {
  97. cs.system.Array.Copy(nids, ni, ansIds, ansi, nlen - ni);
  98. cs.system.Array.Copy(nfields, ni, ansFields, ansi, nlen - ni);
  99. ansi += nlen - ni;
  100. }
  101. // trace(cs.Lib.array(ansIds));
  102. fieldIds = ansIds;
  103. fields = ansFields;
  104. length = ansi;
  105. });
  106. }
  107. }
  108. //s cannot be null here
  109. private static inline function doHash(s:String):Int
  110. {
  111. var acc = 0; //alloc_int
  112. for (i in 0...s.length)
  113. {
  114. acc = (( 223 * (acc >> 1) + cast(s[i], Int)) << 1);
  115. }
  116. return acc >>> 1; //always positive
  117. }
  118. public static function lookupHash(key:Int):String
  119. {
  120. //start of binary search algorithm
  121. var ids = fieldIds;
  122. var min = 0;
  123. var max = length;
  124. while (min < max)
  125. {
  126. var mid = min + Std.int((max - min) / 2);
  127. var imid = ids[mid];
  128. if (key < imid)
  129. {
  130. max = mid;
  131. } else if (key > imid) {
  132. min = mid + 1;
  133. } else {
  134. return fields[mid];
  135. }
  136. }
  137. //if not found, it's definitely an error
  138. throw "Field not found for hash " + key;
  139. }
  140. public static function hash(s:String):Int
  141. {
  142. if (s == null) return 0;
  143. var key = doHash(s);
  144. //start of binary search algorithm
  145. var ids = fieldIds,
  146. fld = fields;
  147. var min = 0;
  148. var max = length;
  149. var len = length;
  150. while (min < max)
  151. {
  152. var mid = Std.int(min + (max - min) / 2); //overflow safe
  153. var imid = ids[mid];
  154. if (key < imid)
  155. {
  156. max = mid;
  157. } else if (key > imid) {
  158. min = mid + 1;
  159. } else {
  160. var field = fld[mid];
  161. if (field != s)
  162. return ~key; //special case
  163. return key;
  164. }
  165. }
  166. //if not found, min holds the value where we should insert the key
  167. //ensure thread safety:
  168. cs.Lib.lock(FieldLookup, {
  169. if (len != length) //race condition which will very rarely happen - other thread modified sooner.
  170. return hash(s); //since we already own the lock, this second try will always succeed
  171. #if erase_generics
  172. fieldIds = insertInt(fieldIds, length, min, key);
  173. fields = insertString(fields, length, min, s);
  174. #else
  175. insert(fieldIds, length, min, key);
  176. insert(fields, length, min, s);
  177. // ids.insert(min, key);
  178. // fields.insert(min, s);
  179. #end
  180. ++length;
  181. });
  182. return key;
  183. }
  184. public static function findHash(hash:Int, hashs:cs.NativeArray<Int>, length:Int):Int
  185. {
  186. var min = 0;
  187. var max = length;
  188. while (min < max)
  189. {
  190. var mid = Std.int((max + min) / 2); //overflow safe
  191. var imid = hashs[mid];
  192. if (hash < imid)
  193. {
  194. max = mid;
  195. } else if (hash > imid) {
  196. min = mid + 1;
  197. } else {
  198. return mid;
  199. }
  200. }
  201. //if not found, return a negative value of where it should be inserted
  202. return ~min;
  203. }
  204. #if !erase_generics
  205. static function remove<T>(a:cs.NativeArray<T>, length:Int, pos:Int)
  206. {
  207. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  208. a[length - 1] = null;
  209. }
  210. static function insert<T>(a:cs.Ref<cs.NativeArray<T>>, length:Int, pos:Int, x:T)
  211. {
  212. var capacity = a.Length;
  213. if (pos == length)
  214. {
  215. if (capacity == length)
  216. {
  217. var newarr = new NativeArray((length << 1) + 1);
  218. a.CopyTo(newarr, 0);
  219. a = newarr;
  220. }
  221. }
  222. else if (pos == 0)
  223. {
  224. if (capacity == length)
  225. {
  226. var newarr = new NativeArray((length << 1) + 1);
  227. cs.system.Array.Copy(a, 0, newarr, 1, length);
  228. a = newarr;
  229. }
  230. else
  231. {
  232. cs.system.Array.Copy(a, 0, a, 1, length);
  233. }
  234. }
  235. else
  236. {
  237. if (capacity == length)
  238. {
  239. var newarr = new NativeArray((length << 1) + 1);
  240. cs.system.Array.Copy(a, 0, newarr, 0, pos);
  241. cs.system.Array.Copy(a, pos, newarr, pos + 1, length - pos);
  242. a = newarr;
  243. }
  244. else
  245. {
  246. cs.system.Array.Copy(a, pos, a, pos + 1, length - pos);
  247. cs.system.Array.Copy(a, 0, a, 0, pos);
  248. }
  249. }
  250. a[pos] = x;
  251. }
  252. #else
  253. static function removeInt(a:cs.NativeArray<Int>, length:Int, pos:Int)
  254. {
  255. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  256. a[length - 1] = 0;
  257. }
  258. static function removeFloat(a:cs.NativeArray<Float>, length:Int, pos:Int)
  259. {
  260. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  261. a[length - 1] = 0;
  262. }
  263. static function removeDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int)
  264. {
  265. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  266. a[length - 1] = null;
  267. }
  268. @:extern
  269. static inline function __insert<T>(a:cs.NativeArray<T>, length:Int, pos:Int, x:T):cs.NativeArray<T>
  270. {
  271. var capacity = a.Length;
  272. if (pos == length)
  273. {
  274. if (capacity == length)
  275. {
  276. var newarr = new NativeArray((length << 1) + 1);
  277. a.CopyTo(newarr, 0);
  278. a = newarr;
  279. }
  280. }
  281. else if (pos == 0)
  282. {
  283. if (capacity == length)
  284. {
  285. var newarr = new NativeArray((length << 1) + 1);
  286. cs.system.Array.Copy(a, 0, newarr, 1, length);
  287. a = newarr;
  288. }
  289. else
  290. {
  291. cs.system.Array.Copy(a, 0, a, 1, length);
  292. }
  293. }
  294. else
  295. {
  296. if (capacity == length)
  297. {
  298. var newarr = new NativeArray((length << 1) + 1);
  299. cs.system.Array.Copy(a, 0, newarr, 0, pos);
  300. cs.system.Array.Copy(a, pos, newarr, pos + 1, length - pos);
  301. a = newarr;
  302. }
  303. else
  304. {
  305. cs.system.Array.Copy(a, pos, a, pos + 1, length - pos);
  306. cs.system.Array.Copy(a, 0, a, 0, pos);
  307. }
  308. }
  309. a[pos] = x;
  310. return a;
  311. }
  312. static function insertInt(a:cs.NativeArray<Int>, length:Int, pos:Int, x:Int):cs.NativeArray<Int> return __insert(a, length, pos, x);
  313. static function insertFloat(a:cs.NativeArray<Float>, length:Int, pos:Int, x:Float):cs.NativeArray<Float> return __insert(a, length, pos, x);
  314. static function insertDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<Dynamic> return __insert(a, length, pos, x);
  315. static function insertString(a:cs.NativeArray<String>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<String> return __insert(a, length, pos, x);
  316. #end
  317. static function getHashConflict(head:FieldHashConflict, hash:Int, name:String):FieldHashConflict {
  318. while (head != null) {
  319. if (head.hash == hash && head.name == name) {
  320. return head;
  321. }
  322. head = head.next;
  323. }
  324. return null;
  325. }
  326. static function setHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String, value:Dynamic):Void {
  327. var node = head;
  328. while (node != null) {
  329. if (node.hash == hash && node.name == name) {
  330. node.value = value;
  331. return;
  332. }
  333. node = node.next;
  334. }
  335. head = new FieldHashConflict(hash, name, value, head);
  336. }
  337. static function deleteHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String):Bool {
  338. // no conflicting fields at all
  339. if (head == null) {
  340. return false;
  341. }
  342. // list head is conflicting - just point it to the next one
  343. if (head.hash == hash && head.name == name) {
  344. head = head.next;
  345. return true;
  346. }
  347. // loop through the list, removing node if there's one
  348. var prev = head, node = head.next;
  349. while (node != null) {
  350. if (node.hash == hash && node.name == name) {
  351. prev.next = node.next;
  352. return true;
  353. }
  354. node = node.next;
  355. }
  356. // not found
  357. return false;
  358. }
  359. static function addHashConflictNames(head:FieldHashConflict, arr:Array<String>):Void {
  360. while (head != null) {
  361. arr.push(head.name);
  362. head = head.next;
  363. }
  364. }
  365. }