tdb_succinct/tfc/
decimal.rs1use super::integer::{bigint_to_storage, storage_to_bigint_and_sign, NEGATIVE_ZERO};
2use bytes::Buf;
3use lazy_static::lazy_static;
4use regex::Regex;
5use rug::Integer;
6use thiserror::Error;
7
8#[derive(PartialEq, Debug)]
9pub struct Decimal(pub(crate) String);
10
11#[derive(Debug, Error)]
12#[error("Invalid format for decimal: `{value}`")]
13pub struct DecimalValidationError {
14 pub value: String,
15}
16
17impl Decimal {
18 pub fn new(s: String) -> Result<Self, DecimalValidationError> {
19 validate_decimal(&s)?;
20 Ok(Decimal(s))
21 }
22}
23
24pub fn validate_decimal(s: &str) -> Result<(), DecimalValidationError> {
25 lazy_static! {
26 static ref RE: Regex = Regex::new(r"^-?\d+(\.\d+)?([eE@](-|\+)?\d+)?$").unwrap();
27 }
28 if RE.is_match(s) {
29 Ok(())
30 } else {
31 Err(DecimalValidationError {
32 value: s.to_string(),
33 })
34 }
35}
36
37fn encode_fraction(fraction: Option<&str>) -> Vec<u8> {
38 if let Some(f) = fraction {
39 if f.is_empty() {
40 return vec![0x00]; }
42 let len = f.len();
43 let size = len / 2 + usize::from(len % 2 != 0);
44 let mut bcd = Vec::with_capacity(size);
45 for i in 0..size {
46 let last = if i * 2 + 2 > len {
47 i * 2 + 1
48 } else {
49 i * 2 + 2
50 };
51 let two = &f[2 * i..last];
52 let mut this_int = centary_decimal_encode(two);
53 this_int <<= 1;
54 if i != size - 1 {
55 this_int |= 1 }
57 bcd.push(this_int)
58 }
59 bcd
60 } else {
61 vec![0x00] }
63}
64
65fn centary_decimal_encode(s: &str) -> u8 {
66 if s.len() == 1 {
67 let i = s.parse::<u8>().unwrap();
68 i * 11 + 1
69 } else {
70 let i = s.parse::<u8>().unwrap();
71 let o = i / 10 + 1;
72 i + o + 1
73 }
74}
75
76fn centary_decimal_decode(i: u8) -> String {
77 let j = i - 1;
78 if j % 11 == 0 {
79 let num = j / 11;
80 format!("{num:}")
81 } else {
82 let d = j / 11;
83 let num = j - d - 1;
84 format!("{num:02}")
85 }
86}
87
88pub fn decode_fraction<B: Buf>(fraction_buf: &mut B, is_pos: bool) -> String {
89 let mut first_byte = fraction_buf.chunk()[0];
90 if !is_pos {
91 first_byte = !first_byte;
92 }
93 if first_byte == 0x00 {
94 "".to_string()
95 } else {
96 let mut s = String::new();
97 while fraction_buf.has_remaining() {
98 let mut byte = fraction_buf.get_u8();
99 if !is_pos {
100 byte = !byte;
101 }
102 let num = byte >> 1;
103 let res = centary_decimal_decode(num);
104 s.push_str(&res);
105 if res.len() == 1 || byte & 1 == 0 {
106 break;
107 }
108 }
109 s
110 }
111}
112
113pub fn decimal_to_storage(decimal: &str) -> Vec<u8> {
114 lazy_static! {
115 static ref STD: Regex = Regex::new(r"^-?\d+(\.\d*)?$").unwrap();
116 static ref SCIENTIFIC: Regex = Regex::new(
117 r"^(?P<sign>-)?(?P<integer>\d+)(\.(?P<fraction>\d+))?([eE@](?P<exp>(-|\+)?\d+))?$"
118 )
119 .unwrap();
120 }
121 if STD.is_match(decimal) {
122 let mut parts = decimal.split('.');
123 let bigint = parts.next().unwrap_or(decimal);
124 let fraction = parts.next();
125 let integer_part = bigint.parse::<Integer>().unwrap();
126 let is_neg = decimal.starts_with('-');
127 integer_and_fraction_to_storage(is_neg, integer_part, fraction)
128 } else {
129 let captures = SCIENTIFIC.captures(decimal).unwrap(); let is_neg = captures.name("sign").is_some();
131 let exp: i32 = if let Some(exp_string) = captures.name("exp") {
132 exp_string.as_str().parse::<i32>().unwrap()
133 } else {
134 0_i32
135 };
136 let integer_str = captures.name("integer").map(|m| m.as_str()).unwrap();
137 let fraction_str = captures.name("fraction").map_or_else(|| "", |m| m.as_str());
138 let left_pad = if -exp > integer_str.len() as i32 {
139 "0.".to_string()
140 + &"0".repeat((-exp - integer_str.len() as i32).unsigned_abs() as usize)
141 } else {
142 "".to_string()
143 };
144 let right_pad = if exp > fraction_str.len() as i32 {
145 "0".repeat((exp - fraction_str.len() as i32) as usize)
146 } else {
147 "".to_string()
148 };
149 let left_len = left_pad.len() as i32 + integer_str.len() as i32;
150 let combined = left_pad + integer_str + fraction_str + &right_pad;
151 let shift = (left_len + exp) as usize;
152 let integer_str = &combined[0..shift];
153 let sign = if is_neg { -1 } else { 1 };
154 let integer_part = sign
155 * integer_str
156 .parse::<Integer>()
157 .unwrap_or_else(|_| Integer::from(0));
158 let fraction = &combined[shift..];
159 let fraction = if fraction.is_empty() {
160 None
161 } else {
162 Some(fraction)
163 };
164 integer_and_fraction_to_storage(is_neg, integer_part, fraction)
165 }
166}
167
168pub fn storage_to_decimal<B: Buf>(bytes: &mut B) -> String {
169 let (int, is_pos) = storage_to_bigint_and_sign(bytes);
170 let fraction = decode_fraction(bytes, is_pos);
171 if fraction.is_empty() {
172 format!("{int:}")
173 } else {
174 let sign = if int == 0 && !is_pos { "-" } else { "" };
175 format!("{sign:}{int:}.{fraction:}")
176 }
177}
178
179pub fn integer_and_fraction_to_storage(
180 is_neg: bool,
181 integer: Integer,
182 fraction: Option<&str>,
183) -> Vec<u8> {
184 let prefix = bigint_to_storage(integer.clone());
185 let mut prefix = if integer == 0 && is_neg {
186 vec![NEGATIVE_ZERO] } else {
188 prefix
189 };
190 let suffix = if is_neg {
191 let mut suffix = encode_fraction(fraction);
192 for elt in &mut suffix {
193 *elt = !(*elt)
194 }
195 suffix
196 } else {
197 encode_fraction(fraction)
198 };
199 prefix.extend(suffix);
200 prefix
201}