Coverage Report

Created: 2026-08-14 08:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/revision-0.30.0/src/implementations/string.rs
Line
Count
Source
1
use core::str;
2
3
use crate::{DeserializeRevisioned, Error, Revisioned, SerializeRevisioned};
4
5
use super::vecs::serialize_bytes;
6
7
impl SerializeRevisioned for String {
8
  #[inline]
9
25.6k
  fn serialize_revisioned<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
10
25.6k
    serialize_bytes(self.as_bytes(), writer)
11
25.6k
  }
<alloc::string::String as revision::SerializeRevisioned>::serialize_revisioned::<alloc::vec::Vec<u8>>
Line
Count
Source
9
25.6k
  fn serialize_revisioned<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
10
25.6k
    serialize_bytes(self.as_bytes(), writer)
11
25.6k
  }
Unexecuted instantiation: <alloc::string::String as revision::SerializeRevisioned>::serialize_revisioned::<_>
12
}
13
14
impl DeserializeRevisioned for String {
15
  /// Reads the length-prefixed byte payload in a single bulk `read_exact`
16
  /// and validates it as UTF-8 in place, avoiding both the per-byte fallback
17
  /// when `specialised-vectors` is disabled and the `Take::read_to_end`
18
  /// overhead of the `Vec<u8>` specialised path.
19
  #[inline]
20
139k
  fn deserialize_revisioned<R: std::io::Read>(reader: &mut R) -> Result<Self, Error> {
21
139k
    let len = usize::deserialize_revisioned(reader)?;
22
139k
    if len == 0 {
23
8
      return Ok(String::new());
24
139k
    }
25
    // Zero-initialise before handing the buffer to `read_exact`. Passing an
26
    // uninitialised slice to `Read::read` is explicitly documented as UB,
27
    // and constructing a `&mut [u8]` over uninitialised memory would itself
28
    // be UB per `slice::from_raw_parts_mut`'s initialisation requirement.
29
    // The memset is negligible compared to the pending I/O and matches the
30
    // pattern used in `uuid.rs` and the numeric specialised Vec impls.
31
139k
    let mut buf = vec![0u8; len];
32
139k
    reader.read_exact(&mut buf).map_err(Error::Io)?;
33
139k
    String::from_utf8(buf).map_err(|x| Error::Utf8Error(x.utf8_error()))
Unexecuted instantiation: <alloc::string::String as revision::DeserializeRevisioned>::deserialize_revisioned::<&[u8]>::{closure#0}
Unexecuted instantiation: <alloc::string::String as revision::DeserializeRevisioned>::deserialize_revisioned::<_>::{closure#0}
34
139k
  }
<alloc::string::String as revision::DeserializeRevisioned>::deserialize_revisioned::<&[u8]>
Line
Count
Source
20
139k
  fn deserialize_revisioned<R: std::io::Read>(reader: &mut R) -> Result<Self, Error> {
21
139k
    let len = usize::deserialize_revisioned(reader)?;
22
139k
    if len == 0 {
23
8
      return Ok(String::new());
24
139k
    }
25
    // Zero-initialise before handing the buffer to `read_exact`. Passing an
26
    // uninitialised slice to `Read::read` is explicitly documented as UB,
27
    // and constructing a `&mut [u8]` over uninitialised memory would itself
28
    // be UB per `slice::from_raw_parts_mut`'s initialisation requirement.
29
    // The memset is negligible compared to the pending I/O and matches the
30
    // pattern used in `uuid.rs` and the numeric specialised Vec impls.
31
139k
    let mut buf = vec![0u8; len];
32
139k
    reader.read_exact(&mut buf).map_err(Error::Io)?;
33
139k
    String::from_utf8(buf).map_err(|x| Error::Utf8Error(x.utf8_error()))
34
139k
  }
Unexecuted instantiation: <alloc::string::String as revision::DeserializeRevisioned>::deserialize_revisioned::<_>
35
}
36
37
impl Revisioned for String {
38
  #[inline]
39
0
  fn revision() -> u16 {
40
0
    1
41
0
  }
Unexecuted instantiation: <alloc::string::String as revision::Revisioned>::revision
Unexecuted instantiation: <alloc::string::String as revision::Revisioned>::revision
42
}
43
44
impl SerializeRevisioned for str {
45
  #[inline]
46
168k
  fn serialize_revisioned<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
47
168k
    serialize_bytes(self.as_bytes(), writer)
48
168k
  }
<str as revision::SerializeRevisioned>::serialize_revisioned::<alloc::vec::Vec<u8>>
Line
Count
Source
46
168k
  fn serialize_revisioned<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
47
168k
    serialize_bytes(self.as_bytes(), writer)
48
168k
  }
Unexecuted instantiation: <str as revision::SerializeRevisioned>::serialize_revisioned::<_>
49
}
50
51
impl Revisioned for str {
52
  #[inline]
53
0
  fn revision() -> u16 {
54
0
    1
55
0
  }
56
}
57
58
impl SerializeRevisioned for char {
59
  #[inline]
60
0
  fn serialize_revisioned<W: std::io::Write>(&self, w: &mut W) -> Result<(), Error> {
61
0
    let buffer = &mut [0u8; 4];
62
0
    w.write_all(self.encode_utf8(buffer).as_bytes()).map_err(Error::Io)
63
0
  }
64
}
65
66
impl DeserializeRevisioned for char {
67
  #[inline]
68
0
  fn deserialize_revisioned<R: std::io::Read>(r: &mut R) -> Result<Self, Error> {
69
0
    let mut buffer = [0u8; 4];
70
0
    r.read_exact(&mut buffer[..1]).map_err(Error::Io)?;
71
72
0
    let len = CHAR_LENGTH[buffer[0] as usize];
73
74
0
    if len == 0 {
75
0
      return Err(Error::InvalidCharEncoding);
76
0
    }
77
78
0
    r.read_exact(&mut buffer[1..(len as usize)]).map_err(Error::Io)?;
79
80
0
    str::from_utf8(&buffer[..(len as usize)])
81
0
      .map_err(|_| Error::InvalidCharEncoding)
82
0
      .map(|x| x.chars().next().unwrap())
83
0
  }
84
}
85
86
impl Revisioned for char {
87
  #[inline]
88
0
  fn revision() -> u16 {
89
0
    1
90
0
  }
91
}
92
93
static CHAR_LENGTH: [u8; 256] = const {
94
  let mut r = [0u8; 256];
95
  let mut i = 0;
96
  while i < 256 {
97
    if i & 0b1000_0000 == 0 {
98
      r[i] = 1;
99
    } else if i & 0b1110_0000 == 0b1100_0000 {
100
      r[i] = 2;
101
    } else if i & 0b1111_0000 == 0b1110_0000 {
102
      r[i] = 3;
103
    } else if i & 0b1111_1000 == 0b1111_0000 {
104
      r[i] = 4;
105
    }
106
107
    i += 1;
108
  }
109
110
  r
111
};
112
113
#[cfg(test)]
114
mod tests {
115
116
  use super::*;
117
118
  use crate::implementations::assert_bincode_compat;
119
120
  #[test]
121
  fn test_string() {
122
    let val = String::from("this is a test");
123
    let mut mem: Vec<u8> = vec![];
124
    val.serialize_revisioned(&mut mem).unwrap();
125
    #[cfg(not(feature = "fixed-width-encoding"))]
126
    assert_eq!(mem.len(), 15);
127
    #[cfg(feature = "fixed-width-encoding")]
128
    assert_eq!(mem.len(), 22);
129
    let out =
130
      <String as DeserializeRevisioned>::deserialize_revisioned(&mut mem.as_slice()).unwrap();
131
    assert_eq!(val, out);
132
  }
133
134
  #[test]
135
  fn test_char() {
136
    let char = '𐃌';
137
    let mut mem = Vec::new();
138
    char.serialize_revisioned(&mut mem).unwrap();
139
    let out = DeserializeRevisioned::deserialize_revisioned(&mut mem.as_slice()).unwrap();
140
    assert_eq!(char, out);
141
  }
142
143
  #[test]
144
  fn bincode_compat_char() {
145
    assert_bincode_compat(&char::MAX);
146
    assert_bincode_compat(&'\0');
147
    assert_bincode_compat(&'z');
148
    assert_bincode_compat(&'0');
149
    // in the 0x7F - 0x07FF range
150
    assert_bincode_compat(&'ʘ');
151
    // in the 0x7FF - 0xFFFF range
152
    assert_bincode_compat(&'ꚸ');
153
    // in the 0xFFFF - 0x10FFFF range
154
    assert_bincode_compat(&'𐃌');
155
  }
156
157
  #[test]
158
  fn str_and_string_serialize_identically() {
159
    // `str` and `String` must produce byte-for-byte identical output
160
    // so that a `String` can round-trip values originally serialised
161
    // from a `&str` (and vice versa). Cover empty, ASCII, multi-byte
162
    // UTF-8, embedded NULs, and a longer payload that exercises the
163
    // length-prefix encoding beyond a single byte.
164
    let cases: &[&str] = &[
165
      "",
166
      "a",
167
      "this is a test",
168
      "unicode: 🚀🔥✨",
169
      "with\0embedded\0nuls",
170
      &"x".repeat(300),
171
    ];
172
    for &s in cases {
173
      let mut from_str: Vec<u8> = Vec::new();
174
      <str as SerializeRevisioned>::serialize_revisioned(s, &mut from_str).unwrap();
175
176
      let owned = s.to_owned();
177
      let mut from_string: Vec<u8> = Vec::new();
178
      owned.serialize_revisioned(&mut from_string).unwrap();
179
180
      assert_eq!(
181
        from_str, from_string,
182
        "str and String serialisation diverged for input {:?}",
183
        s
184
      );
185
186
      let out =
187
        <String as DeserializeRevisioned>::deserialize_revisioned(&mut from_str.as_slice())
188
          .unwrap();
189
      assert_eq!(out, owned);
190
    }
191
  }
192
193
  #[test]
194
  fn bincode_compat_string() {
195
    assert_bincode_compat(&char::MAX.to_string());
196
    assert_bincode_compat(&'\0'.to_string());
197
    assert_bincode_compat(&'z'.to_string());
198
    assert_bincode_compat(&'0'.to_string());
199
    // in the 0x7F - 0x07FF range
200
    assert_bincode_compat(&'ʘ'.to_string());
201
    // in the 0x7FF - 0xFFFF range
202
    assert_bincode_compat(&'ꚸ'.to_string());
203
    // in the 0xFFFF - 0x10FFFF range
204
    assert_bincode_compat(&'𐃌'.to_string());
205
  }
206
}