Hashtable.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Реализация среды исполнения языка программирования
    Объектно-ориентированный продвинутый векторный транслятор

    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;
    }
}