-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
gh-125543: Add an internal C API for dynamic arrays #125554
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
Closed
Closed
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
111fb4b
Setup build.
ZeroIntensity 6e305d1
Simple implementation.
ZeroIntensity 210d4d2
Add to header file.
ZeroIntensity 2c77397
Add convenience macros.
ZeroIntensity d90329a
Add docstrings and some sanity checks.
ZeroIntensity 3173c08
Add some simple tests.
ZeroIntensity 6a8f026
Clean up tests.
ZeroIntensity 84a922a
Add a test for deallocators.
ZeroIntensity caf037c
Boy oh boy do I hate the Windows build system.
ZeroIntensity 14e46a4
Correction: I hate all build systems.
ZeroIntensity be1de70
Add set and remove operations.
ZeroIntensity db5adbf
Add tests for set and remove.
ZeroIntensity aad50d8
Update docstrings.
ZeroIntensity 0ecc65f
Add pop.
ZeroIntensity 6a46733
Add tests for pop.
ZeroIntensity 4d732dc
Add insert.
ZeroIntensity 95c7753
Add insertion.
ZeroIntensity 51664b8
Add more heap tests and fix insertion.
ZeroIntensity b2be58f
Don't use GCC extension.
ZeroIntensity a24fec2
> instead of >=
Eclips4 23902f9
Fix behavior upon allocation failure.
ZeroIntensity 86f78c6
Remove useless comment.
ZeroIntensity File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
#ifndef Py_INTERNAL_DYNARRAY_H | ||
#define Py_INTERNAL_DYNARRAY_H | ||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#include "Python.h" // Py_ssize_t | ||
|
||
#ifndef Py_BUILD_CORE | ||
# error "this header requires Py_BUILD_CORE define" | ||
#endif | ||
|
||
#define _PyDynArray_DEFAULT_SIZE 16 | ||
|
||
/* | ||
* Deallocator for items on a _PyDynArray structure. A NULL pointer | ||
* will never be given to the deallocator. | ||
*/ | ||
typedef void (*_PyDynArray_Deallocator)(void *); | ||
|
||
/* | ||
* Internal only dynamic array for CPython. | ||
*/ | ||
typedef struct { | ||
/* | ||
* The actual items in the dynamic array. | ||
* Don't access this field publicly to get | ||
* items--use _PyDynArray_GET_ITEM() instead. | ||
*/ | ||
void **items; | ||
/* | ||
* The length of the actual items array allocation. | ||
*/ | ||
Py_ssize_t capacity; | ||
/* | ||
* The number of items in the array. | ||
* Don't use this field publicly--use _PyDynArray_LENGTH() | ||
*/ | ||
Py_ssize_t length; | ||
/* | ||
* The deallocator, set by one of the initializer functions. | ||
* This may be NULL. | ||
*/ | ||
_PyDynArray_Deallocator deallocator; | ||
} _PyDynArray; | ||
|
||
|
||
static inline void | ||
_PyDynArray_ASSERT_VALID(_PyDynArray *array) | ||
{ | ||
assert(array != NULL); | ||
assert(array->items != NULL); | ||
} | ||
|
||
static inline void | ||
_PyDynArray_ASSERT_INDEX(_PyDynArray *array, Py_ssize_t index) | ||
{ | ||
// Ensure the index is valid | ||
assert(index >= 0); | ||
assert(index < array->length); | ||
} | ||
|
||
/* | ||
* Initialize a dynamic array with an initial size and deallocator. | ||
* | ||
* If the deallocator is NULL, then nothing happens to items upon | ||
* removal and upon array clearing. | ||
* | ||
* Returns -1 upon failure, 0 otherwise. | ||
*/ | ||
PyAPI_FUNC(int) | ||
_PyDynArray_InitWithSize(_PyDynArray *array, | ||
_PyDynArray_Deallocator deallocator, | ||
Py_ssize_t initial); | ||
|
||
/* | ||
* Append to the array. | ||
* | ||
* Returns -1 upon failure, 0 otherwise. | ||
* If this fails, the deallocator is not ran on the item. | ||
*/ | ||
PyAPI_FUNC(int) _PyDynArray_Append(_PyDynArray *array, void *item); | ||
|
||
/* | ||
* Insert an item at the target index. The index | ||
* must currently be a valid index in the array. | ||
* | ||
* Returns -1 upon failure, 0 otherwise. | ||
* If this fails, the deallocator is not ran on the item. | ||
*/ | ||
PyAPI_FUNC(int) | ||
_PyDynArray_Insert(_PyDynArray *array, Py_ssize_t index, void *item); | ||
|
||
|
||
/* | ||
* Clear all the fields on the array. | ||
* | ||
* Note that this does *not* free the actual dynamic array | ||
* structure--use _PyDynArray_Free() for that. | ||
* | ||
* It's safe to call _PyDynArray_Init() or InitWithSize() again | ||
* on the array after calling this. | ||
*/ | ||
PyAPI_FUNC(void) _PyDynArray_Clear(_PyDynArray *array); | ||
|
||
/* | ||
* Set a value at index in the array. | ||
* | ||
* If an item already exists at the target index, the deallocator | ||
* is called on it, if the array has one set. | ||
* | ||
* This cannot fail. | ||
*/ | ||
PyAPI_FUNC(void) | ||
_PyDynArray_Set(_PyDynArray *array, Py_ssize_t index, void *item); | ||
|
||
/* | ||
* Remove the item at the index, and call the deallocator on it (if the array | ||
* has one set). | ||
* | ||
* This cannot fail. | ||
*/ | ||
PyAPI_FUNC(void) | ||
_PyDynArray_Remove(_PyDynArray *array, Py_ssize_t index); | ||
|
||
/* | ||
* Remove the item at the index *without* deallocating it, and | ||
* return the item. | ||
* | ||
* This cannot fail. | ||
*/ | ||
PyAPI_FUNC(void *) | ||
_PyDynArray_Pop(_PyDynArray *array, Py_ssize_t index); | ||
|
||
/* | ||
* Clear all the fields on a dynamic array, and then | ||
* free the dynamic array structure itself. | ||
* | ||
* The array must have been created by _PyDynArray_New() | ||
*/ | ||
static inline void | ||
_PyDynArray_Free(_PyDynArray *array) | ||
{ | ||
_PyDynArray_ASSERT_VALID(array); | ||
_PyDynArray_Clear(array); | ||
PyMem_RawFree(array); | ||
} | ||
|
||
/* | ||
* Equivalent to _PyDynArray_InitWithSize() with a default size of 16. | ||
* | ||
* Returns -1 upon failure, 0 otherwise. | ||
*/ | ||
static inline int | ||
_PyDynArray_Init(_PyDynArray *array, _PyDynArray_Deallocator deallocator) | ||
{ | ||
return _PyDynArray_InitWithSize(array, deallocator, _PyDynArray_DEFAULT_SIZE); | ||
} | ||
|
||
/* | ||
* Allocate and create a new dynamic array on the heap. | ||
* | ||
* The returned pointer should be freed with _PyDynArray_Free() | ||
* If this function fails, it returns NULL. | ||
*/ | ||
static inline _PyDynArray * | ||
_PyDynArray_NewWithSize(_PyDynArray_Deallocator deallocator, Py_ssize_t initial) | ||
{ | ||
_PyDynArray *array = PyMem_RawMalloc(sizeof(_PyDynArray)); | ||
if (array == NULL) | ||
{ | ||
return NULL; | ||
} | ||
|
||
if (_PyDynArray_InitWithSize(array, deallocator, initial) < 0) | ||
{ | ||
PyMem_RawFree(array); | ||
return NULL; | ||
} | ||
|
||
_PyDynArray_ASSERT_VALID(array); // Sanity check | ||
return array; | ||
} | ||
|
||
/* | ||
* Equivalent to _PyDynArray_NewWithSize() with a size of 16. | ||
* | ||
* The returned array must be freed with _PyDynArray_Free(). | ||
* Returns NULL on failure. | ||
*/ | ||
static inline _PyDynArray * | ||
_PyDynArray_New(_PyDynArray_Deallocator deallocator) | ||
{ | ||
return _PyDynArray_NewWithSize(deallocator, _PyDynArray_DEFAULT_SIZE); | ||
} | ||
|
||
/* | ||
* Get an item from the array. This cannot fail. | ||
* | ||
* If the index is not valid, this is undefined behavior. | ||
*/ | ||
static inline void * | ||
_PyDynArray_GET_ITEM(_PyDynArray *array, Py_ssize_t index) | ||
{ | ||
_PyDynArray_ASSERT_VALID(array); | ||
_PyDynArray_ASSERT_INDEX(array, index); | ||
return array->items[index]; | ||
} | ||
|
||
/* | ||
* Get the length of the array. This cannot fail. | ||
*/ | ||
static inline Py_ssize_t | ||
_PyDynArray_LENGTH(_PyDynArray *array) | ||
{ | ||
_PyDynArray_ASSERT_VALID(array); | ||
return array->length; | ||
} | ||
|
||
/* | ||
* Pop the item at the end the array. | ||
* This function cannot fail. | ||
*/ | ||
static inline void * | ||
_PyDynArray_PopTop(_PyDynArray *array) | ||
{ | ||
return _PyDynArray_Pop(array, _PyDynArray_LENGTH(array) - 1); | ||
} | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
#endif /* !Py_INTERNAL_DYNARRAY_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.