1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use std::{
    cmp::min,
    convert::TryInto,
    ptr::{null, null_mut},
};

use widestring::U16CStr;

/// Clamps a usize between `0` and `i16::MAX`.
pub fn clamp_small_int(n: usize) -> i16 {
    min(n, i16::MAX as usize) as i16
}

/// Clamps a usize between `0` and `i32::MAX`.
pub fn clamp_int(n: usize) -> i32 {
    min(n, i32::MAX as usize) as i32
}

/// Returns a pointer suitable to be passed as an output buffer to ODBC functions. Most notably it
/// will return NULL for empty buffers.
pub fn mut_buf_ptr<T>(buffer: &mut [T]) -> *mut T {
    if buffer.is_empty() {
        null_mut()
    } else {
        buffer.as_mut_ptr()
    }
}

/// Returns a pointer suitable to be passed as an output buffer to ODBC functions. Most notably it
/// will return NULL for empty buffers.
pub fn buf_ptr<T>(buffer: &[T]) -> *const T {
    if buffer.is_empty() {
        null()
    } else {
        buffer.as_ptr()
    }
}

/// We use this as an output buffer for strings. Allows for detecting truncation.
pub struct OutputStringBuffer {
    /// Buffer holding the string. This crate uses wide methods (i.e. UTF-16) so we allocate a
    /// `Vec<u16>` instead of a `Vec<u8>`. Must also contain space for a terminating zero.
    buffer: Vec<u16>,
    /// After the buffer has been filled, this should contain the actual length of the string. Can
    /// be used to detect truncation.
    actual_length: i16,
}

impl OutputStringBuffer {
    /// Creates a new instance of an output string buffer which can hold strings up to a size of
    /// `max_str_len` characters.
    pub fn with_buffer_size(max_str_len: usize) -> Self {
        Self {
            buffer: vec![0; max_str_len + 1],
            actual_length: 0,
        }
    }

    /// Ptr to the internal buffer. Used by ODBC API calls to fill the buffer.
    pub fn mut_buf_ptr(&mut self) -> *mut u16 {
        mut_buf_ptr(&mut self.buffer)
    }

    /// Length of the internal buffer in characters excluding the terminating zero.
    pub fn buf_len(&self) -> i16 {
        if self.buffer.is_empty() {
            0
        } else {
            (self.buffer.len() - 1).try_into().unwrap()
        }
    }

    /// Mutable pointer to actual output string length. Used by ODBC API calls to report truncation.
    pub fn mut_actual_len_ptr(&mut self) -> *mut i16 {
        &mut self.actual_length as *mut i16
    }

    /// Interpret buffer as UTF16 string. Panics if no terminating zero present.
    pub fn as_ucstr(&self) -> &U16CStr {
        U16CStr::from_slice_truncate(&self.buffer).unwrap()
    }

    /// Call this method to extract string from buffer after ODBC has filled it.
    pub fn to_utf8(&self) -> String {
        self.as_ucstr().to_string().unwrap()
    }

    /// True if the buffer had not been large enough to hold the string.
    pub fn is_truncated(&self) -> bool {
        let len: usize = self.actual_length.try_into().unwrap();
        // One character is needed for the terminating zero, but string size is reported in
        // characters without terminating zero.
        len >= self.buffer.len()
    }
}