123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- package cs.internal;
- @:native('haxe.lang.FieldHashConflict')
- @:nativeGen @:keep
- final class FieldHashConflict {
- @:readOnly public var hash(default, never):Int;
- @:readOnly public var name(default, never):String;
- public var value:Dynamic;
- public var next:FieldHashConflict;
- public function new(hash, name, value:Dynamic, next) {
- untyped this.hash = hash;
- untyped this.name = name;
- this.value = value;
- this.next = next;
- }
- }
- @:native('haxe.lang.FieldLookup')
- @:classCode("#pragma warning disable 628\n")
- @:nativeGen @:keep @:static
- final class FieldLookup {
- @:protected private static var fieldIds:cs.NativeArray<Int>;
- @:protected private static var fields:cs.NativeArray<String>;
- @:protected private static var length:Int;
- static function __init__() {
- length = fieldIds.Length;
- }
- private static function addFields(nids:cs.NativeArray<Int>, nfields:cs.NativeArray<String>):Void {
- // first see if we need to add anything
- var cids = fieldIds, cfields = fields;
- var nlen = nids.Length;
- var clen = length;
- if (nfields.Length != nlen)
- throw 'Different fields length: $nlen and ${nfields.Length}';
- // TODO optimize
- var needsChange = false;
- for (i in nids) {
- if (findHash(i, cids, clen) < 0) {
- needsChange = true;
- break;
- }
- }
- // if we do, lock and merge
- if (needsChange) {
- cs.Lib.lock(FieldLookup, {
- // trace(cs.Lib.array(nids), cs.Lib.array(cids));
- var ansIds = new cs.NativeArray(clen + nlen),
- ansFields = new cs.NativeArray(clen + nlen);
- var ci = 0, ni = 0, ansi = 0;
- while (ci < clen && ni < nlen) {
- if (cids[ci] < nids[ni]) {
- ansIds[ansi] = cids[ci];
- ansFields[ansi] = cfields[ci];
- ++ci;
- } else {
- ansIds[ansi] = nids[ni];
- ansFields[ansi] = nfields[ni];
- ++ni;
- }
- ++ansi;
- }
- if (ci < clen) {
- cs.system.Array.Copy(cids, ci, ansIds, ansi, clen - ci);
- cs.system.Array.Copy(cfields, ci, ansFields, ansi, clen - ci);
- ansi += clen - ci;
- }
- if (ni < nlen) {
- cs.system.Array.Copy(nids, ni, ansIds, ansi, nlen - ni);
- cs.system.Array.Copy(nfields, ni, ansFields, ansi, nlen - ni);
- ansi += nlen - ni;
- }
- // trace(cs.Lib.array(ansIds));
- fieldIds = ansIds;
- fields = ansFields;
- length = ansi;
- });
- }
- }
- // s cannot be null here
- private static inline function doHash(s:String):Int {
- var acc = 0; // alloc_int
- for (i in 0...s.length) {
- acc = ((223 * (acc >> 1) + cast(s[i], Int)) << 1);
- }
- return acc >>> 1; // always positive
- }
- public static function lookupHash(key:Int):String {
- var ids = fieldIds;
- var min = 0;
- var max = length;
- while (min < max) {
- var mid = min + Std.int((max - min) / 2);
- var imid = ids[mid];
- if (key < imid) {
- max = mid;
- } else if (key > imid) {
- min = mid + 1;
- } else {
- return fields[mid];
- }
- }
- // if not found, it's definitely an error
- throw "Field not found for hash " + key;
- }
- public static function hash(s:String):Int {
- if (s == null)
- return 0;
- var key = doHash(s);
- var ids = fieldIds, fld = fields;
- var min = 0;
- var max = length;
- var len = length;
- while (min < max) {
- var mid = Std.int(min + (max - min) / 2); // overflow safe
- var imid = ids[mid];
- if (key < imid) {
- max = mid;
- } else if (key > imid) {
- min = mid + 1;
- } else {
- var field = fld[mid];
- if (field != s)
- return ~key; // special case
- return key;
- }
- }
- // if not found, min holds the value where we should insert the key
- // ensure thread safety:
- cs.Lib.lock(FieldLookup, {
- if (len != length) // race condition which will very rarely happen - other thread modified sooner.
- return hash(s); // since we already own the lock, this second try will always succeed
- fieldIds = insertInt(fieldIds, length, min, key);
- fields = insertString(fields, length, min, s);
- ++length;
- });
- return key;
- }
- public static function findHash(hash:Int, hashs:cs.NativeArray<Int>, length:Int):Int {
- var min = 0;
- var max = length;
- while (min < max) {
- var mid = Std.int((max + min) / 2);
- var imid = hashs[mid];
- if (hash < imid) {
- max = mid;
- } else if (hash > imid) {
- min = mid + 1;
- } else {
- return mid;
- }
- }
- // if not found, return a negative value of where it should be inserted
- return ~min;
- }
- public static function removeInt(a:cs.NativeArray<Int>, length:Int, pos:Int) {
- cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
- a[length - 1] = 0;
- }
- public static function removeFloat(a:cs.NativeArray<Float>, length:Int, pos:Int) {
- cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
- a[length - 1] = 0;
- }
- public static function removeDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int) {
- cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
- a[length - 1] = null;
- }
- extern static inline function __insert<T>(a:cs.NativeArray<T>, length:Int, pos:Int, x:T):cs.NativeArray<T> {
- var capacity = a.Length;
- if (pos == length) {
- if (capacity == length) {
- var newarr = new NativeArray((length << 1) + 1);
- a.CopyTo(newarr, 0);
- a = newarr;
- }
- } else if (pos == 0) {
- if (capacity == length) {
- var newarr = new NativeArray((length << 1) + 1);
- cs.system.Array.Copy(a, 0, newarr, 1, length);
- a = newarr;
- } else {
- cs.system.Array.Copy(a, 0, a, 1, length);
- }
- } else {
- if (capacity == length) {
- var newarr = new NativeArray((length << 1) + 1);
- cs.system.Array.Copy(a, 0, newarr, 0, pos);
- cs.system.Array.Copy(a, pos, newarr, pos + 1, length - pos);
- a = newarr;
- } else {
- cs.system.Array.Copy(a, pos, a, pos + 1, length - pos);
- cs.system.Array.Copy(a, 0, a, 0, pos);
- }
- }
- a[pos] = x;
- return a;
- }
- public static function insertInt(a:cs.NativeArray<Int>, length:Int, pos:Int, x:Int):cs.NativeArray<Int>
- return __insert(a, length, pos, x);
- public static function insertFloat(a:cs.NativeArray<Float>, length:Int, pos:Int, x:Float):cs.NativeArray<Float>
- return __insert(a, length, pos, x);
- public static function insertDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<Dynamic>
- return __insert(a, length, pos, x);
- public static function insertString(a:cs.NativeArray<String>, length:Int, pos:Int, x:String):cs.NativeArray<String>
- return __insert(a, length, pos, x);
- public static function getHashConflict(head:FieldHashConflict, hash:Int, name:String):FieldHashConflict {
- while (head != null) {
- if (head.hash == hash && head.name == name) {
- return head;
- }
- head = head.next;
- }
- return null;
- }
- public static function setHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String, value:Dynamic):Void {
- var node = head;
- while (node != null) {
- if (node.hash == hash && node.name == name) {
- node.value = value;
- return;
- }
- node = node.next;
- }
- head = new FieldHashConflict(hash, name, value, head);
- }
- public static function deleteHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String):Bool {
- // no conflicting fields at all
- if (head == null) {
- return false;
- }
- // list head is conflicting - just point it to the next one
- if (head.hash == hash && head.name == name) {
- head = head.next;
- return true;
- }
- // loop through the list, removing node if there's one
- var prev = head, node = head.next;
- while (node != null) {
- if (node.hash == hash && node.name == name) {
- prev.next = node.next;
- return true;
- }
- node = node.next;
- }
- // not found
- return false;
- }
- public static function addHashConflictNames(head:FieldHashConflict, arr:Array<String>):Void {
- while (head != null) {
- arr.push(head.name);
- head = head.next;
- }
- }
- }
|