@@ -182,12 +182,12 @@ impl<'a> Bech32Writer<'a> {
182182
183183 /// Write out the checksum at the end. If this method isn't called this will happen on drop.
184184 pub fn finalize ( mut self ) -> fmt:: Result {
185- self . inner_finalize ( ) ?;
185+ self . write_checksum ( ) ?;
186186 mem:: forget ( self ) ;
187187 Ok ( ( ) )
188188 }
189189
190- fn inner_finalize ( & mut self ) -> fmt:: Result {
190+ fn write_checksum ( & mut self ) -> fmt:: Result {
191191 // Pad with 6 zeros
192192 for _ in 0 ..CHECKSUM_LENGTH {
193193 self . polymod_step ( u5 ( 0 ) )
@@ -203,6 +203,7 @@ impl<'a> Bech32Writer<'a> {
203203 Ok ( ( ) )
204204 }
205205}
206+
206207impl < ' a > WriteBase32 for Bech32Writer < ' a > {
207208 type Err = fmt:: Error ;
208209
@@ -215,7 +216,7 @@ impl<'a> WriteBase32 for Bech32Writer<'a> {
215216
216217impl < ' a > Drop for Bech32Writer < ' a > {
217218 fn drop ( & mut self ) {
218- self . inner_finalize ( )
219+ self . write_checksum ( )
219220 . expect ( "Unhandled error writing the checksum on drop." )
220221 }
221222}
@@ -421,6 +422,32 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
421422 } ) ?)
422423}
423424
425+ /// Encode a bech32 payload without a checksum to an [fmt::Write].
426+ /// This method is intended for implementing traits from [std::fmt].
427+ ///
428+ /// # Errors
429+ /// * If [check_hrp] returns an error for the given HRP.
430+ /// * If `fmt` fails on write
431+ /// # Deviations from standard
432+ /// * No length limits are enforced for the data part
433+ pub fn encode_without_checksum_to_fmt < T : AsRef < [ u5 ] > > (
434+ fmt : & mut fmt:: Write ,
435+ hrp : & str ,
436+ data : T ,
437+ ) -> Result < ( ) , Error > {
438+ let hrp = match check_hrp ( hrp) ? {
439+ Case :: Upper => Cow :: Owned ( hrp. to_lowercase ( ) ) ,
440+ Case :: Lower | Case :: None => Cow :: Borrowed ( hrp) ,
441+ } ;
442+
443+ fmt. write_str ( & hrp) ?;
444+ fmt. write_char ( SEP ) ?;
445+ for b in data. as_ref ( ) {
446+ fmt. write_char ( b. to_char ( ) ) ?;
447+ }
448+ Ok ( ( ) )
449+ }
450+
424451/// Used for encode/decode operations for the two variants of Bech32
425452#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Hash , Debug ) ]
426453pub enum Variant {
@@ -463,15 +490,44 @@ pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<St
463490 Ok ( buf)
464491}
465492
493+ /// Encode a bech32 payload to string without the checksum.
494+ ///
495+ /// # Errors
496+ /// * If [check_hrp] returns an error for the given HRP.
497+ /// # Deviations from standard
498+ /// * No length limits are enforced for the data part
499+ pub fn encode_without_checksum < T : AsRef < [ u5 ] > > ( hrp : & str , data : T ) -> Result < String , Error > {
500+ let mut buf = String :: new ( ) ;
501+ encode_without_checksum_to_fmt ( & mut buf, hrp, data) ?;
502+ Ok ( buf)
503+ }
504+
466505/// Decode a bech32 string into the raw HRP and the data bytes.
467506///
468- /// Returns the HRP in lowercase. .
507+ /// Returns the HRP in lowercase, the data with the checksum removed, and the encoding .
469508pub fn decode ( s : & str ) -> Result < ( String , Vec < u5 > , Variant ) , Error > {
470- // Ensure overall length is within bounds
471- if s . len ( ) < CHECKSUM_LENGTH + 2 {
509+ let ( hrp_lower , mut data ) = decode_without_checksum ( s ) ? ;
510+ if data . len ( ) < CHECKSUM_LENGTH {
472511 return Err ( Error :: InvalidLength ) ;
473512 }
474513
514+ // Ensure checksum
515+ match verify_checksum ( hrp_lower. as_bytes ( ) , & data) {
516+ Some ( variant) => {
517+ // Remove checksum from data payload
518+ let dbl: usize = data. len ( ) ;
519+ data. truncate ( dbl - CHECKSUM_LENGTH ) ;
520+
521+ Ok ( ( hrp_lower, data, variant) )
522+ }
523+ None => Err ( Error :: InvalidChecksum ) ,
524+ }
525+ }
526+
527+ /// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
528+ ///
529+ /// Returns the HRP in lowercase and the data with the checksum removed.
530+ pub fn decode_without_checksum ( s : & str ) -> Result < ( String , Vec < u5 > ) , Error > {
475531 // Split at separator and check for two pieces
476532 let ( raw_hrp, raw_data) = match s. rfind ( SEP ) {
477533 None => return Err ( Error :: MissingSeparator ) ,
@@ -480,9 +536,6 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
480536 ( hrp, & data[ 1 ..] )
481537 }
482538 } ;
483- if raw_data. len ( ) < CHECKSUM_LENGTH {
484- return Err ( Error :: InvalidLength ) ;
485- }
486539
487540 let mut case = check_hrp ( raw_hrp) ?;
488541 let hrp_lower = match case {
@@ -492,7 +545,7 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
492545 } ;
493546
494547 // Check data payload
495- let mut data = raw_data
548+ let data = raw_data
496549 . chars ( )
497550 . map ( |c| {
498551 // Only check if c is in the ASCII range, all invalid ASCII
@@ -527,17 +580,7 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
527580 } )
528581 . collect :: < Result < Vec < u5 > , Error > > ( ) ?;
529582
530- // Ensure checksum
531- match verify_checksum ( hrp_lower. as_bytes ( ) , & data) {
532- Some ( variant) => {
533- // Remove checksum from data payload
534- let dbl: usize = data. len ( ) ;
535- data. truncate ( dbl - CHECKSUM_LENGTH ) ;
536-
537- Ok ( ( hrp_lower, data, variant) )
538- }
539- None => Err ( Error :: InvalidChecksum ) ,
540- }
583+ Ok ( ( hrp_lower, data) )
541584}
542585
543586fn verify_checksum ( hrp : & [ u8 ] , data : & [ u5 ] ) -> Option < Variant > {
@@ -801,6 +844,8 @@ mod tests {
801844 Error :: InvalidLength ) ,
802845 ( "1p2gdwpf" ,
803846 Error :: InvalidLength ) ,
847+ ( "bc1p2" ,
848+ Error :: InvalidLength ) ,
804849 ) ;
805850 for p in pairs {
806851 let ( s, expected_error) = p;
@@ -930,7 +975,7 @@ mod tests {
930975 }
931976
932977 #[ test]
933- fn writer ( ) {
978+ fn write_with_checksum ( ) {
934979 let hrp = "lnbc" ;
935980 let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
936981
@@ -947,7 +992,23 @@ mod tests {
947992 }
948993
949994 #[ test]
950- fn write_on_drop ( ) {
995+ fn write_without_checksum ( ) {
996+ let hrp = "lnbc" ;
997+ let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
998+
999+ let mut written_str = String :: new ( ) ;
1000+ {
1001+ let mut writer = Bech32Writer :: new ( hrp, Variant :: Bech32 , & mut written_str) . unwrap ( ) ;
1002+ writer. write ( & data) . unwrap ( ) ;
1003+ }
1004+
1005+ let encoded_str = encode_without_checksum ( hrp, data) . unwrap ( ) ;
1006+
1007+ assert_eq ! ( encoded_str, written_str[ ..written_str. len( ) - CHECKSUM_LENGTH ] ) ;
1008+ }
1009+
1010+ #[ test]
1011+ fn write_with_checksum_on_drop ( ) {
9511012 let hrp = "lntb" ;
9521013 let data = "Hello World!" . as_bytes ( ) . to_base32 ( ) ;
9531014
0 commit comments