opensrv_clickhouse/types/
decimal.rs1use std::cmp::Ordering;
16use std::fmt;
17
18static FACTORS10: &[i64] = &[
19 1,
20 10,
21 100,
22 1000,
23 10000,
24 100_000,
25 1_000_000,
26 10_000_000,
27 100_000_000,
28 1_000_000_000,
29 10_000_000_000,
30 100_000_000_000,
31 1_000_000_000_000,
32 10_000_000_000_000,
33 100_000_000_000_000,
34 1_000_000_000_000_000,
35 10_000_000_000_000_000,
36 100_000_000_000_000_000,
37 1_000_000_000_000_000_000,
38];
39
40pub trait Base {
41 fn scale(self, scale: i64) -> i64;
42}
43
44pub trait InternalResult {
45 fn get(underlying: i64) -> Self;
46}
47
48#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
49pub enum NoBits {
50 N32,
51 N64,
52}
53
54#[derive(Clone)]
56pub struct Decimal {
57 pub(crate) underlying: i64,
58 pub(crate) nobits: NoBits, pub(crate) precision: u8,
60 pub(crate) scale: u8,
61}
62
63impl Default for Decimal {
64 fn default() -> Self {
65 Decimal {
66 underlying: 0,
67 precision: 9,
68 scale: 4,
69 nobits: NoBits::N32,
70 }
71 }
72}
73
74macro_rules! base_for {
75 ( $( $t:ty: $cast:expr ),* ) => {
76 $(
77 impl Base for $t {
78 fn scale(self, scale: i64) -> i64 {
79 $cast(self * (scale as $t)) as i64
80 }
81 }
82 )*
83 };
84}
85
86base_for! {
87 f32: std::convert::identity,
88 f64: std::convert::identity,
89 i8: i64::from,
90 i16: i64::from,
91 i32: i64::from,
92 i64: std::convert::identity,
93 u8: i64::from,
94 u16: i64::from,
95 u32: i64::from,
96 u64 : std::convert::identity
97}
98
99impl InternalResult for i32 {
100 #[inline(always)]
101 fn get(underlying: i64) -> Self {
102 underlying as Self
103 }
104}
105
106impl InternalResult for i64 {
107 #[inline(always)]
108 fn get(underlying: i64) -> Self {
109 underlying
110 }
111}
112
113impl NoBits {
114 pub(crate) fn from_precision(precision: u8) -> Option<NoBits> {
115 if precision <= 9 {
116 Some(NoBits::N32)
117 } else if precision <= 18 {
118 Some(NoBits::N64)
119 } else {
120 None
121 }
122 }
123}
124
125impl PartialEq for Decimal {
126 fn eq(&self, other: &Self) -> bool {
127 match self.scale.cmp(&other.scale) {
128 Ordering::Less => {
129 let delta = other.scale() - self.scale();
130 let underlying = self.underlying * FACTORS10[delta];
131 other.underlying == underlying
132 }
133 Ordering::Equal => self.underlying == other.underlying,
134 Ordering::Greater => {
135 let delta = self.scale() - other.scale();
136 let underlying = other.underlying * FACTORS10[delta];
137 self.underlying == underlying
138 }
139 }
140 }
141}
142
143pub fn decimal2str(decimal: &Decimal) -> String {
144 let mut r = format!("{}", decimal.underlying);
145 while r.len() < decimal.scale() {
146 r.insert(0, '0');
147 }
148 let pos = r.len() - decimal.scale();
149 r.insert(pos, '.');
150 if r.starts_with('.') {
151 r.insert(0, '0');
152 }
153 r
154}
155
156impl fmt::Display for Decimal {
157 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
158 write!(f, "{}", decimal2str(self))
159 }
160}
161
162impl fmt::Debug for Decimal {
163 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
164 write!(f, "{}", decimal2str(self))
165 }
166}
167
168impl From<Decimal> for f32 {
169 fn from(value: Decimal) -> Self {
170 value.underlying as f32 / FACTORS10[value.scale()] as f32
171 }
172}
173
174impl From<Decimal> for f64 {
175 fn from(value: Decimal) -> Self {
176 value.underlying as f64 / FACTORS10[value.scale()] as f64
177 }
178}
179
180impl Decimal {
181 pub fn new(underlying: i64, scale: u8) -> Decimal {
183 let precision = 18;
184 if scale > precision {
185 panic!("scale can't be greater than 18");
186 }
187
188 Decimal {
189 underlying,
190 precision,
191 scale,
192 nobits: NoBits::N64,
193 }
194 }
195
196 pub fn of<B: Base>(source: B, scale: u8) -> Decimal {
197 let precision = 18;
198 if scale > precision {
199 panic!("scale can't be greater than 18");
200 }
201
202 let underlying = source.scale(FACTORS10[scale as usize]);
203 if underlying > FACTORS10[precision as usize] {
204 panic!("{} > {}", underlying, FACTORS10[precision as usize]);
205 }
206
207 Decimal {
208 underlying,
209 precision,
210 scale,
211 nobits: NoBits::N64,
212 }
213 }
214
215 #[inline(always)]
239 pub fn internal<I: InternalResult>(&self) -> I {
240 InternalResult::get(self.underlying)
241 }
242
243 pub fn scale(&self) -> usize {
245 self.scale as usize
246 }
247
248 #[must_use]
249 pub fn set_scale(self, scale: u8) -> Self {
250 let underlying = match scale.cmp(&self.scale) {
251 Ordering::Less => {
252 let delta = self.scale() - scale as usize;
253 self.underlying / FACTORS10[delta]
254 }
255 Ordering::Equal => return self,
256 Ordering::Greater => {
257 let delta = scale as usize - self.scale();
258 self.underlying * FACTORS10[delta]
259 }
260 };
261
262 Decimal {
263 underlying,
264 precision: self.precision,
265 scale,
266 nobits: self.nobits,
267 }
268 }
269}