Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

value: Cql[Varint/Decimal]Borrowed #1148

Merged
merged 3 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/source/data-types/varint.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ To make use of `num_bigint::BigInt` type, user should enable one of the availabl

## value::CqlVarint

Without any feature flags, the user can interact with `Varint` type by making use of `value::CqlVarint` which
is a very simple wrapper representing the value as signed binary number in big-endian order.
Without any feature flags, the user can interact with `Varint` type by making use of `value::CqlVarint` or `value::CqlVarintBorrowed` which are very simple wrappers representing the value as signed binary number in big-endian order.

## Example

Expand Down
95 changes: 93 additions & 2 deletions scylla-cql/src/frame/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ impl std::hash::Hash for CqlTimeuuid {
/// The library support (e.g. conversion from [`CqlValue`]) for these types is
/// enabled via `num-bigint-03` and `num-bigint-04` crate features.
///
/// This struct holds owned bytes. If you wish to borrow the bytes instead,
/// see [`CqlVarintBorrowed`] documentation.
///
/// # DB data format
/// Notice that [constructors](CqlVarint#impl-CqlVarint)
/// don't perform any normalization on the provided data.
Expand All @@ -233,6 +236,13 @@ impl std::hash::Hash for CqlTimeuuid {
#[derive(Clone, Eq, Debug)]
pub struct CqlVarint(Vec<u8>);

/// A borrowed version of native CQL `varint` representation.
///
/// Refer to the documentation of [`CqlVarint`].
/// Especially, see the disclaimer about [non-normalized values](CqlVarint#db-data-format).
#[derive(Clone, Eq, Debug)]
pub struct CqlVarintBorrowed<'b>(&'b [u8]);

muzarski marked this conversation as resolved.
Show resolved Hide resolved
/// Constructors from bytes
impl CqlVarint {
/// Creates a [`CqlVarint`] from an array of bytes in
Expand All @@ -252,6 +262,17 @@ impl CqlVarint {
}
}

/// Constructors from bytes
impl<'b> CqlVarintBorrowed<'b> {
/// Creates a [`CqlVarintBorrowed`] from a slice of bytes in
/// two's complement binary big-endian representation.
///
/// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
pub fn from_signed_bytes_be_slice(digits: &'b [u8]) -> Self {
Self(digits)
}
}

/// Conversion to bytes
impl CqlVarint {
/// Converts [`CqlVarint`] to an array of bytes in two's
Expand All @@ -267,9 +288,39 @@ impl CqlVarint {
}
}

impl CqlVarint {
/// Conversion to bytes
impl CqlVarintBorrowed<'_> {
/// Returns a slice of bytes in two's complement
/// binary big-endian representation.
pub fn as_signed_bytes_be_slice(&self) -> &[u8] {
self.0
}
}

/// An internal utility trait used to implement [`AsNormalizedVarintSlice`]
/// for both [`CqlVarint`] and [`CqlVarintBorrowed`].
trait AsVarintSlice {
fn as_slice(&self) -> &[u8];
}
impl AsVarintSlice for CqlVarint {
fn as_slice(&self) -> &[u8] {
self.as_signed_bytes_be_slice()
}
}
impl AsVarintSlice for CqlVarintBorrowed<'_> {
fn as_slice(&self) -> &[u8] {
self.as_signed_bytes_be_slice()
}
}

/// An internal utility trait used to implement [`PartialEq`] and [`std::hash::Hash`]
/// for [`CqlVarint`] and [`CqlVarintBorrowed`].
trait AsNormalizedVarintSlice {
fn as_normalized_slice(&self) -> &[u8];
}
impl<V: AsVarintSlice> AsNormalizedVarintSlice for V {
muzarski marked this conversation as resolved.
Show resolved Hide resolved
fn as_normalized_slice(&self) -> &[u8] {
let digits = self.0.as_slice();
let digits = self.as_slice();
if digits.is_empty() {
// num-bigint crate normalizes empty vector to 0.
// We will follow the same approach.
Expand Down Expand Up @@ -329,6 +380,32 @@ impl std::hash::Hash for CqlVarint {
}
}

/// Compares two [`CqlVarintBorrowed`] values after normalization.
///
/// # Example
///
/// ```rust
/// # use scylla_cql::frame::value::CqlVarintBorrowed;
/// let non_normalized_bytes = &[0x00, 0x01];
/// let normalized_bytes = &[0x01];
/// assert_eq!(
/// CqlVarintBorrowed::from_signed_bytes_be_slice(non_normalized_bytes),
/// CqlVarintBorrowed::from_signed_bytes_be_slice(normalized_bytes)
/// );
/// ```
impl PartialEq for CqlVarintBorrowed<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_normalized_slice() == other.as_normalized_slice()
}
}

