apache_avro/
decimal.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::{AvroResult, Error};
19use num_bigint::{BigInt, Sign};
20
21#[derive(Debug, Clone, Eq, serde::Serialize, serde::Deserialize)]
22pub struct Decimal {
23    value: BigInt,
24    len: usize,
25}
26
27// We only care about value equality, not byte length. Can two equal `BigInt`s have two different
28// byte lengths?
29impl PartialEq for Decimal {
30    fn eq(&self, other: &Self) -> bool {
31        self.value == other.value
32    }
33}
34
35impl Decimal {
36    pub(crate) fn len(&self) -> usize {
37        self.len
38    }
39
40    pub(crate) fn to_vec(&self) -> AvroResult<Vec<u8>> {
41        self.to_sign_extended_bytes_with_len(self.len)
42    }
43
44    pub(crate) fn to_sign_extended_bytes_with_len(&self, len: usize) -> AvroResult<Vec<u8>> {
45        let sign_byte = 0xFF * u8::from(self.value.sign() == Sign::Minus);
46        let mut decimal_bytes = vec![sign_byte; len];
47        let raw_bytes = self.value.to_signed_bytes_be();
48        let num_raw_bytes = raw_bytes.len();
49        let start_byte_index = len.checked_sub(num_raw_bytes).ok_or(Error::SignExtend {
50            requested: len,
51            needed: num_raw_bytes,
52        })?;
53        decimal_bytes[start_byte_index..].copy_from_slice(&raw_bytes);
54        Ok(decimal_bytes)
55    }
56}
57
58impl From<Decimal> for BigInt {
59    fn from(decimal: Decimal) -> Self {
60        decimal.value
61    }
62}
63
64/// Gets the internal byte array representation of a referenced decimal.
65/// Usage:
66/// ```
67/// use apache_avro::Decimal;
68/// use std::convert::TryFrom;
69///
70/// let decimal = Decimal::from(vec![1, 24]);
71/// let maybe_bytes = <Vec<u8>>::try_from(&decimal);
72/// ```
73impl std::convert::TryFrom<&Decimal> for Vec<u8> {
74    type Error = Error;
75
76    fn try_from(decimal: &Decimal) -> Result<Self, Self::Error> {
77        decimal.to_vec()
78    }
79}
80
81/// Gets the internal byte array representation of an owned decimal.
82/// Usage:
83/// ```
84/// use apache_avro::Decimal;
85/// use std::convert::TryFrom;
86///
87/// let decimal = Decimal::from(vec![1, 24]);
88/// let maybe_bytes = <Vec<u8>>::try_from(decimal);
89/// ```
90impl std::convert::TryFrom<Decimal> for Vec<u8> {
91    type Error = Error;
92
93    fn try_from(decimal: Decimal) -> Result<Self, Self::Error> {
94        decimal.to_vec()
95    }
96}
97
98impl<T: AsRef<[u8]>> From<T> for Decimal {
99    fn from(bytes: T) -> Self {
100        let bytes_ref = bytes.as_ref();
101        Self {
102            value: BigInt::from_signed_bytes_be(bytes_ref),
103            len: bytes_ref.len(),
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use apache_avro_test_helper::TestResult;
112    use pretty_assertions::assert_eq;
113
114    #[test]
115    fn test_decimal_from_bytes_from_ref_decimal() -> TestResult {
116        let input = vec![1, 24];
117        let d = Decimal::from(&input);
118
119        let output = <Vec<u8>>::try_from(&d)?;
120        assert_eq!(output, input);
121
122        Ok(())
123    }
124
125    #[test]
126    fn test_decimal_from_bytes_from_owned_decimal() -> TestResult {
127        let input = vec![1, 24];
128        let d = Decimal::from(&input);
129
130        let output = <Vec<u8>>::try_from(d)?;
131        assert_eq!(output, input);
132
133        Ok(())
134    }
135
136    #[test]
137    fn avro_3949_decimal_serde() -> TestResult {
138        let decimal = Decimal::from(&[1, 2, 3]);
139
140        let ser = serde_json::to_string(&decimal)?;
141        let de = serde_json::from_str(&ser)?;
142        std::assert_eq!(decimal, de);
143
144        Ok(())
145    }
146}