///|
/// Only matching instructions, retaining ordinals from the complete sequence.
pub struct OpcodeSearchFunction {
stat : FunctionStat
match_count : Int
matched_encoded_bytes : Int
sites : Array[FunctionInstructionRecord]
}
///|
pub struct OpcodeSearchReport {
file_size : Int
sha256 : String
query : String
matching_rule : String
scope : String
imported_function_count : Int
scanned_local_function_count : Int
matched_function_count : Int
unmatched_function_count : Int
instruction_count : Int
instruction_bytes : Int
match_count : Int
matched_encoded_bytes : Int
other_instruction_bytes : Int
functions : Array[OpcodeSearchFunction]
}
///|
/// Existing module opcode errors retain structure/body/evidence distinctions.
pub enum OpcodeSearchError {
InvalidOpcodeSearchQuery
OpcodeSearchInputError(ModuleOpcodeError)
}
///|
pub fn describe_opcode_search_error(error : OpcodeSearchError) -> String {
match error {
InvalidOpcodeSearchQuery =>
"opcode query must not be empty or all whitespace"
OpcodeSearchInputError(error) => describe_module_opcode_error(error)
}
}
///|
/// decoded stays local; only matching records escape. No allocation of records
/// for nonmatching instructions, and no promise of immediate memory reclamation.
fn opcode_search_function(
data : Bytes,
stat : FunctionStat,
query : String,
) -> Result[(OpcodeSearchFunction?, Int, Int), ModuleOpcodeError] {
if stat.body_size < 0 || stat.body_size >= stat.total_size {
return Err(
InvalidModuleOpcodeEvidence(
Some(stat.index),
Some(stat.offset),
"body/prefix range differs",
),
)
}
let reader = match Reader::new_range(data, stat.offset, stat.total_size) {
Ok(reader) => reader
Err(error) => return Err(ModuleOpcodeBodyError(stat.index, error))
}
match reader.read_bytes(stat.total_size - stat.body_size) {
Ok(_) => ()
Err(error) => return Err(ModuleOpcodeBodyError(stat.index, error))
}
let body_offset = reader.position()
match reader.read_bytes(stat.body_size) {
Ok(_) => ()
Err(error) => return Err(ModuleOpcodeBodyError(stat.index, error))
}
let body_end = reader.position()
let decoded = match decode_instructions(data, body_offset, stat.body_size) {
Ok(decoded) => decoded
Err(error) => return Err(ModuleOpcodeBodyError(stat.index, error))
}
let first = if decoded.is_empty() { body_end } else { decoded[0].offset }
if first < body_offset || first > body_end {
return Err(
InvalidModuleOpcodeEvidence(
Some(stat.index),
Some(body_offset),
"locals boundary outside body",
),
)
}
let sites : Array[FunctionInstructionRecord] = []
let mut cursor = first
let mut instruction_bytes = 0
let mut matched_encoded_bytes = 0
for ordinal, instruction in decoded {
if instruction.offset != cursor ||
instruction.size <= 0 ||
instruction.size > body_end - cursor {
return Err(
InvalidModuleOpcodeEvidence(
Some(stat.index),
Some(instruction.offset),
"noncontiguous or out-of-bounds instruction",
),
)
}
let mnemonic = instruction.opcode.name()
if mnemonic == query {
sites.push({
ordinal,
mnemonic,
file_offset: cursor,
body_relative_offset: cursor - body_offset,
encoded_size: instruction.size,
})
matched_encoded_bytes += instruction.size
}
cursor += instruction.size
instruction_bytes += instruction.size
}
if cursor != body_end ||
instruction_bytes != stat.body_size - (first - body_offset) {
return Err(
InvalidModuleOpcodeEvidence(
Some(stat.index),
Some(cursor),
"instruction ledger does not close",
),
)
}
let matched = if sites.is_empty() {
None
} else {
Some({ stat, match_count: sites.length(), matched_encoded_bytes, sites, })
}
Ok((matched, decoded.length(), instruction_bytes))
}
///|
/// Inspect query first; collect module/function/name ranges once, then decode
/// each local body once. No prior statistics report, graph or per-function inspector.
pub fn search_opcode(
data : Bytes,
query : String,
) -> Result[OpcodeSearchReport, OpcodeSearchError] {
if query.trim().is_empty() {
return Err(InvalidOpcodeSearchQuery)
}
let parsed = match parse_module(data) {
Ok(parsed) => parsed
Err(error) =>
return Err(OpcodeSearchInputError(ModuleOpcodeInputError(error)))
}
let sections = find_sections(parsed)
let imported = match sections.imports {
Some(section) =>
match count_imported_functions(data, section) {
Ok(count) => count
Err(error) =>
return Err(OpcodeSearchInputError(ModuleOpcodeInputError(error)))
}
None => 0
}
let bodies = match sections.code {
Some(section) =>
match parse_code_section(data, section, base_index=imported) {
Ok(bodies) => bodies
Err(error) =>
return Err(OpcodeSearchInputError(ModuleOpcodeInputError(error)))
}
None => []
}
let names = match sections.names {
Some(section) => parse_name_section(data, section)
None => None
}
let stats = function_stats(bodies, names)
let functions : Array[OpcodeSearchFunction] = []
let mut instruction_count = 0
let mut instruction_bytes = 0
let mut match_count = 0
let mut matched_encoded_bytes = 0
for local_ordinal, stat in stats {
// The bounded code parser establishes index/file order and complete payload.
if stat.index != imported + local_ordinal {
return Err(
OpcodeSearchInputError(
InvalidModuleOpcodeEvidence(
Some(stat.index),
Some(stat.offset),
"function index differs from local declaration order",
),
),
)
}
let (matched, count, bytes) = match
opcode_search_function(data, stat, query) {
Ok(result) => result
Err(error) => return Err(OpcodeSearchInputError(error))
}
instruction_count += count
instruction_bytes += bytes
if matched is Some(matched) {
match_count += matched.match_count
matched_encoded_bytes += matched.matched_encoded_bytes
functions.push(matched)
}
}
Ok({
file_size: data.length(),
sha256: experiment_sha256(data),
query,
matching_rule: "Case-sensitive exact equality with the complete decoded Opcode.name(); no trimming, case folding, substring, regex or numeric opcode interpretation. Unknown nonempty names may have no matches.",
scope: "Static instruction encodings in all local function bodies, including unreachable bodies. Imports have no body. Excludes names/custom/data bytes, locals declarations and global/element/data initializer expressions. File offsets are not memory addresses; ordinal belongs to the complete body instruction sequence. Encoded sizes include opcode, subopcode and immediates. No operand values or source attribution; not execution traces, runtime frequency, full semantic validation or optimization/removal/rebuilt/gzip savings. An empty result says nothing about other regions of the module. Every body is decoded once; any unsupported or malformed body fails the whole report. Only matching records are retained. Top limits text position rows only, not scan cost, full JSON or memory usage; many matches may require substantial memory.",
imported_function_count: imported,
scanned_local_function_count: stats.length(),
matched_function_count: functions.length(),
unmatched_function_count: stats.length() - functions.length(),
instruction_count,
instruction_bytes,
match_count,
matched_encoded_bytes,
other_instruction_bytes: instruction_bytes - matched_encoded_bytes,
functions,
})
}