1#![allow(clippy::arithmetic_side_effects)]
5
6use core::{fmt::Debug, ops::RangeInclusive};
7
8pub mod bcrypt;
9pub mod crypt;
10pub mod pbkdf2;
11pub mod shacrypt;
12pub mod standard;
13pub mod url;
14
15pub trait Alphabet: 'static + Copy + Debug + Eq + Send + Sized + Sync {
17 const BASE: u8;
19
20 const DECODER: &'static [DecodeStep];
22
23 const ENCODER: &'static [EncodeStep];
25
26 const PADDED: bool;
28
29 type Unpadded: Alphabet;
33
34 #[inline(always)]
36 fn decode_3bytes(src: &[u8], dst: &mut [u8]) -> i16 {
37 debug_assert_eq!(src.len(), 4);
38 debug_assert!(dst.len() >= 3, "dst too short: {}", dst.len());
39
40 let c0 = Self::decode_6bits(src[0]);
41 let c1 = Self::decode_6bits(src[1]);
42 let c2 = Self::decode_6bits(src[2]);
43 let c3 = Self::decode_6bits(src[3]);
44
45 dst[0] = ((c0 << 2) | (c1 >> 4)) as u8;
46 dst[1] = ((c1 << 4) | (c2 >> 2)) as u8;
47 dst[2] = ((c2 << 6) | c3) as u8;
48
49 ((c0 | c1 | c2 | c3) >> 8) & 1
50 }
51
52 fn decode_6bits(src: u8) -> i16 {
54 let mut ret: i16 = -1;
55
56 for step in Self::DECODER {
57 ret += match step {
58 DecodeStep::Range(range, offset) => {
59 let start = *range.start() as i16 - 1;
61 let end = *range.end() as i16 + 1;
62 (((start - src as i16) & (src as i16 - end)) >> 8) & (src as i16 + *offset)
63 }
64 DecodeStep::Eq(value, offset) => {
65 let start = *value as i16 - 1;
66 let end = *value as i16 + 1;
67 (((start - src as i16) & (src as i16 - end)) >> 8) & *offset
68 }
69 };
70 }
71
72 ret
73 }
74
75 #[inline(always)]
77 fn encode_3bytes(src: &[u8], dst: &mut [u8]) {
78 debug_assert_eq!(src.len(), 3);
79 debug_assert!(dst.len() >= 4, "dst too short: {}", dst.len());
80
81 let b0 = src[0] as i16;
82 let b1 = src[1] as i16;
83 let b2 = src[2] as i16;
84
85 dst[0] = Self::encode_6bits(b0 >> 2);
86 dst[1] = Self::encode_6bits(((b0 << 4) | (b1 >> 4)) & 63);
87 dst[2] = Self::encode_6bits(((b1 << 2) | (b2 >> 6)) & 63);
88 dst[3] = Self::encode_6bits(b2 & 63);
89 }
90
91 #[inline(always)]
93 fn encode_6bits(src: i16) -> u8 {
94 let mut diff = src + Self::BASE as i16;
95
96 for &step in Self::ENCODER {
97 diff += match step {
98 EncodeStep::Apply(threshold, offset) => ((threshold as i16 - diff) >> 8) & offset,
99 EncodeStep::Diff(threshold, offset) => ((threshold as i16 - src) >> 8) & offset,
100 };
101 }
102
103 diff as u8
104 }
105}
106
107#[derive(Debug)]
109pub enum DecodeStep {
110 Range(RangeInclusive<u8>, i16),
112
113 Eq(u8, i16),
115}
116
117#[derive(Copy, Clone, Debug)]
119pub enum EncodeStep {
120 Apply(u8, i16),
122
123 Diff(u8, i16),
125}