/* hashmap.vala * * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * Copyright (C) 1997-2000 GLib Team and others * Copyright (C) 2007-2008 Jürg Billeter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Jürg Billeter */ using GLib; /** * Hashtable implementation of the Map interface. */ public class Gee.HashMap : Object, Map { public int size { get { return _nnodes; } } public HashFunc key_hash_func { set { _key_hash_func = value; } } public EqualFunc key_equal_func { set { _key_equal_func = value; } } public EqualFunc value_equal_func { set { _value_equal_func = value; } } private int _array_size; private int _nnodes; private Node[] _nodes; // concurrent modification protection private int _stamp = 0; private HashFunc _key_hash_func; private EqualFunc _key_equal_func; private EqualFunc _value_equal_func; private const int MIN_SIZE = 11; private const int MAX_SIZE = 13845163; public HashMap (construct HashFunc key_hash_func = GLib.direct_hash, construct EqualFunc key_equal_func = GLib.direct_equal, construct EqualFunc value_equal_func = GLib.direct_equal) { } construct { _array_size = MIN_SIZE; _nodes = new Node[_array_size]; } public Set get_keys () { return new KeySet (this); } public Collection get_values () { return new ValueCollection (this); } private Node** lookup_node (K key) { uint hash_value = _key_hash_func (key); Node** node = &_nodes[hash_value % _array_size]; while ((*node) != null && (hash_value != (*node)->key_hash || !_key_equal_func ((*node)->key, key))) { node = &((*node)->next); } return node; } public bool contains (K key) { Node** node = lookup_node (key); return (*node != null); } public V get (K key) { Node* node = (*lookup_node (key)); if (node != null) { return node->value; } else { return null; } } public void set (K key, V value) { Node** node = lookup_node (key); if (*node != null) { (*node)->value = value; } else { uint hash_value = _key_hash_func (key); *node = new Node (key, value, hash_value); _nnodes++; resize (); } _stamp++; } public bool remove (K key) { Node** node = lookup_node (key); if (*node != null) { (*node)->key = null; (*node)->value = null; *node = (*node)->next; _nnodes--; resize (); _stamp++; return true; } return false; } public void clear () { for (int i = 0; i < _array_size; i++) { Node node = #_nodes[i]; while (node != null) { Node next = #node.next; node.key = null; node.value = null; node = #next; } } _nnodes = 0; resize (); } private void resize () { if ((_array_size >= 3 * _nnodes && _array_size >= MIN_SIZE) || (3 * _array_size <= _nnodes && _array_size < MAX_SIZE)) { int new_array_size = (int) SpacedPrimes.closest (_nnodes); new_array_size = new_array_size.clamp (MIN_SIZE, MAX_SIZE); Node[] new_nodes = new Node[new_array_size]; for (int i = 0; i < _array_size; i++) { Node node; Node next; for (node = #_nodes[i]; node != null; node = #next) { next = #node.next; uint hash_val = node.key_hash % new_array_size; node.next = #new_nodes[hash_val]; new_nodes[hash_val] = #node; } } _nodes = #new_nodes; _array_size = new_array_size; } } ~HashSet () { clear (); } private class Node { public K key; public V value; public Node next; public uint key_hash; public Node (K# k, V# v, uint hash) { key = #k; value = #v; key_hash = hash; } } private class KeySet : Object, Iterable, Collection, Set { public HashMap map { set { _map = value; } } private HashMap _map; public KeySet (construct HashMap! map) { } public Type get_element_type () { return typeof (K); } public Iterator iterator () { return new KeyIterator (_map); } public int size { get { return _map.size; } } public bool add (K key) { assert_not_reached (); } public void clear () { assert_not_reached (); } public bool remove (K key) { assert_not_reached (); } public bool contains (K key) { return _map.contains (key); } } private class KeyIterator : Object, Iterator { public HashMap map { set { _map = value; _stamp = _map._stamp; } } private HashMap _map; private int _index = -1; private weak Node _node; // concurrent modification protection private int _stamp; public KeyIterator (construct HashMap! map) { } public bool next () { if (_node != null) { _node = _node.next; } while (_node == null && _index + 1 < _map._array_size) { _index++; _node = _map._nodes[_index]; } return (_node != null); } public K get () { assert (_stamp == _map._stamp); assert (_node != null); return _node.key; } } private class ValueCollection : Object, Iterable, Collection { public HashMap map { set { _map = value; } } private HashMap _map; public ValueCollection (construct HashMap! map) { } public Type get_element_type () { return typeof (V); } public Iterator iterator () { return new ValueIterator (_map); } public int size { get { return _map.size; } } public bool add (V value) { assert_not_reached (); } public void clear () { assert_not_reached (); } public bool remove (V value) { assert_not_reached (); } public bool contains (V value) { Iterator it = iterator (); while (it.next ()) { if (_map._value_equal_func (it.get (), value)) { return true; } } return false; } } private class ValueIterator : Object, Iterator { public HashMap map { set { _map = value; _stamp = _map._stamp; } } private HashMap _map; private int _index = -1; private weak Node _node; // concurrent modification protection private int _stamp; public ValueIterator (construct HashMap! map) { } public bool next () { if (_node != null) { _node = _node.next; } while (_node == null && _index + 1 < _map._array_size) { _index++; _node = _map._nodes[_index]; } return (_node != null); } public V get () { assert (_stamp == _map._stamp); assert (_node != null); return _node.value; } } }