1use std::fmt::Write;
4use std::os::raw::c_int;
5use std::ptr::NonNull;
6
7use bytes::{Bytes, BytesMut};
8use pyo3::buffer::PyBuffer;
9use pyo3::exceptions::{PyIndexError, PyValueError};
10use pyo3::prelude::*;
11use pyo3::types::{PyDict, PySlice, PyTuple};
12use pyo3::{ffi, IntoPyObjectExt};
13
14#[pyclass(
36 name = "Bytes",
37 subclass,
38 frozen,
39 sequence,
40 weakref,
41 skip_from_py_object
42)]
43#[derive(Clone, Default, Hash, PartialEq, PartialOrd, Eq, Ord)]
44pub struct PyBytes(Bytes);
45
46impl AsRef<Bytes> for PyBytes {
47 fn as_ref(&self) -> &Bytes {
48 &self.0
49 }
50}
51
52impl AsRef<[u8]> for PyBytes {
53 fn as_ref(&self) -> &[u8] {
54 self.0.as_ref()
55 }
56}
57
58impl PyBytes {
59 pub fn new(buffer: Bytes) -> Self {
61 Self(buffer)
62 }
63
64 pub fn into_inner(self) -> Bytes {
66 self.0
67 }
68
69 pub fn as_slice(&self) -> &[u8] {
71 self.as_ref()
72 }
73
74 fn slice(&self, slice: &Bound<'_, PySlice>) -> PyResult<PyBytes> {
84 let bytes_length = self.0.len() as isize;
85 let (start, stop, step) = {
86 let slice_indices = slice.indices(bytes_length)?;
87 (slice_indices.start, slice_indices.stop, slice_indices.step)
88 };
89
90 let new_capacity = if (step > 0 && stop > start) || (step < 0 && stop < start) {
91 (((stop - start).abs() + step.abs() - 1) / step.abs()) as usize
92 } else {
93 0
94 };
95
96 if new_capacity == 0 {
97 return Ok(PyBytes(Bytes::new()));
98 }
99 if step == 1 {
100 if start < 0 && stop >= bytes_length {
102 let out = self.0.slice(..);
103 let py_bytes = PyBytes(out);
104 return Ok(py_bytes);
105 }
106
107 if start >= 0 && stop <= bytes_length && start < stop {
108 let out = self.0.slice(start as usize..stop as usize);
109 let py_bytes = PyBytes(out);
110 return Ok(py_bytes);
111 }
112 }
114 if step > 0 {
115 let mut new_buf = BytesMut::with_capacity(new_capacity);
117 new_buf.extend(
118 (start..stop)
119 .step_by(step as usize)
120 .map(|i| self.0[i as usize]),
121 );
122 Ok(PyBytes(new_buf.freeze()))
123 } else {
124 let mut new_buf = BytesMut::with_capacity(new_capacity);
126 new_buf.extend(
127 (stop + 1..=start)
128 .rev()
129 .step_by((-step) as usize)
130 .map(|i| self.0[i as usize]),
131 );
132 Ok(PyBytes(new_buf.freeze()))
133 }
134 }
135}
136
137impl From<PyBytes> for Bytes {
138 fn from(value: PyBytes) -> Self {
139 value.0
140 }
141}
142
143impl From<Vec<u8>> for PyBytes {
144 fn from(value: Vec<u8>) -> Self {
145 PyBytes(value.into())
146 }
147}
148
149impl From<Bytes> for PyBytes {
150 fn from(value: Bytes) -> Self {
151 PyBytes(value)
152 }
153}
154
155impl From<BytesMut> for PyBytes {
156 fn from(value: BytesMut) -> Self {
157 PyBytes(value.into())
158 }
159}
160
161#[pymethods]
162impl PyBytes {
163 #[new]
166 #[pyo3(signature = (buf = PyBytes(Bytes::new())), text_signature = "(buf = b'')")]
167 fn py_new(buf: PyBytes) -> Self {
168 buf
169 }
170
171 fn __getnewargs_ex__<'py>(&'py self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
172 let py_bytes = self.to_bytes(py);
173 let args = PyTuple::new(py, vec![py_bytes])?.into_bound_py_any(py)?;
174 let kwargs = PyDict::new(py).into_bound_py_any(py)?;
175 PyTuple::new(py, [args, kwargs])
176 }
177
178 fn __len__(&self) -> usize {
180 self.0.len()
181 }
182
183 fn __repr__(&self) -> String {
184 format!("{self:?}")
185 }
186
187 fn __add__(&self, other: PyBytes) -> PyBytes {
188 let total_length = self.0.len() + other.0.len();
189 let mut new_buffer = BytesMut::with_capacity(total_length);
190 new_buffer.extend_from_slice(&self.0);
191 new_buffer.extend_from_slice(&other.0);
192 new_buffer.into()
193 }
194
195 fn __contains__(&self, item: PyBytes) -> bool {
196 self.0
197 .windows(item.0.len())
198 .any(|window| window == item.as_slice())
199 }
200
201 fn __eq__(&self, other: PyBytes) -> bool {
202 self.0.as_ref() == other.0.as_ref()
203 }
204
205 fn __getitem__<'py>(
206 &self,
207 py: Python<'py>,
208 key: BytesGetItemKey<'py>,
209 ) -> PyResult<Bound<'py, PyAny>> {
210 match key {
211 BytesGetItemKey::Int(mut index) => {
212 if index < 0 {
213 index += self.0.len() as isize;
214 }
215 if index < 0 {
216 return Err(PyIndexError::new_err("Index out of range"));
217 }
218 self.0
219 .get(index as usize)
220 .ok_or(PyIndexError::new_err("Index out of range"))?
221 .into_bound_py_any(py)
222 }
223 BytesGetItemKey::Slice(slice) => {
224 let s = self.slice(&slice)?;
225 s.into_bound_py_any(py)
226 }
227 }
228 }
229
230 fn __mul__(&self, value: usize) -> PyBytes {
231 let mut out_buf = BytesMut::with_capacity(self.0.len() * value);
232 (0..value).for_each(|_| out_buf.extend_from_slice(self.0.as_ref()));
233 out_buf.into()
234 }
235
236 #[allow(unsafe_code)]
239 unsafe fn __getbuffer__(
240 slf: PyRef<Self>,
241 view: *mut ffi::Py_buffer,
242 flags: c_int,
243 ) -> PyResult<()> {
244 let bytes = slf.0.as_ref();
245 let ret = ffi::PyBuffer_FillInfo(
246 view,
247 slf.as_ptr() as *mut _,
248 bytes.as_ptr() as *mut _,
249 bytes.len().try_into().unwrap(),
250 1, flags,
252 );
253 if ret == -1 {
254 return Err(PyErr::fetch(slf.py()));
255 }
256 Ok(())
257 }
258
259 #[allow(unsafe_code)]
265 unsafe fn __releasebuffer__(&self, _view: *mut ffi::Py_buffer) {}
266
267 #[pyo3(signature = (prefix, /))]
270 fn removeprefix(&self, prefix: PyBytes) -> PyBytes {
271 if self.0.starts_with(prefix.as_ref()) {
272 self.0.slice(prefix.0.len()..).into()
273 } else {
274 self.0.clone().into()
275 }
276 }
277
278 #[pyo3(signature = (suffix, /))]
281 fn removesuffix(&self, suffix: PyBytes) -> PyBytes {
282 if self.0.ends_with(suffix.as_ref()) {
283 self.0.slice(0..self.0.len() - suffix.0.len()).into()
284 } else {
285 self.0.clone().into()
286 }
287 }
288
289 fn isalnum(&self) -> bool {
294 if self.0.is_empty() {
295 return false;
296 }
297
298 for c in self.0.as_ref() {
299 if !c.is_ascii_alphanumeric() {
300 return false;
301 }
302 }
303 true
304 }
305
306 fn isalpha(&self) -> bool {
310 if self.0.is_empty() {
311 return false;
312 }
313
314 for c in self.0.as_ref() {
315 if !c.is_ascii_alphabetic() {
316 return false;
317 }
318 }
319 true
320 }
321
322 fn isascii(&self) -> bool {
325 for c in self.0.as_ref() {
326 if !c.is_ascii() {
327 return false;
328 }
329 }
330 true
331 }
332
333 fn isdigit(&self) -> bool {
337 if self.0.is_empty() {
338 return false;
339 }
340
341 for c in self.0.as_ref() {
342 if !c.is_ascii_digit() {
343 return false;
344 }
345 }
346 true
347 }
348
349 fn islower(&self) -> bool {
352 let mut has_lower = false;
353 for c in self.0.as_ref() {
354 if c.is_ascii_uppercase() {
355 return false;
356 }
357 if !has_lower && c.is_ascii_lowercase() {
358 has_lower = true;
359 }
360 }
361
362 has_lower
363 }
364
365 fn isspace(&self) -> bool {
369 if self.0.is_empty() {
370 return false;
371 }
372
373 for c in self.0.as_ref() {
374 if !(c.is_ascii_whitespace() || *c == b'\x0b') {
376 return false;
377 }
378 }
379 true
380 }
381
382 fn isupper(&self) -> bool {
385 let mut has_upper = false;
386 for c in self.0.as_ref() {
387 if c.is_ascii_lowercase() {
388 return false;
389 }
390 if !has_upper && c.is_ascii_uppercase() {
391 has_upper = true;
392 }
393 }
394
395 has_upper
396 }
397
398 fn lower(&self) -> PyBytes {
401 self.0.to_ascii_lowercase().into()
402 }
403
404 fn upper(&self) -> PyBytes {
407 self.0.to_ascii_uppercase().into()
408 }
409
410 fn to_bytes<'py>(&'py self, py: Python<'py>) -> Bound<'py, pyo3::types::PyBytes> {
412 pyo3::types::PyBytes::new(py, &self.0)
413 }
414}
415
416impl<'py> FromPyObject<'_, 'py> for PyBytes {
417 type Error = PyErr;
418
419 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
420 let buffer = obj.extract::<PyBytesWrapper>()?;
421 let bytes = Bytes::from_owner(buffer);
422 Ok(Self(bytes))
423 }
424}
425
426#[derive(Debug)]
431struct PyBytesWrapper(PyBuffer<u8>);
432
433impl AsRef<[u8]> for PyBytesWrapper {
434 #[allow(unsafe_code)]
435 fn as_ref(&self) -> &[u8] {
436 let len = self.0.item_count();
437
438 let ptr = NonNull::new(self.0.buf_ptr() as _).expect("Expected buffer ptr to be non null");
439
440 unsafe { std::slice::from_raw_parts(ptr.as_ptr() as *const u8, len) }
446 }
447}
448
449fn validate_buffer(buf: &PyBuffer<u8>) -> PyResult<()> {
450 if !buf.is_c_contiguous() {
451 return Err(PyValueError::new_err("Buffer is not C contiguous"));
452 }
453
454 if buf.strides().iter().any(|s| *s != 1) {
455 return Err(PyValueError::new_err(format!(
456 "strides other than 1 not supported, got: {:?} ",
457 buf.strides()
458 )));
459 }
460
461 Ok(())
462}
463
464impl<'py> FromPyObject<'_, 'py> for PyBytesWrapper {
465 type Error = PyErr;
466
467 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
468 let buffer = obj.extract::<PyBuffer<u8>>()?;
469 validate_buffer(&buffer)?;
470 Ok(Self(buffer))
471 }
472}
473
474impl std::fmt::Debug for PyBytes {
481 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482 f.write_str("Bytes(b\"")?;
483 for &byte in self.0.as_ref() {
484 match byte {
485 b'\\' => f.write_str(r"\\")?,
487 b'"' => f.write_str("\\\"")?,
488 b'\n' => f.write_str(r"\n")?,
489 b'\r' => f.write_str(r"\r")?,
490 b'\t' => f.write_str(r"\t")?,
491 0x20..=0x7E => f.write_char(byte as char)?,
493 _ => write!(f, "\\x{byte:02x}")?,
494 }
495 }
496 f.write_str("\")")?;
497 Ok(())
498 }
499}
500
501#[derive(FromPyObject)]
503enum BytesGetItemKey<'py> {
504 Int(isize),
506 Slice(Bound<'py, PySlice>),
508}