Skip to content

Commit

Permalink
value: introduce CqlVarintBorrowed
Browse files Browse the repository at this point in the history
This is a native representation of varint that holds borrowed bytes.

Implemented SerializeValue and DeserializeValue for new type.
  • Loading branch information
muzarski committed Dec 11, 2024
1 parent d541339 commit 10add75
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 6 deletions.
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]);

/// 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 {
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

0 comments on commit 10add75

Please sign in to comment.