1 module evael.lib.containers.dictionary;
2 
3 import evael.lib.memory;
4 import evael.lib.containers.array;
5 
6 struct KeyValueNode(K, V) 
7 {
8     public K key;
9     public V value;
10 
11     private KeyValueNode* next;
12 
13     @nogc
14     public this(K key, V value, KeyValueNode* next) 
15     {
16         this.key = key;
17         this.value = value;
18         this.next = next;
19     }
20 }
21 
22 struct Dictionary(K, V)
23 {
24     alias Node = KeyValueNode!(K, V);
25 
26     private Array!(Node*) m_buckets;
27 
28 	/**
29 	 * Dictionary constructor.
30 	 * Params:
31 	 *		capacity : bucket capacity
32 	 */
33     @nogc
34     public this(in size_t capacity) 
35     {
36         this.m_buckets = Array!(Node*)(capacity, null);
37     }
38 
39 	/**
40 	 * Dictionary destructor.
41 	 */
42 	@nogc
43 	public void dispose()
44 	{
45         foreach (Node* node; this.m_buckets)
46 		{
47 			Node* currentNode = node;
48             while (currentNode !is null)
49 			{
50                 Node* prevNode = currentNode;
51                 currentNode = currentNode.next;
52                 MemoryHelper.dispose(prevNode);
53             }
54         }
55 
56 		this.m_buckets.dispose();
57 	}
58 
59 	/**
60 	 * Inserts a key and his value.
61 	 * Params:
62 	 *		key : key
63 	 *		value : value
64 	 */
65     @nogc
66     public void insert(K key, V value) 
67     {
68         immutable hash = this.getHash(key);
69 
70         Node* node = this.m_buckets[hash];
71 
72         if (node is null) 
73         {
74             this.m_buckets[hash] = MemoryHelper.create!Node(key, value, null);
75         } 
76         else 
77         {
78             // We search for the same key and replace his value
79             while (node !is null)
80             {
81                 if (node.key == key)
82                 {
83                     node.value = value;
84                     return;
85                 }
86                 node = node.next;
87             }
88             
89 			node.next = MemoryHelper.create!Node(key, value, null);
90         }
91     } 
92 
93     /**
94      * Returns value of key.
95 	 * Params:
96 	 *		key : key
97 	 *		notFoundValue : value to return if key has not been found
98      */
99     @nogc
100     public V get(K key, V notFoundValue = V.init) 
101     {
102         immutable hash = this.getHash(key);
103 
104         Node* currentNode = this.m_buckets[hash];
105 
106         while (currentNode !is null) 
107         {
108             if (currentNode.key == key)
109             {
110                 return currentNode.value;
111             }
112             currentNode = currentNode.next;
113         }
114         
115         return notFoundValue;
116     }
117 
118 	/**
119 	 * Removes a key and his value from the dictionary.
120 	 * Params:
121 	 *		key : key
122 	 */
123     @nogc
124     public bool remove(K key)
125     {
126         immutable hash = this.getHash(key);
127 
128 		Node* prevNode = null;
129         Node* currentNode = this.m_buckets[hash];
130 
131         while (currentNode !is null && currentNode.key != key) 
132 		{
133             prevNode = currentNode;
134             currentNode = currentNode.next;
135         }
136 
137         if (currentNode is null)
138 		{
139             return false;
140         }
141 
142 		if (prevNode is null) 
143 		{
144 			this.m_buckets[hash] = currentNode.next;
145 		} 
146 		else 
147 		{
148 			prevNode.next = currentNode.next;
149 		}
150 
151 		MemoryHelper.dispose(currentNode);
152 
153         return true;
154     }
155 
156     /**
157      * Index assignment support.
158      */
159     pragma(inline, true)
160     @nogc
161     public void opIndexAssign(V value, K key)
162     {
163         this.insert(key, value);
164     }
165 
166     @nogc
167     private size_t getHash(K)(K key) if(is(K == int))
168     {
169         return key % this.m_buckets.length;
170     }
171 
172     @nogc
173     private size_t getHash(K)(K key) if(is(K == string))
174     {
175         int hash = 7;
176         foreach (c; key)
177         {
178             hash = hash * 31 + c;
179         }
180 
181         return hash % this.m_buckets.length;
182     }
183 }