├── .gitignore ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── setup.py ├── src ├── __init__.py └── bitmap.py └── test ├── bitmap.py └── test_bitmap.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .ropeproject 3 | *.egg-info 4 | build 5 | dist 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 WAN Ji 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BitMap for python 2 | ================= 3 | 4 | This package provides a `BitMap` class which is an array of bits stored in compact format. 5 | 6 | # Installation 7 | 8 | `bitmap` can be installed from `pip`: 9 | 10 | ```bash 11 | $ sudo pip install bitmap 12 | ``` 13 | 14 | # Functions 15 | 16 | - `BitMap(maxnum)`: construct a `BitMap` object with `maxnum` bits 17 | - `set(pos)`: set the bit at position `pos` to 1 18 | - `reset(pos)`: reset the bit at position `pos` to 0 19 | - `flip(pos)`: flip the bit at position `pos` 20 | - `count()`: return the number of 1s 21 | - `size()`: return the size of the `BitMap` 22 | - `test(pos)`: check if bit at position `pos` has been set to 1 23 | - `any()`: check if any bit in the `BitMap` has been set to 1 24 | - `none()`: check if none of the bits in the `BitMap` has been set to 1 25 | - `all()`: check if all bits in the `BitMap` has been set to 1 26 | - `nonzero()`: return indexes of all non-zero bits 27 | - `tostring()`: convert a `BitMap` object to `0` and `1` string 28 | - `fromstring(bitstring)`: create a `BitMap` object from `0` and `1` string 29 | 30 | # Examples 31 | 32 | ```python 33 | from bitmap import BitMap 34 | bm = BitMap(32) 35 | print bm.tostring() 36 | bm.set(1) 37 | print bm.tostring() 38 | 39 | bm = BitMap.fromstring("00011101") 40 | print bm.tostring() 41 | bm.flip(1) 42 | print bm.tostring() 43 | ``` 44 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | """ 4 | File Name: setup.py 5 | Author: Wan Ji 6 | E-mail: wanji@live.com 7 | Created on: Thu May 1 15:24:31 2014 CST 8 | """ 9 | DESCRIPTION = """ 10 | """ 11 | try: 12 | from setuptools import setup 13 | except ImportError: 14 | from distutils.core import setup 15 | 16 | setup(name='bitmap', 17 | version='0.0.7', 18 | author='WAN Ji', 19 | author_email='wanji@live.com', 20 | package_dir={'bitmap': 'src'}, 21 | packages=['bitmap'], 22 | url='https://github.com/wanji/bitmap', 23 | # license='LICENSE.txt', 24 | description='.', 25 | long_description=open('README.md').read(), 26 | long_description_content_type="text/markdown", 27 | install_requires=[ 28 | 'future', 29 | ], 30 | classifiers=[ 31 | 'Programming Language :: Python :: 2.7', 32 | 'Programming Language :: Python :: 3.7', 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from .bitmap import BitMap 2 | -------------------------------------------------------------------------------- /src/bitmap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | File Name: bitmap.py 6 | Author: Wan Ji 7 | E-mail: wanji@live.com 8 | Created on: Thu May 1 15:26:18 2014 CST 9 | """ 10 | DESCRIPTION = """ 11 | BitMap class 12 | """ 13 | 14 | import array 15 | try: 16 | from past.builtins import xrange 17 | except ImportError: 18 | pass 19 | 20 | class BitMap(object): 21 | """ 22 | BitMap class 23 | """ 24 | 25 | BITMASK = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80] 26 | BIT_CNT = [bin(i).count("1") for i in xrange(256)] 27 | 28 | def __init__(self, maxnum=0): 29 | """ 30 | Create a BitMap 31 | """ 32 | nbytes = (maxnum + 7) // 8 33 | self.bitmap = array.array('B', [0 for i in range(nbytes)]) 34 | 35 | def __del__(self): 36 | """ 37 | Destroy the BitMap 38 | """ 39 | pass 40 | 41 | def set(self, pos): 42 | """ 43 | Set the value of bit@pos to 1 44 | """ 45 | self.bitmap[pos // 8] |= self.BITMASK[pos % 8] 46 | 47 | def reset(self, pos): 48 | """ 49 | Reset the value of bit@pos to 0 50 | """ 51 | self.bitmap[pos // 8] &= ~self.BITMASK[pos % 8] 52 | 53 | def flip(self, pos): 54 | """ 55 | Flip the value of bit@pos 56 | """ 57 | self.bitmap[pos // 8] ^= self.BITMASK[pos % 8] 58 | 59 | def count(self): 60 | """ 61 | Count bits set 62 | """ 63 | return sum([self.BIT_CNT[x] for x in self.bitmap]) 64 | 65 | def size(self): 66 | """ 67 | Return size 68 | """ 69 | return len(self.bitmap) * 8 70 | 71 | def test(self, pos): 72 | """ 73 | Return bit value 74 | """ 75 | return (self.bitmap[pos // 8] & self.BITMASK[pos % 8]) != 0 76 | 77 | def any(self): 78 | """ 79 | Test if any bit is set 80 | """ 81 | return self.count() > 0 82 | 83 | def none(self): 84 | """ 85 | Test if no bit is set 86 | """ 87 | return self.count() == 0 88 | 89 | def all(self): 90 | """ 91 | Test if all bits are set 92 | """ 93 | return (self.count() + 7) // 8 * 8 == self.size() 94 | 95 | def nonzero(self): 96 | """ 97 | Get all non-zero bits 98 | """ 99 | return [i for i in xrange(self.size()) if self.test(i)] 100 | 101 | def tostring(self): 102 | """ 103 | Convert BitMap to string 104 | """ 105 | return "".join([("%s" % bin(x)[2:]).zfill(8) 106 | for x in self.bitmap[::-1]]) 107 | 108 | def __str__(self): 109 | """ 110 | Overloads string operator 111 | """ 112 | return self.tostring() 113 | 114 | def __getitem__(self, item): 115 | """ 116 | Return a bit when indexing like a array 117 | """ 118 | return self.test(item) 119 | 120 | def __setitem__(self, key, value): 121 | """ 122 | Sets a bit when indexing like a array 123 | """ 124 | if value is True: 125 | self.set(key) 126 | elif value is False: 127 | self.reset(key) 128 | else: 129 | raise Exception("Use a boolean value to assign to a bitfield") 130 | 131 | def tohexstring(self): 132 | """ 133 | Returns a hexadecimal string 134 | """ 135 | val = self.tostring() 136 | st = "{0:0x}".format(int(val, 2)) 137 | return st.zfill(len(self.bitmap)*2) 138 | 139 | @classmethod 140 | def fromhexstring(cls, hexstring): 141 | """ 142 | Construct BitMap from hex string 143 | """ 144 | bitstring = format(int(hexstring, 16), "0" + str(len(hexstring)/4) + "b") 145 | return cls.fromstring(bitstring) 146 | 147 | @classmethod 148 | def fromstring(cls, bitstring): 149 | """ 150 | Construct BitMap from string 151 | """ 152 | nbits = len(bitstring) 153 | bm = cls(nbits) 154 | for i in xrange(nbits): 155 | if bitstring[-i-1] == '1': 156 | bm.set(i) 157 | elif bitstring[-i-1] != '0': 158 | raise Exception("Invalid bit string!") 159 | return bm 160 | -------------------------------------------------------------------------------- /test/bitmap.py: -------------------------------------------------------------------------------- 1 | ../src/bitmap.py -------------------------------------------------------------------------------- /test/test_bitmap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | ######################################################################### 5 | ######################################################################### 6 | 7 | """ 8 | File Name: test_bitmap.py 9 | Author: Wan Ji 10 | E-mail: wanji@live.com 11 | Created on: Sun Jan 25 00:05:50 2015 CST 12 | """ 13 | DESCRIPTION = """ 14 | """ 15 | 16 | import unittest 17 | from bitmap import BitMap 18 | try: 19 | from past.builtins import xrange 20 | except ImportError: 21 | pass 22 | 23 | 24 | class TestBitMap(unittest.TestCase): 25 | def setUp(self): 26 | pass 27 | 28 | def tearDown(self): 29 | pass 30 | 31 | @classmethod 32 | def setUpClass(cls): 33 | cls.v_str = [ 34 | "00001111000011110000111100001111", 35 | "10101001000010101000001101010101", 36 | "00101000100101000010101100111101", 37 | "0000111101111000011110000", 38 | "1010100101101000001101010100000110101", 39 | "00101000110100001010110011", 40 | "11111111111111111111111111111111", 41 | "00000000000000000000000000000000", 42 | "111111111111111111111111111", 43 | "000000000000000000000000000"] 44 | 45 | @classmethod 46 | def tearDownClass(cls): 47 | pass 48 | 49 | def helper_str_zfill(self, bitstr): 50 | return bitstr.zfill((len(bitstr) + 7) // 8 * 8) 51 | 52 | def test_str(self): 53 | """ Test BitMap: create 54 | """ 55 | for bitstr in self.v_str: 56 | bm = BitMap.fromstring(bitstr) 57 | self.assertEqual(self.helper_str_zfill(bitstr), bm.tostring()) 58 | 59 | def test_count(self): 60 | """ Test BitMap: create 61 | """ 62 | for bitstr in self.v_str: 63 | bm = BitMap.fromstring(bitstr) 64 | self.assertEqual(bitstr.count("1"), bm.count()) 65 | self.assertEqual(bitstr.count("1"), 66 | len([i for i in xrange(bm.size()) if bm.test(i)])) 67 | 68 | for bitstr in self.v_str[:-4]: 69 | self.assertTrue(BitMap.fromstring(bitstr).any()) 70 | self.assertTrue(BitMap.fromstring(self.v_str[-2]).all()) 71 | self.assertTrue(BitMap.fromstring(self.v_str[-1]).none()) 72 | 73 | def test_op(self): 74 | """ Test BitMap: create 75 | """ 76 | bitstr = "000000000000000000000000000000000" 77 | bitlst = list(bitstr) 78 | bm = BitMap.fromstring(bitstr) 79 | 80 | v_pos = [1, 2, 3, 5, 9] 81 | 82 | for i in v_pos: 83 | bm.set(i) 84 | bitlst[-i-1] = '1' 85 | self.assertEqual(self.helper_str_zfill("".join(bitlst)), 86 | bm.tostring()) 87 | self.assertEqual(bm.count(), len(v_pos)) 88 | 89 | for i in v_pos: 90 | bm.reset(i) 91 | bitlst[-i-1] = '0' 92 | self.assertEqual(self.helper_str_zfill("".join(bitlst)), 93 | bm.tostring()) 94 | self.assertEqual(self.helper_str_zfill(bitstr), 95 | bm.tostring()) 96 | self.assertEqual(bm.count(), 0) 97 | 98 | for i in v_pos: 99 | bm.flip(i) 100 | bitlst[-i-1] = '1' 101 | self.assertEqual(self.helper_str_zfill("".join(bitlst)), 102 | bm.tostring()) 103 | self.assertEqual(bm.count(), len(v_pos)) 104 | 105 | 106 | if __name__ == '__main__': 107 | unittest.main(failfast=True) 108 | # cProfile.run('unittest.main(failfast=True)') 109 | --------------------------------------------------------------------------------