Utilities

A small set of helpers exposed under PyMemoryEditor.util for advanced callers that want to operate on bytes directly β€” convert between Python and ctypes types, compile AOB patterns without a live process, or chunk an arbitrary region for scanning.

from PyMemoryEditor.util import (
    resolve_bufflength,
    resolve_bufflength_for_value,
    convert_from_byte_array,
    value_to_bytes,
    values_to_bytes,
    get_c_type_of,
    compile_pattern,
    iter_region_chunks,
    scan_memory,
    scan_memory_for_exact_value,
    PatternLike,
    DEFAULT_MAX_REGION_CHUNK,
    NUMPY_AVAILABLE,
)

Type conversion

resolve_bufflength(pytype, bufflength)

Return a concrete buffer length: the caller-provided value, or the default for numeric pytype when bufflength is None.

Raises:

ValueError – bufflength is required for pytype=str / pytype=bytes.

resolve_bufflength_for_value(pytype, bufflength, *values)

Like resolve_bufflength(), but for operations that already carry the value(s) being matched (the search_by_value family). When bufflength is None: numeric / bool types fall back to the default width (int→4, float→8, bool→1); str / bytes infer the width from the longest encoded value instead of raising (str encoded as UTF-8), so search_by_value(str, value="hi") works without the caller counting bytes. For a range search the shorter endpoint is NUL-padded up to this width.

convert_from_byte_array(byte_array, pytype, length)

Convert a ctypes byte array to a Python value of type pytype. String decoding uses errors="replace".

value_to_bytes(pytype, bufflength, value)

Encode a single value as fixed-width bytes using the same ctypes representation the backend compares against.

values_to_bytes(pytype, bufflength, value)

Convert either a single value or a tuple of values (for VALUE_BETWEEN / NOT_VALUE_BETWEEN) to the corresponding byte form.

get_c_type_of(pytype, length)

Return the underlying ctypes object for the given Python type and width.

Pattern compilation

compile_pattern(pattern, *, byte_length=0)

Compile pattern into a (re.Pattern[bytes], byte_length) pair.

Parameters:
  • pattern – an IDA-style hex string, a raw bytes regex, or a compiled re.Pattern[bytes].

  • byte_length (int) – required for regex / compiled patterns β€” the number of bytes one match consumes.

Raises:
  • ValueError – malformed IDA-style token, or byte_length omitted for a regex / pre-compiled pattern.

  • TypeError – if pattern is not a str, bytes or re.Pattern[bytes].

Example

from PyMemoryEditor.util import compile_pattern

regex, byte_length = compile_pattern("48 8B ? 00 00")
print(regex.pattern)   # b'H\x8b.\x00\x00'  (re.escape prints 0x48 as 'H')
print(byte_length)     # 5
PatternLike

Type alias: Union[str, bytes, re.Pattern[bytes]]. The set of input forms compile_pattern accepts.

Region chunking

DEFAULT_MAX_REGION_CHUNK

Maximum chunk size used by iter_region_chunks() (currently 256 MiB). Tunes the trade-off between syscall overhead (small chunks) and peak memory use (huge chunks).

iter_region_chunks(region_size, target_value_size, max_chunk=DEFAULT_MAX_REGION_CHUNK)

Return an iterable of (offset, chunk_size) pairs that walk a single memory region in bounded-size chunks. Regions up to max_chunk return a single-element tuple (avoiding generator overhead in the common, hot path); larger ones return a lazy generator that yields contiguous, non-overlapping chunks whose size is a multiple of target_value_size so a typed numeric scan never splits a value across a boundary. Boundary handling for patterns is done one level up by the scanner (it overlaps consecutive chunks by pattern_length - 1 bytes); arbitrary str matches in a region larger than max_chunk may still be missed at a chunk boundary β€” a documented limitation.

scan_memory(...)
scan_memory_for_exact_value(...)

Low-level scan kernels used by the backends. Public for advanced use only β€” the high-level search_by_value() / search_by_pattern() methods are the recommended API.

NUMPY_AVAILABLE

True when NumPy is importable, in which case eligible numeric scans use the vectorized fast path. Install it via the speed extra (pip install PyMemoryEditor[speed]). Scan results are identical with or without it.

Region predicates

The helpers in PyMemoryEditor.process.region operate on a raw platform struct β€” the same struct field carried by a MemoryRegion. Useful when you’ve obtained a platform descriptor outside the normal flow and want to compute the portable booleans yourself.

from PyMemoryEditor.process.region import (
    is_region_readable,
    is_region_writable,
    is_region_executable,
    is_region_shared,
    region_path,
    make_region,
)
is_region_readable(struct)
is_region_writable(struct)
is_region_executable(struct)
is_region_shared(struct)

True/False from a platform descriptor (MEMORY_BASIC_INFORMATION_32 / MEMORY_BASIC_INFORMATION_64 on Windows; MEMORY_BASIC_INFORMATION on Linux; the VM struct on macOS). For a fully-populated region, prefer the boolean attributes on MemoryRegion (region.is_readable, etc.).

region_path(struct)

Best-effort path of the file backing the region, or "" when unknown. Linux reads it from /proc/<pid>/maps; Windows uses GetMappedFileNameW (NT device path); macOS uses proc_regionfilename.

make_region(address, size, struct, *, path='')

Build a fully-populated MemoryRegion from a platform struct. The four boolean fields and path are computed once via the predicates above. When path is non-empty it overrides the struct-based region_path() lookup. Backends call this once per region; user code rarely needs it.