summaryrefslogtreecommitdiff
path: root/src/inc/bitvector.h
blob: a4181dbb8a46e68aba4dce621903f97d899e756e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


/***************************************************************************/
/*                           BitVector.h                                   */
/***************************************************************************/
//  Routines to support a growable bitvector
/***************************************************************************/

#ifndef BITVECTOR_H
#define BITVECTOR_H 1


#ifndef LIMITED_METHOD_CONTRACT
#define LIMITED_METHOD_CONTRACT
#define UNDEF_LIMITED_METHOD_CONTRACT
#endif

#ifndef WRAPPER_NO_CONTRACT
#define WRAPPER_NO_CONTRACT
#define UNDEF_WRAPPER_NO_CONTRACT
#endif

#ifndef SUPPORTS_DAC
#define SUPPORTS_DAC
#define UNDEF_SUPPORTS_DAC
#endif

#ifndef _ASSERTE
#define _ASSERTE(x)
#define UNDEF_ASSERTE
#endif

#define USE_BITVECTOR 1
#if USE_BITVECTOR

/* The bitvector class is meant to be a drop in replacement for an integer
   (that is you use it like an integer), however it grows as needed.

   Features:
       plug compatible with normal integers;
       grows as needed
       Optimized for the small case when the vector fits in machine word
       Uses one machine word if vector fits in machine word (minus a bit)

       Some caveates:
           You should use mutator operators  &=, |= ... instead of the 
           non-mutators whenever possible to avoid creating a temps

           Specifically did NOT supply automatic coersions to
           and from short types so that the programmer is aware of
           when code was being injected on his behalf.  The upshot of this
           is that you have to use the  BitVector() toUnsigned() to convert 
*/

/***************************************************************************/

class BitVector {
    // Set this to be unsigned char to do testing, should be UINT_PTR for real life

    typedef UINT_PTR ChunkType;  // The size of integer type that the machine can operate on directly  
//  typedef BYTE ChunkType;      // Use for testing

    // Maximum number of bits in our bitvector
#define MAX_PTRARG_OFS 1024

    enum {
        IS_BIG     = 1,                             // The low bit is used to discrimate m_val and m_vals
        CHUNK_BITS = sizeof(ChunkType)*8,           // The number of bits that we can manipuate as a chunk
        SMALL_BITS = CHUNK_BITS - 1,                // The number of bits we can fit in the small representation
//      SMALL_BITS = 5,                             // TESTING ONLY: The number of bits we can fit in the small representation
        VALS_COUNT = MAX_PTRARG_OFS / CHUNK_BITS,   // The number of ChunkType elements in the Vals array
    };

public:
    BitVector()
    { 
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;

        m_val = 0;
    }

    BOOL isBig() const
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;

        return ((m_val & IS_BIG) != 0);
    }

    void toBig()
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;

        if (!isBig())
        {
            doBigInit(smallBits());
        }
    }

    explicit BitVector(ChunkType arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (arg > MaxVal)
        {
            doBigInit(arg);
        }
        else 
        {
            m_val = ChunkType(arg << 1);
        }
    }

    BitVector(ChunkType arg, UINT shift)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if ((arg > MaxVal) || (shift >= SMALL_BITS) || (arg > (MaxVal >> shift)))
        {
            doBigInit(arg);
            doBigLeftShiftAssign(shift);
        }
        else
        {
            m_val = ChunkType(arg << (shift+1));
        }
    }

#define CONSTRUCT_ptrArgTP(arg,shift)   BitVector((arg), (shift))

    BitVector(const BitVector& arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (arg.isBig())
        {
            doBigInit(arg);
        }
        else
        {
            m_val = arg.m_val;
        }
    }
    
    void operator <<=(unsigned shift)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if ((m_val == 0) || (shift == 0))     // Zero is a special case, don't need to do anything
            return;

        if (isBig() || (shift >= SMALL_BITS) || (m_val > (MaxVal >> (shift-1))))
        {
            doBigLeftShiftAssign(shift);
        }
        else 
        {
            m_val <<= shift;
        }
    }

    void operator >>=(unsigned shift)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (isBig())
        {
            doBigRightShiftAssign(shift);
        }
        else
        {
            m_val >>= shift;
            m_val &= ~IS_BIG;  // clear the isBig bit if it got set
        }
    }
    
    void operator |=(const BitVector& arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (((m_val | arg.m_val) & IS_BIG) != 0)
        {
            doBigOrAssign(arg);
        }
        else
        {
            m_val |= arg.m_val;
        }
    }

    // Note that that is set difference, not subtration
    void operator -=(const BitVector& arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (((m_val | arg.m_val) & IS_BIG) != 0)
        {
            doBigDiffAssign(arg);
        }
        else
        {
            m_val &= ~arg.m_val;
        }
    }

    void operator &=(const BitVector& arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (((m_val | arg.m_val) & IS_BIG) != 0)
        {
            doBigAndAssign(arg);
        }
        else
        {
            m_val &= arg.m_val;
        }
    }

    friend void setDiff(BitVector& target, const BitVector& arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        target -= arg;
    }

    friend BOOL intersect(const BitVector& arg1, const BitVector& arg2)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (((arg1.m_val | arg2.m_val) & IS_BIG) != 0)
        {
            return arg1.doBigIntersect(arg2);
        }
        else
        {
            return ((arg1.m_val & arg2.m_val) != 0);
        }
    }
    
    BOOL operator ==(const BitVector& arg) const
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if ((m_val | arg.m_val) & IS_BIG)
        {
            return doBigEquals(arg);
        }
        else
        {
            return m_val == arg.m_val;
        }
    }

    BOOL operator !=(const BitVector& arg) const
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        return !(*this == arg);
    }

    friend ChunkType toUnsigned(const BitVector& arg)
    { 
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        if (arg.isBig())
        {
            return arg.m_vals.m_chunks[0];   // Note truncation
        }
        else 
        {
            return arg.smallBits();
        }
    }

    // Note that we require the invariant that zero is always stored in the
    // small form so that this works bitvector is zero iff (m_val == 0)
    friend BOOL isZero(const BitVector& arg)
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        return arg.m_val == 0;
    }

    /* currently only used in asserts */
    BitVector operator &(const BitVector& arg) const
    {
        WRAPPER_NO_CONTRACT;
        SUPPORTS_DAC;

        BitVector ret = *this;
        ret &= arg;
        return ret;
    }

    int  NumBits() const;

