Skip to content

Commit f4ce826

Browse files
authored
minor: raw BSON perf and API improvements (#335)
1 parent fa97946 commit f4ce826

File tree

8 files changed

+54
-50
lines changed

8 files changed

+54
-50
lines changed

src/bson.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,7 +1050,10 @@ impl Display for Regex {
10501050
/// Represents a BSON code with scope value.
10511051
#[derive(Debug, Clone, PartialEq)]
10521052
pub struct JavaScriptCodeWithScope {
1053+
/// The JavaScript code.
10531054
pub code: String,
1055+
1056+
/// The scope document containing variable bindings.
10541057
pub scope: Document,
10551058
}
10561059

src/raw/array.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ impl RawArray {
212212
pub fn as_bytes(&self) -> &[u8] {
213213
self.doc.as_bytes()
214214
}
215+
216+
/// Whether this array contains any elements or not.
217+
pub fn is_empty(&self) -> bool {
218+
self.doc.is_empty()
219+
}
215220
}
216221

217222
impl std::fmt::Debug for RawArray {

src/raw/bson_ref.rs

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -533,20 +533,17 @@ impl<'a> From<&'a Binary> for RawBsonRef<'a> {
533533
/// A BSON regex referencing raw bytes stored elsewhere.
534534
#[derive(Clone, Copy, Debug, PartialEq)]
535535
pub struct RawRegexRef<'a> {
536-
pub(crate) pattern: &'a str,
537-
pub(crate) options: &'a str,
538-
}
539-
540-
impl<'a> RawRegexRef<'a> {
541-
/// Gets the pattern portion of the regex.
542-
pub fn pattern(self) -> &'a str {
543-
self.pattern
544-
}
536+
/// The regex pattern to match.
537+
pub pattern: &'a str,
545538

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

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

599-
pub(crate) scope: &'a RawDocument,
597+
/// The scope document containing variable bindings.
598+
pub scope: &'a RawDocument,
600599
}
601600

602601
impl<'a> RawJavaScriptCodeWithScopeRef<'a> {
603-
/// Gets the code in the value.
604-
pub fn code(self) -> &'a str {
605-
self.code
606-
}
607-
608-
/// Gets the scope in the value.
609-
pub fn scope(self) -> &'a RawDocument {
610-
self.scope
611-
}
612-
613602
pub(crate) fn len(self) -> i32 {
614603
4 + 4 + self.code.len() as i32 + 1 + self.scope.as_bytes().len() as i32
615604
}

