FieldLookup.hx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. @:protected private static var fieldIds:cs.NativeArray<Int>;
  42. @:protected private static var fields:cs.NativeArray<String>;
  43. @:protected private static var length:Int;
  44. static function __init__() {
  45. length = fieldIds.Length;
  46. }
  47. private static function addFields(nids:cs.NativeArray<Int>, nfields:cs.NativeArray<String>):Void {
  48. // first see if we need to add anything
  49. var cids = fieldIds, cfields = fields;
  50. var nlen = nids.Length;
  51. var clen = length;
  52. if (nfields.Length != nlen)
  53. throw 'Different fields length: $nlen and ${nfields.Length}';
  54. // TODO optimize
  55. var needsChange = false;
  56. for (i in nids) {
  57. if (findHash(i, cids, clen) < 0) {
  58. needsChange = true;
  59. break;
  60. }
  61. }
  62. // if we do, lock and merge
  63. if (needsChange) {
  64. cs.Lib.lock(FieldLookup, {
  65. // trace(cs.Lib.array(nids), cs.Lib.array(cids));
  66. var ansIds = new cs.NativeArray(clen + nlen),
  67. ansFields = new cs.NativeArray(clen + nlen);
  68. var ci = 0, ni = 0, ansi = 0;
  69. while (ci < clen && ni < nlen) {
  70. if (cids[ci] < nids[ni]) {
  71. ansIds[ansi] = cids[ci];
  72. ansFields[ansi] = cfields[ci];
  73. ++ci;
  74. } else {
  75. ansIds[ansi] = nids[ni];
  76. ansFields[ansi] = nfields[ni];
  77. ++ni;
  78. }
  79. ++ansi;
  80. }
  81. if (ci < clen) {
  82. cs.system.Array.Copy(cids, ci, ansIds, ansi, clen - ci);
  83. cs.system.Array.Copy(cfields, ci, ansFields, ansi, clen - ci);
  84. ansi += clen - ci;
  85. }
  86. if (ni < nlen) {
  87. cs.system.Array.Copy(nids, ni, ansIds, ansi, nlen - ni);
  88. cs.system.Array.Copy(nfields, ni, ansFields, ansi, nlen - ni);
  89. ansi += nlen - ni;
  90. }
  91. // trace(cs.Lib.array(ansIds));
  92. fieldIds = ansIds;
  93. fields = ansFields;
  94. length = ansi;
  95. });
  96. }
  97. }
  98. // s cannot be null here
  99. private static inline function doHash(s:String):Int {
  100. var acc = 0; // alloc_int
  101. for (i in 0...s.length) {
  102. acc = ((223 * (acc >> 1) + cast(s[i], Int)) << 1);
  103. }
  104. return acc >>> 1; // always positive
  105. }
  106. public static function lookupHash(key:Int):String {
  107. var ids = fieldIds;
  108. var min = 0;
  109. var max = length;
  110. while (min < max) {
  111. var mid = min + Std.int((max - min) / 2);
  112. var imid = ids[mid];
  113. if (key < imid) {
  114. max = mid;
  115. } else if (key > imid) {
  116. min = mid + 1;
  117. } else {
  118. return fields[mid];
  119. }
  120. }
  121. // if not found, it's definitely an error
  122. throw "Field not found for hash " + key;
  123. }
  124. public static function hash(s:String):Int {
  125. if (s == null)
  126. return 0;
  127. var key = doHash(s);
  128. var ids = fieldIds, fld = fields;
  129. var min = 0;
  130. var max = length;
  131. var len = length;
  132. while (min < max) {
  133. var mid = Std.int(min + (max - min) / 2); // overflow safe
  134. var imid = ids[mid];
  135. if (key < imid) {
  136. max = mid;
  137. } else if (key > imid) {
  138. min = mid + 1;
  139. } else {
  140. var field = fld[mid];
  141. if (field != s)
  142. return ~key; // special case
  143. return key;
  144. }
  145. }
  146. // if not found, min holds the value where we should insert the key
  147. // ensure thread safety:
  148. cs.Lib.lock(FieldLookup, {
  149. if (len != length) // race condition which will very rarely happen - other thread modified sooner.
  150. return hash(s); // since we already own the lock, this second try will always succeed
  151. fieldIds = insertInt(fieldIds, length, min, key);
  152. fields = insertString(fields, length, min, s);
  153. ++length;
  154. });
  155. return key;
  156. }
  157. public static function findHash(hash:Int, hashs:cs.NativeArray<Int>, length:Int):Int {
  158. var min = 0;
  159. var max = length;
  160. while (min < max) {
  161. var mid = Std.int((max + min) / 2);
  162. var imid = hashs[mid];
  163. if (hash < imid) {
  164. max = mid;
  165. } else if (hash > imid) {
  166. min = mid + 1;
  167. } else {
  168. return mid;
  169. }
  170. }
  171. // if not found, return a negative value of where it should be inserted
  172. return ~min;
  173. }
  174. public static function removeInt(a:cs.NativeArray<Int>, length:Int, pos:Int) {
  175. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  176. a[length - 1] = 0;
  177. }
  178. public static function removeFloat(a:cs.NativeArray<Float>, length:Int, pos:Int) {
  179. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  180. a[length - 1] = 0;
  181. }
  182. public static function removeDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int) {
  183. cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
  184. a[length - 1] = null;
  185. }
  186. extern static inline function __insert<T>(a:cs.NativeArray<T>, length:Int, pos:Int, x:T):cs.NativeArray<T> {
  187. var capacity = a.Length;
  188. if (pos == length) {
  189. if (capacity == length) {
  190. var newarr = new NativeArray((length << 1) + 1);
  191. a.CopyTo(newarr, 0);
  192. a = newarr;
  193. }
  194. } else if (pos == 0) {
  195. if (capacity == length) {
  196. var newarr = new NativeArray((length << 1) + 1);
  197. cs.system.Array.Copy(a, 0, newarr, 1, length);
  198. a = newarr;
  199. } else {
  200. cs.system.Array.Copy(a, 0, a, 1, length);
  201. }
  202. } else {
  203. if (capacity == length) {
  204. var newarr = new NativeArray((length << 1) + 1);
  205. cs.system.Array.Copy(a, 0, newarr, 0, pos);
  206. cs.system.Array.Copy(a, pos, newarr, pos + 1, length - pos);
  207. a = newarr;
  208. } else {
  209. cs.system.Array.Copy(a, pos, a, pos + 1, length - pos);
  210. cs.system.Array.Copy(a, 0, a, 0, pos);
  211. }
  212. }
  213. a[pos] = x;
  214. return a;
  215. }
  216. public static function insertInt(a:cs.NativeArray<Int>, length:Int, pos:Int, x:Int):cs.NativeArray<Int>
  217. return __insert(a, length, pos, x);
  218. public static function insertFloat(a:cs.NativeArray<Float>, length:Int, pos:Int, x:Float):cs.NativeArray<Float>
  219. return __insert(a, length, pos, x);
  220. public static function insertDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<Dynamic>
  221. return __insert(a, length, pos, x);
  222. public static function insertString(a:cs.NativeArray<String>, length:Int, pos:Int, x:String):cs.NativeArray<String>
  223. return __insert(a, length, pos, x);
  224. public static function getHashConflict(head:FieldHashConflict, hash:Int, name:String):FieldHashConflict {
  225. while (head != null) {
  226. if (head.hash == hash && head.name == name) {
  227. return head;
  228. }
  229. head = head.next;
  230. }
  231. return null;
  232. }
  233. public static function setHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String, value:Dynamic):Void {
  234. var node = head;
  235. while (node != null) {
  236. if (node.hash == hash && node.name == name) {
  237. node.value = value;
  238. return;
  239. }
  240. node = node.next;
  241. }
  242. head = new FieldHashConflict(hash, name, value, head);
  243. }
  244. public static function deleteHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String):Bool {
  245. // no conflicting fields at all
  246. if (head == null) {
  247. return false;
  248. }
  249. // list head is conflicting - just point it to the next one
  250. if (head.hash == hash && head.name == name) {
  251. head = head.next;
  252. return true;
  253. }
  254. // loop through the list, removing node if there's one
  255. var prev = head, node = head.next;
  256. while (node != null) {
  257. if (node.hash == hash && node.name == name) {
  258. prev.next = node.next;
  259. return true;
  260. }
  261. node = node.next;
  262. }
  263. // not found
  264. return false;
  265. }
  266. public static function addHashConflictNames(head:FieldHashConflict, arr:Array<String>):Void {
  267. while (head != null) {
  268. arr.push(head.name);
  269. head = head.next;
  270. }
  271. }
  272. }