/*
Реализация среды исполнения языка программирования
Объектно-ориентированный продвинутый векторный транслятор
Copyright © 2021, 2024, 2026 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии 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[0x0f];
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()).append("{ ");
synchronized(fldMonitor)
{
with(new HashtableMapEnumeration(fldTable)) for(int index = fldLength; index-- > 0; )
{
result.append(nextElement()).append('=').append(value());
if(index > 0) result.append(", ");
}
}
return result.append(" }").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 contains(Object key) {
if(key == null) return false;
long8 hash = key.hashCodeAsLong8();
boolean result = false;
synchronized(fldMonitor)
{
for(HashtableEntry[] table = fldTable, 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(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 enumerate() { return new HashtableMapEnumeration(fldTable); }
public Enumeration enumerateValues() { return new HashtableEnumeration(false, fldTable); }
public int length { read = fldLength }
public int capacity { read = fldTable.length }
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 [](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)
{
for(HashtableEntry[] table = fldTable, 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;
public (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;
public (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)
{
public (HashtableEntry[] table): super(true, table) { }
public Object value() {
HashtableEntry entry = fldCurrent;
if(entry == null)
{
throw new NoSuchElementException(package.getResourceString("empty.enumeration"));
}
return entry.value;
}
}