/// Computes the hash of normalized [`CqlVarintBorrowed`].
impl std::hash::Hash for CqlVarintBorrowed<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_normalized_slice().hash(state)
}
}

#[cfg(feature = "num-bigint-03")]
impl From<num_bigint_03::BigInt> for CqlVarint {
fn from(value: num_bigint_03::BigInt) -> Self {
Expand All @@ -343,6 +420,13 @@ impl From<CqlVarint> for num_bigint_03::BigInt {
}
}

#[cfg(feature = "num-bigint-03")]
impl From<CqlVarintBorrowed<'_>> for num_bigint_03::BigInt {
fn from(val: CqlVarintBorrowed<'_>) -> Self {
num_bigint_03::BigInt::from_signed_bytes_be(val.0)
}
}

#[cfg(feature = "num-bigint-04")]
impl From<num_bigint_04::BigInt> for CqlVarint {
fn from(value: num_bigint_04::BigInt) -> Self {
Expand All @@ -357,6 +441,13 @@ impl From<CqlVarint> for num_bigint_04::BigInt {
}
}

#[cfg(feature = "num-bigint-04")]
impl From<CqlVarintBorrowed<'_>> for num_bigint_04::BigInt {
fn from(val: CqlVarintBorrowed<'_>) -> Self {
num_bigint_04::BigInt::from_signed_bytes_be(val.0)
}
}

/// Native CQL `decimal` representation.
///
/// Represented as a pair:
Expand Down
12 changes: 11 additions & 1 deletion scylla-cql/src/types/deserialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ use std::fmt::Display;
use thiserror::Error;

use super::{make_error_replace_rust_name, DeserializationError, FrameSlice, TypeCheckError};
use crate::frame::frame_errors::LowLevelDeserializationError;
use crate::frame::response::result::{deser_cql_value, ColumnType, CqlValue};
use crate::frame::types;
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
};
use crate::frame::{frame_errors::LowLevelDeserializationError, value::CqlVarintBorrowed};

/// A type that can be deserialized from a column value inside a row that was
/// returned from a query.
Expand Down Expand Up @@ -222,6 +222,16 @@ impl_emptiable_strict_type!(
}
);

impl_emptiable_strict_type!(
CqlVarintBorrowed<'b>,
Varint,
|typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>| {
let val = ensure_not_null_slice::<Self>(typ, v)?;
Ok(CqlVarintBorrowed::from_signed_bytes_be_slice(val))
},
'b
);

#[cfg(feature = "num-bigint-03")]
impl_emptiable_strict_type!(
num_bigint_03::BigInt,
Expand Down
7 changes: 7 additions & 0 deletions scylla-cql/src/types/deserialize/value_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::frame::response::result::{ColumnType, CqlValue};
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
CqlVarintBorrowed,
};
use crate::types::deserialize::value::{TupleDeserializationErrorKind, TupleTypeCheckErrorKind};
use crate::types::deserialize::{DeserializationError, FrameSlice, TypeCheckError};
Expand Down Expand Up @@ -159,6 +160,12 @@ fn test_varlen_numbers() {
&mut Bytes::new(),
);

assert_ser_de_identity(
&ColumnType::Varint,
&CqlVarintBorrowed::from_signed_bytes_be_slice(b"Ala ma kota"),
&mut Bytes::new(),
);

#[cfg(feature = "num-bigint-03")]
assert_ser_de_identity(
&ColumnType::Varint,
Expand Down
10 changes: 9 additions & 1 deletion scylla-cql/src/types/serialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::frame::response::result::{ColumnType, CqlValue};
use crate::frame::types::vint_encode;
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
MaybeUnset, Unset, Value,
CqlVarintBorrowed, MaybeUnset, Unset, Value,
};

#[cfg(feature = "chrono-04")]
Expand Down Expand Up @@ -252,6 +252,14 @@ impl SerializeValue for CqlVarint {
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for CqlVarintBorrowed<'_> {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Varint);
writer
.set_value(me.as_signed_bytes_be_slice())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
#[cfg(feature = "num-bigint-03")]
impl SerializeValue for num_bigint_03::BigInt {
impl_serialize_via_writer!(|me, typ, writer| {
Expand Down