312 lines
8.7 KiB
Haxe
312 lines
8.7 KiB
Haxe
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|