iterator support in StringSet class
[public/netxms.git] / src / libnetxms / strset.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** NetXMS Foundation Library
4 ** Copyright (C) 2003-2016 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published
8 ** by the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: strset.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <uthash.h>
26
27 /**
28 * Entry
29 */
30 struct StringSetEntry
31 {
32 UT_hash_handle hh;
33 TCHAR *str;
34 };
35
36 /**
37 * Constructor
38 */
39 StringSet::StringSet()
40 {
41 m_data = NULL;
42 }
43
44 /**
45 * Destructor
46 */
47 StringSet::~StringSet()
48 {
49 clear();
50 }
51
52 /**
53 * Add string to set
54 */
55 void StringSet::add(const TCHAR *str)
56 {
57 StringSetEntry *entry;
58 int keyLen = (int)(_tcslen(str) * sizeof(TCHAR));
59 HASH_FIND(hh, m_data, str, keyLen, entry);
60 if (entry == NULL)
61 {
62 entry = (StringSetEntry *)malloc(sizeof(StringSetEntry));
63 entry->str = _tcsdup(str);
64 HASH_ADD_KEYPTR(hh, m_data, entry->str, keyLen, entry);
65 }
66 }
67
68 /**
69 * Add string to set - must be allocated by malloc()
70 */
71 void StringSet::addPreallocated(TCHAR *str)
72 {
73 StringSetEntry *entry;
74 int keyLen = (int)(_tcslen(str) * sizeof(TCHAR));
75 HASH_FIND(hh, m_data, str, keyLen, entry);
76 if (entry == NULL)
77 {
78 entry = (StringSetEntry *)malloc(sizeof(StringSetEntry));
79 entry->str = str;
80 HASH_ADD_KEYPTR(hh, m_data, entry->str, keyLen, entry);
81 }
82 else
83 {
84 free(str);
85 }
86 }
87
88 /**
89 * Remove string from set
90 */
91 void StringSet::remove(const TCHAR *str)
92 {
93 StringSetEntry *entry;
94 int keyLen = (int)(_tcslen(str) * sizeof(TCHAR));
95 HASH_FIND(hh, m_data, str, keyLen, entry);
96 if (entry != NULL)
97 {
98 HASH_DEL(m_data, entry);
99 free(entry->str);
100 free(entry);
101 }
102 }
103
104 /**
105 * Clear set
106 */
107 void StringSet::clear()
108 {
109 StringSetEntry *entry, *tmp;
110 HASH_ITER(hh, m_data, entry, tmp)
111 {
112 HASH_DEL(m_data, entry);
113 free(entry->str);
114 free(entry);
115 }
116 }
117
118 /**
119 * Check if given string is in set
120 */
121 bool StringSet::contains(const TCHAR *str) const
122 {
123 StringSetEntry *entry;
124 int keyLen = (int)(_tcslen(str) * sizeof(TCHAR));
125 HASH_FIND(hh, m_data, str, keyLen, entry);
126 return entry != NULL;
127 }
128
129 /**
130 * Check if two given sets are equal
131 */
132 bool StringSet::equals(const StringSet *s) const
133 {
134 if (s->size() != size())
135 return false;
136
137 StringSetEntry *entry, *tmp;
138 HASH_ITER(hh, m_data, entry, tmp)
139 {
140 if (!s->contains(entry->str))
141 return false;
142 }
143 return true;
144 }
145
146 /**
147 * Get set size
148 */
149 int StringSet::size() const
150 {
151 return HASH_COUNT(m_data);
152 }
153
154 /**
155 * Enumerate entries
156 */
157 void StringSet::forEach(bool (*cb)(const TCHAR *, void *), void *userData) const
158 {
159 StringSetEntry *entry, *tmp;
160 HASH_ITER(hh, m_data, entry, tmp)
161 {
162 if (!cb(entry->str, userData))
163 break;
164 }
165 }
166
167 /**
168 * Split source string and add all elements
169 */
170 void StringSet::splitAndAdd(const TCHAR *src, const TCHAR *separator)
171 {
172 int slen = (int)_tcslen(separator);
173 if (slen == 0)
174 {
175 add(src);
176 return;
177 }
178
179 const TCHAR *curr = src;
180 while(curr != NULL)
181 {
182 const TCHAR *next = _tcsstr(curr, separator);
183 if (next != NULL)
184 {
185 int len = (int)(next - curr);
186 TCHAR *value = (TCHAR *)malloc((len + 1) * sizeof(TCHAR));
187 memcpy(value, curr, len * sizeof(TCHAR));
188 value[len] = 0;
189 addPreallocated(value);
190 next += slen;
191 }
192 else
193 {
194 add(curr);
195 }
196 curr = next;
197 }
198 }
199
200 /**
201 * Add all entries from source set
202 */
203 void StringSet::addAll(StringSet *src)
204 {
205 StringSetEntry *entry, *tmp;
206 HASH_ITER(hh, src->m_data, entry, tmp)
207 {
208 add(entry->str);
209 }
210 }
211
212 /**
213 * Add all entries from TCHAR pointer arrays
214 */
215 void StringSet::addAll(TCHAR **strings, int count)
216 {
217 for(int i = 0; i < count; i++)
218 if (strings[i] != NULL)
219 add(strings[i]);
220 }
221
222 /**
223 * Add all entries from TCHAR pointer arrays. Takes ownership of pre-allocated strings.
224 */
225 void StringSet::addAllPreallocated(TCHAR **strings, int count)
226 {
227 for(int i = 0; i < count; i++)
228 if (strings[i] != NULL)
229 addPreallocated(strings[i]);
230 }
231
232 /**
233 * Fill NXCP message with string set data
234 *
235 * @param msg NXCP message
236 * @param baseId base ID for data fields
237 * @param countId ID of field where number of strings should be placed
238 */
239 void StringSet::fillMessage(NXCPMessage *msg, UINT32 baseId, UINT32 countId) const
240 {
241 UINT32 varId = baseId;
242 StringSetEntry *entry, *tmp;
243 HASH_ITER(hh, m_data, entry, tmp)
244 {
245 msg->setField(varId++, entry->str);
246 }
247 msg->setField(countId, varId - baseId);
248 }
249
250 /**
251 * Add all strings from NXCP message
252 *
253 * @param msg NXCP message
254 * @param baseId base ID for data fields
255 * @param countId ID of field with number of strings
256 * @param clearBeforeAdd if set to true, existing content will be deleted
257 * @param toUppercase if set to true, all strings will be converted to upper case before adding
258 */
259 void StringSet::addAllFromMessage(const NXCPMessage *msg, UINT32 baseId, UINT32 countId, bool clearBeforeAdd, bool toUppercase)
260 {
261 if (clearBeforeAdd)
262 clear();
263
264 int count = (int)msg->getFieldAsUInt32(countId);
265 UINT32 varId = baseId;
266 for(int i = 0; i < count; i++)
267 {
268 TCHAR *str = msg->getFieldAsString(varId++);
269 if (str != NULL)
270 {
271 if (toUppercase)
272 _tcsupr(str);
273 addPreallocated(str);
274 }
275 }
276 }
277
278 /**
279 * Get all entries as one string, optionally separated by given separator
280 *
281 * @parm separator optional separator, may be NULL
282 */
283 String StringSet::join(const TCHAR *separator)
284 {
285 String result;
286 result.setAllocationStep(4096);
287 StringSetEntry *entry, *tmp;
288 HASH_ITER(hh, m_data, entry, tmp)
289 {
290 if ((separator != NULL) && (result.length() > 0))
291 result += separator;
292 result += entry->str;
293 }
294 return result;
295 }
296
297 /**
298 * Hash map iterator
299 */
300 StringSetIterator::StringSetIterator(StringSet *stringSet)
301 {
302 m_stringSet = stringSet;
303 m_curr = NULL;
304 m_next = NULL;
305 }
306
307 /**
308 * Next element availability indicator
309 */
310 bool StringSetIterator::hasNext()
311 {
312 if (m_stringSet->m_data == NULL)
313 return false;
314
315 return (m_curr != NULL) ? (m_next != NULL) : true;
316 }
317
318 /**
319 * Get next element
320 */
321 void *StringSetIterator::next()
322 {
323 if (m_stringSet->m_data == NULL)
324 return NULL;
325
326 if (m_curr == NULL) // iteration not started
327 {
328 HASH_ITER_START(hh, m_stringSet->m_data, m_curr, m_next);
329 }
330 else
331 {
332 if (m_next == NULL)
333 return NULL;
334
335 HASH_ITER_NEXT(hh, m_curr, m_next);
336 }
337 return m_curr->str;
338 }
339
340 /**
341 * Remove current element
342 */
343 void StringSetIterator::remove()
344 {
345 if (m_curr == NULL)
346 return;
347
348 HASH_DEL(m_stringSet->m_data, m_curr);
349 free(m_curr->str);
350 free(m_curr);
351 }