src/raw/document.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{
66
use serde::{ser::SerializeMap, Deserialize, Serialize};
77

88
use crate::{
9+
de::MIN_BSON_DOCUMENT_SIZE,
910
raw::{error::ErrorKind, serde::OwnedOrBorrowedRawDocument, RAW_DOCUMENT_NEWTYPE},
1011
DateTime,
1112
Timestamp,
@@ -398,8 +399,8 @@ impl RawDocument {
398399
/// "bool": true,
399400
/// };
400401
///
401-
/// assert_eq!(doc.get_regex("regex")?.pattern(), r"end\s*$");
402-
/// assert_eq!(doc.get_regex("regex")?.options(), "i");
402+
/// assert_eq!(doc.get_regex("regex")?.pattern, r"end\s*$");
403+
/// assert_eq!(doc.get_regex("regex")?.options, "i");
403404
/// assert!(matches!(doc.get_regex("bool").unwrap_err().kind, ValueAccessErrorKind::UnexpectedType { .. }));
404405
/// assert!(matches!(doc.get_regex("unknown").unwrap_err().kind, ValueAccessErrorKind::NotPresent));
405406
/// # Ok::<(), Box<dyn std::error::Error>>(())
@@ -486,6 +487,11 @@ impl RawDocument {
486487
pub fn as_bytes(&self) -> &[u8] {
487488
&self.data
488489
}
490+
491+
/// Returns whether this document contains any elements or not.
492+
pub fn is_empty(&self) -> bool {
493+
self.as_bytes().len() == MIN_BSON_DOCUMENT_SIZE as usize
494+
}
489495
}
490496

491497
impl<'de: 'a, 'a> Deserialize<'de> for &'a RawDocument {

src/raw/document_buf.rs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ pub struct RawDocumentBuf {
7070
impl RawDocumentBuf {
7171
/// Creates a new, empty [`RawDocumentBuf`].
7272
pub fn new() -> RawDocumentBuf {
73-
let mut data: Vec<u8> = MIN_BSON_DOCUMENT_SIZE.to_le_bytes().to_vec();
73+
let mut data = Vec::new();
74+
data.extend(&MIN_BSON_DOCUMENT_SIZE.to_le_bytes());
7475
data.push(0);
7576
Self { data }
7677
}
@@ -192,27 +193,27 @@ impl RawDocumentBuf {
192193
/// assert_eq!(doc.to_document()?, expected);
193194
/// # Ok::<(), Error>(())
194195
/// ```
195-
pub fn append(&mut self, key: impl Into<String>, value: impl Into<RawBson>) {
196-
fn append_string(doc: &mut RawDocumentBuf, value: String) {
196+
pub fn append(&mut self, key: impl AsRef<str>, value: impl Into<RawBson>) {
197+
fn append_string(doc: &mut RawDocumentBuf, value: &str) {
197198
doc.data
198199
.extend(&((value.as_bytes().len() + 1) as i32).to_le_bytes());
199-
doc.data.extend(value.into_bytes());
200+
doc.data.extend(value.as_bytes());
200201
doc.data.push(0);
201202
}
202203

203-
fn append_cstring(doc: &mut RawDocumentBuf, value: String) {
204+
fn append_cstring(doc: &mut RawDocumentBuf, value: &str) {
204205
if value.contains('\0') {
205206
panic!("cstr includes interior null byte: {}", value)
206207
}
207-
doc.data.extend(value.into_bytes());
208+
doc.data.extend(value.as_bytes());
208209
doc.data.push(0);
209210
}
210211

211212
let original_len = self.data.len();
212213

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

217218
let value = value.into();
218219
let element_type = value.element_type();
@@ -222,7 +223,7 @@ impl RawDocumentBuf {
222223
self.data.extend(&i.to_le_bytes());
223224
}
224225
RawBson::String(s) => {
225-
append_string(self, s);
226+
append_string(self, s.as_str());
226227
}
227228
RawBson::Document(d) => {
228229
self.data.extend(d.into_bytes());
@@ -251,7 +252,7 @@ impl RawDocumentBuf {
251252
self.data.extend(&dt.timestamp_millis().to_le_bytes());
252253
}
253254
RawBson::DbPointer(dbp) => {
254-
append_string(self, dbp.namespace);
255+
append_string(self, dbp.namespace.as_str());
255256
self.data.extend(&dbp.id.bytes());
256257
}
257258
RawBson::Decimal128(d) => {
@@ -264,11 +265,11 @@ impl RawDocumentBuf {
264265
self.data.extend(&i.to_le_bytes());
265266
}
266267
RawBson::RegularExpression(re) => {
267-
append_cstring(self, re.pattern);
268-
append_cstring(self, re.options);
268+
append_cstring(self, re.pattern.as_str());
269+
append_cstring(self, re.options.as_str());
269270
}
270271
RawBson::JavaScriptCode(js) => {
271-
append_string(self, js);
272+
append_string(self, js.as_str());
272273
}
273274
RawBson::JavaScriptCodeWithScope(code_w_scope) => {
274275
let len = RawJavaScriptCodeWithScopeRef {
@@ -277,7 +278,7 @@ impl RawDocumentBuf {
277278
}
278279
.len();
279280
self.data.extend(&len.to_le_bytes());
280-
append_string(self, code_w_scope.code);
281+
append_string(self, code_w_scope.code.as_str());
281282
self.data.extend(code_w_scope.scope.into_bytes());
282283
}
283284
RawBson::Timestamp(ts) => {
@@ -287,7 +288,7 @@ impl RawDocumentBuf {
287288
self.data.extend(&oid.bytes());
288289
}
289290
RawBson::Symbol(s) => {
290-
append_string(self, s);
291+
append_string(self, s.as_str());
291292
}
292293
RawBson::Null | RawBson::Undefined | RawBson::MinKey | RawBson::MaxKey => {}
293294
}
@@ -296,8 +297,8 @@ impl RawDocumentBuf {
296297
// append trailing null byte
297298
self.data.push(0);
298299
// update length
299-
self.data
300-
.splice(0..4, (self.data.len() as i32).to_le_bytes().iter().cloned());
300+
let new_len = (self.data.len() as i32).to_le_bytes();
301+
self.data[0..4].copy_from_slice(&new_len);
301302
}
302303

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

392-
impl<S: Into<String>, T: Into<RawBson>> FromIterator<(S, T)> for RawDocumentBuf {
393+
impl<S: AsRef<str>, T: Into<RawBson>> FromIterator<(S, T)> for RawDocumentBuf {
393394
fn from_iter<I: IntoIterator<Item = (S, T)>>(iter: I) -> Self {
394395
let mut buf = RawDocumentBuf::new();
395396
for (k, v) in iter {

src/raw/serde.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ impl<'de> Visitor<'de> for OwnedOrBorrowedRawBsonVisitor {
349349
let v: RawBson = map.next_value()?;
350350
doc.append(first_key, v);
351351

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

356356
Ok(RawBson::Document(doc).into())

src/raw/test/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,9 @@ fn javascript_with_scope() {
310310
.expect("no key javascript_with_scope")
311311
.as_javascript_with_scope()
312312
.expect("was not javascript with scope");
313-
assert_eq!(js_with_scope.code(), "console.log(msg);");
313+
assert_eq!(js_with_scope.code, "console.log(msg);");
314314
let (scope_key, scope_value_bson) = js_with_scope
315-
.scope()
315+
.scope
316316
.into_iter()
317317
.next()
318318
.expect("no next value in scope")

src/ser/raw/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ impl Serializer {
9797
/// Replace an i32 value at the given index with the given value.
9898
#[inline]
9999
fn replace_i32(&mut self, at: usize, with: i32) {
100-
self.bytes
101-
.splice(at..at + 4, with.to_le_bytes().iter().cloned());
100+
let portion = &mut self.bytes[at..at + 4];
101+
portion.copy_from_slice(&with.to_le_bytes());
102102
}
103103
}
104104

0 commit comments

Comments
 (0)