11use core:: fmt;
22
3- use alloc:: string:: ToString ;
4-
53#[ derive( Debug , Copy , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
64#[ repr( transparent) ]
75pub 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