private:

    static const ChunkType MaxVal = ((ChunkType)1 << SMALL_BITS) - 1;    // Maximum value that can be stored in m_val

    // This is the structure that we use when the bit vector overflows.  
    // It is a simple vector.  
    struct Vals {
        unsigned m_encodedLength;         // An encoding of the current length of the 'm_chunks' array
        ChunkType m_chunks[VALS_COUNT]; 

        BOOL isBig() const
        {
            LIMITED_METHOD_CONTRACT;
            SUPPORTS_DAC;

            return ((m_encodedLength & IS_BIG) != 0);
        }

        unsigned GetLength() const
        {
            LIMITED_METHOD_CONTRACT;
            SUPPORTS_DAC;
            
            if (isBig())
            {
                unsigned length = (m_encodedLength >> 1);
                _ASSERTE(length > 0);
                return length;
            }
            else
            {
                return 0;
            }
        }

        void SetLength(unsigned length)
        {
            LIMITED_METHOD_CONTRACT;
            SUPPORTS_DAC;

            _ASSERTE(length > 0);
            _ASSERTE(length <= VALS_COUNT);

            m_encodedLength  = (ChunkType) (length << 1);
            m_encodedLength |= (ChunkType) IS_BIG;
         }
    };

    //
    // This is the instance data for the bitvector
    //
    // We discrimininate on this
    union {
        ChunkType m_val;     // if m_val bit 0 is false, then bits 1-N are the bit vector
        Vals      m_vals;    // if m_val bit 1 is true, then use Vals
    };


    ChunkType smallBits() const
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;

        _ASSERTE(!isBig());
        return (m_val >> 1);
    }

#ifdef STRIKE
    void doBigInit(ChunkType arg) {}
#else
    void doBigInit(ChunkType arg);
#endif
    void doBigInit(const BitVector& arg);
    void doBigLeftShiftAssign(unsigned arg);
    void doBigRightShiftAssign(unsigned arg);
    void doBigDiffAssign(const BitVector&);
    void doBigAndAssign(const BitVector&);
    void doBigOrAssign(const BitVector& arg);
    BOOL doBigEquals(const BitVector&) const;
    BOOL doBigIntersect(const BitVector&) const;
};

typedef BitVector ptrArgTP;

#else // !USE_BITVECTOR

typedef unsigned __int64 ptrArgTP;

    // Maximum number of bits in our bitvector
#define MAX_PTRARG_OFS (sizeof(ptrArgTP) * 8)

#define CONSTRUCT_ptrArgTP(arg,shift)   (((ptrArgTP) (arg)) << (shift))

inline BOOL isZero(const ptrArgTP& arg)
{
    LIMITED_METHOD_CONTRACT;
    SUPPORTS_DAC;
    return (arg == 0);
}

inline ptrArgTP toUnsigned(const ptrArgTP& arg)
{
    LIMITED_METHOD_CONTRACT;
    SUPPORTS_DAC;
    return arg;
}

inline void setDiff(ptrArgTP& target, const ptrArgTP& arg)
{
    LIMITED_METHOD_CONTRACT;
    SUPPORTS_DAC;

    target &= ~arg;
}

inline BOOL intersect(const ptrArgTP arg1, const ptrArgTP arg2)
{
    LIMITED_METHOD_CONTRACT;
    SUPPORTS_DAC;

    return ((arg1 & arg2) != 0);
}

#endif  // !USE_BITVECTOR

#ifdef UNDEF_LIMITED_METHOD_CONTRACT
#undef LIMITED_METHOD_CONTRACT
#undef UNDEF_LIMITED_METHOD_CONTRACT
#endif

#ifdef UNDEF_WRAPPER_NO_CONTRACT
#undef WRAPPER_NO_CONTRACT
#undef UNDEF_WRAPPER_NO_CONTRACT
#endif

#ifdef UNDEF_SUPPORTS_DAC
#undef SUPPORTS_DAC
#undef UNDEF_SUPPORTS_DAC
#endif

#ifdef UNDEF_ASSERTE
#undef _ASSERTE
#undef UNDEF_ASSERTE
#endif

#endif // BITVECTOR_H