@@ -8,6 +8,9 @@ use core::str::FromStr;
88use crate :: prelude:: * ;
99use crate :: { errstr, Error , MAX_RECURSION_DEPTH } ;
1010
11+ /// Allowed characters are descriptor strings.
12+ pub const INPUT_CHARSET : & str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\" \\ " ;
13+
1114#[ derive( Debug ) ]
1215/// A token of the form `x(...)` or `x`
1316pub struct Tree < ' a > {
@@ -166,13 +169,7 @@ impl<'a> Tree<'a> {
166169 /// Parses a tree from a string
167170 #[ allow( clippy:: should_implement_trait) ] // Cannot use std::str::FromStr because of lifetimes.
168171 pub fn from_str ( s : & ' a str ) -> Result < Tree < ' a > , Error > {
169- // Filter out non-ASCII because we byte-index strings all over the
170- // place and Rust gets very upset when you splinch a string.
171- for ch in s. bytes ( ) {
172- if !ch. is_ascii ( ) {
173- return Err ( Error :: Unprintable ( ch) ) ;
174- }
175- }
172+ check_valid_chars ( s) ?;
176173
177174 let ( top, rem) = Tree :: from_slice ( s) ?;
178175 if rem. is_empty ( ) {
@@ -183,6 +180,21 @@ impl<'a> Tree<'a> {
183180 }
184181}
185182
183+ /// Filter out non-ASCII because we byte-index strings all over the
184+ /// place and Rust gets very upset when you splinch a string.
185+ pub fn check_valid_chars ( s : & str ) -> Result < ( ) , Error > {
186+ for ch in s. bytes ( ) {
187+ if !ch. is_ascii ( ) {
188+ return Err ( Error :: Unprintable ( ch) ) ;
189+ }
190+ // TODO: Avoid linear search overhead by using OnceCell to cache this in a BTreeMap.
191+ INPUT_CHARSET
192+ . find ( char:: from ( ch) )
193+ . ok_or_else ( || Error :: Unprintable ( ch) ) ?;
194+ }
195+ Ok ( ( ) )
196+ }
197+
186198/// Parse a string as a u32, for timelocks or thresholds
187199pub fn parse_num ( s : & str ) -> Result < u32 , Error > {
188200 if s. len ( ) > 1 {
0 commit comments