1 #ifndef _WDL_ASSOCARRAY_H_
2 #define _WDL_ASSOCARRAY_H_
7 // on all of these, if valdispose is set, the array will dispose of values as needed.
8 // if keydup/keydispose are set, copies of (any) key data will be made/destroyed as necessary
11 // WDL_AssocArrayImpl can be used on its own, and can contain structs for keys or values
12 template <class KEY
, class VAL
> class WDL_AssocArrayImpl
14 WDL_AssocArrayImpl(const WDL_AssocArrayImpl
&cp
) { CopyContents(cp
); }
16 WDL_AssocArrayImpl
&operator=(const WDL_AssocArrayImpl
&cp
) { CopyContents(cp
); return *this; }
20 explicit WDL_AssocArrayImpl(int (*keycmp
)(KEY
*k1
, KEY
*k2
), KEY (*keydup
)(KEY
)=0, void (*keydispose
)(KEY
)=0, void (*valdispose
)(VAL
)=0)
24 m_keydispose
= keydispose
;
25 m_valdispose
= valdispose
;
33 VAL
* GetPtr(KEY key
, KEY
*keyPtrOut
=NULL
) const
36 int i
= LowerBound(key
, &ismatch
);
39 KeyVal
* kv
= m_data
.Get()+i
;
40 if (keyPtrOut
) *keyPtrOut
= kv
->key
;
46 bool Exists(KEY key
) const
49 LowerBound(key
, &ismatch
);
53 int Insert(KEY key
, VAL val
)
56 int i
= LowerBound(key
, &ismatch
);
59 KeyVal
* kv
= m_data
.Get()+i
;
60 if (m_valdispose
) m_valdispose(kv
->val
);
65 KeyVal
* kv
= m_data
.Resize(m_data
.GetSize()+1)+i
;
66 memmove(kv
+1, kv
, (m_data
.GetSize()-i
-1)*(unsigned int)sizeof(KeyVal
));
67 if (m_keydup
) key
= m_keydup(key
);
77 int i
= LowerBound(key
, &ismatch
);
80 KeyVal
* kv
= m_data
.Get()+i
;
81 if (m_keydispose
) m_keydispose(kv
->key
);
82 if (m_valdispose
) m_valdispose(kv
->val
);
87 void DeleteByIndex(int idx
)
89 if (idx
>= 0 && idx
< m_data
.GetSize())
91 KeyVal
* kv
= m_data
.Get()+idx
;
92 if (m_keydispose
) m_keydispose(kv
->key
);
93 if (m_valdispose
) m_valdispose(kv
->val
);
98 void DeleteAll(bool resizedown
=false)
100 if (m_keydispose
|| m_valdispose
)
103 for (i
= 0; i
< m_data
.GetSize(); ++i
)
105 KeyVal
* kv
= m_data
.Get()+i
;
106 if (m_keydispose
) m_keydispose(kv
->key
);
107 if (m_valdispose
) m_valdispose(kv
->val
);
110 m_data
.Resize(0, resizedown
);
115 return m_data
.GetSize();
118 VAL
* EnumeratePtr(int i
, KEY
* key
=0) const
120 if (i
>= 0 && i
< m_data
.GetSize())
122 KeyVal
* kv
= m_data
.Get()+i
;
123 if (key
) *key
= kv
->key
;
129 KEY
* ReverseLookupPtr(VAL val
) const
132 for (i
= 0; i
< m_data
.GetSize(); ++i
)
134 KeyVal
* kv
= m_data
.Get()+i
;
135 if (kv
->val
== val
) return &kv
->key
;
140 void ChangeKey(KEY oldkey
, KEY newkey
)
143 int i
= LowerBound(oldkey
, &ismatch
);
146 KeyVal
* kv
= m_data
.Get()+i
;
147 if (m_keydispose
) m_keydispose(kv
->key
);
148 if (m_keydup
) newkey
= m_keydup(newkey
);
154 void ChangeKeyByIndex(int idx
, KEY newkey
, bool needsort
)
156 if (idx
>= 0 && idx
< m_data
.GetSize())
158 KeyVal
* kv
= m_data
.Get()+idx
;
159 if (m_keydispose
) m_keydispose(kv
->key
);
160 if (m_keydup
) newkey
= m_keydup(newkey
);
162 if (needsort
) Resort();
166 // fast add-block mode
167 void AddUnsorted(KEY key
, VAL val
)
169 int i
=m_data
.GetSize();
170 KeyVal
* kv
= m_data
.Resize(i
+1)+i
;
171 if (m_keydup
) key
= m_keydup(key
);
178 if (m_data
.GetSize() > 1 && m_keycmp
)
180 qsort(m_data
.Get(),m_data
.GetSize(),sizeof(KeyVal
),(int(*)(const void *,const void *))m_keycmp
);
182 // AddUnsorted can add duplicate keys
183 // unfortunately qsort is not guaranteed to preserve order,
184 // ideally this filter would always preserve the last-added key
186 for (i
=0; i
< m_data
.GetSize()-1; ++i
)
188 KeyVal
* kv
=m_data
.Get()+i
;
190 if (!m_keycmp(&kv
->key
, &nv
->key
))
198 int LowerBound(KEY key
, bool* ismatch
) const
201 int c
= m_data
.GetSize();
205 KeyVal
* kv
=m_data
.Get()+b
;
206 int cmp
= m_keycmp(&key
, &kv
->key
);
207 if (cmp
> 0) a
= b
+1;
208 else if (cmp
< 0) c
= b
;
219 int GetIdx(KEY key
) const
222 int i
= LowerBound(key
, &ismatch
);
223 if (ismatch
) return i
;
227 void SetGranul(int gran
)
229 m_data
.SetGranul(gran
);
232 void CopyContents(const WDL_AssocArrayImpl
&cp
)
235 m_keycmp
= cp
.m_keycmp
;
236 m_keydup
= cp
.m_keydup
;
237 m_keydispose
= m_keydup
? cp
.m_keydispose
: NULL
;
238 m_valdispose
= NULL
; // avoid disposing of values twice, since we don't have a valdup, we can't have a fully valid copy
242 const int n
=m_data
.GetSize();
245 KeyVal
*kv
=m_data
.Get()+x
;
246 if (kv
->key
) kv
->key
= m_keydup(kv
->key
);
251 void CopyContentsAsReference(const WDL_AssocArrayImpl
&cp
)
254 m_keydup
= NULL
; // this no longer can own any data
268 WDL_TypedBuf
<KeyVal
> m_data
;
270 int (*m_keycmp
)(KEY
*k1
, KEY
*k2
);
271 KEY (*m_keydup
)(KEY
);
272 void (*m_keydispose
)(KEY
);
273 void (*m_valdispose
)(VAL
);
278 // WDL_AssocArray adds useful functions but cannot contain structs for keys or values
279 template <class KEY
, class VAL
> class WDL_AssocArray
: public WDL_AssocArrayImpl
<KEY
, VAL
>
283 explicit WDL_AssocArray(int (*keycmp
)(KEY
*k1
, KEY
*k2
), KEY (*keydup
)(KEY
)=0, void (*keydispose
)(KEY
)=0, void (*valdispose
)(VAL
)=0)
284 : WDL_AssocArrayImpl
<KEY
, VAL
>(keycmp
, keydup
, keydispose
, valdispose
)
288 VAL
Get(KEY key
, VAL notfound
=0) const
290 VAL
* p
= this->GetPtr(key
);
295 VAL
Enumerate(int i
, KEY
* key
=0, VAL notfound
=0) const
297 VAL
* p
= this->EnumeratePtr(i
, key
);
302 KEY
ReverseLookup(VAL val
, KEY notfound
=0) const
304 KEY
* p
=this->ReverseLookupPtr(val
);
311 template <class VAL
> class WDL_IntKeyedArray
: public WDL_AssocArray
<int, VAL
>
315 explicit WDL_IntKeyedArray(void (*valdispose
)(VAL
)=0) : WDL_AssocArray
<int, VAL
>(cmpint
, NULL
, NULL
, valdispose
) {}
316 ~WDL_IntKeyedArray() {}
320 static int cmpint(int *i1
, int *i2
) { return *i1
-*i2
; }
324 template <class VAL
> class WDL_StringKeyedArray
: public WDL_AssocArray
<const char *, VAL
>
328 explicit WDL_StringKeyedArray(bool caseSensitive
=true, void (*valdispose
)(VAL
)=0) : WDL_AssocArray
<const char*, VAL
>(caseSensitive
?cmpstr
:cmpistr
, dupstr
, freestr
, valdispose
) {}
330 ~WDL_StringKeyedArray() { }
332 static const char *dupstr(const char *s
) { return strdup(s
); } // these might not be necessary but depending on the libc maybe...
333 static int cmpstr(const char **s1
, const char **s2
) { return strcmp(*s1
, *s2
); }
334 static int cmpistr(const char **a
, const char **b
) { return stricmp(*a
,*b
); }
335 static void freestr(const char* s
) { free((void*)s
); }
336 static void freecharptr(char *p
) { free(p
); }
340 template <class VAL
> class WDL_StringKeyedArray2
: public WDL_AssocArrayImpl
<const char *, VAL
>
344 explicit WDL_StringKeyedArray2(bool caseSensitive
=true, void (*valdispose
)(VAL
)=0) : WDL_AssocArrayImpl
<const char*, VAL
>(caseSensitive
?cmpstr
:cmpistr
, dupstr
, freestr
, valdispose
) {}
346 ~WDL_StringKeyedArray2() { }
348 static const char *dupstr(const char *s
) { return strdup(s
); } // these might not be necessary but depending on the libc maybe...
349 static int cmpstr(const char **s1
, const char **s2
) { return strcmp(*s1
, *s2
); }
350 static int cmpistr(const char **a
, const char **b
) { return stricmp(*a
,*b
); }
351 static void freestr(const char* s
) { free((void*)s
); }
352 static void freecharptr(char *p
) { free(p
); }
355 // sorts text as text, sorts anything that looks like a number as a number
356 template <class VAL
> class WDL_LogicalSortStringKeyedArray
: public WDL_StringKeyedArray
<VAL
>
360 explicit WDL_LogicalSortStringKeyedArray(bool caseSensitive
=true, void (*valdispose
)(VAL
)=0) : WDL_StringKeyedArray
<VAL
>(caseSensitive
, valdispose
)
362 WDL_StringKeyedArray
<VAL
>::m_keycmp
= caseSensitive
?cmpstr
:cmpistr
; // override
365 ~WDL_LogicalSortStringKeyedArray() { }
369 static int cmpstr(const char **a
, const char **b
) { return _cmpstr(*a
, *b
, true); }
370 static int cmpistr(const char **a
, const char **b
) { return _cmpstr(*a
, *b
, false); }
372 static int _cmpstr(const char *s1
, const char *s2
, bool case_sensitive
)
374 // this also exists as WDL_strcmp_logical in wdlcstring.h
375 char lastNonZeroChar
=0;
376 // last matching character, updated if not 0. this allows us to track whether
377 // we are inside of a number with the same leading digits
381 char c1
=*s1
++, c2
=*s2
++;
382 if (!c1
) return c1
-c2
;
386 if (c1
>= '0' && c1
<= '9' && c2
>= '0' && c2
<= '9')
389 if (lastNonZeroChar
< '1' || lastNonZeroChar
> '9')
391 while (c1
== '0') { c1
=*s1
++; lzdiff
--; }
392 while (c2
== '0') { c2
=*s2
++; lzdiff
++; } // lzdiff = lz2-lz1, more leading 0s = earlier in list
397 if (c1
>= '0' && c1
<= '9')
399 if (c2
< '0' || c2
> '9') return 1;
406 if (c2
>= '0' && c2
<= '9') return -1;
416 const int d
= *s1
++ - *s2
++;
420 if (lzdiff
) return lzdiff
;
426 if (c1
>='a' && c1
<='z') c1
+='A'-'a';
427 if (c2
>='a' && c2
<='z') c2
+='A'-'a';
429 if (c1
!= c2
) return c1
-c2
;
432 else if (c1
!= '0') lastNonZeroChar
=c1
;
438 template <class VAL
> class WDL_PtrKeyedArray
: public WDL_AssocArray
<INT_PTR
, VAL
>
442 explicit WDL_PtrKeyedArray(void (*valdispose
)(VAL
)=0) : WDL_AssocArray
<INT_PTR
, VAL
>(cmpptr
, 0, 0, valdispose
) {}
444 ~WDL_PtrKeyedArray() {}
448 static int cmpptr(INT_PTR
* a
, INT_PTR
* b
) { const INT_PTR d
= *a
- *b
; return d
<0?-1:(d
!=0); }