Skip to content

Commit 5690aaa

Browse files
committed
reduce number of heap allocations
1 parent c67285f commit 5690aaa

2 files changed

Lines changed: 84 additions & 29 deletions

File tree

lib/src/analyzer.rs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use core::fmt::Write;
1515

1616
use alloc::boxed::Box;
1717
use alloc::format;
18-
use alloc::string::{String, ToString};
18+
use alloc::string::String;
1919
use alloc::vec::Vec;
2020

2121
struct LocktimeRequirement {
@@ -51,25 +51,18 @@ impl LocktimeRequirement {
5151
None => "unknown",
5252
};
5353

54-
let tmp;
55-
Some(format!(
56-
"type: {}, minValue: {}{}",
57-
type_,
58-
min_value,
59-
if !self.exprs.is_empty() {
60-
tmp = format!(
61-
", stack elements: {}",
62-
self.exprs
63-
.iter()
64-
.map(|s| s.to_string())
65-
.collect::<Vec<_>>()
66-
.join(", ")
67-
);
68-
&tmp
69-
} else {
70-
""
54+
let mut ret = format!("type: {type_}, minValue: {min_value}");
55+
56+
let mut exprs = self.exprs.iter();
57+
if let Some(expr) = exprs.next() {
58+
let _ = write!(ret, ", stack elements: {expr}");
59+
60+
for expr in exprs {
61+
let _ = write!(ret, ", {expr}");
7162
}
72-
))
63+
}
64+
65+
Some(ret)
7366
}
7467
}
7568

@@ -194,7 +187,7 @@ pub fn analyze_script(
194187
.peekable();
195188

196189
if results.peek().is_none() {
197-
return Err("Script is unspendable".to_string());
190+
return Err(String::from("Script is unspendable"));
198191
}
199192

200193
let mut s = String::from("Spending paths:");

lib/src/opcode.rs

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use core::fmt;
22

3-
use alloc::string::ToString;
4-
53
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
64
#[repr(transparent)]
75
pub struct Opcode {
@@ -26,9 +24,22 @@ macro_rules! opcodes {
2624
}
2725

2826
impl Opcode {
29-
pub fn from_name_exact(name: &str) -> Option<Self> {
27+
const LONGEST_NAME_LENGTH: usize = {
28+
let mut max = 0;
29+
3030
$(
31-
if name == stringify!($k) {
31+
let len = stringify!($k).len();
32+
if max < len {
33+
max = len;
34+
}
35+
)*
36+
37+
max
38+
};
39+
40+
pub fn from_name_exact_unprefixed(name_bytes: &[u8]) -> Option<Self> {
41+
$(
42+
if name_bytes == &stringify!($k).as_bytes()[3..] {
3243
let op = Opcode { opcode: $v };
3344

3445
if !op.is_internal() {
@@ -307,13 +318,27 @@ impl Opcode {
307318
}
308319

309320
pub fn from_name(name: &str) -> Option<Self> {
310-
// TODO replace with more efficient implementation of to_uppercase
311-
let name_upper = name.to_uppercase();
312-
if let Some(opcode) = Self::from_name_exact(&name_upper) {
313-
return Some(opcode);
321+
const ASCII_UPPERCASE_MASK: u8 = !(1 << 5);
322+
323+
let mut name = name.as_bytes();
324+
325+
match name.split_first_chunk() {
326+
Some((&[a, b, c], tail))
327+
if a & ASCII_UPPERCASE_MASK == b'O'
328+
&& b & ASCII_UPPERCASE_MASK == b'P'
329+
&& c == b'_' =>
330+
{
331+
name = tail;
332+
}
333+
_ => {}
314334
}
315335

316-
Self::from_name_exact(&("OP_".to_string() + &name_upper))
336+
let mut buf = [0; Self::LONGEST_NAME_LENGTH - 3];
337+
let buf = buf.get_mut(..name.len())?;
338+
buf.copy_from_slice(name);
339+
buf.make_ascii_uppercase();
340+
341+
Self::from_name_exact_unprefixed(buf)
317342
}
318343
}
319344

@@ -362,3 +387,40 @@ impl Opcode {
362387
}
363388
}
364389
}
390+
391+
#[cfg(test)]
392+
mod tests {
393+
use super::*;
394+
395+
#[test]
396+
fn test_opcode_from_name() {
397+
use super::opcodes::*;
398+
399+
let cases = &[
400+
("", None),
401+
("0", Some(OP_0)),
402+
("1", Some(OP_1)),
403+
("OP_0", Some(OP_0)),
404+
("Op_0", Some(OP_0)),
405+
("oP_0", Some(OP_0)),
406+
("op_0", Some(OP_0)),
407+
(str::from_utf8(&[b'_'; 100]).unwrap(), None),
408+
("false", Some(OP_0)),
409+
("False", Some(OP_0)),
410+
("FaLsE", Some(OP_0)),
411+
("trUE", Some(OP_1)),
412+
("OP_trUE", Some(OP_1)),
413+
("3DUP", Some(OP_3DUP)),
414+
("3Dup", Some(OP_3DUP)),
415+
("fromaltstack", Some(OP_FROMALTSTACK)),
416+
("csv", Some(OP_CHECKSEQUENCEVERIFY)),
417+
("cltv", Some(OP_CHECKLOCKTIMEVERIFY)),
418+
("OP_INTERNAL_NOT", None),
419+
("OP_CHECKMULTISIGVERIFY", Some(OP_CHECKMULTISIGVERIFY)),
420+
];
421+
422+
for &(name, expected_opcode) in cases {
423+
assert_eq!(Opcode::from_name(name), expected_opcode, "name = {name}");
424+
}
425+
}
426+
}

0 commit comments

Comments
 (0)