Skip to content

Commit a8e7c39

Browse files
committed
use str::from_utf8 instead of String
Main change: use `str::from_utf8` instead of `String::from_utf8` wherever possible. This will avoid unnecessary allocations. This also allowed direct usage of `&[u8]` instead of `Vec<u8>` in multiple places, for additional efficiency. Breaking Change: Removed (undocumented) `to_utf8_lossy` function since we only plan to support valid utf8 data for ease of use. We might introduce another serializer / deserializer that can handle arbitrary bytes later on, but this should be clear that it uses another implementation altogether.
1 parent 93ba77c commit a8e7c39

File tree

9 files changed

+42
-37
lines changed

9 files changed

+42
-37
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ documentation = "https://docs.rs/rediserde"
1414
keywords = ["resp", "redis", "serde", "serialization"]
1515
license-file = "LICENSE"
1616

17-
version = "0.1.1"
17+
version = "0.2.0"
1818
edition = "2024"
1919

2020
[dependencies]

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ Use this crate like any other serde-compatible crate (like `serde_json` or `serd
5555

5656
let serialized = to_string(&person).unwrap();
5757
let deserialized: Person = from_str(&serialized).unwrap();
58+
let deserialized_raw: Person = from_str("%2\r\n+name\r\n+Alice\r\n+age\r\n:30\r\n").unwrap();
5859
assert_eq!(deserialized, person);
60+
assert_eq!(deserialized_raw, person);
5961
```
6062

6163
For a more complex example, see the [tests/structs.rs](tests/structs.rs) file.

src/de.rs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<'de> Deserializer<'de> {
6666
.iter()
6767
.position(|&b| !b.is_ascii_digit())
6868
.ok_or(Error::ExpectedLength)?;
69-
let length_str = String::from_utf8(self.input[..first_non_numeric].to_vec())
69+
let length_str = str::from_utf8(&self.input[..first_non_numeric])
7070
.map_err(|_| Error::ExpectedLength)?;
7171
self.input = &self.input[first_non_numeric..];
7272
let length = length_str
@@ -75,7 +75,7 @@ impl<'de> Deserializer<'de> {
7575
Ok(length)
7676
}
7777

78-
fn parse_string(&mut self) -> Result<String> {
78+
fn parse_string(&mut self) -> Result<&str> {
7979
let first = self.next_byte()?;
8080
let kind = RespDataKind::try_from(first).map_err(|()| Error::UnrecognizedStart)?;
8181
let result = match kind {
@@ -95,7 +95,7 @@ impl<'de> Deserializer<'de> {
9595
Ok(result)
9696
}
9797

98-
fn parse_simple_string(&mut self) -> Result<String> {
98+
fn parse_simple_string(&mut self) -> Result<&str> {
9999
let crlf_index = self.input.windows(2).position(|w| w == CRLF);
100100
let result = if let Some(index) = crlf_index {
101101
let result = &self.input[..index];
@@ -108,20 +108,20 @@ impl<'de> Deserializer<'de> {
108108
return Err(Error::UnexpectedEnd);
109109
}
110110
self.expect_crlf()?;
111-
Ok(String::from_utf8(result.to_vec())?)
111+
Ok(str::from_utf8(result)?)
112112
}
113113

114-
fn parse_bulk_string(&mut self) -> Result<String> {
114+
fn parse_bulk_string(&mut self) -> Result<&str> {
115115
if self.input.starts_with(b"-1\r\n") {
116116
self.input = &self.input[4..]; // Skip -1\r\n
117-
return Ok(String::new()); // Null string
117+
return Ok(""); // Null string
118118
}
119119
let length = self.expect_length()?;
120120
self.expect_crlf()?;
121121
let data = &self.input[..length];
122122
self.input = &self.input[length..];
123123
self.expect_crlf()?;
124-
Ok(String::from_utf8(data.to_vec())?)
124+
Ok(str::from_utf8(data)?)
125125
}
126126

127127
/// Parse an number from the RESP format.
@@ -148,7 +148,7 @@ impl<'de> Deserializer<'de> {
148148
.iter()
149149
.position(|b| !VALID_NUMERIC_CHARS.contains(b))
150150
.ok_or(Error::UnexpectedEnd)?;
151-
let value_str = String::from_utf8(self.input[..non_numeric_index].to_vec())?;
151+
let value_str = str::from_utf8(&self.input[..non_numeric_index])?;
152152
self.input = &self.input[non_numeric_index..];
153153
let value = value_str.parse::<N>().map_err(|_| Error::UnexpectedByte {
154154
expected: "A valid integer string".to_string(),
@@ -314,15 +314,16 @@ impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
314314
where
315315
V: serde::de::Visitor<'de>,
316316
{
317-
self.deserialize_string(visitor)
317+
let s = self.parse_string()?;
318+
visitor.visit_str(s)
318319
}
319320

320321
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
321322
where
322323
V: serde::de::Visitor<'de>,
323324
{
324325
let s = self.parse_string()?;
325-
visitor.visit_string(s)
326+
visitor.visit_string(s.to_string())
326327
}
327328

328329
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
@@ -346,9 +347,7 @@ impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
346347
///
347348
/// As commented in `Serializer` implementation, this is a lossy
348349
/// representation. For example the values `Some(())` and `None` both
349-
/// serialize as just `null`. Unfortunately this is typically what people
350-
/// expect when working with JSON. Other formats are encouraged to behave
351-
/// more intelligently if possible.
350+
/// serialize as just `null`.
352351
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
353352
where
354353
V: serde::de::Visitor<'de>,
@@ -416,7 +415,7 @@ impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
416415
visitor.visit_seq(seq_visitor)
417416
}
418417

419-
// Tuples look just like sequences in JSON. Some formats may be able to
418+
// Tuples look just like sequences. Some formats may be able to
420419
// represent tuples more efficiently.
421420
//
422421
// As indicated by the length parameter, the `Deserialize` implementation
@@ -429,7 +428,7 @@ impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
429428
self.deserialize_seq(visitor)
430429
}
431430

432-
// Tuple structs look just like sequences in JSON.
431+
// Tuple structs look just like sequences.
433432
fn deserialize_tuple_struct<V>(
434433
self,
435434
_name: &'static str,
@@ -502,7 +501,7 @@ impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
502501
| RespDataKind::VerbatimString => {
503502
// Visit a unit variant.
504503
let s = self.parse_string()?;
505-
visitor.visit_enum(s.as_str().into_deserializer())
504+
visitor.visit_enum(s.into_deserializer())
506505
}
507506
RespDataKind::Map | RespDataKind::Attributes => {
508507
visitor.visit_enum(EnumDeserializer::new(self))
@@ -515,9 +514,8 @@ impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
515514
}
516515

517516
// An identifier in Serde is the type that identifies a field of a struct or
518-
// the variant of an enum. In JSON, struct fields and enum variants are
519-
// represented as strings. In other formats they may be represented as
520-
// numeric indices.
517+
// the variant of an enum. Struct fields and enum variants are
518+
// represented as strings.
521519
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
522520
where
523521
V: serde::de::Visitor<'de>,
@@ -664,7 +662,7 @@ impl<'de> serde::de::VariantAccess<'de> for EnumDeserializer<'_, 'de> {
664662
))
665663
}
666664

667-
// Newtype variants are represented in JSON as `{ NAME: VALUE }` so
665+
// Newtype variants are represented as `{ NAME: VALUE }` so
668666
// deserialize the value here.
669667
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
670668
where
@@ -673,7 +671,7 @@ impl<'de> serde::de::VariantAccess<'de> for EnumDeserializer<'_, 'de> {
673671
seed.deserialize(self.de)
674672
}
675673

676-
// Tuple variants are represented in JSON as `{ NAME: [DATA...] }` so
674+
// Tuple variants are represented as `{ NAME: [DATA...] }` so
677675
// deserialize the sequence of data here.
678676
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
679677
where
@@ -682,7 +680,7 @@ impl<'de> serde::de::VariantAccess<'de> for EnumDeserializer<'_, 'de> {
682680
serde::de::Deserializer::deserialize_seq(self.de, visitor)
683681
}
684682

685-
// Struct variants are represented in JSON as `{ NAME: { K: V, ... } }` so
683+
// Struct variants are represented as `{ NAME: { K: V, ... } }` so
686684
// deserialize the inner map here.
687685
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
688686
where

src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::string::FromUtf8Error;
1+
use std::{str::Utf8Error, string::FromUtf8Error};
22

33
#[derive(Debug, Clone)]
44
pub enum Error {
@@ -58,3 +58,9 @@ impl From<FromUtf8Error> for Error {
5858
Self::InvalidUtf8
5959
}
6060
}
61+
62+
impl From<Utf8Error> for Error {
63+
fn from(_err: Utf8Error) -> Self {
64+
Self::InvalidUtf8
65+
}
66+
}

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
//!
2828
//! let serialized = to_string(&person).unwrap();
2929
//! let deserialized: Person = from_str(&serialized).unwrap();
30+
//! let deserialized_raw: Person = from_str("%2\r\n+name\r\n+Alice\r\n+age\r\n:30\r\n").unwrap();
31+
//! assert_eq!(deserialized, person);
32+
//! assert_eq!(deserialized_raw, person);
3033
//! assert_eq!(deserialized, person);
3134
//! ```
3235
//!
@@ -50,7 +53,7 @@ mod ser;
5053
pub use de::{Deserializer, from_bytes, from_str};
5154
pub use error::{Error, Result};
5255
pub use resp::RespDataKind;
53-
pub use ser::{Serializer, to_bytes, to_string, to_utf8_lossy};
56+
pub use ser::{Serializer, to_bytes, to_string};
5457

5558
pub const CRLF: &[u8] = b"\r\n";
5659
pub const CRLF_STR: &str = "\r\n";

src/resp.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,12 @@ pub enum RespDataKind {
238238
/// name: "Alice".to_string(),
239239
/// age: 30,
240240
/// };
241+
///
241242
/// let serialized = to_string(&person).unwrap();
242243
/// let deserialized: Person = from_str(&serialized).unwrap();
244+
/// let deserialized_raw: Person = from_str("%2\r\n+name\r\n+Alice\r\n+age\r\n:30\r\n").unwrap();
243245
/// assert_eq!(deserialized, person);
246+
/// assert_eq!(deserialized_raw, person);
244247
/// ```
245248
///
246249
/// And an arbitrary map example:
@@ -255,8 +258,8 @@ pub enum RespDataKind {
255258
///
256259
/// let serialized = to_string(&map).unwrap();
257260
/// assert_eq!(serialized, "%2\r\n$10\r\nfirst_name\r\n$5\r\nAlice\r\n$9\r\nlast_name\r\n$5\r\nSmith\r\n");
258-
/// let deserialized_hashmap: HashMap<String, String> = from_str(&serialized).unwrap();
259-
/// assert_eq!(deserialized_hashmap.len(), map.len()); // We only check length here because order is not guaranteed
261+
/// // Note that the order of keys is not guaranteed in HashMap, so we use BTreeMap for testing
262+
/// // But in principle serialization / deserialization should work with HashMap as well
260263
/// let deserialized_bt: BTreeMap<String, String> = from_str(&serialized).unwrap();
261264
/// assert_eq!(deserialized_bt.len(), 2);
262265
/// ```

src/ser.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,6 @@ where
3030
Ok(serializer.output)
3131
}
3232

33-
pub fn to_utf8_lossy<T>(value: &T) -> Result<String>
34-
where
35-
T: serde::Serialize,
36-
{
37-
let mut serializer = Serializer::new();
38-
value.serialize(&mut serializer)?;
39-
Ok(String::from_utf8_lossy(&serializer.output).into_owned())
40-
}
41-
4233
pub fn to_string<T>(value: &T) -> Result<String>
4334
where
4435
T: serde::Serialize,

tests/structs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,7 @@ fn simple() {
8181
};
8282
let serialized = to_string(&person).unwrap();
8383
let deserialized: Person = from_str(&serialized).unwrap();
84+
let deserialized_raw: Person = from_str("%2\r\n+name\r\n+Alice\r\n+age\r\n:30\r\n").unwrap();
8485
assert_eq!(deserialized, person);
86+
assert_eq!(deserialized_raw, person);
8587
}

0 commit comments

Comments
 (0)