summaryrefslogtreecommitdiff
path: root/python/binaryview.py
diff options
context:
space:
mode:
authorBrian Potchik <brian@vector35.com>2025-03-05 11:36:34 -0500
committerBrian Potchik <brian@vector35.com>2025-03-05 11:36:34 -0500
commit55577a41417ce72f82d46dd04cc90729ecc12391 (patch)
tree84991af908fc219aae742ff77eff1c3f667bdbb2 /python/binaryview.py
parent9f1c62e99e6dc797ee81716f7d3eb6b722b5270d (diff)
Improvements for advanced binary search.
Diffstat (limited to 'python/binaryview.py')
-rw-r--r--python/binaryview.py38
1 files changed, 34 insertions, 4 deletions
diff --git a/python/binaryview.py b/python/binaryview.py
index dcdfe2fe..c6a1c096 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -9211,7 +9211,8 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
return self.QueueGenerator(t, results)
- def search(self, pattern: str, start: int = None, end: int = None, raw: bool = False, ignore_case: bool = False, overlap: bool = False, align: int = 1) -> QueueGenerator:
+ def search(self, pattern: str, start: int = None, end: int = None, raw: bool = False, ignore_case: bool = False, overlap: bool = False, align: int = 1,
+ limit: int = None, progress_callback: Optional[ProgressFuncType] = None, match_callback: Optional[DataMatchCallbackType] = None) -> QueueGenerator:
r"""
Searches for matches of the specified `pattern` within this BinaryView with an optionally provided address range specified by `start` and `end`.
The search pattern can be interpreted in various ways:
@@ -9230,6 +9231,12 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
:param bool ignore_case: Whether to perform case-insensitive matching (default: False).
:param bool overlap: Whether to allow matches to overlap (default: False).
:param int align: The alignment of matches, must be a power of 2 (default: 1).
+ :param int limit: The maximum number of matches to return (default: None).
+ :param callback progress_callback: An optional function to be called with the current progress and total count. \
+ This function should return a boolean value that decides whether the search should continue or stop.
+ :param callback match_callback: A function that gets called when a match is found. The callback takes two parameters: \
+ the address of the match, and the actual DataBuffer that satisfies the search. This function can return a boolean value \
+ that decides whether the search should continue or stop.
:return: A generator object that yields the offset and matched DataBuffer for each match found.
:rtype: QueueGenerator
@@ -9260,10 +9267,33 @@ to a the type "tagRECT" found in the typelibrary "winX64common"
"overlap": overlap,
"align": align
}
+
+ if progress_callback:
+ progress_callback_obj = ctypes.CFUNCTYPE(
+ ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong
+ )(lambda ctxt, cur, total: progress_callback(cur, total))
+ else:
+ progress_callback_obj = ctypes.CFUNCTYPE(
+ ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong
+ )(lambda ctxt, cur, total: True)
+
+ match_count = 0
+ def internal_match_callback(ctxt, offset, match):
+ nonlocal match_count
+ match_count += 1
+ data_buffer = databuffer.DataBuffer(handle=match)
+ results.put((offset, data_buffer))
+ if match_callback is not None:
+ should_continue = match_callback(offset, data_buffer)
+ if not should_continue:
+ return False
+ if limit is not None and match_count >= limit:
+ return False
+ return True
+
+ match_callback_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.POINTER(core.BNDataBuffer))(internal_match_callback)
results = queue.Queue()
- match_callback_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.POINTER(core.BNDataBuffer)
- )(lambda ctxt, offset, match: results.put((offset, databuffer.DataBuffer(handle=match))) or True)
- t = threading.Thread(target=lambda: core.BNSearch(self.handle, json.dumps(query), None, match_callback_obj))
+ t = threading.Thread(target=lambda: core.BNSearch(self.handle, json.dumps(query), None, progress_callback_obj, None, match_callback_obj))
return self.QueueGenerator(t, results)
def reanalyze(self) -> None: