312 lines
8.7 KiB
Haxe
Raw Permalink Normal View History

2025-01-22 16:18:30 +01:00
/*
* 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;
}
}
}