use std::{
borrow::{Borrow, BorrowMut},
ffi::c_void,
marker::PhantomData,
mem::{size_of, size_of_val},
num::NonZeroUsize,
str::Utf8Error,
};
use odbc_sys::{CDataType, NULL_DATA};
use widestring::{U16Str, U16String};
use crate::{
DataType, OutputParameter,
buffers::{FetchRowMember, Indicator},
handles::{ASSUMED_MAX_LENGTH_OF_W_VARCHAR, CData, CDataMut, HasDataType},
};
use super::CElement;
pub unsafe trait VarKind: Send {
type Element: Copy + Eq + Send;
const ZERO: Self::Element;
const TERMINATING_ZEROES: usize;
const C_DATA_TYPE: CDataType;
fn relational_type(buffer_length: usize) -> DataType;
}
#[derive(Clone, Copy)]
pub struct Text;
unsafe impl VarKind for Text {
type Element = u8;
const ZERO: u8 = 0;
const TERMINATING_ZEROES: usize = 1;
const C_DATA_TYPE: CDataType = CDataType::Char;
fn relational_type(length: usize) -> DataType {
DataType::Varchar {
length: NonZeroUsize::new(length),
}
}
}
#[derive(Clone, Copy)]
pub struct WideText;
unsafe impl VarKind for WideText {
type Element = u16;
const ZERO: u16 = 0;
const TERMINATING_ZEROES: usize = 1;
const C_DATA_TYPE: CDataType = CDataType::WChar;
fn relational_type(length: usize) -> DataType {
if length <= ASSUMED_MAX_LENGTH_OF_W_VARCHAR {
DataType::WVarchar {
length: NonZeroUsize::new(length),
}
} else {
DataType::WLongVarchar {
length: NonZeroUsize::new(length),
}
}
}
}
#[derive(Clone, Copy)]
pub struct Binary;
unsafe impl VarKind for Binary {
type Element = u8;
const ZERO: u8 = 0;
const TERMINATING_ZEROES: usize = 0;
const C_DATA_TYPE: CDataType = CDataType::Binary;
fn relational_type(length: usize) -> DataType {
DataType::Varbinary {
length: NonZeroUsize::new(length),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct VarCell<B, K> {
buffer: B,
indicator: isize,
kind: PhantomData<K>,
}
pub type VarBinary<B> = VarCell<B, Binary>;
pub type VarChar<B> = VarCell<B, Text>;
pub type VarWChar<B> = VarCell<B, WideText>;
pub type VarCharBox = VarChar<Box<[u8]>>;
pub type VarWCharBox = VarWChar<Box<[u16]>>;
pub type VarBinaryBox = VarBinary<Box<[u8]>>;
impl<K> VarCell<Box<[K::Element]>, K>
where
K: VarKind,
{
pub fn null() -> Self {
Self::from_buffer(Box::new([K::ZERO]), Indicator::Null)
}
pub fn from_vec(val: Vec<K::Element>) -> Self {
let indicator = Indicator::Length(val.len() * size_of::<K::Element>());
let buffer = val.into_boxed_slice();
Self::from_buffer(buffer, indicator)
}
}
impl<K> VarCell<Box<[u8]>, K>
where
K: VarKind<Element = u8>,
{
pub fn from_string(val: String) -> Self {
Self::from_vec(val.into_bytes())
}
}
impl<K> VarCell<Box<[u16]>, K>
where
K: VarKind<Element = u16>,
{
pub fn from_u16_string(val: U16String) -> Self {
Self::from_vec(val.into_vec())
}
pub fn from_str_slice(val: &str) -> Self {
let utf16 = U16String::from_str(val);
Self::from_u16_string(utf16)
}
}
impl<B, K> VarCell<B, K>
where
K: VarKind,
B: Borrow<[K::Element]>,
{
pub fn from_buffer(buffer: B, indicator: Indicator) -> Self {
let buf = buffer.borrow();
if indicator.is_truncated(size_of_val(buf)) {
if !ends_in_zeroes(buf, K::TERMINATING_ZEROES, K::ZERO) {
panic!("Truncated value must be terminated with zero.")
}
}
Self {
buffer,
indicator: indicator.to_isize(),
kind: PhantomData,
}
}
pub fn is_complete(&self) -> bool {
let slice = self.buffer.borrow();
let max_value_length = if ends_in_zeroes(slice, K::TERMINATING_ZEROES, K::ZERO) {
slice.len() - K::TERMINATING_ZEROES
} else {
slice.len()
};
!self
.indicator()
.is_truncated(max_value_length * size_of::<K::Element>())
}
pub fn indicator(&self) -> Indicator {
Indicator::from_isize(self.indicator)
}
pub fn hide_truncation(&mut self) {
if !self.is_complete() {
let binary_length = size_of_val(self.buffer.borrow());
self.indicator = (binary_length - K::TERMINATING_ZEROES).try_into().unwrap();
}
}
pub fn len_in_bytes(&self) -> Option<usize> {
let max_trunc_len_in_bytes =
(self.buffer.borrow().len() - K::TERMINATING_ZEROES) * size_of::<K::Element>();
match self.indicator() {
Indicator::Null => None,
Indicator::NoTotal => Some(max_trunc_len_in_bytes),
Indicator::Length(len) => {
if self.is_complete() {
Some(len)
} else {
Some(max_trunc_len_in_bytes)
}
}
}
}
pub fn capacity_in_bytes(&self) -> usize {
size_of_val(self.buffer.borrow())
}
fn impl_assert_completness(&self) {
let slice = self.buffer.borrow();
let max_len_bytes = size_of_val(slice);
if self.indicator().is_truncated(max_len_bytes) {
panic!("Truncated values must not be used be bound as input parameters.")
}
}
}
impl<B, K> VarCell<B, K>
where
B: Borrow<[K::Element]>,
K: VarKind,
{
pub fn as_slice(&self) -> Option<&[K::Element]> {
let slice = self.buffer.borrow();
self.len_in_bytes()
.map(|len| &slice[..(len / size_of::<K::Element>())])
}
}
impl<B, K> VarCell<B, K>
where
B: Borrow<[u8]>,
K: VarKind<Element = u8>,
{
pub fn as_bytes(&self) -> Option<&[u8]> {
self.as_slice()
}
}
impl<B> VarCell<B, Text>
where
B: Borrow<[u8]>,
{
pub fn as_str(&self) -> Result<Option<&str>, Utf8Error> {
if let Some(bytes) = self.as_bytes() {
let text = std::str::from_utf8(bytes)?;
Ok(Some(text))
} else {
Ok(None)
}
}
}
impl<B> VarCell<B, WideText>
where
B: Borrow<[u16]>,
{
pub fn as_utf16(&self) -> Option<&U16Str> {
if let Some(chars) = self.as_slice() {
let text = U16Str::from_slice(chars);
Some(text)
} else {
None
}
}
}
unsafe impl<B, K> CData for VarCell<B, K>
where
B: Borrow<[K::Element]>,
K: VarKind,
{
fn cdata_type(&self) -> CDataType {
K::C_DATA_TYPE
}
fn indicator_ptr(&self) -> *const isize {
&self.indicator as *const isize
}
fn value_ptr(&self) -> *const c_void {
self.buffer.borrow().as_ptr() as *const c_void
}
fn buffer_length(&self) -> isize {
size_of_val(self.buffer.borrow()).try_into().unwrap()
}
}
impl<B, K> HasDataType for VarCell<B, K>
where
B: Borrow<[K::Element]>,
K: VarKind,
{
fn data_type(&self) -> DataType {
K::relational_type(self.buffer.borrow().len())
}
}
unsafe impl<B, K> CDataMut for VarCell<B, K>
where
B: BorrowMut<[K::Element]>,
K: VarKind,
{
fn mut_indicator_ptr(&mut self) -> *mut isize {
&mut self.indicator as *mut isize
}
fn mut_value_ptr(&mut self) -> *mut c_void {
self.buffer.borrow_mut().as_mut_ptr() as *mut c_void
}
}
pub type VarCharSlice<'a> = VarChar<&'a [u8]>;
pub type VarWCharSlice<'a> = VarWChar<&'a [u16]>;
pub type VarBinarySlice<'a> = VarBinary<&'a [u8]>;
impl<K> VarCell<&'_ [u8], K> {
pub const NULL: Self = Self {
buffer: &[0],
indicator: NULL_DATA,
kind: PhantomData,
};
}
impl<K> VarCell<&'_ [u16], K> {
pub const NULL: Self = Self {
buffer: &[0],
indicator: NULL_DATA,
kind: PhantomData,
};
}
impl<'a, K> VarCell<&'a [K::Element], K>
where
K: VarKind,
{
pub fn new(value: &'a [K::Element]) -> Self {
Self::from_buffer(value, Indicator::Length(size_of_val(value)))
}
}
pub type VarCharSliceMut<'a> = VarChar<&'a mut [u8]>;
pub type VarWCharSliceMut<'a> = VarWChar<&'a mut [u8]>;
pub type VarBinarySliceMut<'a> = VarBinary<&'a mut [u8]>;
pub type VarCharArray<const LENGTH: usize> = VarChar<[u8; LENGTH]>;
pub type VarWCharArray<const LENGTH: usize> = VarWChar<[u16; LENGTH]>;
pub type VarBinaryArray<const LENGTH: usize> = VarBinary<[u8; LENGTH]>;
impl<const LENGTH: usize, K, E> Default for VarCell<[E; LENGTH], K>
where
E: Default + Copy,
{
fn default() -> Self {
Self {
buffer: [E::default(); LENGTH],
indicator: Indicator::Null.to_isize(),
kind: Default::default(),
}
}
}
impl<const LENGTH: usize, K: VarKind> VarCell<[K::Element; LENGTH], K> {
pub const NULL: Self = Self {
buffer: [K::ZERO; LENGTH],
indicator: NULL_DATA,
kind: PhantomData,
};
pub fn new(elements: &[K::Element]) -> Self {
let indicator = (size_of_val(elements)).try_into().unwrap();
let mut buffer = [K::ZERO; LENGTH];
if elements.len() > LENGTH {
buffer.copy_from_slice(&elements[..LENGTH]);
*buffer.last_mut().unwrap() = K::ZERO;
} else {
buffer[..elements.len()].copy_from_slice(elements);
};
Self {
buffer,
indicator,
kind: PhantomData,
}
}
}
fn ends_in_zeroes<T>(buffer: &[T], number_of_zeroes: usize, zero: T) -> bool
where
T: Copy + Eq,
{
buffer.len() >= number_of_zeroes
&& buffer
.iter()
.rev()
.copied()
.take(number_of_zeroes)
.all(|byte| byte == zero)
}
unsafe impl<K: VarKind> CElement for VarCell<&'_ [K::Element], K> {
fn assert_completness(&self) {
self.impl_assert_completness()
}
}
unsafe impl<const LENGTH: usize, K: VarKind> CElement for VarCell<[K::Element; LENGTH], K> {
fn assert_completness(&self) {
self.impl_assert_completness()
}
}
unsafe impl<const LENGTH: usize, K: VarKind> OutputParameter for VarCell<[K::Element; LENGTH], K> {}
unsafe impl<K: VarKind> CElement for VarCell<&'_ mut [K::Element], K> {
fn assert_completness(&self) {
self.impl_assert_completness()
}
}
unsafe impl<K: VarKind> OutputParameter for VarCell<&'_ mut [K::Element], K> {}
unsafe impl<K: VarKind> CElement for VarCell<Box<[K::Element]>, K> {
fn assert_completness(&self) {
self.impl_assert_completness()
}
}
unsafe impl<K: VarKind> OutputParameter for VarCell<Box<[K::Element]>, K> {}
unsafe impl<const LENGTH: usize> FetchRowMember for VarCharArray<LENGTH> {
fn indicator(&self) -> Option<Indicator> {
Some(self.indicator())
}
}
unsafe impl<const LENGTH: usize> FetchRowMember for VarWCharArray<LENGTH> {
fn indicator(&self) -> Option<Indicator> {
Some(self.indicator())
}
}
unsafe impl<const LENGTH: usize> FetchRowMember for VarBinaryArray<LENGTH> {
fn indicator(&self) -> Option<Indicator> {
Some(self.indicator())
}
}
#[cfg(test)]
mod tests {
use super::{Indicator, VarCharSlice};
#[test]
fn must_accept_fitting_values_and_correctly_truncated_ones() {
VarCharSlice::from_buffer(b"12345", Indicator::Length(5));
VarCharSlice::from_buffer(b"1234\0", Indicator::Length(10));
}
#[test]
#[should_panic]
fn must_ensure_truncated_values_are_terminated() {
VarCharSlice::from_buffer(b"12345", Indicator::Length(10));
}
}