1/*---------------------------------------------------------------------------*
2 *  HashMapImpl.c  *
3 *                                                                           *
4 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
5 *                                                                           *
6 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7 *  you may not use this file except in compliance with the License.         *
8 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
10 *      http://www.apache.org/licenses/LICENSE-2.0                           *
11 *                                                                           *
12 *  Unless required by applicable law or agreed to in writing, software      *
13 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 *  See the License for the specific language governing permissions and      *
16 *  limitations under the License.                                           *
17 *                                                                           *
18 *---------------------------------------------------------------------------*/
19
20
21#include "HashMap.h"
22#include "HashMapImpl.h"
23#include "plog.h"
24#include "pmemory.h"
25#include "string.h"
26
27#define MTAG NULL
28
29static ESR_ReturnCode HashMapCreate_Internal(PHashTableArgs *hashArgs,
30    HashMap **self)
31{
32  HashMapImpl* impl;
33  ESR_ReturnCode rc = ESR_SUCCESS;
34
35  if (self == NULL)
36    return ESR_INVALID_ARGUMENT;
37  impl = NEW(HashMapImpl, MTAG);
38  if (impl == NULL)
39    return ESR_OUT_OF_MEMORY;
40
41  if ((rc = PHashTableCreate(hashArgs, MTAG, &impl->table)) != ESR_SUCCESS)
42  {
43    FREE(impl);
44    return rc;
45  }
46
47  impl->Interface.put = &HashMap_Put;
48  impl->Interface.remove = &HashMap_Remove;
49  impl->Interface.removeAndFree = &HashMap_RemoveAndFree;
50  impl->Interface.removeAll = &HashMap_RemoveAll;
51  impl->Interface.removeAndFreeAll = &HashMap_RemoveAndFreeAll;
52  impl->Interface.removeAtIndex = &HashMap_RemoveAtIndex;
53  impl->Interface.containsKey = &HashMap_ContainsKey;
54  impl->Interface.getKeyAtIndex = &HashMap_GetKeyAtIndex;
55  impl->Interface.get = &HashMap_Get;
56  impl->Interface.getValueAtIndex = &HashMap_GetValueAtIndex;
57  impl->Interface.getSize = &HashMap_GetSize;
58  impl->Interface.destroy = &HashMap_Destroy;
59
60  *self = (HashMap*) impl;
61  return ESR_SUCCESS;
62}
63
64ESR_ReturnCode HashMapCreate(HashMap** self)
65{
66  return HashMapCreate_Internal(NULL, self);
67}
68
69ESR_ReturnCode HashMapCreateBins(size_t nbBins, HashMap** self)
70{
71  PHashTableArgs hashArgs;
72  hashArgs.capacity = nbBins;
73  hashArgs.maxLoadFactor = PHASH_TABLE_DEFAULT_MAX_LOAD_FACTOR;
74  hashArgs.hashFunction = PHASH_TABLE_DEFAULT_HASH_FUNCTION;
75  hashArgs.compFunction = PHASH_TABLE_DEFAULT_COMP_FUNCTION;
76  return HashMapCreate_Internal(&hashArgs, self);
77}
78
79ESR_ReturnCode HashMap_Put(HashMap* self, const LCHAR* key, void* value)
80{
81  HashMapImpl* impl = (HashMapImpl*) self;
82  PHashTableEntry *entry = NULL;
83  ESR_ReturnCode rc;
84  ESR_BOOL exists;
85
86  CHKLOG(rc, PHashTableContainsKey(impl->table, key, &exists));
87  if (!exists)
88  {
89    /* Not found, clone the key and insert it. */
90    LCHAR *clone = (LCHAR *) MALLOC(sizeof(LCHAR) * (LSTRLEN(key) + 1), MTAG);
91    if (clone == NULL) return ESR_OUT_OF_MEMORY;
92    LSTRCPY(clone, key);
93    if ((rc = PHashTablePutValue(impl->table, clone, value, NULL)) != ESR_SUCCESS)
94    {
95      FREE(clone);
96    }
97  }
98  else
99  {
100    /* Key already present in table, just change the value. */
101    CHKLOG(rc, PHashTableGetEntry(impl->table, key, &entry));
102    rc = PHashTableEntrySetValue(entry, value, NULL);
103  }
104  return rc;
105CLEANUP:
106  return rc;
107}
108
109static ESR_ReturnCode HashMap_Remove_Internal(HashMapImpl* impl, const LCHAR* key, ESR_BOOL freeValue)
110{
111  PHashTableEntry *entry = NULL;
112  ESR_ReturnCode rc = ESR_SUCCESS;
113  LCHAR *clonedKey = NULL;
114  void *value = NULL;
115
116  CHK(rc, PHashTableGetEntry(impl->table, key, &entry));
117  CHK(rc, PHashTableEntryGetKeyValue(entry, (void **)&clonedKey, (void **)&value));
118
119  if (clonedKey)
120    FREE(clonedKey);
121  if (freeValue && value)
122    FREE(value);
123
124  return PHashTableEntryRemove(entry);
125CLEANUP:
126  return rc;
127}
128
129ESR_ReturnCode HashMap_Remove(HashMap* self, const LCHAR* key)
130{
131  return HashMap_Remove_Internal((HashMapImpl*) self, key, ESR_FALSE);
132}
133
134ESR_ReturnCode HashMap_RemoveAndFree(HashMap* self, const LCHAR* key)
135{
136  return HashMap_Remove_Internal((HashMapImpl*) self, key, ESR_TRUE);
137}
138
139static ESR_ReturnCode HashMap_RemoveAll_Internal(HashMapImpl *impl, ESR_BOOL freeValues)
140{
141  PHashTableEntry *entry1 = NULL;
142  PHashTableEntry *entry2 = NULL;
143
144  ESR_ReturnCode rc = ESR_SUCCESS;
145  LCHAR *key = NULL;
146  void *value = NULL;
147
148  if ((rc = PHashTableEntryGetFirst(impl->table, &entry1)) != ESR_SUCCESS)
149    goto end;
150
151  while (entry1 != NULL)
152  {
153    if ((rc = PHashTableEntryGetKeyValue(entry1, (void **)&key, (void **)&value)) != ESR_SUCCESS)
154      goto end;
155    if (key) FREE(key);
156    if (freeValues && value) FREE(value);
157    entry2 = entry1;
158    if ((rc = PHashTableEntryAdvance(&entry1)) != ESR_SUCCESS)
159      goto end;
160    if ((rc =  PHashTableEntryRemove(entry2)) != ESR_SUCCESS)
161      goto end;
162  }
163end:
164  return rc;
165}
166
167ESR_ReturnCode HashMap_RemoveAll(HashMap* self)
168{
169  return HashMap_RemoveAll_Internal((HashMapImpl *) self, ESR_FALSE);
170}
171
172ESR_ReturnCode HashMap_RemoveAndFreeAll(HashMap* self)
173{
174  return HashMap_RemoveAll_Internal((HashMapImpl *) self, ESR_TRUE);
175}
176
177ESR_ReturnCode HashMap_ContainsKey(HashMap* self, const LCHAR* key, ESR_BOOL* exists)
178{
179  HashMapImpl* impl = (HashMapImpl*) self;
180  ESR_ReturnCode rc = ESR_SUCCESS;
181
182  CHKLOG(rc, PHashTableContainsKey(impl->table, key, exists));
183  return rc;
184CLEANUP:
185  return rc;
186}
187
188ESR_ReturnCode HashMap_Get(HashMap* self, const LCHAR* key, void** value)
189{
190  HashMapImpl* impl = (HashMapImpl*) self;
191  PHashTableEntry *entry = NULL;
192  ESR_ReturnCode rc = ESR_SUCCESS;
193
194  CHK(rc, PHashTableGetEntry(impl->table, key, &entry));
195  CHK(rc, PHashTableEntryGetKeyValue(entry, (void **)NULL, (void **)value));
196  return ESR_SUCCESS;
197CLEANUP:
198  return rc;
199}
200
201static ESR_ReturnCode HashMap_GetEntryAtIndex(HashMapImpl *impl, const size_t index,
202    PHashTableEntry **entry)
203{
204  ESR_ReturnCode rc = ESR_SUCCESS;
205  size_t i = 0;
206
207  if ((rc = PHashTableEntryGetFirst(impl->table, entry)) != ESR_SUCCESS)
208    goto end;
209
210  while (*entry != NULL && i < index)
211  {
212    ++i;
213    if ((rc = PHashTableEntryAdvance(entry)) != ESR_SUCCESS)
214      goto end;
215  }
216  if (*entry == NULL)
217    rc = ESR_ARGUMENT_OUT_OF_BOUNDS;
218end:
219  return rc;
220}
221
222
223ESR_ReturnCode HashMap_GetKeyAtIndex(HashMap* self, const size_t index, LCHAR** key)
224{
225  HashMapImpl* impl = (HashMapImpl*) self;
226  PHashTableEntry *entry = NULL;
227  ESR_ReturnCode rc;
228
229  if ((rc = HashMap_GetEntryAtIndex(impl, index, &entry)) != ESR_SUCCESS)
230    goto end;
231
232  rc = PHashTableEntryGetKeyValue(entry, (void **) key, (void **) NULL);
233
234end:
235  return rc;
236}
237
238ESR_ReturnCode HashMap_GetValueAtIndex(HashMap* self, const size_t index, void** value)
239{
240  HashMapImpl* impl = (HashMapImpl*) self;
241  PHashTableEntry *entry = NULL;
242  ESR_ReturnCode rc;
243
244  if ((rc = HashMap_GetEntryAtIndex(impl, index, &entry)) != ESR_SUCCESS)
245    goto end;
246
247  rc = PHashTableEntryGetKeyValue(entry, (void **)NULL, (void **)value);
248
249end:
250  return rc;
251}
252
253ESR_ReturnCode HashMap_RemoveAtIndex(HashMap* self, const size_t index)
254{
255  HashMapImpl* impl = (HashMapImpl*) self;
256  PHashTableEntry *entry = NULL;
257  ESR_ReturnCode rc;
258  void *key;
259
260  if ((rc = HashMap_GetEntryAtIndex(impl, index, &entry)) != ESR_SUCCESS)
261    goto end;
262
263  if ((rc = PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)NULL)) != ESR_SUCCESS)
264    goto end;
265
266  if (key != NULL) FREE(key);
267
268  rc = PHashTableEntryRemove(entry);
269
270end:
271  return rc;
272}
273
274ESR_ReturnCode HashMap_GetSize(HashMap* self, size_t* size)
275{
276  HashMapImpl* impl = (HashMapImpl*) self;
277  return PHashTableGetSize(impl->table, size);
278}
279
280ESR_ReturnCode HashMap_Destroy(HashMap* self)
281{
282  HashMapImpl* impl = (HashMapImpl*) self;
283  ESR_ReturnCode rc = ESR_SUCCESS;
284
285  if ((rc = self->removeAll(self)) != ESR_SUCCESS)
286    goto end;
287
288  if (impl->table != NULL)
289    rc = PHashTableDestroy(impl->table);
290  FREE(impl);
291end:
292  return rc;
293}
294