diff --git a/docs/source/data-types/varint.md b/docs/source/data-types/varint.md index 59a515eb6..c2309bb1b 100644 --- a/docs/source/data-types/varint.md +++ b/docs/source/data-types/varint.md @@ -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 diff --git a/scylla-cql/src/frame/value.rs b/scylla-cql/src/frame/value.rs index 75c557d7b..0cac789b1 100644 --- a/scylla-cql/src/frame/value.rs +++ b/scylla-cql/src/frame/value.rs @@ -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. @@ -233,6 +236,13 @@ impl std::hash::Hash for CqlTimeuuid { #[derive(Clone, Eq, Debug)] pub struct CqlVarint(Vec); +/// 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 @@ -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 @@ -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 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. @@ -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(&self, state: &mut H) { + self.as_normalized_slice().hash(state) + } +} + #[cfg(feature = "num-bigint-03")] impl From for CqlVarint { fn from(value: num_bigint_03::BigInt) -> Self { @@ -343,6 +420,13 @@ impl From for num_bigint_03::BigInt { } } +#[cfg(feature = "num-bigint-03")] +impl From> 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 for CqlVarint { fn from(value: num_bigint_04::BigInt) -> Self { @@ -357,6 +441,13 @@ impl From for num_bigint_04::BigInt { } } +#[cfg(feature = "num-bigint-04")] +impl From> 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: diff --git a/scylla-cql/src/types/deserialize/value.rs b/scylla-cql/src/types/deserialize/value.rs index 2ff5d228e..9ac6fabb6 100644 --- a/scylla-cql/src/types/deserialize/value.rs +++ b/scylla-cql/src/types/deserialize/value.rs @@ -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. @@ -222,6 +222,16 @@ impl_emptiable_strict_type!( } ); +impl_emptiable_strict_type!( + CqlVarintBorrowed<'b>, + Varint, + |typ: &'metadata ColumnType<'metadata>, v: Option>| { + let val = ensure_not_null_slice::(typ, v)?; + Ok(CqlVarintBorrowed::from_signed_bytes_be_slice(val)) + }, + 'b +); + #[cfg(feature = "num-bigint-03")] impl_emptiable_strict_type!( num_bigint_03::BigInt, diff --git a/scylla-cql/src/types/deserialize/value_tests.rs b/scylla-cql/src/types/deserialize/value_tests.rs index e02915588..fbded1e06 100644 --- a/scylla-cql/src/types/deserialize/value_tests.rs +++ b/scylla-cql/src/types/deserialize/value_tests.rs @@ -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}; @@ -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, diff --git a/scylla-cql/src/types/serialize/value.rs b/scylla-cql/src/types/serialize/value.rs index 78e169aa4..399302acf 100644 --- a/scylla-cql/src/types/serialize/value.rs +++ b/scylla-cql/src/types/serialize/value.rs @@ -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")] @@ -252,6 +252,14 @@ impl SerializeValue for CqlVarint { .map_err(|_| mk_ser_err::(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::(typ, BuiltinSerializationErrorKind::SizeOverflow))? + }); +} #[cfg(feature = "num-bigint-03")] impl SerializeValue for num_bigint_03::BigInt { impl_serialize_via_writer!(|me, typ, writer| {