mirror of
https://github.com/fluencelabs/wasm-utils
synced 2025-03-15 11:10:49 +00:00
Perform gas metering injection step in linear time.
Previously the code was quadratic in the worst case as inserting into the middle of a vector is a linear-time operation.
This commit is contained in:
parent
c3d10a2619
commit
4c0f42c6fc
51
src/gas.rs
51
src/gas.rs
@ -4,6 +4,7 @@
|
|||||||
//! module into one that charges gas for code to be executed. See function documentation for usage
|
//! module into one that charges gas for code to be executed. See function documentation for usage
|
||||||
//! and details.
|
//! and details.
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use parity_wasm::{elements, builder};
|
use parity_wasm::{elements, builder};
|
||||||
@ -176,16 +177,50 @@ pub fn inject_counter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then insert metering calls.
|
insert_metering_calls(instructions, counter.blocks, gas_func)
|
||||||
let mut cumulative_offset = 0;
|
}
|
||||||
for block in counter.blocks {
|
|
||||||
let effective_pos = block.start_pos + cumulative_offset;
|
|
||||||
|
|
||||||
instructions.elements_mut().insert(effective_pos, I32Const(block.cost as i32));
|
// Then insert metering calls into a sequence of instructions given the block locations and costs.
|
||||||
instructions.elements_mut().insert(effective_pos+1, Call(gas_func));
|
fn insert_metering_calls(
|
||||||
|
instructions: &mut elements::Instructions,
|
||||||
|
blocks: Vec<BlockEntry>,
|
||||||
|
gas_func: u32,
|
||||||
|
)
|
||||||
|
-> Result<(), ()>
|
||||||
|
{
|
||||||
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
// Take into account these two inserted instructions.
|
// To do this in linear time, construct a new vector of instructions, copying over old
|
||||||
cumulative_offset += 2;
|
// instructions one by one and injecting new ones as required.
|
||||||
|
let new_instrs_len = instructions.elements().len() + 2 * blocks.len();
|
||||||
|
let original_instrs = mem::replace(
|
||||||
|
instructions.elements_mut(), Vec::with_capacity(new_instrs_len)
|
||||||
|
);
|
||||||
|
let new_instrs = instructions.elements_mut();
|
||||||
|
|
||||||
|
let mut original_pos = 0;
|
||||||
|
let mut block_iter = blocks.into_iter().peekable();
|
||||||
|
for instr in original_instrs.into_iter() {
|
||||||
|
// If there the next block starts at this position, inject metering instructions.
|
||||||
|
let used_block = if let Some(ref block) = block_iter.peek() {
|
||||||
|
if block.start_pos == original_pos {
|
||||||
|
new_instrs.push(I32Const(block.cost as i32));
|
||||||
|
new_instrs.push(Call(gas_func));
|
||||||
|
true
|
||||||
|
} else { false }
|
||||||
|
} else { false };
|
||||||
|
|
||||||
|
if used_block {
|
||||||
|
block_iter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over the original instruction.
|
||||||
|
new_instrs.push(instr);
|
||||||
|
original_pos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if block_iter.next().is_some() {
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user