Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions frozenlist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,37 @@

@property
def frozen(self):
"""A read-only property, ``True`` if the list is *frozen* (modifications are forbidden)."""
return self._frozen

def freeze(self):
"""Freeze the list. There is no way to *thaw* it back."""
self._frozen = True

# def __mul__(self, value):
# return self.__class__(self._items.__mul__(value))
Comment on lines +36 to +37

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

def __imul__(self, value):

Check notice

Code scanning / CodeQL

Non-standard exception raised in special method Note

This method raises
RuntimeError
- should raise an ArithmeticError or return NotImplemented instead.
if self._frozen:
raise RuntimeError("Cannot modify frozen list.")
self._items.__imul__(value)
return self

# def __rmul__(self, value):
# return self.__class__(self._items.__rmul__(value))

# def __add__(self, value):
# return self.__class__(self._items.__add__(value))
Comment on lines +45 to +49

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

def __contains__(self, value):
return self._items.__contains__(value)

def __iadd__(self, values):

Check notice

Code scanning / CodeQL

Non-standard exception raised in special method Note

This method raises
RuntimeError
- should raise an ArithmeticError or return NotImplemented instead.
if self._frozen:
raise RuntimeError("Cannot modify frozen list.")
self._items.__iadd__(values)
return self

def __getitem__(self, index):
return self._items[index]

Expand Down
62 changes: 62 additions & 0 deletions frozenlist/_frozenlib/compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#ifndef __COMPAT_H__
#define __COMPAT_H__

#include <Python.h>

#ifdef __cplusplus
extern "C" {
#endif

/* Exposed public version of _PyArg_CheckPositional
to preverse compatability for normal python list exceptions */
int
PyArg_CheckPositional(const char *name, Py_ssize_t nargs,
Py_ssize_t min, Py_ssize_t max)
{
assert(min >= 0);
assert(min <= max);

if (nargs < min) {
if (name != NULL)
PyErr_Format(
PyExc_TypeError,
"%.200s expected %s%zd argument%s, got %zd",
name, (min == max ? "" : "at least "), min, min == 1 ? "" : "s", nargs);
else
PyErr_Format(
PyExc_TypeError,
"unpacked tuple should have %s%zd element%s,"
" but has %zd",
(min == max ? "" : "at least "), min, min == 1 ? "" : "s", nargs);
return 0;
}

if (nargs == 0) {
return 1;
}

if (nargs > max) {
if (name != NULL)
PyErr_Format(
PyExc_TypeError,
"%.200s expected %s%zd argument%s, got %zd",
name, (min == max ? "" : "at most "), max, max == 1 ? "" : "s", nargs);
else
PyErr_Format(
PyExc_TypeError,
"unpacked tuple should have %s%zd element%s,"
" but has %zd",
(min == max ? "" : "at most "), max, max == 1 ? "" : "s", nargs);
return 0;
}

return 1;
}



#ifdef __cplusplus
}
#endif

#endif // __COMPAT_H__
111 changes: 111 additions & 0 deletions frozenlist/_frozenlib/fl_atomics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#ifndef __FL_ATOMICS_H__
#define __FL_ATOMICS_H__

/* from pyatomic_std.h & pyatomic_msvc.h, pyatomic_gcc.h for backwards & public compatability
and combined with only required or needed functions */

#include <stdint.h>

#ifndef _FL_USE_GCC_BUILTIN_ATOMICS
# if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
# define _FL_USE_GCC_BUILTIN_ATOMICS 1
# elif defined(__clang__)
# if __has_builtin(__atomic_load)
# define _FL_USE_GCC_BUILTIN_ATOMICS 1
# endif
# endif
#endif

static inline void atomic_store_uint8(uint8_t* obj, uint8_t value);
static inline int atomic_compare_exchange_uint8(uint8_t *obj, uint8_t *expected, uint8_t desired);

/* Last one is used by newer python lists that are Free-Threaded 3.14+ */


#if _FL_USE_GCC_BUILTIN_ATOMICS
/* GCC OR CLANG */
static inline uint8_t
atomic_load_uint8(const uint8_t *obj)
{ return __atomic_load_n(obj, __ATOMIC_SEQ_CST); }

static inline int
atomic_compare_exchange_uint8(uint8_t *obj, uint8_t *expected, uint8_t desired)
{ return __atomic_compare_exchange_n(obj, expected, desired, 0,
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); }

static inline void
atomic_store_uint8(uint8_t *obj, uint8_t value)
{ __atomic_store_n(obj, value, __ATOMIC_SEQ_CST); }

#elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
/* STANDARD LIBRARY */
#ifdef __cplusplus
extern "C++" {
# include <atomic>
}
# define _FL_USING_STD using namespace std
# define _Atomic(tp) atomic<tp>
#else
# define _FL_USING_STD
# include <stdatomic.h>
#endif
static inline int
atomic_compare_exchange_uint8(uint8_t *obj, uint8_t *expected, uint8_t desired)
{
_FL_USING_STD;
return atomic_compare_exchange_strong((_Atomic(uint8_t)*)obj,
expected, desired);
}
static inline uint8_t
atomic_load_uint8(const uint8_t *obj)
{
_FL_USING_STD;
return atomic_load((const _Atomic(uint8_t)*)obj);
}
static inline void
atomic_store_uint8(uint8_t *obj, uint8_t value){
_FL_USING_STD;
atomic_store((_Atomic(uint8_t)*)obj, value);
}


#elif defined(_MSC_VER)
/* WINDOWS */
#include <intrin.h>

