/*
Реализация среды исполнения языка программирования
Объектно-ориентированный продвинутый векторный транслятор
Copyright © 2021, 2024 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package avt.util;
import avt.lang.array.*;
import platform.independent.streamformat.*;
public class Hashtable(Object, MutableDataHolder, DataHolder, Cloneable, Measureable)
{
private static int getIndex(long8 hash, int length) {
int8 temp = (int8) (hash ^ (hash >> 32));
int result = 0x55555555;
for(int index = 8; index-- > 0; ) result ^= temp[index];
return result %% length;
}
private int fldLength;
private HashtableEntry[] fldTable;
private final Object fldMonitor;
public () {
fldTable = new HashtableEntry[15];
fldMonitor = new Object();
}
public (int initialCapacity) {
if(initialCapacity < 1) initialCapacity = 1;
fldTable = new HashtableEntry[initialCapacity];
fldMonitor = new Object();
}
public String toString() {
StringBuilder result = new StringBuilder() + "{ ";
synchronized(fldMonitor)
{
with(new HashtableMapEnumeration(fldTable)) for(int index = fldLength; index-- > 0; )
{
result + nextElement() + '=' + value();
if(index > 0) result + ", ";
}
}
return (result + " }").toString();
}
public void clear() {
synchronized(fldMonitor)
{
fldLength = 0;
Object[] table = fldTable;
Array.fill(table, 0, table.length, null);
}
}
public boolean isEmpty() { return fldLength <= 0; }
public Hashtable clone() {
Hashtable result;
synchronized(fldMonitor)
{
HashtableEntry[] curTable = fldTable;
int capacity = curTable.length;
HashtableEntry[] newTable = (result = new Hashtable(capacity)).fldTable;
for(int index = capacity; index-- > 0; ) for(HashtableEntry entry = curTable[index]; entry != null; entry = entry.next)
{
newTable[index] = new HashtableEntry(entry.hash, entry.key, entry.value, newTable[index]);
}
result.fldLength = fldLength;
}
return result;
}
public boolean containsKey(boolean key) { return containsKey(key ? Boolean.TRUE : Boolean.FALSE); }
public boolean containsKey(char key) { return containsKey(new Char(key)); }
public boolean containsKey(byte key) { return containsKey(new Byte(key)); }
public boolean containsKey(byte2 key) { return containsKey(new Byte2(key)); }
public boolean containsKey(byte4 key) { return containsKey(new Byte4(key)); }
public boolean containsKey(byte8 key) { return containsKey(new Byte8(key)); }
public boolean containsKey(short key) { return containsKey(new Short(key)); }
public boolean containsKey(short2 key) { return containsKey(new Short2(key)); }
public boolean containsKey(short4 key) { return containsKey(new Short4(key)); }
public boolean containsKey(short8 key) { return containsKey(new Short8(key)); }
public boolean containsKey(int key) { return containsKey(new Int(key)); }
public boolean containsKey(int2 key) { return containsKey(new Int2(key)); }
public boolean containsKey(int4 key) { return containsKey(new Int4(key)); }
public boolean containsKey(int8 key) { return containsKey(new Int8(key)); }
public boolean containsKey(long key) { return containsKey(new Long(key)); }
public boolean containsKey(long2 key) { return containsKey(new Long2(key)); }
public boolean containsKey(long4 key) { return containsKey(new Long4(key)); }
public boolean containsKey(long8 key) { return containsKey(new Long8(key)); }
public boolean containsKey(Object key) {
if(key == null) return false;
long8 hash = key.hashCodeAsLong8();
boolean result = false;
synchronized(fldMonitor)
{
HashtableEntry[] table = fldTable;
for(HashtableEntry entry = table[getIndex(hash, table.length)]; entry != null; entry = entry.next) if(hash == entry.hash && key.equals(entry.key))
{
result = true;
break;
}
}
return result;
}
public boolean containsValue(boolean value) { return containsValue(value ? Boolean.TRUE : Boolean.FALSE); }
public boolean containsValue(char value) { return containsValue(new Char(value)); }
public boolean containsValue(byte value) { return containsValue(new Byte(value)); }
public boolean containsValue(byte2 value) { return containsValue(new Byte2(value)); }
public boolean containsValue(byte4 value) { return containsValue(new Byte4(value)); }
public boolean containsValue(byte8 value) { return containsValue(new Byte8(value)); }
public boolean containsValue(short value) { return containsValue(new Short(value)); }
public boolean containsValue(short2 value) { return containsValue(new Short2(value)); }
public boolean containsValue(short4 value) { return containsValue(new Short4(value)); }
public boolean containsValue(short8 value) { return containsValue(new Short8(value)); }
public boolean containsValue(int value) { return containsValue(new Int(value)); }
public boolean containsValue(int2 value) { return containsValue(new Int2(value)); }
public boolean containsValue(int4 value) { return containsValue(new Int4(value)); }
public boolean containsValue(int8 value) { return containsValue(new Int8(value)); }
public boolean containsValue(long value) { return containsValue(new Long(value)); }
public boolean containsValue(long2 value) { return containsValue(new Long2(value)); }
public boolean containsValue(long4 value) { return containsValue(new Long4(value)); }
public boolean containsValue(long8 value) { return containsValue(new Long8(value)); }
public boolean containsValue(Object value) {
if(value == null) return false;
boolean result = false;
synchronized(fldMonitor)
{
HashtableEntry[] table = fldTable;
label0: for(int index = table.length; index-- > 0; ) for(HashtableEntry entry = table[index]; entry != null; entry = entry.next) if(value.equals(entry.value))
{
result = true;
break label0;
}
}
return result;
}
public MapEnumeration enumerateKeys() { return new HashtableMapEnumeration(fldTable); }
public Enumeration enumerateValues() { return new HashtableEnumeration(false, fldTable); }
public int length { read = fldLength }
public final int capacity { read = fldTable.length }
public void operator []=(boolean key, Object value) { operator []=(key ? Boolean.TRUE : Boolean.FALSE, value); }
public void operator []=(char key, Object value) { operator []=(new Char(key), value); }
public void operator []=(byte key, Object value) { operator []=(new Byte(key), value); }
public void operator []=(byte2 key, Object value) { operator []=(new Byte2(key), value); }
public void operator []=(byte4 key, Object value) { operator []=(new Byte4(key), value); }
public void operator []=(byte8 key, Object value) { operator []=(new Byte8(key), value); }
public void operator []=(short key, Object value) { operator []=(new Short(key), value); }
public void operator []=(short2 key, Object value) { operator []=(new Short2(key), value); }
public void operator []=(short4 key, Object value) { operator []=(new Short4(key), value); }
public void operator []=(short8 key, Object value) { operator []=(new Short8(key), value); }
public void operator []=(int key, Object value) { operator []=(new Int(key), value); }
public void operator []=(int2 key, Object value) { operator []=(new Int2(key), value); }
public void operator []=(int4 key, Object value) { operator []=(new Int4(key), value); }
public void operator []=(int8 key, Object value) { operator []=(new Int8(key), value); }
public void operator []=(long key, Object value) { operator []=(new Long(key), value); }
public void operator []=(long2 key, Object value) { operator []=(new Long2(key), value); }
public void operator []=(long4 key, Object value) { operator []=(new Long4(key), value); }
public void operator []=(long8 key, Object value) { operator []=(new Long8(key), value); }
public void operator []=(Object key, Object value) {
if(key == null)
{
throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "key" }));
}
if(value == null)
{
remove(key);
return;
}
put(key, value);
}
public Object operator [](boolean key) { return operator [](key ? Boolean.TRUE : Boolean.FALSE); }
public Object operator [](char key) { return operator [](new Char(key)); }
public Object operator [](byte key) { return operator [](new Byte(key)); }
public Object operator [](byte2 key) { return operator [](new Byte2(key)); }
public Object operator [](byte4 key) { return operator [](new Byte4(key)); }
public Object operator [](byte8 key) { return operator [](new Byte8(key)); }
public Object operator [](short key) { return operator [](new Short(key)); }
public Object operator [](short2 key) { return operator [](new Short2(key)); }
public Object operator [](short4 key) { return operator [](new Short4(key)); }
public Object operator [](short8 key) { return operator [](new Short8(key)); }
public Object operator [](int key) { return operator [](new Int(key)); }
public Object operator [](int2 key) { return operator [](new Int2(key)); }
public Object operator [](int4 key) { return operator [](new Int4(key)); }
public Object operator [](int8 key) { return operator [](new Int8(key)); }
public Object operator [](long key) { return operator [](new Long(key)); }
public Object operator [](long2 key) { return operator [](new Long2(key)); }
public Object operator [](long4 key) { return operator [](new Long4(key)); }
public Object operator [](long8 key) { return operator [](new Long8(key)); }
public Object operator [](Object key) {
if(key == null)
{
throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "key" }));
}
long8 hash = key.hashCodeAsLong8();
Object result = null;
synchronized(fldMonitor)
{
HashtableEntry[] table = fldTable;
for(HashtableEntry entry = table[getIndex(hash, table.length)]; entry != null; entry = entry.next) if(hash == entry.hash && key.equals(entry.key))
{
result = entry.value;
break;
}
}
return result;
}
protected void rehash() {
HashtableEntry[] oldTable = fldTable;
int oldCapacity = oldTable.length;
int newCapacity = oldCapacity << 1 | 1;
if(newCapacity < 0) newCapacity = Int.MAX_VALUE;
HashtableEntry[] newTable = fldTable = new HashtableEntry[newCapacity];
for(int oldIndex = oldCapacity; oldIndex-- > 0; ) for(HashtableEntry oldEntry = oldTable[oldIndex]; oldEntry != null; )
{
int newIndex = getIndex(oldEntry.hash, newCapacity);
HashtableEntry newEntry = oldEntry;
oldEntry = oldEntry.next;
newEntry.next = newTable[newIndex];
newTable[newIndex] = newEntry;
}
}
private void put(Object key, Object value) {
long8 hash = key.hashCodeAsLong8();
boolean isError = false;
synchronized(fldMonitor)
{
label0:
{
HashtableEntry[] table = fldTable;
int capacity = table.length;
int index = getIndex(hash, capacity);
for(HashtableEntry entry = table[index]; entry != null; entry = entry.next) if(hash == entry.hash && key.equals(entry.key))
{
entry.value = value;
break label0;
}
int length = fldLength;
if(length >= Int.MAX_VALUE)
{
isError = true;
break label0;
}
if(length++ >= capacity)
{
rehash();
index = getIndex(hash, (table = fldTable).length);
}
table[index] = new HashtableEntry(hash, key, value, table[index]);
fldLength = length;
}
}
if(isError)
{
throw new BufferTooLargeError(avt.lang.package.getResourceString("!error.buffer-too-large"));
}
}
private void remove(Object key) {
long8 hash = key.hashCodeAsLong8();
synchronized(fldMonitor)
{
HashtableEntry[] table = fldTable;
int index = getIndex(hash, table.length);
for(HashtableEntry prev = null, HashtableEntry entry = table[index]; entry != null; entry = (prev = entry).next) if(hash == entry.hash && key.equals(entry.key))
{
if(prev != null)
{
prev.next = entry.next;
} else
{
table[index] = entry.next;
}
fldLength--;
break;
}
}
}
}
final class HashtableEntry(Object)
{
HashtableEntry next;
Object value;
final long8 hash;
final Object key;
(long8 hash, Object key, Object value, HashtableEntry next) {
this.next = next;
this.value = value;
this.hash = hash;
this.key = key;
}
}
class HashtableEnumeration(Enumeration)
{
int fldIndex;
HashtableEntry fldNext;
HashtableEntry fldCurrent;
final boolean fldKeys;
final HashtableEntry[] fldTable;
(boolean keys, HashtableEntry[] table) {
fldIndex = table.length;
fldKeys = keys;
fldTable = table;
}
public boolean hasMoreElements() {
if(fldNext != null)
{
return true;
}
for(HashtableEntry[] table = fldTable, int index = fldIndex; index-- > 0; ) if((fldNext = table[index]) != null)
{
fldIndex = index;
return true;
}
fldIndex = -1;
return false;
}
public Object nextElement() {
HashtableEntry entry = fldNext;
if(entry == null)
{
int index = fldIndex;
HashtableEntry[] table = fldTable;
while(index-- > 0 && (entry = table[index]) == null);
fldNext = entry;
fldIndex = index < 0 ? -1 : index;
}
if(entry == null)
{
fldCurrent = null;
throw new NoSuchElementException(package.getResourceString("empty.enumeration"));
}
fldNext = (fldCurrent = entry).next;
return fldKeys ? entry.key : entry.value;
}
}
class HashtableMapEnumeration(HashtableEnumeration, MapEnumeration)
{
(HashtableEntry[] table): super(true, table) { }
public Object value() {
HashtableEntry entry = fldCurrent;
if(entry == null)
{
throw new NoSuchElementException(package.getResourceString("empty.enumeration"));
}
return entry.value;
}
}