3
3
//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
4
4
//! checksum of a descriptor
5
5
6
+ #![ allow( dead_code) ] // will be removed in next commit
7
+ use core:: fmt;
6
8
use core:: iter:: FromIterator ;
7
9
8
10
use crate :: prelude:: * ;
9
11
use crate :: Error ;
10
12
11
13
const INPUT_CHARSET : & str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\" \\ " ;
12
- const CHECKSUM_CHARSET : & str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
14
+ const CHECKSUM_CHARSET : & [ u8 ] = b "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
13
15
14
16
fn poly_mod ( mut c : u64 , val : u64 ) -> u64 {
15
17
let c0 = c >> 35 ;
@@ -39,40 +41,9 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
39
41
/// descriptor string is syntactically correct or not.
40
42
/// This only computes the checksum
41
43
pub 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 ( ) )
76
47
}
77
48
78
49
/// Helper function for FromStr for various
@@ -99,6 +70,102 @@ pub(super) fn verify_checksum(s: &str) -> Result<&str, Error> {
99
70
}
100
71
Ok ( desc_str)
101
72
}
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
+
102
169
#[ cfg( test) ]
103
170
mod test {
104
171
use core:: str;
0 commit comments