Skip to content

Commit afa2df2

Browse files
committed
Start switching frontend serialization to functions
Allows us to avoid intermediate allocations for bind in particular
1 parent 25fd59a commit afa2df2

File tree

3 files changed

+108
-94
lines changed

3 files changed

+108
-94
lines changed

src/lib.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ extern crate fallible_iterator;
1515
extern crate hex;
1616
extern crate md5;
1717

18-
use std::io;
18+
use byteorder::{WriteBytesExt, BigEndian};
19+
use std::io::{self, Cursor};
1920

2021
pub mod authentication;
2122
pub mod message;
@@ -24,6 +25,29 @@ pub mod types;
2425
/// A Postgres OID.
2526
pub type Oid = u32;
2627

28+
/// An enum indicating if a value is `NULL` or not.
29+
pub enum IsNull {
30+
/// The value is `NULL`.
31+
Yes,
32+
/// The value is not `NULL`.
33+
No,
34+
}
35+
36+
fn write_nullable<F, E>(serializer: F, buf: &mut Vec<u8>) -> Result<(), E>
37+
where F: FnOnce(&mut Vec<u8>) -> Result<IsNull, E>,
38+
E: From<io::Error>
39+
{
40+
let base = buf.len();
41+
buf.extend_from_slice(&[0; 4]);
42+
let size = match try!(serializer(buf)) {
43+
IsNull::No => try!(i32::from_usize(buf.len() - base - 4)),
44+
IsNull::Yes => -1,
45+
};
46+
Cursor::new(&mut buf[base..base + 4]).write_i32::<BigEndian>(size).unwrap();
47+
48+
Ok(())
49+
}
50+
2751
trait FromUsize: Sized {
2852
fn from_usize(x: usize) -> Result<Self, io::Error>;
2953
}
@@ -42,5 +66,5 @@ macro_rules! from_usize {
4266
}
4367
}
4468

45-
from_usize!(u16);
69+
from_usize!(i16);
4670
from_usize!(i32);

src/message/frontend.rs

Lines changed: 80 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,107 +2,111 @@
22
#![allow(missing_docs)]
33

44
use byteorder::{WriteBytesExt, BigEndian};
5+
use std::error::Error;
56
use std::io::{self, Cursor};
7+
use std::marker;
68

7-
use {Oid, FromUsize};
9+
use {Oid, FromUsize, IsNull, write_nullable};
810

9-
/// A trait implemented by types serializable as frontend Postgres messages.
10-
pub trait Message {
11-
/// Serializes this message to a buffer.
12-
fn write(&self, buf: &mut Vec<u8>) -> Result<(), io::Error>;
13-
}
14-
15-
fn write_body<F>(buf: &mut Vec<u8>, f: F) -> Result<(), io::Error>
16-
where F: FnOnce(&mut Vec<u8>) -> Result<(), io::Error>
11+
fn write_body<F, E>(buf: &mut Vec<u8>, f: F) -> Result<(), E>
12+
where F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
13+
E: From<io::Error>
1714
{
1815
let base = buf.len();
1916
buf.extend_from_slice(&[0; 4]);
2017

2118
try!(f(buf));
2219

2320
let size = try!(i32::from_usize(buf.len() - base));
24-
try!(Cursor::new(&mut buf[base..base + 4]).write_i32::<BigEndian>(size));
21+
Cursor::new(&mut buf[base..base + 4]).write_i32::<BigEndian>(size).unwrap();
2522
Ok(())
2623
}
2724