static inline int
atomic_compare_exchange_uint8(int8_t *obj, uint8_t *expected, uint8_t value)
{
uint8_t initial = (uint8_t)_InterlockedCompareExchange8(
(volatile char *)obj,
(char)value,
(char)*expected);
if (initial == *expected) {
return 1;
}
*expected = initial;
return 0;
}
static inline uint8_t
atomic_load_uint8(const uint8_t *obj)
{
#if defined(_M_X64) || defined(_M_IX86)
return *(volatile uint8_t *)obj;
#elif defined(_M_ARM64)
return (uint8_t)__ldar8((unsigned __int8 volatile *)obj);
#else
# error "no implementation of atomic_load_uint8"
#endif
}
static inline void atomic_store_uint8(uint8_t *obj, const uint8_t value){
return (void)_InterlockedExchange8((volatile char *)obj, (char)value);
}


#else
# error "no available atomic implementation for this platform/compiler"
#endif



#endif // __FL_ATOMICS_H__
128 changes: 128 additions & 0 deletions frozenlist/_frozenlib/frozenlist_object.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#ifndef __FROZENLIST_OBJECT_H__
#define __FROZENLIST_OBJECT_H__

#include <stddef.h> /* size_t */
#include "fl_atomics.h"
#include "state.h"
#include "pythoncapi_compat.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <Python.h>


/* TODO: take in PyList's attributes instead of using the PyList when an approch for
Dealing with Py_GIL_DISABLED can be fully agreed on */

/* Can't let _frozenlistobject subclass PyList because in the CPython API
PyList would overpower freezing of the object */
#if PY_VERSION_HEX >= 0x030c00f0
#define MANAGED_WEAKREFS
#endif

typedef struct _frozenlistobject {
PyObject_HEAD;
#ifndef MANAGED_WEAKREFS
PyObject *weaklist;
#endif
PyObject* _items;
uint8_t _frozen; /* C-Atomic variable */
} FrozenListObject;

/* Macros */
#define FrozenList_CAST(op) ((FrozenListObject*)op)
#define FrozenList_GET_SIZE(op) PyList_GET_SIZE(op->_items)
#define FrozenList_GET_ITEM(op, i) PyList_GET_ITEM(op->_items, i)
#define FrozenList_SET_ITEM(op, i, v) PyList_SET_ITEM(op->_items, i, v)

/* Small optimization for checking weather or not an index is valid... */
#define FrozenList_IS_VALID_INDEX(op, i) \
((size_t)(i) < (size_t)(FrozenList_GET_SIZE(op)))

#define FrozenList_CHECK_FROZEN(op) atomic_load_uint8((op->_frozen))

/* Kept around to make mergeing to a CPython Module a bit easier
There will not be a C-API Capsule however as there's a possibility
it breaks ABI Changes. https://github.com/aio-libs/frozenlist/pull/685
Maybe this idea will be changed in the future but for now IDK Everything is
Questionable at the moment :/ - Vizonex
*/

/* NOTE: Refs are handled in the main C Module */

static inline int
fl_check_frozen(FrozenListObject* self){
if (atomic_load_uint8(&(self->_frozen))){
PyErr_SetString(PyExc_RuntimeError, "Cannot modify frozen list.");
return -1;
}
return 0;
}



// static inline PyObject*
// fl_get_item(FrozenListObject* self, Py_ssize_t i){
// if (!FrozenList_IS_VALID_INDEX(self, i)){
// PyErr_SetString(PyExc_IndexError, "list index out of range");
// return NULL;
// }
// return Py_NewRef(FrozenList_GET_ITEM(self, i));
// }

// static inline int
// fl_set_item(FrozenListObject* self, Py_ssize_t i, PyObject* v){
// if (fl_check_frozen(self) < 0){
// return -1;
// }
// if (!FrozenList_IS_VALID_INDEX(self, i)){
// PyErr_SetString(PyExc_IndexError, "list assignment index out of range");
// return -1;
// }
// FrozenList_SET_ITEM(self, i, v);
// return 0;
// }

// static inline int fl_append(FrozenListObject* self, PyObject* v){
// return (fl_check_frozen(self) < 0) ? -1: PyList_Append(self->_items, v);
// }

// static inline Py_ssize_t fl_index(FrozenListObject* self, PyObject* item){
// return PySequence_Index(self->_items, item);
// }

// static inline int fl__iadd__(FrozenListObject* self, PyObject* items){
// if (fl_check_frozen(self) < 0){
// return -1;
// }
// PyObject* items = PySequence_InPlaceConcat(self->_items, items);
// if (items == NULL){
// return -1;
// }
// self->_items = items;
// return 0;
// }

// static inline PyObject* fl_get_slice(FrozenListObject* self, Py_ssize_t start, Py_ssize_t end){
// return PyList_GetSlice(self->_items, start, end);
// }

// static int fl_insert(FrozenListObject* self, Py_ssize_t index, PyObject* value){
// return (fl_check_frozen(self) < 0) ? -1 : PyList_Insert(self->_items, index, value);
// }

// static int fl_set_slice(FrozenListObject* self, Py_ssize_t start, Py_ssize_t end, PyObject* value){
// return (fl_check_frozen(self) < 0) ? -1 : PyList_SetSlice(self->_items, start, end, value);
// }

// static int fl_del_item(FrozenListObject* self, Py_ssize_t index){
// return (fl_check_frozen(self) < 0) ? -1 : PySequence_DelItem(self->_items, index);
// }

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif // __FROZENLIST_OBJECT_H__
Loading
Loading