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, PyMemoryError, PyOverflowError, PyValueError};
10use pyo3::prelude::*;
11use pyo3::types::{PyDict, PySlice, PyTuple};
12use pyo3::{ffi, IntoPyObjectExt};
13
14#[pyclass(name = "Bytes", subclass, frozen, sequence, weakref)]
36#[derive(Clone, Default, Hash, PartialEq, PartialOrd, Eq, Ord)]
37pub struct PyBytes(Bytes);
38
39impl AsRef<Bytes> for PyBytes {
40 fn as_ref(&self) -> &Bytes {
41 &self.0
42 }
43}
44
45impl AsRef<[u8]> for PyBytes {
46 fn as_ref(&self) -> &[u8] {
47 self.0.as_ref()
48 }
49}
50
51impl PyBytes {
52 pub fn new(buffer: Bytes) -> Self {
54 Self(buffer)
55 }
56
57 pub fn into_inner(self) -> Bytes {
59 self.0
60 }
61
62 pub fn as_slice(&self) -> &[u8] {
64 self.as_ref()
65 }
66
67 fn slice(&self, slice: &Bound<'_, PySlice>) -> PyResult<PyBytes> {
77 let bytes_length = self.0.len() as isize;
78 let (start, stop, step) = {
79 let slice_indices = slice.indices(bytes_length)?;
80 (slice_indices.start, slice_indices.stop, slice_indices.step)
81 };
82
83 let new_capacity = if (step > 0 && stop > start) || (step < 0 && stop < start) {
84 (((stop - start).abs() + step.abs() - 1) / step.abs()) as usize
85 } else {
86 0
87 };
88
89 if new_capacity == 0 {
90 return Ok(PyBytes(Bytes::new()));
91 }
92 if step == 1 {
93 if start < 0 && stop >= bytes_length {
95 let out = self.0.slice(..);
96 let py_bytes = PyBytes(out);
97 return Ok(py_bytes);
98 }
99
100 if start >= 0 && stop <= bytes_length && start < stop {
101 let out = self.0.slice(start as usize..stop as usize);
102 let py_bytes = PyBytes(out);
103 return Ok(py_bytes);
104 }
105 }
107 if step > 0 {
108 let mut new_buf = BytesMut::with_capacity(new_capacity);
110 new_buf.extend(
111 (start..stop)
112 .step_by(step as usize)
113 .map(|i| self.0[i as usize]),
114 );
115 Ok(PyBytes(new_buf.freeze()))
116 } else {
117 let mut new_buf = BytesMut::with_capacity(new_capacity);
119 new_buf.extend(
120 (stop + 1..=start)
121 .rev()
122 .step_by((-step) as usize)
123 .map(|i| self.0[i as usize]),
124 );
125 Ok(PyBytes(new_buf.freeze()))
126 }
127 }
128}
129
130impl From<PyBytes> for Bytes {
131 fn from(value: PyBytes) -> Self {
132 value.0
133 }
134}
135
136impl From<Vec<u8>> for PyBytes {
137 fn from(value: Vec<u8>) -> Self {
138 PyBytes(value.into())
139 }
140}
141
142impl From<Bytes> for PyBytes {
143 fn from(value: Bytes) -> Self {
144 PyBytes(value)
145 }
146}
147
148impl From<BytesMut> for PyBytes {
149 fn from(value: BytesMut) -> Self {
150 PyBytes(value.into())
151 }
152}
153
154#[pymethods]
155impl PyBytes {
156 #[new]
159 #[pyo3(signature = (buf = PyBytes(Bytes::new())), text_signature = "(buf = b'')")]
160 fn py_new(buf: PyBytes) -> Self {
161 buf
162 }
163
164 fn __getnewargs_ex__<'py>(&'py self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
165 let py_bytes = self.to_bytes(py);
166 let args = PyTuple::new(py, vec![py_bytes])?.into_bound_py_any(py)?;
167 let kwargs = PyDict::new(py).into_bound_py_any(py)?;
168 PyTuple::new(py, [args, kwargs])
169 }
170
171 fn __len__(&self) -> usize {
173 self.0.len()
174 }
175
176 fn __repr__(&self) -> String {
177 format!("{self:?}")
178 }
179
180 fn __add__(&self, other: PyBytes) -> PyBytes {
181 let total_length = self.0.len() + other.0.len();
182 let mut new_buffer = BytesMut::with_capacity(total_length);
183 new_buffer.extend_from_slice(&self.0);
184 new_buffer.extend_from_slice(&other.0);
185 new_buffer.into()
186 }
187
188 fn __contains__(&self, item: PyBytes) -> bool {
189 let needle = item.as_slice();
190 needle.is_empty() || self.0.windows(needle.len()).any(|window| window == needle)
191 }
192
193 fn __eq__(&self, other: PyBytes) -> bool {
194 self.0.as_ref() == other.0.as_ref()
195 }
196
197 fn __getitem__<'py>(
198 &self,
199 py: Python<'py>,
200 key: BytesGetItemKey<'py>,
201 ) -> PyResult<Bound<'py, PyAny>> {
202 match key {
203 BytesGetItemKey::Int(mut index) => {
204 if index < 0 {
205 index += self.0.len() as isize;
206 }
207 if index < 0 {
208 return Err(PyIndexError::new_err("Index out of range"));
209 }
210 self.0
211 .get(index as usize)
212 .ok_or(PyIndexError::new_err("Index out of range"))?
213 .into_bound_py_any(py)
214 }
215 BytesGetItemKey::Slice(slice) => {
216 let s = self.slice(&slice)?;
217 s.into_bound_py_any(py)
218 }
219 }
220 }
221
222 fn __mul__(&self, value: usize) -> PyResult<PyBytes> {
223 let total_length = self
226 .0
227 .len()
228 .checked_mul(value)
229 .filter(|&len| len <= isize::MAX as usize)
230 .ok_or_else(|| PyOverflowError::new_err("repeated bytes are too long"))?;
231
232 if total_length == 0 {
233 return Ok(PyBytes::new(Bytes::new()));
234 }
235
236 let mut out_buf = Vec::new();
238 out_buf
239 .try_reserve_exact(total_length)
240 .map_err(|_| PyMemoryError::new_err(()))?;
241
242 (0..value).for_each(|_| out_buf.extend_from_slice(self.0.as_ref()));
243 Ok(out_buf.into())
244 }
245
246 #[allow(unsafe_code)]
249 unsafe fn __getbuffer__(
250 slf: PyRef<Self>,
251 view: *mut ffi::Py_buffer,
252 flags: c_int,
253 ) -> PyResult<()> {
254 let bytes = slf.0.as_ref();
255 let ret = ffi::PyBuffer_FillInfo(
256 view,
257 slf.as_ptr() as *mut _,
258 bytes.as_ptr() as *mut _,
259 bytes.len().try_into().unwrap(),
260 1, flags,
262 );
263 if ret == -1 {
264 return Err(PyErr::fetch(slf.py()));
265 }
266 Ok(())
267 }
268
269 #[allow(unsafe_code)]
275 unsafe fn __releasebuffer__(&self, _view: *mut ffi::Py_buffer) {}
276
277 #[pyo3(signature = (prefix, /))]
280 fn removeprefix(&self, prefix: PyBytes) -> PyBytes {
281 if self.0.starts_with(prefix.as_ref()) {
282 self.0.slice(prefix.0.len()..).into()
283 } else {
284 self.0.clone().into()
285 }
286 }
287
288 #[pyo3(signature = (suffix, /))]
291 fn removesuffix(&self, suffix: PyBytes) -> PyBytes {
292 if self.0.ends_with(suffix.as_ref()) {
293 self.0.slice(0..self.0.len() - suffix.0.len()).into()
294 } else {
295 self.0.clone().into()
296 }
297 }
298
299 fn isalnum(&self) -> bool {
304 if self.0.is_empty() {
305 return false;
306 }
307
308 for c in self.0.as_ref() {
309 if !c.is_ascii_alphanumeric() {
310 return false;
311 }
312 }
313 true
314 }
315
316 fn isalpha(&self) -> bool {
320 if self.0.is_empty() {
321 return false;
322 }
323
324 for c in self.0.as_ref() {
325 if !c.is_ascii_alphabetic() {
326 return false;
327 }
328 }
329 true
330 }
331
332 fn isascii(&self) -> bool {
335 for c in self.0.as_ref() {
336 if !c.is_ascii() {
337 return false;
338 }
339 }
340 true
341 }
342
343 fn isdigit(&self) -> bool {
347 if self.0.is_empty() {
348 return false;
349 }
350
351 for c in self.0.as_ref() {
352 if !c.is_ascii_digit() {
353 return false;
354 }
355 }
356 true
357 }
358
359 fn islower(&self) -> bool {
362 let mut has_lower = false;
363 for c in self.0.as_ref() {
364 if c.is_ascii_uppercase() {
365 return false;
366 }
367 if !has_lower && c.is_ascii_lowercase() {
368 has_lower = true;
369 }
370 }
371
372 has_lower
373 }
374
375 fn isspace(&self) -> bool {
379 if self.0.is_empty() {
380 return false;
381 }
382
383 for c in self.0.as_ref() {
384 if !(c.is_ascii_whitespace() || *c == b'\x0b') {
386 return false;
387 }
388 }
389 true
390 }
391
392 fn isupper(&self) -> bool {
395 let mut has_upper = false;
396 for c in self.0.as_ref() {
397 if c.is_ascii_lowercase() {
398 return false;
399 }
400 if !has_upper && c.is_ascii_uppercase() {
401 has_upper = true;
402 }
403 }
404
405 has_upper
406 }
407
408 fn lower(&self) -> PyBytes {
411 self.0.to_ascii_lowercase().into()
412 }
413
414 fn upper(&self) -> PyBytes {
417 self.0.to_ascii_uppercase().into()
418 }
419
420 fn to_bytes<'py>(&'py self, py: Python<'py>) -> Bound<'py, pyo3::types::PyBytes> {
422 pyo3::types::PyBytes::new(py, &self.0)
423 }
424}
425
426impl<'py> FromPyObject<'_, 'py> for PyBytes {
427 type Error = PyErr;
428
429 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
430 let buffer = obj.extract::<PyBytesWrapper>()?;
431 let bytes = Bytes::from_owner(buffer);
432 Ok(Self(bytes))
433 }
434}
435
436#[derive(Debug)]
441struct PyBytesWrapper(PyBuffer<u8>);
442
443impl AsRef<[u8]> for PyBytesWrapper {
444 #[allow(unsafe_code)]
445 fn as_ref(&self) -> &[u8] {
446 let len = self.0.item_count();
447
448 let ptr = NonNull::new(self.0.buf_ptr() as _).expect("Expected buffer ptr to be non null");
449
450 unsafe { std::slice::from_raw_parts(ptr.as_ptr() as *const u8, len) }
456 }
457}
458
459fn validate_buffer(buf: &PyBuffer<u8>) -> PyResult<()> {
460 if !buf.is_c_contiguous() {
461 return Err(PyValueError::new_err("Buffer is not C contiguous"));
462 }
463
464 if buf.strides().iter().any(|s| *s != 1) {
465 return Err(PyValueError::new_err(format!(
466 "strides other than 1 not supported, got: {:?} ",
467 buf.strides()
468 )));
469 }
470
471 Ok(())
472}
473
474impl<'py> FromPyObject<'_, 'py> for PyBytesWrapper {
475 type Error = PyErr;
476
477 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
478 let buffer = obj.extract::<PyBuffer<u8>>()?;
479 validate_buffer(&buffer)?;
480 Ok(Self(buffer))
481 }
482}
483
484impl std::fmt::Debug for PyBytes {
491 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
492 f.write_str("Bytes(b\"")?;
493 for &byte in self.0.as_ref() {
494 match byte {
495 b'\\' => f.write_str(r"\\")?,
497 b'"' => f.write_str("\\\"")?,
498 b'\n' => f.write_str(r"\n")?,
499 b'\r' => f.write_str(r"\r")?,
500 b'\t' => f.write_str(r"\t")?,
501 0x20..=0x7E => f.write_char(byte as char)?,
503 _ => write!(f, "\\x{byte:02x}")?,
504 }
505 }
506 f.write_str("\")")?;
507 Ok(())
508 }
509}
510
511#[derive(FromPyObject)]
513enum BytesGetItemKey<'py> {
514 Int(isize),
516 Slice(Bound<'py, PySlice>),
518}