28-
pub struct Bind<'a, T: 'a> {
29-
pub portal: &'a str,
30-
pub statement: &'a str,
31-
pub formats: &'a [i16],
32-
pub values: &'a [Option<T>],
33-
pub result_formats: &'a [i16],
25+
pub enum BindError {
26+
Conversion(Box<Error + marker::Sync + Send>),
27+
Serialization(io::Error),
3428
}
3529

36-
impl<'a, T> Message for Bind<'a, T>
37-
where T: AsRef<[u8]>
38-
{
39-
fn write(&self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
40-
buf.push(b'B');
41-
42-
write_body(buf, |buf| {
43-
try!(buf.write_cstr(self.portal));
44-
try!(buf.write_cstr(self.statement));
30+
impl From<Box<Error + marker::Sync + Send>> for BindError {
31+
fn from(e: Box<Error + marker::Sync + Send>) -> BindError {
32+
BindError::Conversion(e)
33+
}
34+
}
4535

46-
let num_formats = try!(u16::from_usize(self.formats.len()));
47-
try!(buf.write_u16::<BigEndian>(num_formats));
48-
for &format in self.formats {
49-
try!(buf.write_i16::<BigEndian>(format));
50-
}
36+
impl From<io::Error> for BindError {
37+
fn from(e: io::Error) -> BindError {
38+
BindError::Serialization(e)
39+
}
40+
}
5141

52-
let num_values = try!(u16::from_usize(self.values.len()));
53-
try!(buf.write_u16::<BigEndian>(num_values));
54-
for value in self.values {
55-
match *value {
56-
None => try!(buf.write_i32::<BigEndian>(-1)),
57-
Some(ref value) => {
58-
let value = value.as_ref();
59-
let value_len = try!(i32::from_usize(value.len()));
60-
try!(buf.write_i32::<BigEndian>(value_len));
61-
buf.extend_from_slice(value);
62-
}
63-
}
64-
}
42+
pub fn bind<I, J, F, T, K>(portal: &str,
43+
statement: &str,
44+
formats: I,
45+
values: J,
46+
mut serializer: F,
47+
result_formats: K,
48+
buf: &mut Vec<u8>)
49+
-> Result<(), BindError>
50+
where I: IntoIterator<Item = i16>,
51+
J: IntoIterator<Item = T>,
52+
F: FnMut(T, &mut Vec<u8>) -> Result<IsNull, Box<Error + marker::Sync + Send>>,
53+
K: IntoIterator<Item = i16>,
54+
{
55+
buf.push(b'B');
56+
57+
write_body(buf, |buf| {
58+
try!(buf.write_cstr(portal));
59+
try!(buf.write_cstr(statement));
60+
try!(write_counted(formats,
61+
|f, buf| Ok::<(), io::Error>(buf.write_i16::<BigEndian>(f).unwrap()),
62+
buf));
63+
try!(write_counted(values,
64+
|v, buf| write_nullable(|buf| serializer(v, buf), buf),
65+
buf));
66+
try!(write_counted(result_formats,
67+
|f, buf| Ok::<(), io::Error>(buf.write_i16::<BigEndian>(f).unwrap()),
68+
buf));
6569

66-
let num_result_formats = try!(u16::from_usize(self.result_formats.len()));
67-
try!(buf.write_u16::<BigEndian>(num_result_formats));
68-
for &result_format in self.result_formats {
69-
try!(buf.write_i16::<BigEndian>(result_format));
70-
}
70+
Ok(())
71+
})
72+
}
7173

72-
Ok(())
73-
})
74+
fn write_counted<I, T, F, E>(items: I, mut serializer: F, buf: &mut Vec<u8>) -> Result<(), E>
75+
where I: IntoIterator<Item = T>,
76+
F: FnMut(T, &mut Vec<u8>) -> Result<(), E>,
77+
E: From<io::Error>
78+
{
79+
let base = buf.len();
80+
buf.extend_from_slice(&[0; 2]);
81+
let mut count = 0;
82+
for item in items {
83+
try!(serializer(item, buf));
84+
count += 1;
7485
}
75-
}
86+
let count = try!(i16::from_usize(count));
87+
Cursor::new(&mut buf[base..base + 2]).write_i16::<BigEndian>(count).unwrap();
7688

77-
pub struct CancelRequest {
78-
pub process_id: i32,
79-
pub secret_key: i32,
89+
Ok(())
8090
}
8191

82-
impl Message for CancelRequest {
83-
fn write(&self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
84-
write_body(buf, |buf| {
85-
try!(buf.write_i32::<BigEndian>(80877102));
86-
try!(buf.write_i32::<BigEndian>(self.process_id));
87-
try!(buf.write_i32::<BigEndian>(self.secret_key));
88-
Ok(())
89-
})
90-
}
92+
/// A trait implemented by types serializable as frontend Postgres messages.
93+
pub trait Message {
94+
/// Serializes this message to a buffer.
95+
fn write(&self, buf: &mut Vec<u8>) -> Result<(), io::Error>;
9196
}
9297

93-
pub struct Close<'a> {
94-
pub variant: u8,
95-
pub name: &'a str,
98+
pub fn cancel_request(process_id: i32, secret_key: i32, buf: &mut Vec<u8>) {
99+
buf.write_i32::<BigEndian>(80877102).unwrap();
100+
buf.write_i32::<BigEndian>(process_id).unwrap();
101+
buf.write_i32::<BigEndian>(secret_key).unwrap();
96102
}
97103

98-
impl<'a> Message for Close<'a> {
99-
fn write(&self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
100-
buf.push(b'C');
101-
write_body(buf, |buf| {
102-
buf.push(self.variant);
103-
buf.write_cstr(self.name)
104-
})
105-
}
104+
pub fn close(variant: u8, name: &str, buf: &mut Vec<u8>) -> io::Result<()> {
105+
buf.push(b'C');
106+
write_body(buf, |buf| {
107+
buf.push(variant);
108+
buf.write_cstr(name)
109+
})
106110
}
107111

108112
pub struct CopyData<'a> {
@@ -182,8 +186,8 @@ impl<'a> Message for Parse<'a> {
182186
write_body(buf, |buf| {
183187
try!(buf.write_cstr(self.name));
184188
try!(buf.write_cstr(self.query));
185-
let num_param_types = try!(u16::from_usize(self.param_types.len()));
186-
try!(buf.write_u16::<BigEndian>(num_param_types));
189+
let num_param_types = try!(i16::from_usize(self.param_types.len()));
190+
try!(buf.write_i16::<BigEndian>(num_param_types));
187191
for &param_type in self.param_types {
188192
try!(buf.write_u32::<BigEndian>(param_type));
189193
}

src/types.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::error::Error;
55
use std::io::Cursor;
66
use std::str;
77

8-
use {Oid, FromUsize};
8+
use {Oid, IsNull, write_nullable, FromUsize};
99

1010
const RANGE_UPPER_UNBOUNDED: u8 = 0b0001_0000;
1111
const RANGE_LOWER_UNBOUNDED: u8 = 0b0000_1000;
@@ -442,26 +442,12 @@ pub fn array_to_sql<T, I, J, F>(dimensions: I,
442442
.unwrap();
443443

444444
for element in elements {
445-
let base = buf.len();
446-
buf.extend_from_slice(&[0; 4]);
447-
let size = match try!(serializer(element, buf)) {
448-
IsNull::No => try!(i32::from_usize(buf.len() - base - 4)),
449-
IsNull::Yes => -1,
450-
};
451-
Cursor::new(&mut buf[base..base + 4]).write_i32::<BigEndian>(size).unwrap();
445+
try!(write_nullable(|buf| serializer(element, buf), buf));
452446
}
453447

454448
Ok(())
455449
}
456450

457-
/// An enum indicating if a value is `NULL` or not.
458-
pub enum IsNull {
459-
/// The value is `NULL`.
460-
Yes,
461-
/// The value is not `NULL`.
462-
No,
463-
}
464-
465451
/// Deserializes an array value.
466452
#[inline]
467453
pub fn array_from_sql<'a>(mut buf: &'a [u8]) -> Result<Array<'a>, Box<Error + Sync + Send>> {

0 commit comments

Comments
 (0)