Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-139871: Add bytearray.take_bytes([n]) to efficiently extract bytes#140128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
daf8e0239b2d1586faf1da9328f4e9f5ca94784957db19def451c302bab7151cb2377c20175f8e485595b5535d07c6e8a84e27d139887dad6e4b910b6f840328cb8c5f03b895a45f3c2c8943e35bffb7e583ea4b8ee14e6d70e36997be81899e49eff4b62d948afb628c81e03313e78cc028e2ba69b3382a951189680e8a02882afb67d10c6db8822fb84c140258891c4701789681135442692aee0d6d6cc238c2File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -5,25 +5,25 @@ | ||
| /* Object layout */ | ||
| typedef struct { | ||
| PyObject_VAR_HEAD | ||
| Py_ssize_t ob_alloc; /* How many bytes allocated in ob_bytes */ | ||
| /* How many bytes allocated in ob_bytes | ||
| In the current implementation this is equivalent to Py_SIZE(ob_bytes_object). | ||
| The value is always loaded and stored atomically for thread safety. | ||
| There are API compatibilty concerns with removing so keeping for now. */ | ||
| Py_ssize_t ob_alloc; | ||
| char *ob_bytes; /* Physical backing buffer */ | ||
| char *ob_start; /* Logical start inside ob_bytes */ | ||
| Py_ssize_t ob_exports; /* How many buffer exports */ | ||
| PyObject *ob_bytes_object; /* PyBytes for zero-copy bytes conversion */ | ||
cmaloney marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } PyByteArrayObject; | ||
| PyAPI_DATA(char) _PyByteArray_empty_string[]; | ||
cmaloney marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /* Macros and static inline functions, trading safety for speed */ | ||
| #define _PyByteArray_CAST(op) \ | ||
| (assert(PyByteArray_Check(op)), _Py_CAST(PyByteArrayObject*, op)) | ||
| static inline char* PyByteArray_AS_STRING(PyObject *op) | ||
| { | ||
| PyByteArrayObject *self = _PyByteArray_CAST(op); | ||
| if (Py_SIZE(self)) { | ||
| return self->ob_start; | ||
| } | ||
| return _PyByteArray_empty_string; | ||
| return _PyByteArray_CAST(op)->ob_start; | ||
| } | ||
| #define PyByteArray_AS_STRING(self) PyByteArray_AS_STRING(_PyObject_CAST(self)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1397,6 +1397,16 @@ def test_clear(self): | ||
| b.append(ord('p')) | ||
| self.assertEqual(b, b'p') | ||
| # Cleared object should be empty. | ||
| b = bytearray(b'abc') | ||
| b.clear() | ||
| self.assertEqual(b.__alloc__(), 0) | ||
| base_size = sys.getsizeof(bytearray()) | ||
| self.assertEqual(sys.getsizeof(b), base_size) | ||
| c = b.copy() | ||
| self.assertEqual(c.__alloc__(), 0) | ||
| self.assertEqual(sys.getsizeof(c), base_size) | ||
| def test_copy(self): | ||
| b = bytearray(b'abc') | ||
| bb = b.copy() | ||
| @@ -1458,6 +1468,61 @@ def test_resize(self): | ||
| self.assertRaises(MemoryError, bytearray().resize, sys.maxsize) | ||
| self.assertRaises(MemoryError, bytearray(1000).resize, sys.maxsize) | ||
| def test_take_bytes(self): | ||
| ba = bytearray(b'ab') | ||
| self.assertEqual(ba.take_bytes(), b'ab') | ||
| self.assertEqual(len(ba), 0) | ||
| self.assertEqual(ba, bytearray(b'')) | ||
| self.assertEqual(ba.__alloc__(), 0) | ||
cmaloney marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| base_size = sys.getsizeof(bytearray()) | ||
| self.assertEqual(sys.getsizeof(ba), base_size) | ||
| # Positive and negative slicing. | ||
| ba = bytearray(b'abcdef') | ||
| self.assertEqual(ba.take_bytes(1), b'a') | ||
| self.assertEqual(ba, bytearray(b'bcdef')) | ||
| self.assertEqual(len(ba), 5) | ||
| self.assertEqual(ba.take_bytes(-5), b'') | ||
| self.assertEqual(ba, bytearray(b'bcdef')) | ||
| self.assertEqual(len(ba), 5) | ||
| self.assertEqual(ba.take_bytes(-3), b'bc') | ||
| self.assertEqual(ba, bytearray(b'def')) | ||
| self.assertEqual(len(ba), 3) | ||
| self.assertEqual(ba.take_bytes(3), b'def') | ||
| self.assertEqual(ba, bytearray(b'')) | ||
| self.assertEqual(len(ba), 0) | ||
| # Take nothing from emptiness. | ||
| self.assertEqual(ba.take_bytes(0), b'') | ||
| self.assertEqual(ba.take_bytes(), b'') | ||
| self.assertEqual(ba.take_bytes(None), b'') | ||
| # Out of bounds, bad take value. | ||
| self.assertRaises(IndexError, ba.take_bytes, -1) | ||
| self.assertRaises(TypeError, ba.take_bytes, 3.14) | ||
| ba = bytearray(b'abcdef') | ||
| self.assertRaises(IndexError, ba.take_bytes, 7) | ||
| # Offset between physical and logical start (ob_bytes != ob_start). | ||
| ba = bytearray(b'abcde') | ||
| del ba[:2] | ||
| self.assertEqual(ba, bytearray(b'cde')) | ||
| self.assertEqual(ba.take_bytes(), b'cde') | ||
| # Overallocation at end. | ||
| ba = bytearray(b'abcde') | ||
| del ba[-2:] | ||
| self.assertEqual(ba, bytearray(b'abc')) | ||
| self.assertEqual(ba.take_bytes(), b'abc') | ||
| ba = bytearray(b'abcde') | ||
| ba.resize(4) | ||
| self.assertEqual(ba.take_bytes(), b'abcd') | ||
| # Take of a bytearray with references should fail. | ||
| ba = bytearray(b'abc') | ||
| with memoryview(ba) as mv: | ||
| self.assertRaises(BufferError, ba.take_bytes) | ||
| self.assertEqual(ba.take_bytes(), b'abc') | ||
cmaloney marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| def test_setitem(self): | ||
| def setitem_as_mapping(b, i, val): | ||
| @@ -2564,6 +2629,18 @@ def zfill(b, a): | ||
| c = a.zfill(0x400000) | ||
| assert not c or c[-1] not in (0xdd, 0xcd) | ||
| def take_bytes(b, a): # MODIFIES! | ||
| b.wait() | ||
| c = a.take_bytes() | ||
| assert not c or c[0] == 48 # '0' | ||
| def take_bytes_n(b, a): # MODIFIES! | ||
| b.wait() | ||
| try: | ||
| c = a.take_bytes(10) | ||
| assert c == b'0123456789' | ||
| except IndexError: pass | ||
| def check(funcs, a=None, *args): | ||
| if a is None: | ||
| a = bytearray(b'0' * 0x400000) | ||
| @@ -2625,6 +2702,10 @@ def check(funcs, a=None, *args): | ||
| check([clear] + [startswith] * 10) | ||
| check([clear] + [strip] * 10) | ||
| check([clear] + [take_bytes] * 10) | ||
encukou marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| check([take_bytes_n] * 10, bytearray(b'0123456789' * 0x400)) | ||
| check([take_bytes_n] * 10, bytearray(b'0123456789' * 5)) | ||
| check([clear] + [contains] * 10) | ||
| check([clear] + [subscript] * 10) | ||
| check([clear2] + [ass_subscript2] * 10, None, bytearray(b'0' * 0x400000)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Update :class:`bytearray` to use a :class:`bytes` under the hood as its buffer | ||
| and add :func:`bytearray.take_bytes` to take it out. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.