Skip to content

minor: minor raw BSON perf and API improvements #335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 4, 2022
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
3 changes: 3 additions & 0 deletions src/bson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,10 @@ impl Display for Regex {
/// Represents a BSON code with scope value.
#[derive(Debug, Clone, PartialEq)]
pub struct JavaScriptCodeWithScope {
/// The JavaScript code.
pub code: String,

/// The scope document containing variable bindings.
pub scope: Document,
}

Expand Down
5 changes: 5 additions & 0 deletions src/raw/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ impl RawArray {
pub fn as_bytes(&self) -> &[u8] {
self.doc.as_bytes()
}

/// Whether this array contains any elements or not.
pub fn is_empty(&self) -> bool {
self.doc.is_empty()
}
}

impl std::fmt::Debug for RawArray {
Expand Down
39 changes: 14 additions & 25 deletions src/raw/bson_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,20 +533,17 @@ impl<'a> From<&'a Binary> for RawBsonRef<'a> {
/// A BSON regex referencing raw bytes stored elsewhere.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RawRegexRef<'a> {
pub(crate) pattern: &'a str,
pub(crate) options: &'a str,
}

impl<'a> RawRegexRef<'a> {
/// Gets the pattern portion of the regex.
pub fn pattern(self) -> &'a str {
self.pattern
}
/// The regex pattern to match.
pub pattern: &'a str,

/// Gets the options portion of the regex.
pub fn options(self) -> &'a str {
self.options
}
/// The options for the regex.
///
/// Options are identified by characters, which must be stored in
/// alphabetical order. Valid options are 'i' for case insensitive matching, 'm' for
/// multiline matching, 'x' for verbose mode, 'l' to make \w, \W, etc. locale dependent,
/// 's' for dotall mode ('.' matches everything), and 'u' to make \w, \W, etc. match
/// unicode.
pub options: &'a str,
}

impl<'de: 'a, 'a> Deserialize<'de> for RawRegexRef<'a> {
Expand Down Expand Up @@ -594,22 +591,14 @@ impl<'a> From<RawRegexRef<'a>> for RawBsonRef<'a> {
/// A BSON "code with scope" value referencing raw bytes stored elsewhere.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RawJavaScriptCodeWithScopeRef<'a> {
pub(crate) code: &'a str,
/// The JavaScript code.
pub code: &'a str,

pub(crate) scope: &'a RawDocument,
/// The scope document containing variable bindings.
pub scope: &'a RawDocument,
}

impl<'a> RawJavaScriptCodeWithScopeRef<'a> {
/// Gets the code in the value.
pub fn code(self) -> &'a str {
self.code
}

/// Gets the scope in the value.
pub fn scope(self) -> &'a RawDocument {
self.scope
}

