33//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
44//! checksum of a descriptor
55
6+ #![ allow( dead_code) ] // will be removed in next commit
7+ use core:: fmt;
68use core:: iter:: FromIterator ;
79
810use crate :: prelude:: * ;
911use crate :: Error ;
1012
1113const INPUT_CHARSET : & str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\" \\ " ;
12- const CHECKSUM_CHARSET : & str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
14+ const CHECKSUM_CHARSET : & [ u8 ] = b "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
1315
1416fn poly_mod ( mut c : u64 , val : u64 ) -> u64 {
1517 let c0 = c >> 35 ;
@@ -39,40 +41,9 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
3941/// descriptor string is syntactically correct or not.
4042/// This only computes the checksum
4143pub fn desc_checksum ( desc : & str ) -> Result < String , Error > {
42- let mut c = 1 ;
43- let mut cls = 0 ;
44- let mut clscount = 0 ;
45-
46- for ch in desc. chars ( ) {
47- let pos = INPUT_CHARSET . find ( ch) . ok_or_else ( || {
48- Error :: BadDescriptor ( format ! ( "Invalid character in checksum: '{}'" , ch) )
49- } ) ? as u64 ;
50- c = poly_mod ( c, pos & 31 ) ;
51- cls = cls * 3 + ( pos >> 5 ) ;
52- clscount += 1 ;
53- if clscount == 3 {
54- c = poly_mod ( c, cls) ;
55- cls = 0 ;
56- clscount = 0 ;
57- }
58- }
59- if clscount > 0 {
60- c = poly_mod ( c, cls) ;
61- }
62- ( 0 ..8 ) . for_each ( |_| c = poly_mod ( c, 0 ) ) ;
63- c ^= 1 ;
64-
65- let mut chars = Vec :: with_capacity ( 8 ) ;
66- for j in 0 ..8 {
67- chars. push (
68- CHECKSUM_CHARSET
69- . chars ( )
70- . nth ( ( ( c >> ( 5 * ( 7 - j) ) ) & 31 ) as usize )
71- . unwrap ( ) ,
72- ) ;
73- }
74-
75- Ok ( String :: from_iter ( chars) )
44+ let mut eng = Engine :: new ( ) ;
45+ eng. input ( desc) ?;
46+ Ok ( eng. checksum ( ) )
7647}
7748
7849/// Helper function for FromStr for various
@@ -99,6 +70,102 @@ pub(super) fn verify_checksum(s: &str) -> Result<&str, Error> {
9970 }
10071 Ok ( desc_str)
10172}
73+
74+ /// An engine to compute a checksum from a string
75+ pub struct Engine {
76+ c : u64 ,
77+ cls : u64 ,
78+ clscount : u64 ,
79+ }
80+
81+ impl Engine {
82+ /// Construct an engine with no input
83+ pub fn new ( ) -> Self {
84+ Engine {
85+ c : 1 ,
86+ cls : 0 ,
87+ clscount : 0 ,
88+ }
89+ }
90+
91+ /// Checksum some data
92+ ///
93+ /// If this function returns an error, the `Engine` will be left in an indeterminate
94+ /// state! It is safe to continue feeding it data but the result will not be meaningful.
95+ pub fn input ( & mut self , s : & str ) -> Result < ( ) , Error > {
96+ for ch in s. chars ( ) {
97+ let pos = INPUT_CHARSET . find ( ch) . ok_or_else ( || {
98+ Error :: BadDescriptor ( format ! ( "Invalid character in checksum: '{}'" , ch) )
99+ } ) ? as u64 ;
100+ self . c = poly_mod ( self . c , pos & 31 ) ;
101+ self . cls = self . cls * 3 + ( pos >> 5 ) ;
102+ self . clscount += 1 ;
103+ if self . clscount == 3 {
104+ self . c = poly_mod ( self . c , self . cls ) ;
105+ self . cls = 0 ;
106+ self . clscount = 0 ;
107+ }
108+ }
109+ Ok ( ( ) )
110+ }
111+
112+ /// Obtain the checksum of all the data thus-far fed to the engine
113+ pub fn checksum_chars ( & mut self ) -> [ char ; 8 ] {
114+ if self . clscount > 0 {
115+ self . c = poly_mod ( self . c , self . cls ) ;
116+ }
117+ ( 0 ..8 ) . for_each ( |_| self . c = poly_mod ( self . c , 0 ) ) ;
118+ self . c ^= 1 ;
119+
120+ let mut chars = [ 0 as char ; 8 ] ;
121+ for j in 0 ..8 {
122+ chars[ j] = CHECKSUM_CHARSET [ ( ( self . c >> ( 5 * ( 7 - j) ) ) & 31 ) as usize ] as char ;
123+ }
124+ chars
125+ }
126+
127+ /// Obtain the checksum of all the data thus-far fed to the engine
128+ pub fn checksum ( & mut self ) -> String {
129+ String :: from_iter ( self . checksum_chars ( ) )
130+ }
131+ }
132+
133+ /// A wrapper around a `fmt::Formatter` which provides checksumming ability
134+ pub struct Formatter < ' f , ' a > {
135+ fmt : & ' f mut fmt:: Formatter < ' a > ,
136+ eng : Engine ,
137+ }
138+
139+ impl < ' f , ' a > Formatter < ' f , ' a > {
140+ /// Contruct a new `Formatter`, wrapping a given `fmt::Formatter`
141+ pub fn new ( f : & ' f mut fmt:: Formatter < ' a > ) -> Self {
142+ Formatter {
143+ fmt : f,
144+ eng : Engine :: new ( ) ,
145+ }
146+ }
147+
148+ pub fn write_checksum ( & mut self ) -> fmt:: Result {
149+ use fmt:: Write ;
150+ self . fmt . write_char ( '#' ) ?;
151+ for ch in self . eng . checksum_chars ( ) {
152+ self . fmt . write_char ( ch) ?;
153+ }
154+ Ok ( ( ) )
155+ }
156+ }
157+
158+ impl < ' f , ' a > fmt:: Write for Formatter < ' f , ' a > {
159+ fn write_str ( & mut self , s : & str ) -> fmt:: Result {
160+ self . fmt . write_str ( s) ?;
161+ if self . eng . input ( s) . is_ok ( ) {
162+ Ok ( ( ) )
163+ } else {
164+ Err ( fmt:: Error )
165+ }
166+ }
167+ }
168+
102169#[ cfg( test) ]
103170mod test {
104171 use core:: str;
0 commit comments