Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/sql/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ pub mod shared {
pub mod query_status;
#[path = "SQLQueryResultMode.rs"]
pub mod sql_query_result_mode;
#[path = "StackReader.rs"]
pub mod stack_reader;
#[path = "StatementStatus.rs"]
pub mod statement_status;

pub use column_identifier::ColumnIdentifier;
pub use connection_flags::ConnectionFlags;
pub use data::Data;
pub use sql_query_result_mode::SQLQueryResultMode;
pub use stack_reader::StackReader;
}

pub mod mysql {
Expand Down Expand Up @@ -107,7 +110,7 @@ pub mod mysql {
pub use handshake_response41::HandshakeResponse41;
pub use handshake_v10::HandshakeV10;
pub use local_infile_request::LocalInfileRequest;
pub use new_reader::{Decode, NewReader, NewReaderOf, ReadableInt, ReaderContext};
pub use new_reader::{Decode, NewReader, ReadableInt, ReaderContext};
pub use new_writer::{NewWriter, NewWriterWrap, Packet, WriterContext, write_wrap};
pub use ok_packet::OKPacket;
pub use packet_header::PacketHeader;
Expand Down
9 changes: 0 additions & 9 deletions src/sql/mysql/protocol/NewReader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ impl<C: ReaderContext> NewReader<C> {
/// MySQL's u24/i24 are NOT routed through this trait — see `int_u24`/`int_i24`.
pub use bun_core::NativeEndianInt as ReadableInt;

pub type NewReaderOf<C> = NewReader<C>;

impl<C: ReaderContext> From<C> for NewReader<C> {
fn from(wrapped: C) -> Self {
Self { wrapped }
Expand All @@ -142,11 +140,4 @@ pub trait Decode: Sized {
) -> Result<(), AnyMySQLError> {
self.decode_internal(context.into())
}

fn decode_allocator<C: ReaderContext>(
&mut self,
context: impl Into<NewReader<C>>,
) -> Result<(), AnyMySQLError> {
self.decode_internal(context.into())
}
}
106 changes: 15 additions & 91 deletions src/sql/mysql/protocol/StackReader.rs
Original file line number Diff line number Diff line change
@@ -1,116 +1,40 @@
use core::cell::Cell;

use bun_core::strings;

use super::any_mysql_error::Error as AnyMySQLError;
use super::new_reader::{NewReader, ReaderContext};
use crate::shared::data::Data;
use crate::shared::stack_reader::{ShortRead, WrapReader};

#[derive(Clone, Copy)]
pub struct StackReader<'a> {
pub buffer: &'a [u8],
pub offset: &'a Cell<usize>,
pub message_start: &'a Cell<usize>,
}

impl<'a> StackReader<'a> {
pub fn mark_message_start(&self) {
self.message_start.set(self.offset.get());
}

pub fn set_offset_from_start(&self, offset: usize) {
self.offset.set(self.message_start.get() + offset);
}

pub fn ensure_capacity(&self, length: usize) -> bool {
self.offset
.get()
.checked_add(length)
.is_some_and(|end| self.buffer.len() >= end)
}

pub fn init(
buffer: &'a [u8],
offset: &'a Cell<usize>,
message_start: &'a Cell<usize>,
) -> NewReader<StackReader<'a>> {
NewReader {
wrapped: StackReader {
buffer,
offset,
message_start,
},
}
}
pub use crate::shared::stack_reader::StackReader;

pub fn peek(&self) -> &'a [u8] {
&self.buffer[self.offset.get()..]
}

pub fn skip(&self, count: isize) {
if count < 0 {
let abs_count = count.unsigned_abs();
if abs_count > self.offset.get() {
self.offset.set(0);
return;
}
self.offset.set(self.offset.get() - abs_count);
return;
}

let ucount: usize = usize::try_from(count).expect("int cast");
if self.offset.get() + ucount > self.buffer.len() {
self.offset.set(self.buffer.len());
return;
}

self.offset.set(self.offset.get() + ucount);
}

pub fn read(&self, count: usize) -> Result<Data, AnyMySQLError> {
let offset = self.offset.get();
if !self.ensure_capacity(count) {
return Err(AnyMySQLError::ShortRead);
}

self.skip(isize::try_from(count).expect("int cast"));
Ok(Data::Temporary(bun_ptr::RawSlice::new(
&self.buffer[offset..self.offset.get()],
)))
}

pub fn read_z(&self) -> Result<Data, AnyMySQLError> {
let remaining = self.peek();
if let Some(zero) = strings::index_of_char(remaining, 0) {
let zero = zero as usize;
self.skip(isize::try_from(zero + 1).expect("int cast"));
return Ok(Data::Temporary(bun_ptr::RawSlice::new(&remaining[0..zero])));
}
impl ShortRead for AnyMySQLError {
const SHORT_READ: Self = AnyMySQLError::ShortRead;
}

Err(AnyMySQLError::ShortRead)
impl<'a> WrapReader<'a> for NewReader<StackReader<'a>> {
fn wrap(reader: StackReader<'a>) -> Self {
NewReader { wrapped: reader }
}
}

impl<'a> ReaderContext for StackReader<'a> {
fn mark_message_start(self) {
Self::mark_message_start(&self)
StackReader::mark_message_start(&self)
}
fn peek(&self) -> &[u8] {
Self::peek(self)
StackReader::peek(self)
}
fn skip(self, count: isize) {
Self::skip(&self, count)
StackReader::skip(&self, count)
}
fn ensure_capacity(self, count: usize) -> bool {
Self::ensure_capacity(&self, count)
StackReader::ensure_capacity(&self, count)
}
fn read(self, count: usize) -> Result<Data, AnyMySQLError> {
Self::read(&self, count)
StackReader::read(&self, count)
}
fn read_z(self) -> Result<Data, AnyMySQLError> {
Self::read_z(&self)
StackReader::read_z(&self)
}
fn set_offset_from_start(self, offset: usize) {
Self::set_offset_from_start(&self, offset)
StackReader::set_offset_from_start(&self, offset)
}
}
3 changes: 1 addition & 2 deletions src/sql/postgres/protocol/NewReader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ macro_rules! impl_protocol_int {
}
impl_protocol_int!(u8, i8, u16, i16, u32, i32, u64, i64);

// Blanket impl so `NewReaderWrap<&mut C>` works — the inner `Context` is
// non-`Copy` (holds `&mut usize`), so callers reborrow instead.
// Blanket impl so `NewReaderWrap<&mut C>` works for callers that reborrow.
impl<C: ReaderContext + ?Sized> ReaderContext for &mut C {
#[inline]
fn mark_message_start(&mut self) {
Expand Down
94 changes: 16 additions & 78 deletions src/sql/postgres/protocol/StackReader.rs
Original file line number Diff line number Diff line change
@@ -1,101 +1,39 @@
use crate::postgres::any_postgres_error::AnyPostgresError;
use crate::postgres::protocol::new_reader::{NewReader, ReaderContext};
use crate::shared::data::Data;
use bun_core::strings;
use crate::shared::stack_reader::{ShortRead, WrapReader};

pub struct StackReader<'a> {
pub buffer: &'a [u8],
pub offset: &'a mut usize,
pub message_start: &'a mut usize,
}

impl<'a> StackReader<'a> {
pub fn mark_message_start(&mut self) {
*self.message_start = *self.offset;
}

pub fn ensure_length(&self, length: usize) -> bool {
self.buffer.len() >= (*self.offset + length)
}

pub fn init(
buffer: &'a [u8],
offset: &'a mut usize,
message_start: &'a mut usize,
) -> NewReader<StackReader<'a>> {
NewReader {
wrapped: StackReader {
buffer,
offset,
message_start,
},
}
}

pub fn peek(&self) -> &[u8] {
&self.buffer[*self.offset..]
}
pub use crate::shared::stack_reader::StackReader;

pub fn skip(&mut self, count: usize) {
if *self.offset + count > self.buffer.len() {
*self.offset = self.buffer.len();
return;
}

*self.offset += count;
}

pub fn ensure_capacity(&self, count: usize) -> bool {
self.buffer.len() >= (*self.offset + count)
}

pub fn read(&mut self, count: usize) -> Result<Data, AnyPostgresError> {
let offset = *self.offset;
if !self.ensure_capacity(count) {
return Err(AnyPostgresError::ShortRead);
}

self.skip(count);
// Copy the &'a [u8] out before slicing so the returned Data borrows 'a,
// not &mut self.
let buffer: &'a [u8] = self.buffer;
Ok(Data::Temporary(bun_ptr::RawSlice::new(
&buffer[offset..*self.offset],
)))
}

pub fn read_z(&mut self) -> Result<Data, AnyPostgresError> {
// Inline `peek()` so `remaining` borrows 'a (via the Copy &'a [u8])
// instead of &self, allowing `self.skip()` below.
let buffer: &'a [u8] = self.buffer;
let remaining = &buffer[*self.offset..];
if let Some(zero) = strings::index_of_char(remaining, 0) {
let zero = zero as usize;
self.skip(zero + 1);
return Ok(Data::Temporary(bun_ptr::RawSlice::new(&remaining[0..zero])));
}
impl ShortRead for AnyPostgresError {
const SHORT_READ: Self = AnyPostgresError::ShortRead;
}

Err(AnyPostgresError::ShortRead)
impl<'a> WrapReader<'a> for NewReader<StackReader<'a>> {
fn wrap(reader: StackReader<'a>) -> Self {
NewReader { wrapped: reader }
}
}

impl<'a> ReaderContext for StackReader<'a> {
fn mark_message_start(&mut self) {
Self::mark_message_start(self)
StackReader::mark_message_start(self)
}
fn peek(&self) -> &[u8] {
Self::peek(self)
StackReader::peek(self)
}
fn skip(&mut self, count: usize) {
Self::skip(self, count)
// The shared reader's signed skip clamps to the buffer end, matching
// the old unsigned behavior even when `count` exceeds `isize::MAX`.
StackReader::skip(self, isize::try_from(count).unwrap_or(isize::MAX))
}
fn ensure_length(&mut self, count: usize) -> bool {
Self::ensure_length(self, count)
StackReader::ensure_capacity(self, count)
}
fn read(&mut self, count: usize) -> Result<Data, AnyPostgresError> {
Self::read(self, count)
StackReader::read(self, count)
}
fn read_z(&mut self) -> Result<Data, AnyPostgresError> {
Self::read_z(self)
StackReader::read_z(self)
}
}
Loading
Loading