pub(crate) fn len(self) -> i32 {
4 + 4 + self.code.len() as i32 + 1 + self.scope.as_bytes().len() as i32
}
Expand Down
10 changes: 8 additions & 2 deletions src/raw/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{
use serde::{ser::SerializeMap, Deserialize, Serialize};

use crate::{
de::MIN_BSON_DOCUMENT_SIZE,
raw::{error::ErrorKind, serde::OwnedOrBorrowedRawDocument, RAW_DOCUMENT_NEWTYPE},
DateTime,
Timestamp,
Expand Down Expand Up @@ -398,8 +399,8 @@ impl RawDocument {
/// "bool": true,
/// };
///
/// assert_eq!(doc.get_regex("regex")?.pattern(), r"end\s*$");
/// assert_eq!(doc.get_regex("regex")?.options(), "i");
/// assert_eq!(doc.get_regex("regex")?.pattern, r"end\s*$");
/// assert_eq!(doc.get_regex("regex")?.options, "i");
/// assert!(matches!(doc.get_regex("bool").unwrap_err().kind, ValueAccessErrorKind::UnexpectedType { .. }));
/// assert!(matches!(doc.get_regex("unknown").unwrap_err().kind, ValueAccessErrorKind::NotPresent));
/// # Ok::<(), Box<dyn std::error::Error>>(())
Expand Down Expand Up @@ -486,6 +487,11 @@ impl RawDocument {
pub fn as_bytes(&self) -> &[u8] {
&self.data
}

/// Returns whether this document contains any elements or not.
pub fn is_empty(&self) -> bool {
self.as_bytes().len() == MIN_BSON_DOCUMENT_SIZE as usize
}
}

impl<'de: 'a, 'a> Deserialize<'de> for &'a RawDocument {
Expand Down
35 changes: 18 additions & 17 deletions src/raw/document_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ pub struct RawDocumentBuf {
impl RawDocumentBuf {
/// Creates a new, empty [`RawDocumentBuf`].
pub fn new() -> RawDocumentBuf {
let mut data: Vec<u8> = MIN_BSON_DOCUMENT_SIZE.to_le_bytes().to_vec();
let mut data = Vec::new();
data.extend(&MIN_BSON_DOCUMENT_SIZE.to_le_bytes());
data.push(0);
Self { data }
}
Expand Down Expand Up @@ -192,27 +193,27 @@ impl RawDocumentBuf {
/// assert_eq!(doc.to_document()?, expected);
/// # Ok::<(), Error>(())
/// ```
pub fn append(&mut self, key: impl Into<String>, value: impl Into<RawBson>) {
fn append_string(doc: &mut RawDocumentBuf, value: String) {
pub fn append(&mut self, key: impl AsRef<str>, value: impl Into<RawBson>) {
fn append_string(doc: &mut RawDocumentBuf, value: &str) {
doc.data
.extend(&((value.as_bytes().len() + 1) as i32).to_le_bytes());
doc.data.extend(value.into_bytes());
doc.data.extend(value.as_bytes());
doc.data.push(0);
}

fn append_cstring(doc: &mut RawDocumentBuf, value: String) {
fn append_cstring(doc: &mut RawDocumentBuf, value: &str) {
if value.contains('\0') {
panic!("cstr includes interior null byte: {}", value)
}
doc.data.extend(value.into_bytes());
doc.data.extend(value.as_bytes());
doc.data.push(0);
}

let original_len = self.data.len();

// write the key for the next value to the end
// the element type will replace the previous null byte terminator of the document
append_cstring(self, key.into());
append_cstring(self, key.as_ref());

let value = value.into();
let element_type = value.element_type();
Expand All @@ -222,7 +223,7 @@ impl RawDocumentBuf {
self.data.extend(&i.to_le_bytes());
}
RawBson::String(s) => {
append_string(self, s);
append_string(self, s.as_str());
}
RawBson::Document(d) => {
self.data.extend(d.into_bytes());
Expand Down Expand Up @@ -251,7 +252,7 @@ impl RawDocumentBuf {
self.data.extend(&dt.timestamp_millis().to_le_bytes());
}
RawBson::DbPointer(dbp) => {
append_string(self, dbp.namespace);
append_string(self, dbp.namespace.as_str());
self.data.extend(&dbp.id.bytes());
}
RawBson::Decimal128(d) => {
Expand All @@ -264,11 +265,11 @@ impl RawDocumentBuf {
self.data.extend(&i.to_le_bytes());
}
RawBson::RegularExpression(re) => {
append_cstring(self, re.pattern);
append_cstring(self, re.options);
append_cstring(self, re.pattern.as_str());
append_cstring(self, re.options.as_str());
}
RawBson::JavaScriptCode(js) => {
append_string(self, js);
append_string(self, js.as_str());
}
RawBson::JavaScriptCodeWithScope(code_w_scope) => {
let len = RawJavaScriptCodeWithScopeRef {
Expand All @@ -277,7 +278,7 @@ impl RawDocumentBuf {
}
.len();
self.data.extend(&len.to_le_bytes());
append_string(self, code_w_scope.code);
append_string(self, code_w_scope.code.as_str());
self.data.extend(code_w_scope.scope.into_bytes());
}
RawBson::Timestamp(ts) => {
Expand All @@ -287,7 +288,7 @@ impl RawDocumentBuf {
self.data.extend(&oid.bytes());
}
RawBson::Symbol(s) => {
append_string(self, s);
append_string(self, s.as_str());
}
RawBson::Null | RawBson::Undefined | RawBson::MinKey | RawBson::MaxKey => {}
}
Expand All @@ -296,8 +297,8 @@ impl RawDocumentBuf {
// append trailing null byte
self.data.push(0);
// update length
self.data
.splice(0..4, (self.data.len() as i32).to_le_bytes().iter().cloned());
let new_len = (self.data.len() as i32).to_le_bytes();
self.data[0..4].copy_from_slice(&new_len);
}

/// Convert this [`RawDocumentBuf`] to a [`Document`], returning an error
Expand Down Expand Up @@ -389,7 +390,7 @@ impl Borrow<RawDocument> for RawDocumentBuf {
}
}

impl<S: Into<String>, T: Into<RawBson>> FromIterator<(S, T)> for RawDocumentBuf {
impl<S: AsRef<str>, T: Into<RawBson>> FromIterator<(S, T)> for RawDocumentBuf {
fn from_iter<I: IntoIterator<Item = (S, T)>>(iter: I) -> Self {
let mut buf = RawDocumentBuf::new();
for (k, v) in iter {
Expand Down
4 changes: 2 additions & 2 deletions src/raw/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ impl<'de> Visitor<'de> for OwnedOrBorrowedRawBsonVisitor {
let v: RawBson = map.next_value()?;
doc.append(first_key, v);

while let Some((k, v)) = map.next_entry::<String, RawBson>()? {
doc.append(k, v);
while let Some((k, v)) = map.next_entry::<CowStr, RawBson>()? {
doc.append(k.0, v);
}

Ok(RawBson::Document(doc).into())
Expand Down
4 changes: 2 additions & 2 deletions src/raw/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,9 @@ fn javascript_with_scope() {
.expect("no key javascript_with_scope")
.as_javascript_with_scope()
.expect("was not javascript with scope");
assert_eq!(js_with_scope.code(), "console.log(msg);");
assert_eq!(js_with_scope.code, "console.log(msg);");
let (scope_key, scope_value_bson) = js_with_scope
.scope()
.scope
.into_iter()
.next()
.expect("no next value in scope")
Expand Down
4 changes: 2 additions & 2 deletions src/ser/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ impl Serializer {
/// Replace an i32 value at the given index with the given value.
#[inline]
fn replace_i32(&mut self, at: usize, with: i32) {
self.bytes
.splice(at..at + 4, with.to_le_bytes().iter().cloned());
let portion = &mut self.bytes[at..at + 4];
portion.copy_from_slice(&with.to_le_bytes());
}
}

Expand Down