blob: e67430384ebaccf8e4f824bdc9b32c480ed5a418 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::{AvroResult, Error};
use num_bigint::{BigInt, Sign};
#[derive(Debug, Clone)]
pub struct Decimal {
value: BigInt,
len: usize,
}
// We only care about value equality, not byte length. Can two equal `BigInt`s have two different
// byte lengths?
impl PartialEq for Decimal {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl Decimal {
pub(crate) fn len(&self) -> usize {
self.len
}
fn to_vec(&self) -> AvroResult<Vec<u8>> {
self.to_sign_extended_bytes_with_len(self.len)
}
pub(crate) fn to_sign_extended_bytes_with_len(&self, len: usize) -> AvroResult<Vec<u8>> {
let sign_byte = 0xFF * u8::from(self.value.sign() == Sign::Minus);
let mut decimal_bytes = vec![sign_byte; len];
let raw_bytes = self.value.to_signed_bytes_be();
let num_raw_bytes = raw_bytes.len();
let start_byte_index = len.checked_sub(num_raw_bytes).ok_or(Error::SignExtend {
requested: len,
needed: num_raw_bytes,
})?;
decimal_bytes[start_byte_index..].copy_from_slice(&raw_bytes);
Ok(decimal_bytes)
}
}
/// Gets the internal byte array representation of a referenced decimal.
/// Usage:
/// ```
/// use apache_avro::Decimal;
/// use std::convert::TryFrom;
///
/// let decimal = Decimal::from(vec![1, 24]);
/// let maybe_bytes = <Vec<u8>>::try_from(&decimal);
/// ```
impl std::convert::TryFrom<&Decimal> for Vec<u8> {
type Error = Error;
fn try_from(decimal: &Decimal) -> Result<Self, Self::Error> {
decimal.to_vec()
}
}
/// Gets the internal byte array representation of an owned decimal.
/// Usage:
/// ```
/// use apache_avro::Decimal;
/// use std::convert::TryFrom;
///
/// let decimal = Decimal::from(vec![1, 24]);
/// let maybe_bytes = <Vec<u8>>::try_from(decimal);
/// ```
impl std::convert::TryFrom<Decimal> for Vec<u8> {
type Error = Error;
fn try_from(decimal: Decimal) -> Result<Self, Self::Error> {
decimal.to_vec()
}
}
impl<T: AsRef<[u8]>> From<T> for Decimal {
fn from(bytes: T) -> Self {
let bytes_ref = bytes.as_ref();
Self {
value: BigInt::from_signed_bytes_be(bytes_ref),
len: bytes_ref.len(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::convert::TryFrom;
#[test]
fn test_decimal_from_bytes_from_ref_decimal() {
let input = vec![1, 24];
let d = Decimal::from(&input);
let output = <Vec<u8>>::try_from(&d).unwrap();
assert_eq!(output, input);
}
#[test]
fn test_decimal_from_bytes_from_owned_decimal() {
let input = vec![1, 24];
let d = Decimal::from(&input);
let output = <Vec<u8>>::try_from(d).unwrap();
assert_eq!(output, input);
}
}