1 #ifndef _DEPOOLSET_H
2 #define _DEPOOLSET_H
3 /*-------------------------------------------------------------------------
4  * drawElements Memory Pool Library
5  * --------------------------------
6  *
7  * Copyright 2014 The Android Open Source Project
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  *//*!
22  * \file
23  * \brief Memory pool set class.
24  *//*--------------------------------------------------------------------*/
25 
26 #include "deDefs.h"
27 #include "deMemPool.h"
28 #include "dePoolArray.h"
29 #include "deInt32.h"
30 
31 #include <string.h> /* memset() */
32 
33 enum
34 {
35 	DE_SET_ELEMENTS_PER_SLOT	= 4
36 };
37 
38 DE_BEGIN_EXTERN_C
39 
40 void	dePoolSet_selfTest		(void);
41 
42 DE_END_EXTERN_C
43 
44 /*--------------------------------------------------------------------*//*!
45  * \brief Declare a template pool set class interface.
46  * \param TYPENAME	Type name of the declared set.
47  * \param KEYTYPE	Type of the key.
48  *
49  * This macro declares the interface for a set. For the implementation of
50  * the set, see DE_IMPLEMENT_POOL_HASH. Usually this macro is put into the
51  * header file and the implementation macro is put in some .c file.
52  *
53  * \todo [petri] Detailed description.
54  *
55  * The functions for operating the set are:
56  * \todo [petri] Figure out how to comment these in Doxygen-style.
57  *
58  * \code
59  * Set*     Set_create            (deMemPool* pool);
60  * int      Set_getNumElements    (const Set* array);
61  * deBool   Set_exists            (const Set* array, Key key);
62  * deBool   Set_insert            (Set* array, Key key);
63  * void     Set_delete            (Set* array, Key key);
64  * \endcode
65 *//*--------------------------------------------------------------------*/
66 #define DE_DECLARE_POOL_SET(TYPENAME, KEYTYPE)		\
67 \
68 typedef struct TYPENAME##Slot_s TYPENAME##Slot;    \
69 \
70 struct TYPENAME##Slot_s \
71 {    \
72 	int				numUsed; \
73 	TYPENAME##Slot*	nextSlot; \
74 	KEYTYPE			keys[DE_SET_ELEMENTS_PER_SLOT]; \
75 }; \
76 \
77 typedef struct TYPENAME##_s    \
78 {    \
79 	deMemPool*			pool;    \
80 	int					numElements;    \
81 \
82 	int					slotTableSize;    \
83 	TYPENAME##Slot**	slotTable;		\
84 	TYPENAME##Slot*		slotFreeList;		\
85 } TYPENAME;    \
86 \
87 typedef struct TYPENAME##Iter_s \
88 {	\
89 	const TYPENAME*			hash;			\
90 	int						curSlotIndex;	\
91 	const TYPENAME##Slot*	curSlot;		\
92 	int						curElemIndex;	\
93 } TYPENAME##Iter;	\
94 \
95 TYPENAME*	TYPENAME##_create		(deMemPool* pool);    \
96 void		TYPENAME##_reset		(TYPENAME* set);    \
97 deBool		TYPENAME##_reserve		(TYPENAME* set, int capacity);    \
98 deBool		TYPENAME##_exists		(const TYPENAME* set, KEYTYPE key);    \
99 deBool		TYPENAME##_insert		(TYPENAME* set, KEYTYPE key);    \
100 void		TYPENAME##_delete		(TYPENAME* set, KEYTYPE key);    \
101 \
102 DE_INLINE int		TYPENAME##_getNumElements	(const TYPENAME* set)							DE_UNUSED_FUNCTION;	\
103 DE_INLINE void		TYPENAME##Iter_init			(const TYPENAME* hash, TYPENAME##Iter* iter)	DE_UNUSED_FUNCTION;	\
104 DE_INLINE deBool	TYPENAME##Iter_hasItem		(const TYPENAME##Iter* iter)					DE_UNUSED_FUNCTION;	\
105 DE_INLINE void		TYPENAME##Iter_next			(TYPENAME##Iter* iter)							DE_UNUSED_FUNCTION;	\
106 DE_INLINE KEYTYPE	TYPENAME##Iter_getKey		(const TYPENAME##Iter* iter)					DE_UNUSED_FUNCTION;	\
107 DE_INLINE deBool	TYPENAME##_safeInsert		(TYPENAME* set, KEYTYPE key)					DE_UNUSED_FUNCTION;	\
108 DE_INLINE void		TYPENAME##_safeDelete		(TYPENAME* set, KEYTYPE key)					DE_UNUSED_FUNCTION;	\
109 \
110 DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* set)    \
111 {    \
112 	return set->numElements;    \
113 }    \
114 \
115 DE_INLINE void TYPENAME##Iter_init (const TYPENAME* hash, TYPENAME##Iter* iter)    \
116 {	\
117 	iter->hash			= hash;		\
118 	iter->curSlotIndex	= 0;		\
119 	iter->curSlot		= DE_NULL;	\
120 	iter->curElemIndex	= 0;		\
121 	if (TYPENAME##_getNumElements(hash) > 0)		\
122 	{												\
123 		int slotTableSize	= hash->slotTableSize;	\
124 		int slotNdx			= 0;					\
125 		while (slotNdx < slotTableSize)				\
126 		{											\
127 			if (hash->slotTable[slotNdx])			\
128 				break;								\
129 			slotNdx++;								\
130 		}											\
131 		DE_ASSERT(slotNdx < slotTableSize);			\
132 		iter->curSlotIndex = slotNdx;				\
133 		iter->curSlot = hash->slotTable[slotNdx];	\
134 		DE_ASSERT(iter->curSlot);					\
135 	}	\
136 }	\
137 \
138 DE_INLINE deBool TYPENAME##Iter_hasItem	(const TYPENAME##Iter* iter)    \
139 {	\
140 	return (iter->curSlot != DE_NULL); \
141 }	\
142 \
143 DE_INLINE void TYPENAME##Iter_next (TYPENAME##Iter* iter)    \
144 {	\
145 	DE_ASSERT(TYPENAME##Iter_hasItem(iter));	\
146 	if (++iter->curElemIndex == iter->curSlot->numUsed)	\
147 	{													\
148 		iter->curElemIndex = 0;							\
149 		if (iter->curSlot->nextSlot)					\
150 		{												\
151 			iter->curSlot = iter->curSlot->nextSlot;	\
152 		}												\
153 		else											\
154 		{												\
155 			const TYPENAME*	hash			= iter->hash;			\
156 			int				curSlotIndex	= iter->curSlotIndex;	\
157 			int				slotTableSize	= hash->slotTableSize;	\
158 			while (++curSlotIndex < slotTableSize)		\
159 			{											\
160 				if (hash->slotTable[curSlotIndex])		\
161 					break;								\
162 			}											\
163 			iter->curSlotIndex = curSlotIndex;			\
164 			if (curSlotIndex < slotTableSize)					\
165 				iter->curSlot = hash->slotTable[curSlotIndex];	\
166 			else												\
167 				iter->curSlot = DE_NULL;						\
168 		}	\
169 	}	\
170 }	\
171 \
172 DE_INLINE KEYTYPE TYPENAME##Iter_getKey	(const TYPENAME##Iter* iter)    \
173 {	\
174 	DE_ASSERT(TYPENAME##Iter_hasItem(iter));	\
175 	return iter->curSlot->keys[iter->curElemIndex];	\
176 }	\
177 \
178 DE_INLINE deBool TYPENAME##_safeInsert (TYPENAME* set, KEYTYPE key)	\
179 {																	\
180 	DE_ASSERT(set);													\
181 	if (TYPENAME##_exists(set, key))								\
182 		return DE_TRUE;												\
183 	return TYPENAME##_insert(set, key);								\
184 }																	\
185 \
186 DE_INLINE void TYPENAME##_safeDelete (TYPENAME* set, KEYTYPE key)	\
187 {																	\
188 	DE_ASSERT(set);													\
189 	if (TYPENAME##_exists(set, key))								\
190 		TYPENAME##_delete(set, key);								\
191 }																	\
192 \
193 struct TYPENAME##Dummy_s { int dummy; }
194 
195 /*--------------------------------------------------------------------*//*!
196  * \brief Implement a template pool set class.
197  * \param TYPENAME	Type name of the declared set.
198  * \param KEYTYPE	Type of the key.
199  * \param HASHFUNC	Function used for hashing the key.
200  * \param CMPFUNC	Function used for exact matching of the keys.
201  *
202  * This macro has implements the set declared with DE_DECLARE_POOL_SET.
203  * Usually this macro should be used from a .c file, since the macro expands
204  * into multiple functions. The TYPENAME and KEYTYPE parameters
205  * must match those of the declare macro.
206 *//*--------------------------------------------------------------------*/
207 #define DE_IMPLEMENT_POOL_SET(TYPENAME, KEYTYPE, HASHFUNC, CMPFUNC)		\
208 \
209 TYPENAME* TYPENAME##_create (deMemPool* pool)    \
210 {   \
211 	/* Alloc struct. */ \
212 	TYPENAME* set = DE_POOL_NEW(pool, TYPENAME); \
213 	if (!set) \
214 		return DE_NULL; \
215 \
216 	/* Init array. */ \
217 	memset(set, 0, sizeof(TYPENAME)); \
218 	set->pool = pool; \
219 \
220 	return set; \
221 } \
222 \
223 void TYPENAME##_reset (TYPENAME* set)    \
224 {   \
225 	int slotNdx; \
226 	for (slotNdx = 0; slotNdx < set->slotTableSize; slotNdx++)	\
227 	{	\
228 		TYPENAME##Slot* slot = set->slotTable[slotNdx]; \
229 		while (slot) \
230 		{ \
231 			TYPENAME##Slot*	nextSlot = slot->nextSlot;	\
232 			slot->nextSlot = set->slotFreeList;		\
233 			set->slotFreeList = slot;	\
234 			slot->numUsed = 0;			\
235 			slot = nextSlot;			\
236 		}	\
237 		set->slotTable[slotNdx] = DE_NULL; \
238 	}	\
239 	set->numElements = 0; \
240 }	\
241 \
242 TYPENAME##Slot* TYPENAME##_allocSlot (TYPENAME* set)    \
243 {   \
244 	TYPENAME##Slot* slot; \
245 	if (set->slotFreeList) \
246 	{ \
247 		slot = set->slotFreeList; \
248 		set->slotFreeList = set->slotFreeList->nextSlot; \
249 	} \
250 	else \
251 		slot = (TYPENAME##Slot*)deMemPool_alloc(set->pool, sizeof(TYPENAME##Slot) * DE_SET_ELEMENTS_PER_SLOT); \
252 \
253 	if (slot) \
254 	{ \
255 		slot->nextSlot = DE_NULL; \
256 		slot->numUsed = 0; \
257 	} \
258 \
259 	return slot; \
260 } \
261 \
262 deBool TYPENAME##_rehash (TYPENAME* set, int newSlotTableSize)    \
263 {    \
264 	DE_ASSERT(deIsPowerOfTwo32(newSlotTableSize) && newSlotTableSize > 0); \
265 	if (newSlotTableSize > set->slotTableSize)    \
266 	{ \
267 		TYPENAME##Slot**	oldSlotTable = set->slotTable; \
268 		TYPENAME##Slot**	newSlotTable = (TYPENAME##Slot**)deMemPool_alloc(set->pool, sizeof(TYPENAME##Slot*) * newSlotTableSize); \
269 		int					oldSlotTableSize = set->slotTableSize; \
270 		int					slotNdx; \
271 \
272 		if (!newSlotTable) \
273 			return DE_FALSE; \
274 \
275 		for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \
276 			newSlotTable[slotNdx] = oldSlotTable[slotNdx]; \
277 \
278 		for (slotNdx = oldSlotTableSize; slotNdx < newSlotTableSize; slotNdx++) \
279 			newSlotTable[slotNdx] = DE_NULL; \
280 \
281 		set->slotTableSize		= newSlotTableSize; \
282 		set->slotTable			= newSlotTable; \
283 \
284 		for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \
285 		{ \
286 			TYPENAME##Slot* slot = oldSlotTable[slotNdx]; \
287 			newSlotTable[slotNdx] = DE_NULL; \
288 			while (slot) \
289 			{ \
290 				int elemNdx; \
291 				for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
292 				{ \
293 					set->numElements--; \
294 					if (!TYPENAME##_insert(set, slot->keys[elemNdx])) \
295 						return DE_FALSE; \
296 				} \
297 				slot = slot->nextSlot; \
298 			} \
299 		} \
300 	} \
301 \
302 	return DE_TRUE;    \
303 }    \
304 \
305 deBool TYPENAME##_exists (const TYPENAME* set, KEYTYPE key)    \
306 {    \
307 	if (set->numElements > 0) \
308 	{	\
309 		int				slotNdx	= HASHFUNC(key) & (set->slotTableSize - 1); \
310 		TYPENAME##Slot*	slot	= set->slotTable[slotNdx]; \
311 		DE_ASSERT(deInBounds32(slotNdx, 0, set->slotTableSize)); \
312 	\
313 		while (slot) \
314 		{ \
315 			int elemNdx; \
316 			for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
317 			{ \
318 				if (CMPFUNC(slot->keys[elemNdx], key)) \
319 					return DE_TRUE; \
320 			} \
321 			slot = slot->nextSlot; \
322 		} \
323 	} \
324 \
325 	return DE_FALSE; \
326 }    \
327 \
328 deBool TYPENAME##_insert (TYPENAME* set, KEYTYPE key)    \
329 {    \
330 	int				slotNdx; \
331 	TYPENAME##Slot*	slot; \
332 \
333 	DE_ASSERT(set); \
334 	DE_ASSERT(!TYPENAME##_exists(set, key)); \
335 \
336 	if ((set->numElements + 1) >= set->slotTableSize * DE_SET_ELEMENTS_PER_SLOT) \
337 		if (!TYPENAME##_rehash(set, deMax32(4, 2*set->slotTableSize))) \
338 			return DE_FALSE; \
339 \
340 	slotNdx	= HASHFUNC(key) & (set->slotTableSize - 1); \
341 	DE_ASSERT(slotNdx >= 0 && slotNdx < set->slotTableSize); \
342 	slot	= set->slotTable[slotNdx]; \
343 \
344 	if (!slot) \
345 	{ \
346 		slot = TYPENAME##_allocSlot(set); \
347 		if (!slot) return DE_FALSE; \
348 		set->slotTable[slotNdx] = slot; \
349 	} \
350 \
351 	for (;;) \
352 	{ \
353 		if (slot->numUsed == DE_SET_ELEMENTS_PER_SLOT) \
354 		{ \
355 			if (slot->nextSlot) \
356 				slot = slot->nextSlot; \
357 			else \
358 			{ \
359 				TYPENAME##Slot* nextSlot = TYPENAME##_allocSlot(set); \
360 				if (!nextSlot) return DE_FALSE; \
361 				slot->nextSlot = nextSlot; \
362 				slot = nextSlot; \
363 			} \
364 		} \
365 		else \
366 		{ \
367 			slot->keys[slot->numUsed]	= key; \
368 			slot->numUsed++; \
369 			set->numElements++; \
370 			return DE_TRUE; \
371 		} \
372 	} \
373 } \
374 \
375 void TYPENAME##_delete (TYPENAME* set, KEYTYPE key)    \
376 {    \
377 	int				slotNdx; \
378 	TYPENAME##Slot*	slot; \
379 	TYPENAME##Slot*	prevSlot = DE_NULL; \
380 \
381 	DE_ASSERT(set->numElements > 0); \
382 	slotNdx	= HASHFUNC(key) & (set->slotTableSize - 1); \
383 	DE_ASSERT(slotNdx >= 0 && slotNdx < set->slotTableSize); \
384 	slot	= set->slotTable[slotNdx]; \
385 	DE_ASSERT(slot); \
386 \
387 	for (;;) \
388 	{ \
389 		int elemNdx; \
390 		DE_ASSERT(slot->numUsed > 0); \
391 		for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
392 		{ \
393 			if (CMPFUNC(key, slot->keys[elemNdx])) \
394 			{ \
395 				TYPENAME##Slot*	lastSlot = slot; \
396 				while (lastSlot->nextSlot) \
397 				{ \
398 					prevSlot = lastSlot; \
399 					lastSlot = lastSlot->nextSlot; \
400 				} \
401 \
402 				slot->keys[elemNdx] = lastSlot->keys[lastSlot->numUsed-1]; \
403 				lastSlot->numUsed--; \
404 \
405 				if (lastSlot->numUsed == 0) \
406 				{ \
407 					if (prevSlot) \
408 						prevSlot->nextSlot = DE_NULL; \
409 					else \
410 						set->slotTable[slotNdx] = DE_NULL; \
411 \
412 					lastSlot->nextSlot = set->slotFreeList; \
413 					set->slotFreeList = lastSlot; \
414 				} \
415 \
416 				set->numElements--; \
417 				return; \
418 			} \
419 		} \
420 \
421 		prevSlot = slot; \
422 		slot = slot->nextSlot; \
423 		DE_ASSERT(slot); \
424 	} \
425 }    \
426 \
427 struct TYPENAME##Dummy2_s { int dummy; }
428 
429 /* Copy-to-array templates. */
430 
431 #define DE_DECLARE_POOL_SET_TO_ARRAY(SETTYPENAME, ARRAYTYPENAME)		\
432 	deBool SETTYPENAME##_copyToArray(const SETTYPENAME* set, ARRAYTYPENAME* array);	\
433 	struct SETTYPENAME##_##ARRAYTYPENAME##_declare_dummy { int dummy; }
434 
435 #define DE_IMPLEMENT_POOL_SET_TO_ARRAY(SETTYPENAME, ARRAYTYPENAME)		\
436 	deBool SETTYPENAME##_copyToArray(const SETTYPENAME* set, ARRAYTYPENAME* array)	\
437 	{	\
438 		int numElements	= set->numElements;	\
439 		int arrayNdx	= 0;	\
440 		int slotNdx;	\
441 \
442 		if (!ARRAYTYPENAME##_setSize(array, numElements))	\
443 			return DE_FALSE;	\
444 \
445 		for (slotNdx = 0; slotNdx < set->slotTableSize; slotNdx++) \
446 		{ \
447 			const SETTYPENAME##Slot* slot = set->slotTable[slotNdx]; \
448 			while (slot) \
449 			{ \
450 				int elemNdx; \
451 				for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
452 					ARRAYTYPENAME##_set(array, arrayNdx++, slot->keys[elemNdx]); \
453 				slot = slot->nextSlot; \
454 			} \
455 		}	\
456 		DE_ASSERT(arrayNdx == numElements);	\
457 		return DE_TRUE;	\
458 	}	\
459 	struct SETTYPENAME##_##ARRAYTYPENAME##_implement_dummy { int dummy; }
460 
461 /*--------------------------------------------------------------------*//*!
462  * \brief Declare set-wise operations for a set template.
463  * \param TYPENAME	Type name of the declared set.
464  * \param KEYTYPE	Type of the key.
465  *
466  * This macro declares union and intersection operations for a set.
467  * For implementation see DE_IMPLEMENT_POOL_SET_UNION_INTERSECT.
468  *
469  * \todo [petri] Detailed description.
470  *
471  * The functions for operating the set are:
472  * \todo [petri] Figure out how to comment these in Doxygen-style.
473  *
474  * \code
475  * deBool	Set_union				(Set* to, const Set* a, const Set* b);
476  * deBool	Set_unionInplace		(Set* a, const Set* b);
477  * deBool	Set_intersect			(Set* to, const Set* a, const Set* b);
478  * void		Set_intersectInplace	(Set* a, const Set* b);
479  * deBool   Set_difference			(Set* to, const Set* a, const Set* b);
480  * void     Set_differenceInplace	(Set* a, const Set* b);
481  * \endcode
482 *//*--------------------------------------------------------------------*/
483 #define DE_DECLARE_POOL_SET_SETWISE_OPERATIONS(TYPENAME)	\
484 	deBool TYPENAME##_union (TYPENAME* to, const TYPENAME* a, const TYPENAME* b);	\
485 	deBool TYPENAME##_unionInplace (TYPENAME* a, const TYPENAME* b);	\
486 	deBool TYPENAME##_intersect (TYPENAME* to, const TYPENAME* a, const TYPENAME* b);	\
487 	void TYPENAME##_intersectInplace (TYPENAME* a, const TYPENAME* b);	\
488 	deBool TYPENAME##_difference (TYPENAME* to, const TYPENAME* a, const TYPENAME* b);	\
489 	void TYPENAME##_differenceInplace (TYPENAME* a, const TYPENAME* b);	\
490 	struct TYPENAME##SetwiseDeclareDummy_s { int dummy; }
491 
492 #define DE_IMPLEMENT_POOL_SET_SETWISE_OPERATIONS(TYPENAME, KEYTYPE)	\
493 deBool TYPENAME##_union (TYPENAME* to, const TYPENAME* a, const TYPENAME* b)	\
494 {	\
495 	TYPENAME##_reset(to);	\
496 	if (!TYPENAME##_unionInplace(to, a))	\
497 		return DE_FALSE;	\
498 	if (!TYPENAME##_unionInplace(to, b))	\
499 		return DE_FALSE;	\
500 	return DE_TRUE;	\
501 }	\
502 \
503 deBool TYPENAME##_unionInplace (TYPENAME* a, const TYPENAME* b)	\
504 {	\
505 	TYPENAME##Iter iter;	\
506 	for (TYPENAME##Iter_init(b, &iter);	\
507 		 TYPENAME##Iter_hasItem(&iter);	\
508 		 TYPENAME##Iter_next(&iter))	\
509 	{	\
510 		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
511 		if (!TYPENAME##_exists(a, key))	\
512 		{	\
513 			if (!TYPENAME##_insert(a, key))	\
514 				return DE_FALSE;	\
515 		}	\
516 	}	\
517 	return DE_TRUE;	\
518 }	\
519 \
520 deBool TYPENAME##_intersect (TYPENAME* to, const TYPENAME* a, const TYPENAME* b)	\
521 {	\
522 	TYPENAME##Iter iter;	\
523 	TYPENAME##_reset(to);	\
524 	for (TYPENAME##Iter_init(a, &iter);	\
525 		 TYPENAME##Iter_hasItem(&iter);	\
526 		 TYPENAME##Iter_next(&iter))	\
527 	{	\
528 		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
529 		if (TYPENAME##_exists(b, key))	\
530 		{	\
531 			if (!TYPENAME##_insert(to, key))	\
532 				return DE_FALSE;	\
533 		}	\
534 	}	\
535 	return DE_TRUE;	\
536 }	\
537 \
538 void TYPENAME##_intersectInplace (TYPENAME* a, const TYPENAME* b)	\
539 {	\
540 	DE_UNREF(a && b);	\
541 	DE_ASSERT(!"Not implemented.");	\
542 }	\
543 \
544 deBool TYPENAME##_difference (TYPENAME* to, const TYPENAME* a, const TYPENAME* b)	\
545 {	\
546 	TYPENAME##Iter iter;	\
547 	TYPENAME##_reset(to);	\
548 	for (TYPENAME##Iter_init(a, &iter);	\
549 		 TYPENAME##Iter_hasItem(&iter);	\
550 		 TYPENAME##Iter_next(&iter))	\
551 	{	\
552 		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
553 		if (!TYPENAME##_exists(b, key))	\
554 		{	\
555 			if (!TYPENAME##_insert(to, key))	\
556 				return DE_FALSE;	\
557 		}	\
558 	}	\
559 	return DE_TRUE;	\
560 }	\
561 \
562 void TYPENAME##_differenceInplace (TYPENAME* a, const TYPENAME* b)	\
563 {	\
564 	TYPENAME##Iter iter;	\
565 	for (TYPENAME##Iter_init(b, &iter);	\
566 		 TYPENAME##Iter_hasItem(&iter);	\
567 		 TYPENAME##Iter_next(&iter))	\
568 	{	\
569 		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
570 		if (TYPENAME##_exists(a, key))	\
571 			TYPENAME##_delete(a, key);	\
572 	}	\
573 }	\
574 \
575 struct TYPENAME##UnionIntersectImplementDummy_s { int dummy; }
576 
577 #endif /* _DEPOOLSET_H */
578