1use self::Number::*;
2use crate::{constants::*, Encoder, ItemKind, TaggedItem, Writer};
3use std::{borrow::Cow, marker::PhantomData};
4
5#[derive(Debug, Clone, PartialEq)]
7pub enum Number<'a> {
8 Int(i128),
10 IEEE754(f64),
12 Decimal(Exponential<'a, Ten>),
14 Float(Exponential<'a, Two>),
16}
17
18impl<'a> Number<'a> {
19 pub(crate) fn from_bignum(item: TaggedItem<'a>) -> Option<Self> {
21 let tag = item.tags().single()?;
22 let (exp, mant) = if let ItemKind::Array(mut a) = item.kind() {
23 if let (Some(e), Some(m), None) = (a.next(), a.next(), a.next()) {
24 (e, m)
25 } else {
26 return None;
27 }
28 } else {
29 return None;
30 };
31 let exponent = match exp.kind() {
32 ItemKind::Pos(x) => i128::from(x),
33 ItemKind::Neg(x) => -1 - i128::from(x),
34 _ => return None,
35 };
36 if !matches!(
37 mant.kind(),
38 ItemKind::Pos(_) | ItemKind::Neg(_) | ItemKind::Bytes(_)
39 ) {
40 return None;
42 }
43 if let super::CborValue::Number(n) = mant.decode() {
44 match n {
45 Int(mut n) => {
46 let inverted = n < 0;
47 if inverted {
48 n = -1 - n;
49 }
50 let start = n.leading_zeros() as usize / 8;
51 let bytes = n.to_be_bytes();
52 if tag == TAG_BIGDECIMAL {
53 Some(Decimal(Exponential {
54 exponent,
55 mantissa: Cow::Owned(bytes[start..].to_vec()),
56 inverted,
57 _ph: PhantomData,
58 }))
59 } else {
60 Some(Float(Exponential {
61 exponent,
62 mantissa: Cow::Owned(bytes[start..].to_vec()),
63 inverted,
64 _ph: PhantomData,
65 }))
66 }
67 }
68 Decimal(e) => {
69 if tag == TAG_BIGDECIMAL {
70 Some(Decimal(e.with_exponent(exponent)))
71 } else {
72 Some(Float(e.with_exponent(exponent)))
73 }
74 }
75 _ => None,
76 }
77 } else {
78 None
79 }
80 }
81
82 pub(crate) fn encode<E: Encoder>(&self, encoder: E) -> E::Output {
83 match self {
84 Number::Int(i) => {
85 let i = *i;
86 if i >= 0 {
87 if i <= (u64::MAX as i128) {
88 encoder.write_pos(i as u64, None)
89 } else {
90 let bytes = i.to_be_bytes();
91 let first = bytes
92 .iter()
93 .enumerate()
94 .find_map(|(idx, byte)| if *byte != 0 { Some(idx) } else { None })
95 .unwrap();
96 encoder.write_bytes(&bytes[first..], [TAG_BIGNUM_POS])
97 }
98 } else if i >= -(u64::MAX as i128 + 1) {
99 encoder.write_neg((-1 - i) as u64, None)
100 } else {
101 let bytes = (-1 - i).to_be_bytes();
102 let first = bytes
103 .iter()
104 .enumerate()
105 .find_map(|(idx, byte)| if *byte != 0 { Some(idx) } else { None })
106 .unwrap();
107 encoder.write_bytes(&bytes[first..], [TAG_BIGNUM_NEG])
108 }
109 }
110 Number::IEEE754(f) => encoder.encode_f64(*f),
111 Number::Decimal(d) => encode_big(d, encoder),
112 Number::Float(d) => encode_big(d, encoder),
113 }
114 }
115
116 pub fn make_static(self) -> Number<'static> {
118 match self {
119 Int(i) => Int(i),
120 IEEE754(f) => IEEE754(f),
121 Decimal(e) => Decimal(e.make_static()),
122 Float(e) => Float(e.make_static()),
123 }
124 }
125
126 pub fn get_type(&self) -> &'static str {
127 match self {
128 Int(_) => "small integer",
129 IEEE754(_) => "small float",
130 Decimal(_) => "big decimal",
131 Float(_) => "big float",
132 }
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Hash)]
146pub struct Exponential<'a, B: Base> {
147 exponent: i128,
148 mantissa: Cow<'a, [u8]>,
150 inverted: bool,
152 _ph: PhantomData<B>,
153}
154
155impl<'a> Exponential<'a, Ten> {
156 pub(crate) fn from_bytes(item: TaggedItem<'a>) -> Option<Self> {
158 let tag = item.tags().single()?;
159 if let ItemKind::Bytes(bytes) = item.kind() {
160 Some(Exponential {
161 exponent: 0,
162 mantissa: bytes.as_cow(),
163 inverted: tag == TAG_BIGNUM_NEG,
164 _ph: PhantomData,
165 })
166 } else {
167 None
168 }
169 }
170
171 fn with_exponent<BB: Base>(self, exponent: i128) -> Exponential<'a, BB> {
172 Exponential {
173 exponent,
174 mantissa: self.mantissa,
175 inverted: self.inverted,
176 _ph: PhantomData,
177 }
178 }
179}
180
181impl<'a, B: Base> Exponential<'a, B> {
182 pub fn new(exponent: i128, mantissa: Cow<'a, [u8]>, inverted: bool) -> Self {
183 Self {
184 exponent,
185 mantissa,
186 inverted,
187 _ph: PhantomData,
188 }
189 }
190
191 pub fn make_static(self) -> Exponential<'static, B> {
193 Exponential {
194 exponent: self.exponent,
195 mantissa: super::ms(self.mantissa),
196 inverted: self.inverted,
197 _ph: PhantomData,
198 }
199 }
200
201 pub fn exponent(&self) -> i128 {
203 self.exponent
204 }
205
206 pub fn mantissa(&self) -> &[u8] {
208 self.mantissa.as_ref()
209 }
210
211 pub fn inverted(&self) -> bool {
213 self.inverted
214 }
215}
216
217pub trait Base {
218 const BASE: u64;
219 const TAG: u64;
220}
221#[derive(Debug, Clone, PartialEq)]
222pub struct Two;
223impl Base for Two {
224 const BASE: u64 = 2;
225 const TAG: u64 = TAG_BIGFLOAT;
226}
227#[derive(Debug, Clone, PartialEq)]
228pub struct Ten;
229impl Base for Ten {
230 const BASE: u64 = 10;
231 const TAG: u64 = TAG_BIGDECIMAL;
232}
233
234fn encode_big<B: Base, E: Encoder>(d: &Exponential<B>, encoder: E) -> E::Output {
235 let first = d
236 .mantissa()
237 .iter()
238 .enumerate()
239 .find_map(|(idx, byte)| if *byte != 0 { Some(idx) } else { None })
240 .unwrap_or_else(|| d.mantissa().len());
241 let bytes = &d.mantissa()[first..];
242 if bytes.len() <= 8 {
243 let mut be_bytes = [0u8; 8];
244 be_bytes[8 - bytes.len()..].copy_from_slice(bytes);
245 let num = u64::from_be_bytes(be_bytes);
246 if d.exponent() == 0 {
247 if d.inverted() {
248 encoder.write_neg(num, None)
249 } else {
250 encoder.write_pos(num, None)
251 }
252 } else {
253 encoder.write_array([B::TAG], |builder| {
254 let exp = d.exponent();
255 if exp >= 0 {
256 builder.write_pos(exp as u64, None);
257 } else {
258 builder.write_neg((-1 - exp) as u64, None);
259 }
260 if d.inverted() {
261 builder.write_neg(num, None);
262 } else {
263 builder.write_pos(num, None);
264 }
265 })
266 }
267 } else if d.exponent() == 0 {
268 if d.inverted() {
269 encoder.write_bytes(bytes, [TAG_BIGNUM_NEG])
270 } else {
271 encoder.write_bytes(bytes, [TAG_BIGNUM_POS])
272 }
273 } else {
274 encoder.write_array([B::TAG], |builder| {
275 let exp = d.exponent();
276 if exp >= 0 {
277 builder.write_pos(exp as u64, None);
278 } else {
279 builder.write_neg((-1 - exp) as u64, None);
280 }
281 if d.inverted() {
282 builder.write_bytes(bytes, [TAG_BIGNUM_NEG]);
283 } else {
284 builder.write_bytes(bytes, [TAG_BIGNUM_POS]);
285 }
286 })
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293 use crate::{tests::hex, CborBuilder};
294
295 #[test]
296 fn encode() {
297 fn e(n: Number) -> String {
298 CborBuilder::new().encode_number(&n).to_string()
299 }
300 fn d(exp: i128, mant: &str, inv: bool) -> String {
301 e(Decimal(Exponential::new(exp, hex(mant).into(), inv)))
302 }
303 fn f(exp: i128, mant: &str, inv: bool) -> String {
304 e(Float(Exponential::new(exp, hex(mant).into(), inv)))
305 }
306
307 assert_eq!(e(Int(0)), "0");
308 assert_eq!(e(Int(1)), "1");
309 assert_eq!(e(Int(-1)), "-1");
310 assert_eq!(e(Int(u64::MAX.into())), "18446744073709551615");
311 assert_eq!(e(Int(-1 - u64::MAX as i128)), "-18446744073709551616");
312 assert_eq!(e(Int(u64::MAX as i128 + 1)), "2(h'010000000000000000')");
313 assert_eq!(e(Int(-2 - u64::MAX as i128)), "3(h'010000000000000000')");
314
315 assert_eq!(e(IEEE754(-0.0)), "-0.0");
316 assert_eq!(e(IEEE754(1.3e34)), "1.3e34");
317
318 assert_eq!(d(0, "", false), "0");
319 assert_eq!(d(0, "", true), "-1");
320 assert_eq!(d(0, "01", false), "1");
321 assert_eq!(d(0, "01", true), "-2");
322 assert_eq!(d(0, "ffffffffffffffff", false), "18446744073709551615");
323 assert_eq!(d(0, "ffffffffffffffff", true), "-18446744073709551616");
324 assert_eq!(
325 d(0, "010203040506070809", false),
326 "2(h'010203040506070809')"
327 );
328 assert_eq!(d(0, "010203040506070809", true), "3(h'010203040506070809')");
329 assert_eq!(d(1, "", false), "4([1, 0])");
330 assert_eq!(d(1, "", true), "4([1, -1])");
331 assert_eq!(d(1, "01", false), "4([1, 1])");
332 assert_eq!(d(1, "01", true), "4([1, -2])");
333 assert_eq!(
334 d(1, "ffffffffffffffff", false),
335 "4([1, 18446744073709551615])"
336 );
337 assert_eq!(
338 d(1, "ffffffffffffffff", true),
339 "4([1, -18446744073709551616])"
340 );
341 assert_eq!(
342 d(1, "010203040506070809", false),
343 "4([1, 2(h'010203040506070809')])"
344 );
345 assert_eq!(
346 d(1, "010203040506070809", true),
347 "4([1, 3(h'010203040506070809')])"
348 );
349 assert_eq!(d(-1, "", false), "4([-1, 0])");
350 assert_eq!(d(-1, "", true), "4([-1, -1])");
351 assert_eq!(d(-1, "01", false), "4([-1, 1])");
352 assert_eq!(d(-1, "01", true), "4([-1, -2])");
353 assert_eq!(
354 d(-1, "ffffffffffffffff", false),
355 "4([-1, 18446744073709551615])"
356 );
357 assert_eq!(
358 d(-1, "ffffffffffffffff", true),
359 "4([-1, -18446744073709551616])"
360 );
361 assert_eq!(
362 d(-1, "010203040506070809", false),
363 "4([-1, 2(h'010203040506070809')])"
364 );
365 assert_eq!(
366 d(-1, "010203040506070809", true),
367 "4([-1, 3(h'010203040506070809')])"
368 );
369
370 assert_eq!(f(0, "", false), "0");
371 assert_eq!(f(0, "", true), "-1");
372 assert_eq!(f(0, "01", false), "1");
373 assert_eq!(f(0, "01", true), "-2");
374 assert_eq!(f(0, "ffffffffffffffff", false), "18446744073709551615");
375 assert_eq!(f(0, "ffffffffffffffff", true), "-18446744073709551616");
376 assert_eq!(
377 f(0, "010203040506070809", false),
378 "2(h'010203040506070809')"
379 );
380 assert_eq!(f(0, "010203040506070809", true), "3(h'010203040506070809')");
381 assert_eq!(f(1, "", false), "5([1, 0])");
382 assert_eq!(f(1, "", true), "5([1, -1])");
383 assert_eq!(f(1, "01", false), "5([1, 1])");
384 assert_eq!(f(1, "01", true), "5([1, -2])");
385 assert_eq!(
386 f(1, "ffffffffffffffff", false),
387 "5([1, 18446744073709551615])"
388 );
389 assert_eq!(
390 f(1, "ffffffffffffffff", true),
391 "5([1, -18446744073709551616])"
392 );
393 assert_eq!(
394 f(1, "010203040506070809", false),
395 "5([1, 2(h'010203040506070809')])"
396 );
397 assert_eq!(
398 f(1, "010203040506070809", true),
399 "5([1, 3(h'010203040506070809')])"
400 );
401 assert_eq!(f(-1, "", false), "5([-1, 0])");
402 assert_eq!(f(-1, "", true), "5([-1, -1])");
403 assert_eq!(f(-1, "01", false), "5([-1, 1])");
404 assert_eq!(f(-1, "01", true), "5([-1, -2])");
405 assert_eq!(
406 f(-1, "ffffffffffffffff", false),
407 "5([-1, 18446744073709551615])"
408 );
409 assert_eq!(
410 f(-1, "ffffffffffffffff", true),
411 "5([-1, -18446744073709551616])"
412 );
413 assert_eq!(
414 f(-1, "010203040506070809", false),
415 "5([-1, 2(h'010203040506070809')])"
416 );
417 assert_eq!(
418 f(-1, "010203040506070809", true),
419 "5([-1, 3(h'010203040506070809')])"
420 );
421 }
422}