Skip to content

Commit 48e2e93

Browse files
committed
Don't double validate strings
Do the same as the original version and rely on Python for the string UTF-8 validation.
1 parent 10005c6 commit 48e2e93

1 file changed

Lines changed: 17 additions & 27 deletions

File tree

src/lib.rs

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::os::raw::c_char;
2-
31
use anyhow::{anyhow, Result};
42
use cbor4ii::core::{
53
dec::{self, Decode, Read},
@@ -73,7 +71,7 @@ fn cid_to_pydict<'py>(py: Python<'py>, cid: &Cid) -> Bound<'py, PyDict> {
7371
dict_obj
7472
}
7573

76-
fn map_key_cmp(a: &str, b: &str) -> std::cmp::Ordering {
74+
fn map_key_cmp(a: &[u8], b: &[u8]) -> std::cmp::Ordering {
7775
/* The keys in every map must be sorted length-first by the byte representation of the string keys, where:
7876
- If two keys have different lengths, the shorter one sorts earlier;
7977
- If two keys have the same length, the one with the lower value in (byte-wise) lexical order sorts earlier.
@@ -136,17 +134,6 @@ fn get_bytes_from_py_any<'py>(obj: &'py Bound<'py, PyAny>) -> PyResult<&'py [u8]
136134
}
137135
}
138136

139-
fn string_new_bound<'py>(py: Python<'py>, s: &str) -> Result<Bound<'py, PyString>> {
140-
let ptr = s.as_ptr() as *const c_char;
141-
let len = s.len() as ffi::Py_ssize_t;
142-
unsafe {
143-
Ok(
144-
Bound::from_owned_ptr(py, ffi::PyUnicode_FromStringAndSize(ptr, len))
145-
.cast_into_unchecked(),
146-
)
147-
}
148-
}
149-
150137
// Based on cbor4ii code.
151138
fn peek_one<'de, R: dec::Read<'de>>(r: &mut R) -> Result<u8>
152139
where
@@ -181,18 +168,18 @@ where
181168
let byte = peek_one(r)?;
182169
return Ok(match dec::if_major(byte) {
183170
major::UNSIGNED => u64::decode(r)?.into_pyobject(py)?.into(),
184-
major::NEGATIVE => {
185-
i128::decode(r)?.into_pyobject(py)?.into()
186-
}
171+
major::NEGATIVE => i128::decode(r)?.into_pyobject(py)?.into(),
187172
major::BYTES => PyBytes::new(py, <types::Bytes<&[u8]>>::decode(r)?.0)
188173
.into_pyobject(py)?
189174
.into(),
190-
major::STRING => string_new_bound(
191-
py,
192-
<&str>::decode(r).map_err(|_| anyhow!("Invalid UTF-8 string"))?,
193-
)?
194-
.into_pyobject(py)?
195-
.into(),
175+
major::STRING => {
176+
// The UTF-8 validation is done when it's converted into a Python string
177+
<types::UncheckedStr<&[u8]>>::decode(r)
178+
.map_err(|_| anyhow!("Cannot decode as bytes"))?
179+
.0
180+
.into_pyobject(py)?
181+
.into()
182+
}
196183
major::ARRAY => {
197184
let len: ffi::Py_ssize_t =
198185
types::Array::len(r)?.expect("contains length").try_into()?;
@@ -216,10 +203,13 @@ where
216203
let len = types::Map::len(r)?.expect("contains length");
217204
let dict = PyDict::new(py);
218205

219-
let mut prev_key: Option<&str> = None;
206+
let mut prev_key: Option<&[u8]> = None;
220207
for _ in 0..len {
221-
// DAG-CBOR keys are always strings
222-
let key = <&str>::decode(r).map_err(|_| anyhow!("Map keys must be strings"))?;
208+
// DAG-CBOR keys are always strings. Python does the UTF-8 validation when creating
209+
// the string.
210+
let key = <types::UncheckedStr<&[u8]>>::decode(r)
211+
.map_err(|_| anyhow!("Cannot decode as bytes"))?
212+
.0;
223213

224214
if let Some(prev_key) = prev_key {
225215
// it cares about duplicated keys too thanks to Ordering::Equal
@@ -228,7 +218,7 @@ where
228218
}
229219
}
230220

231-
let key_py = string_new_bound(py, key)?.into_pyobject(py)?;
221+
let key_py = PyString::from_bytes(py, key)?;
232222
prev_key = Some(key);
233223

234224
let value_py = decode_dag_cbor_to_pyobject(py, r, depth + 1)?;

0 commit comments

Comments
 (0)