FieldLookup.hx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. * Copyright (C)2005-2019 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. @:nativeGen @:keep
  25. final 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:Dynamic, 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. @:classCode("#pragma warning disable 628\n")
  39. @:nativeGen @:keep @:static
  40. final 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. var ids = fieldIds;
  121. var min = 0;
  122. var max = length;
  123. while (min < max)
  124. {
  125. var mid = min + Std.int((max - min) / 2);
  126. var imid = ids[mid];
  127. if (key < imid)
  128. {
  129. max = mid;
  130. } else if (key > imid) {
  131. min = mid + 1;
  132. } else {
  133. return fields[mid];
  134. }
  135. }
  136. //if not found, it's definitely an error
  137. throw "Field not found for hash " + key;
  138. }
  139. public static function hash(s:String):Int
  140. {
  141. if (s == null) return 0;
  142. var key = doHash(s);
  143. var ids = fieldIds,
  144. fld = fields;
  145. var min = 0;
  146. var max = length;
  147. var len = length;
  148. while (min < max)
  149. {
  150. var mid = Std.int(min + (max - min) / 2); //overflow safe
  151. var imid = ids[mid];
  152. if (key < imid)
  153. {
  154. max = mid;
  155. } else if (key > imid) {
  156. min = mid + 1;
  157. } else {
  158. var field = fld[mid];
  159. if (field != s)
  160. return ~key; //special case
  161. return key;
  162. }
  163. }
  164. //if not found, min holds the value where we should insert the key
  165. //ensure thread safety:
  166. cs.Lib.lock(FieldLookup, {
  167. if (len != length) //race condition which will very rarely happen - other thread modified sooner.
  168. return hash(s); //since we already own the lock, this second try will always succeed
  169. fieldIds = insertInt(fieldIds, length, min, key);
  170. fields = insertString(fields, length, min, s);
  171. ++length;
  172. });
  173. return key;
  174. }
  175. public static function findHash(hash:Int, hashs:cs.NativeArray<Int>, length:Int):Int
  176. {
  177. var min = 0;
  178. var max = length;
  179. while (min < max)
  180. {
  181. var mid = Std.int((max + min) / 2);
  182. var imid = hashs[mid];
  183. if (hash < imid)
  184. {
  185. max = mid;
  186. } else if (hash > imid) {
  187. min = mid + 1;
  188. } else {
  189. return mid;
  190. }
  191. }
  192. //if not found, return a negative value of where it should be inserted
  193. return ~min;
  194. }
  195. public static function removeInt(a:cs.NativeArray<Int>, length:Int, pos:Int)
  196. {
  197. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  198. a[length - 1] = 0;
  199. }
  200. public static function removeFloat(a:cs.NativeArray<Float>, length:Int, pos:Int)
  201. {
  202. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  203. a[length - 1] = 0;
  204. }
  205. public static function removeDynamic(a:cs.NativeArray<Dynamic>, 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. extern
  211. static inline function __insert<T>(a:cs.NativeArray<T>, length:Int, pos:Int, x:T):cs.NativeArray<T>
  212. {
  213. var capacity = a.Length;
  214. if (pos == length)
  215. {
  216. if (capacity == length)
  217. {
  218. var newarr = new NativeArray((length << 1) + 1);
  219. a.CopyTo(newarr, 0);
  220. a = newarr;
  221. }
  222. }
  223. else if (pos == 0)
  224. {
  225. if (capacity == length)
  226. {
  227. var newarr = new NativeArray((length << 1) + 1);
  228. cs.system.Array.Copy(a, 0, newarr, 1, length);
  229. a = newarr;
  230. }
  231. else
  232. {
  233. cs.system.Array.Copy(a, 0, a, 1, length);
  234. }
  235. }
  236. else
  237. {
  238. if (capacity == length)
  239. {
  240. var newarr = new NativeArray((length << 1) + 1);
  241. cs.system.Array.Copy(a, 0, newarr, 0, pos);
  242. cs.system.Array.Copy(a, pos, newarr, pos + 1, length - pos);
  243. a = newarr;
  244. }
  245. else
  246. {
  247. cs.system.Array.Copy(a, pos, a, pos + 1, length - pos);
  248. cs.system.Array.Copy(a, 0, a, 0, pos);
  249. }
  250. }
  251. a[pos] = x;
  252. return a;
  253. }
  254. public static function insertInt(a:cs.NativeArray<Int>, length:Int, pos:Int, x:Int):cs.NativeArray<Int> return __insert(a, length, pos, x);
  255. public static function insertFloat(a:cs.NativeArray<Float>, length:Int, pos:Int, x:Float):cs.NativeArray<Float> return __insert(a, length, pos, x);
  256. public static function insertDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<Dynamic> return __insert(a, length, pos, x);
  257. public static function insertString(a:cs.NativeArray<String>, length:Int, pos:Int, x:String):cs.NativeArray<String> return __insert(a, length, pos, x);
  258. public static function getHashConflict(head:FieldHashConflict, hash:Int, name:String):FieldHashConflict {
  259. while (head != null) {
  260. if (head.hash == hash && head.name == name) {
  261. return head;
  262. }
  263. head = head.next;
  264. }
  265. return null;
  266. }
  267. public static function setHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String, value:Dynamic):Void {
  268. var node = head;
  269. while (node != null) {
  270. if (node.hash == hash && node.name == name) {
  271. node.value = value;
  272. return;
  273. }
  274. node = node.next;
  275. }
  276. head = new FieldHashConflict(hash, name, value, head);
  277. }
  278. public static function deleteHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String):Bool {
  279. // no conflicting fields at all
  280. if (head == null) {
  281. return false;
  282. }
  283. // list head is conflicting - just point it to the next one
  284. if (head.hash == hash && head.name == name) {
  285. head = head.next;
  286. return true;
  287. }
  288. // loop through the list, removing node if there's one
  289. var prev = head, node = head.next;
  290. while (node != null) {
  291. if (node.hash == hash && node.name == name) {
  292. prev.next = node.next;
  293. return true;
  294. }
  295. node = node.next;
  296. }
  297. // not found
  298. return false;
  299. }
  300. public static function addHashConflictNames(head:FieldHashConflict, arr:Array<String>):Void {
  301. while (head != null) {
  302. arr.push(head.name);
  303. head = head.next;
  304. }
  305. }
  306. }