use crate::client::Client;
use crate::constants;
use crate::db_type::DbType;
pub struct WriteBuffer {
buf: Vec<u8>,
}
impl WriteBuffer {
pub fn clear(&mut self) {
self.buf.clear();
}
pub fn get_buf(&self) -> &[u8] {
&self.buf[..]
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn new() -> Self {
Self {
buf: Vec::<u8>::new(),
}
}
pub fn reserve_bytes(&mut self, num_bytes: usize) -> usize {
let orig_len = self.buf.len();
self.buf.resize(orig_len + num_bytes, 0);
orig_len
}
pub fn write_bytes(&mut self, value: &[u8]) {
self.buf.extend(value);
}
pub fn write_bytes_with_double_length(&mut self, value: Option<&[u8]>) {
if let Some(value) = value {
self.write_ub4(value.len().try_into().unwrap());
if !value.is_empty() {
self.write_bytes_with_length(value);
}
} else {
self.write_ub4(0);
}
}
pub fn write_bytes_with_length(&mut self, value: &[u8]) {
let mut num_bytes = value.len();
if num_bytes <= constants::TTC_MAX_SHORT_LENGTH.into() {
self.write_u8(num_bytes.try_into().unwrap());
self.write_bytes(value);
} else {
self.write_u8(constants::TTC_LONG_LENGTH_INDICATOR);
let mut remaining_data = value;
while num_bytes > 0 {
let chunk_len =
std::cmp::min(num_bytes, constants::TTC_CHUNK_SIZE);
self.write_ub4(chunk_len.try_into().unwrap());
self.buf.extend(&remaining_data[..chunk_len]);
remaining_data = &remaining_data[chunk_len..];
num_bytes -= chunk_len;
}
self.write_ub4(0);
}
}
pub fn write_function_header(&mut self, client: &Client, fn_type: u8) {
self.write_rpc_header(
client,
constants::TTC_MSG_TYPE_FUNCTION,
fn_type,
);
}
pub fn write_piggyback_header(
&mut self,
client: &Client,
piggyback_type: u8,
) {
self.write_rpc_header(
client,
constants::TTC_MSG_TYPE_PIGGYBACK,
piggyback_type,
);
}
pub fn write_qlocator(&mut self, encoded_data: &WriteBuffer) {
self.write_ub4(40); self.write_u8(40); self.write_u16be(38); self.write_u16be(4); self.write_u8(
constants::TTC_LOB_LOC_FLAGS_VALUE_BASED
| constants::TTC_LOB_LOC_FLAGS_BLOB
| constants::TTC_LOB_LOC_FLAGS_ABSTRACT,
);
self.write_u8(constants::TTC_LOB_LOC_FLAGS_INIT);
self.write_u16be(0); self.write_u16be(1); self.write_u64be(encoded_data.buf.len().try_into().unwrap());
self.write_u16be(0); self.write_u16be(0); self.write_u16be(0); self.write_u64be(0); self.write_u64be(0); self.write_bytes_with_length(&encoded_data.buf);
}
pub fn write_rpc_header(
&mut self,
client: &Client,
message_type: u8,
rpc_type: u8,
) {
self.write_u8(message_type);
self.write_u8(rpc_type);
self.write_u8(0); if client.supports_ttc_field_version(
constants::TTC_FIELD_VERSION_23_1_EXT_1,
) {
self.write_ub8(0); }
}
pub fn write_str(&mut self, value: &str) {
self.write_bytes(value.as_bytes());
}
pub fn write_ub2(&mut self, value: u16) {
if value == 0 {
self.write_u8(0);
} else if value < u8::MAX.into() {
self.write_u8(1);
self.write_u8(value.try_into().unwrap());
} else {
self.write_u8(2);
self.write_u16be(value);
}
}
pub fn write_ub4(&mut self, value: u32) {
if value == 0 {
self.write_u8(0);
} else if value < u8::MAX.into() {
self.write_u8(1);
self.write_u8(value.try_into().unwrap());
} else if value < u16::MAX.into() {
self.write_u8(2);
self.write_u16be(value.try_into().unwrap());
} else {
self.write_u8(4);
self.write_u32be(value);
}
}
pub fn write_ub8(&mut self, value: u64) {
if value == 0 {
self.write_u8(0);
} else if value < u8::MAX.into() {
self.write_u8(1);
self.write_u8(value.try_into().unwrap());
} else if value < u16::MAX.into() {
self.write_u8(2);
self.write_u16be(value.try_into().unwrap());
} else if value < u32::MAX.into() {
self.write_u8(4);
self.write_u32be(value.try_into().unwrap());
} else {
self.write_u8(8);
self.write_u64be(value);
}
}
pub fn write_u8(&mut self, value: u8) {
self.buf.push(value);
}
pub fn write_u16be(&mut self, value: u16) {
self.buf.extend(value.to_be_bytes());
}
pub fn write_u16le(&mut self, value: u16) {
self.buf.extend(value.to_le_bytes());
}
pub fn write_u32be(&mut self, value: u32) {
self.buf.extend(value.to_be_bytes());
}
pub fn write_u32be_at(&mut self, value: u32, offset: usize) {
let bytes = value.to_be_bytes();
self.buf[offset..offset + 4].copy_from_slice(&bytes);
}
pub fn write_u64be(&mut self, value: u64) {
self.buf.extend(value.to_be_bytes());
}
}
pub trait ToBuf {
fn to_buf(
&self,
buf: &mut WriteBuffer,
db_type: &'static DbType,
write_length: bool,
);
fn to_buf_null(&self, buf: &mut WriteBuffer) {
buf.write_u8(0);
}
}
impl ToBuf for bool {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
if *self {
if write_length {
buf.write_u8(2);
}
buf.write_u8(1);
buf.write_u8(1);
} else {
if write_length {
buf.write_u8(1);
}
buf.write_u8(0);
}
}
}
impl ToBuf for f32 {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
let mut conv_buf = self.to_be_bytes();
if conv_buf[0] & 0x80 == 0 {
conv_buf[0] |= 0x80;
} else {
for value in &mut conv_buf {
*value = !*value;
}
}
if write_length {
buf.write_bytes_with_length(&conv_buf);
} else {
buf.write_bytes(&conv_buf);
}
}
}
impl ToBuf for f64 {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
let mut conv_buf = self.to_be_bytes();
if conv_buf[0] & 0x80 == 0 {
conv_buf[0] |= 0x80;
} else {
for value in &mut conv_buf {
*value = !*value;
}
}
if write_length {
buf.write_bytes_with_length(&conv_buf);
} else {
buf.write_bytes(&conv_buf);
}
}
}
impl ToBuf for String {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
if write_length {
buf.write_bytes_with_length(self.as_bytes());
} else {
buf.write_bytes(self.as_bytes());
}
}
}
impl ToBuf for &str {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
if write_length {
buf.write_bytes_with_length(self.as_bytes());
} else {
buf.write_bytes(self.as_bytes());
}
}
}
impl ToBuf for Vec<u8> {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
if write_length {
buf.write_bytes_with_length(self);
} else {
buf.write_bytes(self);
}
}
}
impl ToBuf for &[u8] {
fn to_buf(
&self,
buf: &mut WriteBuffer,
_db_type: &'static DbType,
write_length: bool,
) {
if write_length {
buf.write_bytes_with_length(self);
} else {
buf.write_bytes(self);
}
}
}
impl<T> ToBuf for Option<T>
where
T: ToBuf + Default,
{
fn to_buf(
&self,
buf: &mut WriteBuffer,
db_type: &'static DbType,
write_length: bool,
) {
if let Some(value) = self {
<T>::to_buf(value, buf, db_type, write_length);
} else {
<T>::default().to_buf_null(buf);
}
}
}