use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::error::{Error, Result};
pub(crate) const MAX_STRING_SIZE: u64 = 1 << 30;
pub(crate) const VAR_UINT_MAX_BYTES: usize = 10;
type VarUintBuf = [u8; VAR_UINT_MAX_BYTES];
fn encode_var_uint(mut value: u64, buf: &mut VarUintBuf) -> usize {
let mut pos = 0;
loop {
#[allow(clippy::cast_possible_truncation)]
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value > 0 {
byte |= 0x80;
}
buf[pos] = byte;
pos += 1;
if value == 0 {
return pos;
}
}
}
#[derive(Default)]
struct VarUintDecoder {
out: u64,
index: usize,
}
impl VarUintDecoder {
fn push(&mut self, octet: u8) -> Result<Option<u64>> {
self.out |= u64::from(octet & 0x7F) << (7 * self.index);
self.index += 1;
if (octet & 0x80) == 0 {
return Ok(Some(self.out));
}
if self.index == VAR_UINT_MAX_BYTES {
return Err(Self::overflow());
}
Ok(None)
}
fn overflow() -> Error {
Error::BadResponse("native protocol: varint continues past 10 bytes".into())
}
}
pub(crate) fn get_var_uint(bytes: &[u8]) -> Result<(u64, usize)> {
let mut decoder = VarUintDecoder::default();
for (i, &octet) in bytes.iter().enumerate() {
if let Some(value) = decoder.push(octet)? {
return Ok((value, i + 1));
}
}
Err(Error::NotEnoughData)
}
fn checked_string_len(len: u64) -> Result<u64> {
if len > MAX_STRING_SIZE {
return Err(Error::BadResponse(format!(
"native protocol: string too large: {len} > {MAX_STRING_SIZE}"
)));
}
Ok(len)
}
fn string_len_usize(len: u64) -> Result<usize> {
usize::try_from(checked_string_len(len)?).map_err(|_| {
Error::BadResponse(format!(
"native protocol: string length {len} exceeds platform usize"
))
})
}
pub(crate) fn with_cap<T>(cap: usize) -> Result<Vec<T>> {
let mut buf = Vec::new();
buf.try_reserve_exact(cap)
.map_err(|_| alloc_refused(cap.saturating_mul(std::mem::size_of::<T>())))?;
Ok(buf)
}
fn alloc_refused(bytes: usize) -> Error {
Error::BadResponse(format!(
"native protocol: refused a {bytes}-byte column buffer the allocator could not back"
))
}
const READ_CHUNK: usize = 64 * 1024;
pub(crate) async fn read_exact_grown<R: AsyncRead + Unpin>(
reader: &mut R,
len: usize,
) -> Result<Vec<u8>> {
let mut buf = Vec::new();
let mut left = len;
while left > 0 {
let chunk = left.min(READ_CHUNK);
let start = buf.len();
buf.try_reserve(chunk)
.map_err(|_| alloc_refused(start.saturating_add(chunk)))?;
buf.resize(start + chunk, 0);
reader.read_exact(&mut buf[start..]).await?;
left -= chunk;
}
Ok(buf)
}
pub(crate) trait ClickHouseRead: AsyncRead + Unpin + Send {
fn read_var_uint(&mut self) -> impl Future<Output = Result<u64>> + Send + '_;
fn read_string(&mut self) -> impl Future<Output = Result<Vec<u8>>> + Send + '_;
fn read_utf8_string(&mut self) -> impl Future<Output = Result<String>> + Send + '_ {
async {
let bytes = self.read_string().await?;
String::from_utf8(bytes)
.map_err(|e| Error::BadResponse(format!("native protocol: invalid utf8: {e}")))
}
}
}
#[cfg(feature = "unified")]
pub(crate) async fn read_var_uint_or_eof<R: ClickHouseRead>(r: &mut R) -> Result<Option<u64>> {
let mut first = [0u8; 1];
if r.read(&mut first).await? == 0 {
return Ok(None);
}
let mut decoder = VarUintDecoder::default();
if let Some(value) = decoder.push(first[0])? {
return Ok(Some(value));
}
loop {
let mut octet = [0u8; 1];
r.read_exact(&mut octet[..]).await?;
if let Some(value) = decoder.push(octet[0])? {
return Ok(Some(value));
}
}
}
impl<T: AsyncRead + Unpin + Send> ClickHouseRead for T {
async fn read_var_uint(&mut self) -> Result<u64> {
let mut decoder = VarUintDecoder::default();
loop {
let mut octet = [0u8];
self.read_exact(&mut octet[..]).await?;
if let Some(value) = decoder.push(octet[0])? {
return Ok(value);
}
}
}
async fn read_string(&mut self) -> Result<Vec<u8>> {
let len = string_len_usize(self.read_var_uint().await?)?;
read_exact_grown(self, len).await
}
}
pub(crate) trait ClickHouseWrite: AsyncWrite + Unpin + Send + Sync {
fn write_var_uint(&mut self, value: u64) -> impl Future<Output = Result<()>> + Send + '_;
fn write_string<V: AsRef<[u8]> + Send>(
&mut self,
value: V,
) -> impl Future<Output = Result<()>> + Send + use<'_, Self, V>;
}
impl<T: AsyncWrite + Unpin + Send + Sync> ClickHouseWrite for T {
async fn write_var_uint(&mut self, value: u64) -> Result<()> {
let mut buf = VarUintBuf::default();
let len = encode_var_uint(value, &mut buf);
self.write_all(&buf[..len]).await?;
Ok(())
}
async fn write_string<V: AsRef<[u8]> + Send>(&mut self, value: V) -> Result<()> {
let value = value.as_ref();
self.write_var_uint(value.len() as u64).await?;
self.write_all(value).await?;
Ok(())
}
}
pub(crate) trait ClickHouseBytesWrite: bytes::BufMut {
fn put_var_uint(&mut self, value: u64);
fn put_string<V: AsRef<[u8]>>(&mut self, value: V);
}
impl<T: bytes::BufMut> ClickHouseBytesWrite for T {
fn put_var_uint(&mut self, value: u64) {
let mut buf = VarUintBuf::default();
let len = encode_var_uint(value, &mut buf);
self.put_slice(&buf[..len]);
}
fn put_string<V: AsRef<[u8]>>(&mut self, value: V) {
let value = value.as_ref();
self.put_var_uint(value.len() as u64);
self.put_slice(value);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_var_uint_roundtrip_async() {
let test_values: &[u64] = &[
0,
1,
127,
128,
255,
256,
16383,
16384,
(1 << 63) - 1,
1 << 63,
u64::MAX,
];
for &val in test_values {
let mut buf = Vec::new();
buf.write_var_uint(val).await.unwrap();
let mut reader = std::io::Cursor::new(buf);
let decoded = reader.read_var_uint().await.unwrap();
assert_eq!(val, decoded, "async roundtrip failed for {val}");
}
}
#[tokio::test]
async fn test_string_roundtrip_async() {
let test_strings: &[&[u8]] = &[b"", b"hello", b"hello world"];
for &val in test_strings {
let mut buf = Vec::new();
buf.write_string(val).await.unwrap();
let mut reader = std::io::Cursor::new(buf);
let decoded = reader.read_string().await.unwrap();
assert_eq!(val, &decoded[..], "async roundtrip failed");
}
}
#[tokio::test]
async fn test_eof_returns_error() {
let mut reader = std::io::Cursor::new(Vec::new());
assert!(reader.read_var_uint().await.is_err());
}
#[tokio::test]
async fn test_var_uint_malformed_too_long_async() {
let malformed = vec![0x81u8; 12];
let mut reader = std::io::Cursor::new(malformed);
let err = reader
.read_var_uint()
.await
.expect_err("over-long varint must be rejected");
assert!(
matches!(err, Error::BadResponse(_)),
"expected BadResponse, got {err:?}"
);
assert!(err.to_string().contains("past 10 bytes"), "got: {err}");
}
#[test]
fn string_len_boundary_is_inclusive() {
assert_eq!(
checked_string_len(MAX_STRING_SIZE).expect("the cap itself is accepted"),
MAX_STRING_SIZE
);
let err = checked_string_len(MAX_STRING_SIZE + 1).expect_err("one past the cap rejects");
assert!(err.to_string().contains("string too large"), "{err}");
}
#[test]
fn get_var_uint_shares_the_ten_byte_ceiling() {
assert_eq!(get_var_uint(&[0x00]).expect("zero decodes"), (0, 1));
assert_eq!(get_var_uint(&[0xAC, 0x02]).expect("decodes"), (300, 2));
let err = get_var_uint(&[0x81u8; 12]).expect_err("an over-long varint rejects");
assert!(err.to_string().contains("past 10 bytes"), "{err}");
assert!(matches!(
get_var_uint(&[0x81, 0x81]),
Err(Error::NotEnoughData)
));
}
}