summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2019-07-30 11:06:36 -0400
committerNed Batchelder <ned@nedbatchelder.com>2019-07-31 10:24:39 -0400
commite94f523162dd39acddfa17b20f4234f1ee5dec7f (patch)
treea793a838a78b13b159b5eec8c2184b6cdf85a9df /coverage
parentad620d081c8508f94846fc01be331f107cb14050 (diff)
downloadpython-coveragepy-git-e94f523162dd39acddfa17b20f4234f1ee5dec7f.tar.gz
Refactor numbits into their own files
Diffstat (limited to 'coverage')
-rw-r--r--coverage/numbits.py42
-rw-r--r--coverage/sqldata.py40
2 files changed, 49 insertions, 33 deletions
diff --git a/coverage/numbits.py b/coverage/numbits.py
new file mode 100644
index 00000000..bc29ed94
--- /dev/null
+++ b/coverage/numbits.py
@@ -0,0 +1,42 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""
+Functions to manipulate packed binary representations of number sets.
+
+To save space, coverage stores sets of line numbers in SQLite using a packed
+binary representation called a numbits. A numbits is stored as a blob in the
+database. The exact meaning of the bytes in the blobs should be considered an
+implementation detail that might change in the future. Use these functions to
+work with those binary blobs of data.
+
+"""
+
+from coverage.backward import bytes_to_ints, binary_bytes, zip_longest
+from coverage.misc import contract
+
+
+@contract(nums='Iterable', returns='bytes')
+def nums_to_numbits(nums):
+ """Convert `nums` (an iterable of ints) into a numbits."""
+ nbytes = max(nums) // 8 + 1
+ b = bytearray(nbytes)
+ for num in nums:
+ b[num//8] |= 1 << num % 8
+ return bytes(b)
+
+@contract(numbits='bytes', returns='list[int]')
+def numbits_to_nums(numbits):
+ """Convert a numbits into a list of numbers."""
+ nums = []
+ for byte_i, byte in enumerate(bytes_to_ints(numbits)):
+ for bit_i in range(8):
+ if (byte & (1 << bit_i)):
+ nums.append(byte_i * 8 + bit_i)
+ return nums
+
+@contract(numbits1='bytes', numbits2='bytes', returns='bytes')
+def merge_numbits(numbits1, numbits2):
+ """Merge two numbits"""
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ return binary_bytes(b1 | b2 for b1, b2 in byte_pairs)
diff --git a/coverage/sqldata.py b/coverage/sqldata.py
index 0404004d..1856ee32 100644
--- a/coverage/sqldata.py
+++ b/coverage/sqldata.py
@@ -17,13 +17,13 @@ import sqlite3
import sys
import zlib
-from coverage.backward import get_thread_id, iitems
-from coverage.backward import bytes_to_ints, binary_bytes, zip_longest, to_bytes, to_string
+from coverage.backward import get_thread_id, iitems, to_bytes, to_string
from coverage.debug import NoDebugging, SimpleReprMixin
from coverage import env
from coverage.files import PathAliases
from coverage.misc import CoverageException, file_be_gone, filename_suffix, isolate_module
from coverage.misc import contract
+from coverage.numbits import nums_to_numbits, numbits_to_nums, merge_numbits
os = isolate_module(os)
@@ -355,12 +355,12 @@ class CoverageData(SimpleReprMixin):
with self._connect() as con:
self._set_context_id()
for filename, linenos in iitems(line_data):
- linemap = nums_to_bitmap(linenos)
+ linemap = nums_to_numbits(linenos)
file_id = self._file_id(filename, add=True)
query = "select bitmap from line_map where file_id = ? and context_id = ?"
existing = list(con.execute(query, (file_id, self._current_context_id)))
if existing:
- linemap = merge_bitmaps(linemap, from_blob(existing[0][0]))
+ linemap = merge_numbits(linemap, from_blob(existing[0][0]))
con.execute(
"insert or replace into line_map (file_id, context_id, bitmap) values (?, ?, ?)",
@@ -583,7 +583,7 @@ class CoverageData(SimpleReprMixin):
key = (aliases.map(path), context)
bitmap = from_blob(bitmap)
if key in lines:
- bitmap = merge_bitmaps(lines[key], bitmap)
+ bitmap = merge_numbits(lines[key], bitmap)
lines[key] = bitmap
cur.close()
@@ -727,7 +727,7 @@ class CoverageData(SimpleReprMixin):
bitmaps = list(con.execute(query, data))
nums = set()
for row in bitmaps:
- nums.update(bitmap_to_nums(from_blob(row[0])))
+ nums.update(numbits_to_nums(from_blob(row[0])))
return sorted(nums)
def arcs(self, filename, contexts=None):
@@ -784,7 +784,7 @@ class CoverageData(SimpleReprMixin):
query += " and l.context_id in (" + ids_array + ")"
data += context_ids
for bitmap, context in con.execute(query, data):
- for lineno in bitmap_to_nums(from_blob(bitmap)):
+ for lineno in numbits_to_nums(from_blob(bitmap)):
lineno_contexts_map[lineno].append(context)
return lineno_contexts_map
@@ -868,29 +868,3 @@ class SqliteDb(SimpleReprMixin):
def dump(self):
"""Return a multi-line string, the dump of the database."""
return "\n".join(self.con.iterdump())
-
-
-@contract(nums='Iterable', returns='bytes')
-def nums_to_bitmap(nums):
- """Convert `nums` (an iterable of ints) into a bitmap."""
- nbytes = max(nums) // 8 + 1
- b = bytearray(nbytes)
- for num in nums:
- b[num//8] |= 1 << num % 8
- return bytes(b)
-
-@contract(bitmap='bytes', returns='list[int]')
-def bitmap_to_nums(bitmap):
- """Convert a bitmap into a list of numbers."""
- nums = []
- for byte_i, byte in enumerate(bytes_to_ints(bitmap)):
- for bit_i in range(8):
- if (byte & (1 << bit_i)):
- nums.append(byte_i * 8 + bit_i)
- return nums
-
-@contract(map1='bytes', map2='bytes', returns='bytes')
-def merge_bitmaps(map1, map2):
- """Merge two bitmaps"""
- byte_pairs = zip_longest(bytes_to_ints(map1), bytes_to_ints(map2), fillvalue=0)
- return binary_bytes(b1 | b2 for b1, b2 in byte_pairs)