2 /*--------------------------------------------------------------------*/
3 /*--- Sets of words, with unique set identifiers. ---*/
4 /*--- hg_wordset.c ---*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Helgrind, a Valgrind tool for detecting errors
11 Copyright (C) 2007-2017 OpenWorks LLP
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29 The GNU General Public License is contained in the file COPYING.
31 Neither the names of the U.S. Department of Energy nor the
32 University of California nor the names of its contributors may be
33 used to endorse or promote products derived from this software
34 without prior written permission.
37 #include "pub_tool_basics.h"
38 #include "pub_tool_libcassert.h"
39 #include "pub_tool_libcbase.h"
40 #include "pub_tool_libcprint.h"
41 #include "pub_tool_threadstate.h"
42 #include "pub_tool_wordfm.h"
44 #include "hg_basics.h"
45 #include "hg_wordset.h" /* self */
47 // define to 1 to have (a lot of) debugging of add/re-use/die WSU entries.
50 //------------------------------------------------------------------//
51 //--- Word Cache ---//
52 //------------------------------------------------------------------//
55 struct { UWord arg1
; UWord arg2
; UWord res
; }
58 /* Each cache is a fixed sized array of N_WCACHE_STAT_MAX entries.
59 However only the first .dynMax are used. This is because at some
60 point, expanding the cache further overall gives a slowdown because
61 searching more entries more than negates any performance advantage
62 from caching those entries in the first place. Hence use .dynMax
63 to allow the size of the cache(s) to be set differently for each
64 different WordSetU. */
65 #define N_WCACHE_STAT_MAX 32
68 WCacheEnt ent
[N_WCACHE_STAT_MAX
];
69 UWord dynMax
; /* 1 .. N_WCACHE_STAT_MAX inclusive */
70 UWord inUse
; /* 0 .. dynMax inclusive */
74 #define WCache_INIT(_zzcache,_zzdynmax) \
76 tl_assert((_zzdynmax) >= 1); \
77 tl_assert((_zzdynmax) <= N_WCACHE_STAT_MAX); \
78 (_zzcache).dynMax = (_zzdynmax); \
79 (_zzcache).inUse = 0; \
82 #define WCache_LOOKUP_AND_RETURN(_retty,_zzcache,_zzarg1,_zzarg2) \
85 UWord _arg1 = (UWord)(_zzarg1); \
86 UWord _arg2 = (UWord)(_zzarg2); \
87 WCache* _cache = &(_zzcache); \
88 tl_assert(_cache->dynMax >= 1); \
89 tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX); \
90 tl_assert(_cache->inUse >= 0); \
91 tl_assert(_cache->inUse <= _cache->dynMax); \
92 if (_cache->inUse > 0) { \
93 if (_cache->ent[0].arg1 == _arg1 \
94 && _cache->ent[0].arg2 == _arg2) \
95 return (_retty)_cache->ent[0].res; \
96 for (_i = 1; _i < _cache->inUse; _i++) { \
97 if (_cache->ent[_i].arg1 == _arg1 \
98 && _cache->ent[_i].arg2 == _arg2) { \
99 WCacheEnt tmp = _cache->ent[_i-1]; \
100 _cache->ent[_i-1] = _cache->ent[_i]; \
101 _cache->ent[_i] = tmp; \
102 return (_retty)_cache->ent[_i-1].res; \
108 #define WCache_UPDATE(_zzcache,_zzarg1,_zzarg2,_zzresult) \
111 UWord _arg1 = (UWord)(_zzarg1); \
112 UWord _arg2 = (UWord)(_zzarg2); \
113 UWord _res = (UWord)(_zzresult); \
114 WCache* _cache = &(_zzcache); \
115 tl_assert(_cache->dynMax >= 1); \
116 tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX); \
117 tl_assert(_cache->inUse >= 0); \
118 tl_assert(_cache->inUse <= _cache->dynMax); \
119 if (_cache->inUse < _cache->dynMax) \
121 for (_i = _cache->inUse-1; _i >= 1; _i--) \
122 _cache->ent[_i] = _cache->ent[_i-1]; \
123 _cache->ent[0].arg1 = _arg1; \
124 _cache->ent[0].arg2 = _arg2; \
125 _cache->ent[0].res = _res; \
129 //------------------------------------------------------------------//
131 //--- Implementation ---//
132 //------------------------------------------------------------------//
136 WordSetU
* owner
; /* for sanity checking */
138 UWord size
; /* Really this should be SizeT */
142 /* ix2vec[0 .. ix2vec_used-1] are pointers to the lock sets (WordVecs)
143 really. vec2ix is the inverse mapping, mapping WordVec* to the
144 corresponding ix2vec entry number. The two mappings are mutually
147 If a WordVec WV is marked as dead by HG(dieWS), WV is removed from
148 vec2ix. The entry of the dead WVs in ix2vec are used to maintain a
149 linked list of free (to be re-used) ix2vec entries. */
151 void* (*alloc
)(const HChar
*,SizeT
);
153 void (*dealloc
)(void*);
154 WordFM
* vec2ix
; /* WordVec-to-WordSet mapping tree */
155 WordVec
** ix2vec
; /* WordSet-to-WordVec mapping array */
158 WordVec
** ix2vec_free
;
159 WordSet empty
; /* cached, for speed */
160 /* Caches for some operations */
162 WCache cache_delFrom
;
163 WCache cache_intersect
;
167 UWord n_add_uncached
;
169 UWord n_del_uncached
;
173 UWord n_intersect_uncached
;
175 UWord n_minus_uncached
;
180 UWord n_anyElementOf
;
184 /* Create a new WordVec of the given size. */
186 static WordVec
* new_WV_of_size ( WordSetU
* wsu
, UWord sz
)
190 wv
= wsu
->alloc( wsu
->cc
, sizeof(WordVec
) );
195 wv
->words
= wsu
->alloc( wsu
->cc
, (SizeT
)sz
* sizeof(UWord
) );
200 static void delete_WV ( WordVec
* wv
)
202 void (*dealloc
)(void*) = wv
->owner
->dealloc
;
208 static void delete_WV_for_FM ( UWord wv
) {
209 delete_WV( (WordVec
*)wv
);
212 static Word
cmp_WordVecs_for_FM ( UWord wv1W
, UWord wv2W
)
215 WordVec
* wv1
= (WordVec
*)wv1W
;
216 WordVec
* wv2
= (WordVec
*)wv2W
;
218 // WordVecs with smaller size are smaller.
219 if (wv1
->size
< wv2
->size
) {
222 if (wv1
->size
> wv2
->size
) {
226 // Sizes are equal => order based on content.
227 for (i
= 0; i
< wv1
->size
; i
++) {
228 if (wv1
->words
[i
] == wv2
->words
[i
])
230 if (wv1
->words
[i
] < wv2
->words
[i
])
232 if (wv1
->words
[i
] > wv2
->words
[i
])
236 return 0; /* identical */
239 static void ensure_ix2vec_space ( WordSetU
* wsu
)
243 tl_assert(wsu
->ix2vec_used
<= wsu
->ix2vec_size
);
244 if (wsu
->ix2vec_used
< wsu
->ix2vec_size
)
246 new_sz
= 2 * wsu
->ix2vec_size
;
247 if (new_sz
== 0) new_sz
= 1;
248 new_vec
= wsu
->alloc( wsu
->cc
, new_sz
* sizeof(WordVec
*) );
250 for (i
= 0; i
< wsu
->ix2vec_size
; i
++)
251 new_vec
[i
] = wsu
->ix2vec
[i
];
253 wsu
->dealloc(wsu
->ix2vec
);
254 wsu
->ix2vec
= new_vec
;
255 wsu
->ix2vec_size
= new_sz
;
258 /* True if wv is a dead entry (i.e. is in the linked list of free to be re-used
259 entries in ix2vec). */
260 static inline Bool
is_dead ( WordSetU
* wsu
, WordVec
* wv
)
262 if (wv
== NULL
) /* last element in free linked list in ix2vec */
265 return (WordVec
**)wv
>= &(wsu
->ix2vec
[1])
266 && (WordVec
**)wv
< &(wsu
->ix2vec
[wsu
->ix2vec_size
]);
268 /* Index into a WordSetU, doing the obvious range check. Failure of
269 the assertions marked XXX and YYY is an indication of passing the
270 wrong WordSetU* in the public API of this module.
271 Accessing a dead ws will assert. */
272 static WordVec
* do_ix2vec ( WordSetU
* wsu
, WordSet ws
)
275 tl_assert(wsu
->ix2vec_used
<= wsu
->ix2vec_size
);
276 if (wsu
->ix2vec_used
> 0)
277 tl_assert(wsu
->ix2vec
);
278 /* If this assertion fails, it may mean you supplied a 'ws'
279 that does not come from the 'wsu' universe. */
280 tl_assert(ws
< wsu
->ix2vec_used
); /* XXX */
281 wv
= wsu
->ix2vec
[ws
];
282 /* Make absolutely sure that 'ws' is a non dead member of 'wsu'. */
284 tl_assert(!is_dead(wsu
,wv
));
285 tl_assert(wv
->owner
== wsu
); /* YYY */
289 /* Same as do_ix2vec but returns NULL for a dead ws. */
290 static WordVec
* do_ix2vec_with_dead ( WordSetU
* wsu
, WordSet ws
)
293 tl_assert(wsu
->ix2vec_used
<= wsu
->ix2vec_size
);
294 if (wsu
->ix2vec_used
> 0)
295 tl_assert(wsu
->ix2vec
);
296 /* If this assertion fails, it may mean you supplied a 'ws'
297 that does not come from the 'wsu' universe. */
298 tl_assert(ws
< wsu
->ix2vec_used
); /* XXX */
299 wv
= wsu
->ix2vec
[ws
];
300 /* Make absolutely sure that 'ws' is either dead or a member of 'wsu'. */
304 tl_assert(wv
->owner
== wsu
); /* YYY */
308 /* See if wv is contained within wsu. If so, deallocate wv and return
309 the index of the already-present copy. If not, add wv to both the
310 vec2ix and ix2vec mappings and return its index.
312 static WordSet
add_or_dealloc_WordVec( WordSetU
* wsu
, WordVec
* wv_new
)
316 UWord
/*Set*/ ix_old
= -1;
317 /* Really WordSet, but need something that can safely be casted to
318 a Word* in the lookupFM. Making it WordSet (which is 32 bits)
319 causes failures on a 64-bit platform. */
320 tl_assert(wv_new
->owner
== wsu
);
321 have
= VG_(lookupFM
)( wsu
->vec2ix
,
322 (UWord
*)&wv_old
, (UWord
*)&ix_old
,
325 tl_assert(wv_old
!= wv_new
);
327 tl_assert(wv_old
->owner
== wsu
);
328 tl_assert(ix_old
< wsu
->ix2vec_used
);
329 tl_assert(wsu
->ix2vec
[ix_old
] == wv_old
);
331 return (WordSet
)ix_old
;
332 } else if (wsu
->ix2vec_free
) {
334 tl_assert(is_dead(wsu
,(WordVec
*)wsu
->ix2vec_free
));
335 ws
= wsu
->ix2vec_free
- &(wsu
->ix2vec
[0]);
336 tl_assert(wsu
->ix2vec
[ws
] == NULL
|| is_dead(wsu
,wsu
->ix2vec
[ws
]));
337 wsu
->ix2vec_free
= (WordVec
**) wsu
->ix2vec
[ws
];
338 wsu
->ix2vec
[ws
] = wv_new
;
339 VG_(addToFM
)( wsu
->vec2ix
, (UWord
)wv_new
, ws
);
340 if (HG_DEBUG
) VG_(printf
)("aodW %s re-use free %d %p\n", wsu
->cc
, (Int
)ws
, wv_new
);
343 ensure_ix2vec_space( wsu
);
344 tl_assert(wsu
->ix2vec
);
345 tl_assert(wsu
->ix2vec_used
< wsu
->ix2vec_size
);
346 wsu
->ix2vec
[wsu
->ix2vec_used
] = wv_new
;
347 VG_(addToFM
)( wsu
->vec2ix
, (Word
)wv_new
, (Word
)wsu
->ix2vec_used
);
348 if (HG_DEBUG
) VG_(printf
)("aodW %s %d %p\n", wsu
->cc
, (Int
)wsu
->ix2vec_used
, wv_new
);
350 tl_assert(wsu
->ix2vec_used
<= wsu
->ix2vec_size
);
351 return (WordSet
)(wsu
->ix2vec_used
- 1);
356 WordSetU
* HG_(newWordSetU
) ( void* (*alloc_nofail
)( const HChar
*, SizeT
),
358 void (*dealloc
)(void*),
364 wsu
= alloc_nofail( cc
, sizeof(WordSetU
) );
365 VG_(memset
)( wsu
, 0, sizeof(WordSetU
) );
366 wsu
->alloc
= alloc_nofail
;
368 wsu
->dealloc
= dealloc
;
369 wsu
->vec2ix
= VG_(newFM
)( alloc_nofail
, cc
,
370 dealloc
, cmp_WordVecs_for_FM
);
371 wsu
->ix2vec_used
= 0;
372 wsu
->ix2vec_size
= 0;
374 wsu
->ix2vec_free
= NULL
;
375 WCache_INIT(wsu
->cache_addTo
, cacheSize
);
376 WCache_INIT(wsu
->cache_delFrom
, cacheSize
);
377 WCache_INIT(wsu
->cache_intersect
, cacheSize
);
378 WCache_INIT(wsu
->cache_minus
, cacheSize
);
379 empty
= new_WV_of_size( wsu
, 0 );
380 wsu
->empty
= add_or_dealloc_WordVec( wsu
, empty
);
385 void HG_(deleteWordSetU
) ( WordSetU
* wsu
)
387 void (*dealloc
)(void*) = wsu
->dealloc
;
388 tl_assert(wsu
->vec2ix
);
389 VG_(deleteFM
)( wsu
->vec2ix
, delete_WV_for_FM
, NULL
/*val-finalizer*/ );
391 dealloc(wsu
->ix2vec
);
395 WordSet
HG_(emptyWS
) ( WordSetU
* wsu
)
400 Bool
HG_(isEmptyWS
) ( WordSetU
* wsu
, WordSet ws
)
402 WordVec
* wv
= do_ix2vec( wsu
, ws
);
405 tl_assert(ws
== wsu
->empty
);
408 tl_assert(ws
!= wsu
->empty
);
413 Bool
HG_(isSingletonWS
) ( WordSetU
* wsu
, WordSet ws
, UWord w
)
417 wsu
->n_isSingleton
++;
418 wv
= do_ix2vec( wsu
, ws
);
419 return (Bool
)(wv
->size
== 1 && wv
->words
[0] == w
);
422 UWord
HG_(cardinalityWS
) ( WordSetU
* wsu
, WordSet ws
)
426 wv
= do_ix2vec( wsu
, ws
);
427 tl_assert(wv
->size
>= 0);
431 UWord
HG_(anyElementOfWS
) ( WordSetU
* wsu
, WordSet ws
)
435 wsu
->n_anyElementOf
++;
436 wv
= do_ix2vec( wsu
, ws
);
437 tl_assert(wv
->size
>= 1);
441 UWord
HG_(cardinalityWSU
) ( WordSetU
* wsu
)
444 return wsu
->ix2vec_used
;
447 void HG_(getPayloadWS
) ( /*OUT*/UWord
** words
, /*OUT*/UWord
* nWords
,
448 WordSetU
* wsu
, WordSet ws
)
451 if (HG_DEBUG
) VG_(printf
)("getPayloadWS %s %d\n", wsu
->cc
, (Int
)ws
);
453 wv
= do_ix2vec( wsu
, ws
);
454 tl_assert(wv
->size
>= 0);
459 void HG_(dieWS
) ( WordSetU
* wsu
, WordSet ws
)
461 WordVec
* wv
= do_ix2vec_with_dead( wsu
, ws
);
462 WordVec
* wv_in_vec2ix
;
463 UWord
/*Set*/ wv_ix
= -1;
465 if (HG_DEBUG
) VG_(printf
)("dieWS %s %d %p\n", wsu
->cc
, (Int
)ws
, wv
);
468 return; // we never die the empty set.
471 return; // already dead. (or a bug ?).
476 wsu
->ix2vec
[ws
] = (WordVec
*) wsu
->ix2vec_free
;
477 wsu
->ix2vec_free
= &wsu
->ix2vec
[ws
];
479 VG_(delFromFM
) ( wsu
->vec2ix
,
480 (UWord
*)&wv_in_vec2ix
, (UWord
*)&wv_ix
,
483 if (HG_DEBUG
) VG_(printf
)("dieWS wv_ix %d\n", (Int
)wv_ix
);
485 tl_assert (wv_ix
== ws
);
489 wsu
->cache_addTo
.inUse
= 0;
490 wsu
->cache_delFrom
.inUse
= 0;
491 wsu
->cache_intersect
.inUse
= 0;
492 wsu
->cache_minus
.inUse
= 0;
495 Bool
HG_(plausibleWS
) ( WordSetU
* wsu
, WordSet ws
)
497 if (wsu
== NULL
) return False
;
498 if (ws
< 0 || ws
>= wsu
->ix2vec_used
)
503 Bool
HG_(saneWS_SLOW
) ( WordSetU
* wsu
, WordSet ws
)
507 if (wsu
== NULL
) return False
;
508 if (ws
< 0 || ws
>= wsu
->ix2vec_used
)
510 wv
= do_ix2vec( wsu
, ws
);
511 /* can never happen .. do_ix2vec will assert instead. Oh well. */
512 if (wv
->owner
!= wsu
) return False
;
513 if (wv
->size
< 0) return False
;
515 for (i
= 0; i
< wv
->size
-1; i
++) {
516 if (wv
->words
[i
] >= wv
->words
[i
+1])
523 Bool
HG_(elemWS
) ( WordSetU
* wsu
, WordSet ws
, UWord w
)
526 WordVec
* wv
= do_ix2vec( wsu
, ws
);
528 for (i
= 0; i
< wv
->size
; i
++) {
529 if (wv
->words
[i
] == w
)
535 WordSet
HG_(doubletonWS
) ( WordSetU
* wsu
, UWord w1
, UWord w2
)
540 wv
= new_WV_of_size(wsu
, 1);
544 wv
= new_WV_of_size(wsu
, 2);
550 wv
= new_WV_of_size(wsu
, 2);
554 return add_or_dealloc_WordVec( wsu
, wv
);
557 WordSet
HG_(singletonWS
) ( WordSetU
* wsu
, UWord w
)
559 return HG_(doubletonWS
)( wsu
, w
, w
);
562 WordSet
HG_(isSubsetOf
) ( WordSetU
* wsu
, WordSet small
, WordSet big
)
565 return small
== HG_(intersectWS
)( wsu
, small
, big
);
568 void HG_(ppWS
) ( WordSetU
* wsu
, WordSet ws
)
573 wv
= do_ix2vec( wsu
, ws
);
575 for (i
= 0; i
< wv
->size
; i
++) {
576 VG_(printf
)("%p", (void*)wv
->words
[i
]);
583 void HG_(ppWSUstats
) ( WordSetU
* wsu
, const HChar
* name
)
585 VG_(printf
)(" WordSet \"%s\":\n", name
);
586 VG_(printf
)(" addTo %10lu (%lu uncached)\n",
587 wsu
->n_add
, wsu
->n_add_uncached
);
588 VG_(printf
)(" delFrom %10lu (%lu uncached)\n",
589 wsu
->n_del
, wsu
->n_del_uncached
);
590 VG_(printf
)(" union %10lu\n", wsu
->n_union
);
591 VG_(printf
)(" intersect %10lu (%lu uncached) "
592 "[nb. incl isSubsetOf]\n",
593 wsu
->n_intersect
, wsu
->n_intersect_uncached
);
594 VG_(printf
)(" minus %10lu (%lu uncached)\n",
595 wsu
->n_minus
, wsu
->n_minus_uncached
);
596 VG_(printf
)(" elem %10lu\n", wsu
->n_elem
);
597 VG_(printf
)(" doubleton %10lu\n", wsu
->n_doubleton
);
598 VG_(printf
)(" isEmpty %10lu\n", wsu
->n_isEmpty
);
599 VG_(printf
)(" isSingleton %10lu\n", wsu
->n_isSingleton
);
600 VG_(printf
)(" anyElementOf %10lu\n", wsu
->n_anyElementOf
);
601 VG_(printf
)(" isSubsetOf %10lu\n", wsu
->n_isSubsetOf
);
602 VG_(printf
)(" dieWS %10lu\n", wsu
->n_die
);
605 WordSet
HG_(addToWS
) ( WordSetU
* wsu
, WordSet ws
, UWord w
)
610 WordSet result
= (WordSet
)(-1); /* bogus */
613 WCache_LOOKUP_AND_RETURN(WordSet
, wsu
->cache_addTo
, ws
, w
);
614 wsu
->n_add_uncached
++;
616 /* If already present, this is a no-op. */
617 wv
= do_ix2vec( wsu
, ws
);
618 for (k
= 0; k
< wv
->size
; k
++) {
619 if (wv
->words
[k
] == w
) {
624 /* Ok, not present. Build a new one ... */
625 wv_new
= new_WV_of_size( wsu
, wv
->size
+ 1 );
627 for (; k
< wv
->size
&& wv
->words
[k
] < w
; k
++) {
628 wv_new
->words
[j
++] = wv
->words
[k
];
630 wv_new
->words
[j
++] = w
;
631 for (; k
< wv
->size
; k
++) {
632 tl_assert(wv
->words
[k
] > w
);
633 wv_new
->words
[j
++] = wv
->words
[k
];
635 tl_assert(j
== wv_new
->size
);
637 /* Find any existing copy, or add the new one. */
638 result
= add_or_dealloc_WordVec( wsu
, wv_new
);
639 tl_assert(result
!= (WordSet
)(-1));
642 WCache_UPDATE(wsu
->cache_addTo
, ws
, w
, result
);
646 WordSet
HG_(delFromWS
) ( WordSetU
* wsu
, WordSet ws
, UWord w
)
650 WordSet result
= (WordSet
)(-1); /* bogus */
651 WordVec
* wv
= do_ix2vec( wsu
, ws
);
655 /* special case empty set */
657 tl_assert(ws
== wsu
->empty
);
661 WCache_LOOKUP_AND_RETURN(WordSet
, wsu
->cache_delFrom
, ws
, w
);
662 wsu
->n_del_uncached
++;
664 /* If not already present, this is a no-op. */
665 for (i
= 0; i
< wv
->size
; i
++) {
666 if (wv
->words
[i
] == w
)
673 /* So w is present in ws, and the new set will be one element
675 tl_assert(i
>= 0 && i
< wv
->size
);
676 tl_assert(wv
->size
> 0);
678 wv_new
= new_WV_of_size( wsu
, wv
->size
- 1 );
680 for (; j
< wv
->size
; j
++) {
683 wv_new
->words
[k
++] = wv
->words
[j
];
685 tl_assert(k
== wv_new
->size
);
687 result
= add_or_dealloc_WordVec( wsu
, wv_new
);
689 tl_assert(result
== wsu
->empty
);
693 WCache_UPDATE(wsu
->cache_delFrom
, ws
, w
, result
);
697 WordSet
HG_(unionWS
) ( WordSetU
* wsu
, WordSet ws1
, WordSet ws2
)
701 WordVec
* wv1
= do_ix2vec( wsu
, ws1
);
702 WordVec
* wv2
= do_ix2vec( wsu
, ws2
);
707 if (i1
>= wv1
->size
|| i2
>= wv2
->size
)
710 if (wv1
->words
[i1
] < wv2
->words
[i2
]) {
713 if (wv1
->words
[i1
] > wv2
->words
[i2
]) {
720 tl_assert(i1
<= wv1
->size
);
721 tl_assert(i2
<= wv2
->size
);
722 tl_assert(i1
== wv1
->size
|| i2
== wv2
->size
);
723 if (i1
== wv1
->size
&& i2
< wv2
->size
) {
724 sz
+= (wv2
->size
- i2
);
726 if (i2
== wv2
->size
&& i1
< wv1
->size
) {
727 sz
+= (wv1
->size
- i1
);
730 wv_new
= new_WV_of_size( wsu
, sz
);
735 if (i1
>= wv1
->size
|| i2
>= wv2
->size
)
737 if (wv1
->words
[i1
] < wv2
->words
[i2
]) {
738 wv_new
->words
[k
++] = wv1
->words
[i1
];
741 if (wv1
->words
[i1
] > wv2
->words
[i2
]) {
742 wv_new
->words
[k
++] = wv2
->words
[i2
];
745 wv_new
->words
[k
++] = wv1
->words
[i1
];
750 tl_assert(i1
<= wv1
->size
);
751 tl_assert(i2
<= wv2
->size
);
752 tl_assert(i1
== wv1
->size
|| i2
== wv2
->size
);
753 if (i1
== wv1
->size
&& i2
< wv2
->size
) {
754 while (i2
< wv2
->size
)
755 wv_new
->words
[k
++] = wv2
->words
[i2
++];
757 if (i2
== wv2
->size
&& i1
< wv1
->size
) {
758 while (i1
< wv1
->size
)
759 wv_new
->words
[k
++] = wv1
->words
[i1
++];
764 return add_or_dealloc_WordVec( wsu
, wv_new
);
767 WordSet
HG_(intersectWS
) ( WordSetU
* wsu
, WordSet ws1
, WordSet ws2
)
770 WordSet ws_new
= (WordSet
)(-1); /* bogus */
777 /* Deal with an obvious case fast. */
781 /* Since intersect(x,y) == intersect(y,x), convert both variants to
782 the same query. This reduces the number of variants the cache
785 WordSet wst
= ws1
; ws1
= ws2
; ws2
= wst
;
788 WCache_LOOKUP_AND_RETURN(WordSet
, wsu
->cache_intersect
, ws1
, ws2
);
789 wsu
->n_intersect_uncached
++;
791 wv1
= do_ix2vec( wsu
, ws1
);
792 wv2
= do_ix2vec( wsu
, ws2
);
796 if (i1
>= wv1
->size
|| i2
>= wv2
->size
)
798 if (wv1
->words
[i1
] < wv2
->words
[i2
]) {
801 if (wv1
->words
[i1
] > wv2
->words
[i2
]) {
809 tl_assert(i1
<= wv1
->size
);
810 tl_assert(i2
<= wv2
->size
);
811 tl_assert(i1
== wv1
->size
|| i2
== wv2
->size
);
813 wv_new
= new_WV_of_size( wsu
, sz
);
818 if (i1
>= wv1
->size
|| i2
>= wv2
->size
)
820 if (wv1
->words
[i1
] < wv2
->words
[i2
]) {
823 if (wv1
->words
[i1
] > wv2
->words
[i2
]) {
826 wv_new
->words
[k
++] = wv1
->words
[i1
];
831 tl_assert(i1
<= wv1
->size
);
832 tl_assert(i2
<= wv2
->size
);
833 tl_assert(i1
== wv1
->size
|| i2
== wv2
->size
);
837 ws_new
= add_or_dealloc_WordVec( wsu
, wv_new
);
839 tl_assert(ws_new
== wsu
->empty
);
842 tl_assert(ws_new
!= (WordSet
)(-1));
843 WCache_UPDATE(wsu
->cache_intersect
, ws1
, ws2
, ws_new
);
848 WordSet
HG_(minusWS
) ( WordSetU
* wsu
, WordSet ws1
, WordSet ws2
)
851 WordSet ws_new
= (WordSet
)(-1); /* bogus */
857 WCache_LOOKUP_AND_RETURN(WordSet
, wsu
->cache_minus
, ws1
, ws2
);
858 wsu
->n_minus_uncached
++;
860 wv1
= do_ix2vec( wsu
, ws1
);
861 wv2
= do_ix2vec( wsu
, ws2
);
865 if (i1
>= wv1
->size
|| i2
>= wv2
->size
)
867 if (wv1
->words
[i1
] < wv2
->words
[i2
]) {
871 if (wv1
->words
[i1
] > wv2
->words
[i2
]) {
878 tl_assert(i1
<= wv1
->size
);
879 tl_assert(i2
<= wv2
->size
);
880 tl_assert(i1
== wv1
->size
|| i2
== wv2
->size
);
881 if (i2
== wv2
->size
&& i1
< wv1
->size
) {
882 sz
+= (wv1
->size
- i1
);
885 wv_new
= new_WV_of_size( wsu
, sz
);
890 if (i1
>= wv1
->size
|| i2
>= wv2
->size
)
892 if (wv1
->words
[i1
] < wv2
->words
[i2
]) {
893 wv_new
->words
[k
++] = wv1
->words
[i1
];
896 if (wv1
->words
[i1
] > wv2
->words
[i2
]) {
903 tl_assert(i1
<= wv1
->size
);
904 tl_assert(i2
<= wv2
->size
);
905 tl_assert(i1
== wv1
->size
|| i2
== wv2
->size
);
906 if (i2
== wv2
->size
&& i1
< wv1
->size
) {
907 while (i1
< wv1
->size
)
908 wv_new
->words
[k
++] = wv1
->words
[i1
++];
913 ws_new
= add_or_dealloc_WordVec( wsu
, wv_new
);
915 tl_assert(ws_new
== wsu
->empty
);
918 tl_assert(ws_new
!= (WordSet
)(-1));
919 WCache_UPDATE(wsu
->cache_minus
, ws1
, ws2
, ws_new
);
924 static __attribute__((unused
))
925 void show_WS ( WordSetU
* wsu
, WordSet ws
)
928 WordVec
* wv
= do_ix2vec( wsu
, ws
);
929 VG_(printf
)("#%u{", ws
);
930 for (i
= 0; i
< wv
->size
; i
++) {
931 VG_(printf
)("%lu", wv
->words
[i
]);
938 //------------------------------------------------------------------//
939 //--- end WordSet ---//
940 //--- Implementation ---//
941 //------------------------------------------------------------------//
943 /*--------------------------------------------------------------------*/
944 /*--- end hg_wordset.c ---*/
945 /*--------------------------------------------------------------------*/