feat: indie status page MVP -- FastAPI + SQLite

- 8 DB models (services, incidents, monitors, subscribers, etc.)
- Full CRUD API for services, incidents, monitors
- Public status page with live data
- Incident detail page with timeline
- API key authentication
- Uptime monitoring scheduler
- 13 tests passing
- TECHNICAL_DESIGN.md with full spec
This commit is contained in:
IndieStatusBot 2026-04-25 05:00:00 +00:00
commit 902133edd3
4655 changed files with 1342691 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,873 @@
#ifndef VEC_H_INCL
#define VEC_H_INCL
// Header for the implementation of librt.vecs, which defines the 'vec' type.
// Refer to librt_vecs.c for more detailed information.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdint.h>
#ifndef MYPYC_EXPERIMENTAL
static int
import_librt_vecs(void)
{
// All librt.vecs features are experimental for now, so don't set up the API here
return 0;
}
#else // MYPYC_EXPERIMENTAL
// Magic (native) integer return value on exception. Caller must also
// use PyErr_Occurred() since this overlaps with valid integer values.
#define MYPYC_INT_ERROR -113
// Item type constants for supported packed/specialized item types; must be
// even but not a multiple of 4 (2 + 4 * n). Each of these has a corresponding
// distinct implementation C extension class. For example, vec[i64] has a
// different runtime type than vec[i32]. All other item types use generic
// implementations.
#define VEC_ITEM_TYPE_I64 2
#define VEC_ITEM_TYPE_I32 6
#define VEC_ITEM_TYPE_I16 10
#define VEC_ITEM_TYPE_U8 14
#define VEC_ITEM_TYPE_FLOAT 18
#define VEC_ITEM_TYPE_BOOL 22
static inline size_t Vec_IsMagicItemType(size_t item_type) {
return item_type & 2;
}
// Buffer objects
// vecbuf[i64]
typedef struct _VecI64BufObject {
PyObject_VAR_HEAD
int64_t items[1];
} VecI64BufObject;
// vecbuf[i32]
typedef struct _VecI32BufObject {
PyObject_VAR_HEAD
int32_t items[1];
} VecI32BufObject;
// vecbuf[i16]
typedef struct _VecI16BufObject {
PyObject_VAR_HEAD
int16_t items[1];
} VecI16BufObject;
// vecbuf[u8]
typedef struct _VecU8BufObject {
PyObject_VAR_HEAD
uint8_t items[1];
} VecU8BufObject;
// vecbuf[float]
typedef struct _VecFloatBufObject {
PyObject_VAR_HEAD
double items[1];
} VecFloatBufObject;
// vecbuf[bool]
typedef struct _VecBoolBufObject {
PyObject_VAR_HEAD
char items[1];
} VecBoolBufObject;
// Simple generic vecbuf: vecbuf[t] when t is a type object
typedef struct _VecTBufObject {
PyObject_VAR_HEAD
// Tagged pointer to PyTypeObject *. The lowest bit is 1 for optional item type.
size_t item_type;
PyObject *items[1];
} VecTBufObject;
typedef struct _VecNestedBufItem {
Py_ssize_t len;
PyObject *buf;
} VecNestedBufItem;
// Nested vec type: vec[vec[...]], vec[vec[...] | None], etc.
typedef struct _VecNestedBufObject {
PyObject_VAR_HEAD
// Tagged pointer to PyTypeObject *. Lowest bit is set for optional item type.
// The second lowest bit is set for a packed item type (VEC_ITEM_TYPE_*).
size_t item_type;
// Number of nested vec types (of any kind, at least 1)
size_t depth;
VecNestedBufItem items[1];
} VecNestedBufObject;
// Unboxed vec objects
typedef struct _VecI64 {
Py_ssize_t len;
VecI64BufObject *buf;
} VecI64;
typedef struct _VecI32 {
Py_ssize_t len;
VecI32BufObject *buf;
} VecI32;
typedef struct _VecI16 {
Py_ssize_t len;
VecI16BufObject *buf;
} VecI16;
typedef struct _VecU8 {
Py_ssize_t len;
VecU8BufObject *buf;
} VecU8;
typedef struct _VecFloat {
Py_ssize_t len;
VecFloatBufObject *buf;
} VecFloat;
typedef struct _VecBool {
Py_ssize_t len;
VecBoolBufObject *buf;
} VecBool;
typedef struct _VecT {
Py_ssize_t len;
VecTBufObject *buf;
} VecT;
typedef struct _VecNested {
Py_ssize_t len;
VecNestedBufObject *buf;
} VecNested;
// Boxed vec objects
// Arbitrary boxed vec object (only shared bits)
typedef struct _VecObject {
PyObject_HEAD
Py_ssize_t len;
} VecObject;
// Base vec type object (for isinstance checks)
// This is an abstract base type that all specialized vec types inherit from.
// It cannot be instantiated directly - only used for isinstance(x, vec).
typedef struct _VecBaseObject {
PyObject_HEAD
} VecBaseObject;
// Boxed vec[i64]
typedef struct _VecI64Object {
PyObject_HEAD
VecI64 vec;
} VecI64Object;
// Boxed vec[i32]
typedef struct _VecI32Object {
PyObject_HEAD
VecI32 vec;
} VecI32Object;
// Boxed vec[i16]
typedef struct _VecI16Object {
PyObject_HEAD
VecI16 vec;
} VecI16Object;
// Boxed vec[u8]
typedef struct _VecU8Object {
PyObject_HEAD
VecU8 vec;
} VecU8Object;
// Boxed vec[float]
typedef struct _VecFloatObject {
PyObject_HEAD
VecFloat vec;
} VecFloatObject;
// Boxed vec[bool]
typedef struct _VecBoolObject {
PyObject_HEAD
VecBool vec;
} VecBoolObject;
// Simple boxed generic vecbuf: vecbuf[t] when t is a type object
typedef struct _VecTObject {
PyObject_HEAD
VecT vec;
} VecTObject;
// Extended generic vec type: vec[t | None], vec[vec[...]], etc.
typedef struct _VecNestedObject {
PyObject_HEAD
VecNested vec;
} VecNestedObject;
#ifndef MYPYC_DECLARED_tuple_T2V88
#define MYPYC_DECLARED_tuple_T2V88
typedef struct tuple_T2V88 {
VecI64 f0;
int64_t f1;
} tuple_T2V88;
static tuple_T2V88 tuple_undefined_T2V88 = { { -1, NULL } , 0 };
#endif
#ifndef MYPYC_DECLARED_tuple_T2V44
#define MYPYC_DECLARED_tuple_T2V44
typedef struct tuple_T2V44 {
VecI32 f0;
int32_t f1;
} tuple_T2V44;
static tuple_T2V44 tuple_undefined_T2V44 = { { -1, NULL } , 0 };
#endif
#ifndef MYPYC_DECLARED_tuple_T2V22
#define MYPYC_DECLARED_tuple_T2V22
typedef struct tuple_T2V22 {
VecI16 f0;
int16_t f1;
} tuple_T2V22;
static tuple_T2V22 tuple_undefined_T2V22 = { { -1, NULL } , 0 };
#endif
#ifndef MYPYC_DECLARED_tuple_T2VU1U1
#define MYPYC_DECLARED_tuple_T2VU1U1
typedef struct tuple_T2VU1U1 {
VecU8 f0;
uint8_t f1;
} tuple_T2VU1U1;
static tuple_T2VU1U1 tuple_undefined_T2VU1U1 = { { -1, NULL } , 0 };
#endif
#ifndef MYPYC_DECLARED_tuple_T2VFF
#define MYPYC_DECLARED_tuple_T2VFF
typedef struct tuple_T2VFF {
VecFloat f0;
double f1;
} tuple_T2VFF;
static tuple_T2VFF tuple_undefined_T2VFF = { { -1, NULL } , 0.0 };
#endif
#ifndef MYPYC_DECLARED_tuple_T2VCC
#define MYPYC_DECLARED_tuple_T2VCC
typedef struct tuple_T2VCC {
VecBool f0;
char f1;
} tuple_T2VCC;
static tuple_T2VCC tuple_undefined_T2VCC = { { -1, NULL } , 0 };
#endif
typedef tuple_T2V88 VecI64PopResult;
typedef tuple_T2V44 VecI32PopResult;
typedef tuple_T2V22 VecI16PopResult;
typedef tuple_T2VU1U1 VecU8PopResult;
typedef tuple_T2VFF VecFloatPopResult;
typedef tuple_T2VCC VecBoolPopResult;
// vec[i64] operations + type objects (stored in a capsule)
typedef struct _VecI64API {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecI64 (*alloc)(Py_ssize_t, Py_ssize_t);
PyObject *(*box)(VecI64);
VecI64 (*unbox)(PyObject *);
VecI64 (*convert_from_nested)(VecNestedBufItem);
VecI64 (*append)(VecI64, int64_t);
VecI64PopResult (*pop)(VecI64, Py_ssize_t);
VecI64 (*remove)(VecI64, int64_t);
// TODO: Py_ssize_t
VecI64 (*slice)(VecI64, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, int64_t);
// iter?
} VecI64API;
// vec[i32] operations + type objects (stored in a capsule)
typedef struct _VecI32API {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecI32 (*alloc)(Py_ssize_t, Py_ssize_t);
PyObject *(*box)(VecI32);
VecI32 (*unbox)(PyObject *);
VecI32 (*convert_from_nested)(VecNestedBufItem);
VecI32 (*append)(VecI32, int32_t);
VecI32PopResult (*pop)(VecI32, Py_ssize_t);
VecI32 (*remove)(VecI32, int32_t);
// TODO: Py_ssize_t
VecI32 (*slice)(VecI32, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, int32_t);
// iter?
} VecI32API;
// vec[i16] operations + type objects (stored in a capsule)
typedef struct _VecI16API {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecI16 (*alloc)(Py_ssize_t, Py_ssize_t);
PyObject *(*box)(VecI16);
VecI16 (*unbox)(PyObject *);
VecI16 (*convert_from_nested)(VecNestedBufItem);
VecI16 (*append)(VecI16, int16_t);
VecI16PopResult (*pop)(VecI16, Py_ssize_t);
VecI16 (*remove)(VecI16, int16_t);
// TODO: Py_ssize_t
VecI16 (*slice)(VecI16, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, int16_t);
// iter?
} VecI16API;
// vec[u8] operations + type objects (stored in a capsule)
typedef struct _VecU8API {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecU8 (*alloc)(Py_ssize_t, Py_ssize_t);
PyObject *(*box)(VecU8);
VecU8 (*unbox)(PyObject *);
VecU8 (*convert_from_nested)(VecNestedBufItem);
VecU8 (*append)(VecU8, uint8_t);
VecU8PopResult (*pop)(VecU8, Py_ssize_t);
VecU8 (*remove)(VecU8, uint8_t);
// TODO: Py_ssize_t
VecU8 (*slice)(VecU8, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, uint8_t);
// iter?
} VecU8API;
// vec[float] operations + type objects (stored in a capsule)
typedef struct _VecFloatAPI {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecFloat (*alloc)(Py_ssize_t, Py_ssize_t);
PyObject *(*box)(VecFloat);
VecFloat (*unbox)(PyObject *);
VecFloat (*convert_from_nested)(VecNestedBufItem);
VecFloat (*append)(VecFloat, double);
VecFloatPopResult (*pop)(VecFloat, Py_ssize_t);
VecFloat (*remove)(VecFloat, double);
// TODO: Py_ssize_t
VecFloat (*slice)(VecFloat, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, double);
// iter?
} VecFloatAPI;
// vec[bool] operations + type objects (stored in a capsule)
typedef struct _VecBoolAPI {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecBool (*alloc)(Py_ssize_t, Py_ssize_t);
PyObject *(*box)(VecBool);
VecBool (*unbox)(PyObject *);
VecBool (*convert_from_nested)(VecNestedBufItem);
VecBool (*append)(VecBool, char);
VecBoolPopResult (*pop)(VecBool, Py_ssize_t);
VecBool (*remove)(VecBool, char);
// TODO: Py_ssize_t
VecBool (*slice)(VecBool, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, char);
// iter?
} VecBoolAPI;
#ifndef MYPYC_DECLARED_tuple_T2VOO
#define MYPYC_DECLARED_tuple_T2VOO
typedef struct tuple_T2VOO {
VecT f0;
PyObject *f1;
} tuple_T2VOO;
static tuple_T2VOO tuple_undefined_T2VOO = { { -1, NULL } , NULL };
#endif
typedef tuple_T2VOO VecTPopResult;
// vec[T] operations + type objects (stored in a capsule)
//
// T is a class type or class type | None
typedef struct _VecTAPI {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecT (*alloc)(Py_ssize_t, Py_ssize_t, size_t);
PyObject *(*box)(VecT, size_t);
VecT (*unbox)(PyObject *, size_t);
VecT (*convert_from_nested)(VecNestedBufItem);
VecT (*append)(VecT, PyObject *, size_t);
VecTPopResult (*pop)(VecT, Py_ssize_t);
VecT (*remove)(VecT, PyObject *);
// TODO: Py_ssize_t
VecT (*slice)(VecT, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, PyObject *);
// iter?
} VecTAPI;
#ifndef MYPYC_DECLARED_tuple_T2VvVi
#define MYPYC_DECLARED_tuple_T2VvVi
typedef struct tuple_T2VvVi {
VecNested f0;
VecNestedBufItem f1;
} tuple_T2VvVi;
static tuple_T2VvVi tuple_undefined_T2VvVi = { { -1, NULL } , { -1, NULL } };
#endif
typedef tuple_T2VvVi VecNestedPopResult;
// Nested vec operations + type objects (stored in a capsule)
typedef struct _VecNestedAPI {
PyTypeObject *boxed_type;
PyTypeObject *buf_type;
VecNested (*alloc)(Py_ssize_t, Py_ssize_t, size_t, size_t depth);
PyObject *(*box)(VecNested);
VecNested (*unbox)(PyObject *, size_t, size_t depth);
VecNested (*convert_from_nested)(VecNestedBufItem);
VecNested (*append)(VecNested, VecNestedBufItem);
VecNestedPopResult (*pop)(VecNested, Py_ssize_t);
VecNested (*remove)(VecNested, VecNestedBufItem);
// TODO: Py_ssize_t
VecNested (*slice)(VecNested, int64_t, int64_t);
// PyObject *(*extend)(PyObject *, PyObject *);
// PyObject *(*concat)(PyObject *, PyObject *);
// bool (*contains)(PyObject *, PyObject *);
// iter?
} VecNestedAPI;
typedef struct {
VecTAPI *t;
VecNestedAPI *nested;
VecI64API *i64;
VecI32API *i32;
VecI16API *i16;
VecU8API *u8;
VecFloatAPI *float_;
VecBoolAPI *bool_;
PyTypeObject *(*get_vec_type)(void); // Function to get base VecType for isinstance checks
} VecCapsule;
#define VEC_BUF_SIZE(b) ((b)->ob_base.ob_size)
#define VEC_ITEM_TYPE(t) ((PyTypeObject *)((t) & ~1))
#define VEC_BUF_ITEM_TYPE(b) VEC_ITEM_TYPE((b)->item_type)
#define VEC_CAP(v) ((v).buf->ob_base.ob_size)
#define VEC_IS_ERROR(v) ((v).len < 0)
#define VEC_DECREF(v) Py_XDECREF((v).buf)
#define VEC_INCREF(v) Py_XINCREF((v).buf)
// Type objects
// Buffer type objects that store vec items
extern PyTypeObject VecI64BufType;
extern PyTypeObject VecI32BufType;
extern PyTypeObject VecI16BufType;
extern PyTypeObject VecU8BufType;
extern PyTypeObject VecFloatBufType;
extern PyTypeObject VecBoolBufType;
extern PyTypeObject VecTBufType;
extern PyTypeObject VecNestedBufType;
// Wrapper type objects for boxed vec values
extern PyTypeObject VecI64Type;
extern PyTypeObject VecI32Type;
extern PyTypeObject VecI16Type;
extern PyTypeObject VecU8Type;
extern PyTypeObject VecFloatType;
extern PyTypeObject VecBoolType;
extern PyTypeObject VecTType;
extern PyTypeObject VecNestedType;
// Type objects corresponding to the 'i64', 'i32', 'i16, and 'u8' types
extern PyTypeObject *LibRTVecs_I64TypeObj;
extern PyTypeObject *LibRTVecs_I32TypeObj;
extern PyTypeObject *LibRTVecs_I16TypeObj;
extern PyTypeObject *LibRTVecs_U8TypeObj;
extern VecI64API Vec_I64API;
extern VecI32API Vec_I32API;
extern VecI16API Vec_I16API;
extern VecU8API Vec_U8API;
extern VecFloatAPI Vec_FloatAPI;
extern VecBoolAPI Vec_BoolAPI;
extern VecTAPI Vec_TAPI;
extern VecNestedAPI Vec_NestedAPI;
static inline int Vec_CheckFloatError(PyObject *o) {
if (PyFloat_Check(o)) {
PyErr_SetString(PyExc_TypeError, "integer argument expected, got float");
return 1;
}
return 0;
}
// vec[i64] operations
static inline int VecI64_Check(PyObject *o) {
return o->ob_type == &VecI64Type;
}
static inline PyObject *VecI64_BoxItem(int64_t x) {
return PyLong_FromLongLong(x);
}
static inline int64_t VecI64_UnboxItem(PyObject *o) {
if (Vec_CheckFloatError(o))
return -1;
return PyLong_AsLongLong(o);
}
static inline int VecI64_IsUnboxError(int64_t x) {
return x == -1 && PyErr_Occurred();
}
PyObject *VecI64_Box(VecI64);
VecI64 VecI64_Append(VecI64, int64_t x);
VecI64 VecI64_Remove(VecI64, int64_t x);
VecI64PopResult VecI64_Pop(VecI64 v, Py_ssize_t index);
// vec[i32] operations
static inline int VecI32_Check(PyObject *o) {
return o->ob_type == &VecI32Type;
}
static inline PyObject *VecI32_BoxItem(int32_t x) {
return PyLong_FromLongLong(x);
}
static inline int32_t VecI32_UnboxItem(PyObject *o) {
if (Vec_CheckFloatError(o))
return -1;
long x = PyLong_AsLong(o);
if (x > INT32_MAX || x < INT32_MIN) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to i32");
return -1;
}
return x;
}
static inline int VecI32_IsUnboxError(int32_t x) {
return x == -1 && PyErr_Occurred();
}
PyObject *VecI32_Box(VecI32);
VecI32 VecI32_Append(VecI32, int32_t x);
VecI32 VecI32_Remove(VecI32, int32_t x);
VecI32PopResult VecI32_Pop(VecI32 v, Py_ssize_t index);
// vec[i16] operations
static inline int VecI16_Check(PyObject *o) {
return o->ob_type == &VecI16Type;
}
static inline PyObject *VecI16_BoxItem(int16_t x) {
return PyLong_FromLongLong(x);
}
static inline int16_t VecI16_UnboxItem(PyObject *o) {
if (Vec_CheckFloatError(o))
return -1;
long x = PyLong_AsLong(o);
if (x >= 32768 || x < -32768) {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to i16");
return -1;
}
return x;
}
static inline int VecI16_IsUnboxError(int16_t x) {
return x == -1 && PyErr_Occurred();
}
PyObject *VecI16_Box(VecI16);
VecI16 VecI16_Append(VecI16, int16_t x);
VecI16 VecI16_Remove(VecI16, int16_t x);
VecI16PopResult VecI16_Pop(VecI16 v, Py_ssize_t index);
// vec[u8] operations
static inline int VecU8_Check(PyObject *o) {
return o->ob_type == &VecU8Type;
}
static inline PyObject *VecU8_BoxItem(uint8_t x) {
return PyLong_FromUnsignedLong(x);
}
static inline uint8_t VecU8_UnboxItem(PyObject *o) {
if (Vec_CheckFloatError(o))
return -1;
unsigned long x = PyLong_AsUnsignedLong(o);
if (x <= 255)
return x;
else if (x == (unsigned long)-1)
return 239;
else {
PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to u8");
return 239;
}
}
static inline int VecU8_IsUnboxError(uint8_t x) {
return x == 239 && PyErr_Occurred();
}
PyObject *VecU8_Box(VecU8);
VecU8 VecU8_Append(VecU8, uint8_t x);
VecU8 VecU8_Remove(VecU8, uint8_t x);
VecU8PopResult VecU8_Pop(VecU8 v, Py_ssize_t index);
// vec[float] operations
static inline int VecFloat_Check(PyObject *o) {
return o->ob_type == &VecFloatType;
}
static inline PyObject *VecFloat_BoxItem(double x) {
return PyFloat_FromDouble(x);
}
static inline double VecFloat_UnboxItem(PyObject *o) {
return PyFloat_AsDouble(o);
}
static inline int VecFloat_IsUnboxError(double x) {
return x == -1.0 && PyErr_Occurred();
}
PyObject *VecFloat_Box(VecFloat);
VecFloat VecFloat_Append(VecFloat, double x);
VecFloat VecFloat_Remove(VecFloat, double x);
VecFloatPopResult VecFloat_Pop(VecFloat v, Py_ssize_t index);
// vec[bool] operations
static inline int VecBool_Check(PyObject *o) {
return o->ob_type == &VecBoolType;
}
static inline PyObject *VecBool_BoxItem(char x) {
if (x == 1) {
Py_INCREF(Py_True);
return Py_True;
} else {
Py_INCREF(Py_False);
return Py_False;
}
}
static inline char VecBool_UnboxItem(PyObject *o) {
if (o == Py_False) {
return 0;
} else if (o == Py_True) {
return 1;
} else {
PyErr_SetString(PyExc_TypeError, "bool value expected");
return 2;
}
}
static inline int VecBool_IsUnboxError(char x) {
return x == 2;
}
PyObject *VecBool_Box(VecBool);
VecBool VecBool_Append(VecBool, char x);
VecBool VecBool_Remove(VecBool, char x);
VecBoolPopResult VecBool_Pop(VecBool v, Py_ssize_t index);
// vec[t] operations
static inline int VecT_Check(PyObject *o) {
return o->ob_type == &VecTType;
}
static inline int VecT_ItemCheck(VecT v, PyObject *item, size_t item_type) {
if (PyObject_TypeCheck(item, VEC_ITEM_TYPE(item_type))) {
return 1;
} else if ((item_type & 1) && item == Py_None) {
return 1;
} else {
// TODO: better error message
PyErr_SetString(PyExc_TypeError, "invalid item type");
return 0;
}
}
VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type);
PyObject *VecT_FromIterable(size_t item_type, PyObject *iterable);
PyObject *VecT_Box(VecT vec, size_t item_type);
VecT VecT_Append(VecT vec, PyObject *x, size_t item_type);
VecT VecT_Remove(VecT vec, PyObject *x);
VecTPopResult VecT_Pop(VecT v, Py_ssize_t index);
// Nested vec operations
static inline int VecNested_Check(PyObject *o) {
return o->ob_type == &VecNestedType;
}
VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_t depth);
PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *iterable);
PyObject *VecNested_Box(VecNested);
VecNested VecNested_Append(VecNested vec, VecNestedBufItem x);
VecNested VecNested_Remove(VecNested vec, VecNestedBufItem x);
VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index);
// Return 0 on success, -1 on error. Store unboxed item in *unboxed if successful.
// Return a *borrowed* reference.
static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufItem *unboxed) {
size_t depth = v.buf->depth;
if (depth == 1) {
if (item->ob_type == &VecTType) {
VecNestedObject *o = (VecNestedObject *)item;
if (o->vec.buf->item_type == v.buf->item_type) {
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
}
} else if (item->ob_type == &VecI64Type && v.buf->item_type == VEC_ITEM_TYPE_I64) {
VecI64Object *o = (VecI64Object *)item;
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
} else if (item->ob_type == &VecU8Type && v.buf->item_type == VEC_ITEM_TYPE_U8) {
VecU8Object *o = (VecU8Object *)item;
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
} else if (item->ob_type == &VecFloatType && v.buf->item_type == VEC_ITEM_TYPE_FLOAT) {
VecFloatObject *o = (VecFloatObject *)item;
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
} else if (item->ob_type == &VecI32Type && v.buf->item_type == VEC_ITEM_TYPE_I32) {
VecI32Object *o = (VecI32Object *)item;
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
} else if (item->ob_type == &VecI16Type && v.buf->item_type == VEC_ITEM_TYPE_I16) {
VecI16Object *o = (VecI16Object *)item;
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
} else if (item->ob_type == &VecBoolType && v.buf->item_type == VEC_ITEM_TYPE_BOOL) {
VecBoolObject *o = (VecBoolObject *)item;
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
}
} else if (item->ob_type == &VecNestedType) {
VecNestedObject *o = (VecNestedObject *)item;
if (o->vec.buf->depth == v.buf->depth - 1
&& o->vec.buf->item_type == v.buf->item_type) {
unboxed->len = o->vec.len;
unboxed->buf = (PyObject *)o->vec.buf;
return 0;
}
}
// TODO: better error message
PyErr_SetString(PyExc_TypeError, "invalid item type");
return -1;
}
static inline PyObject *VecNested_BoxItem(VecNested v, VecNestedBufItem item) {
if (item.len < 0)
Py_RETURN_NONE;
Py_XINCREF(item.buf);
if (v.buf->depth > 1) {
// Item is a nested vec
VecNested v = { .len = item.len, .buf = (VecNestedBufObject *)item.buf };
return VecNested_Box(v);
} else {
// Item is a non-nested vec
size_t item_type = v.buf->item_type;
if (item_type == VEC_ITEM_TYPE_I64) {
VecI64 v = { .len = item.len, .buf = (VecI64BufObject *)item.buf };
return VecI64_Box(v);
} else if (item_type == VEC_ITEM_TYPE_U8) {
VecU8 v = { .len = item.len, .buf = (VecU8BufObject *)item.buf };
return VecU8_Box(v);
} else if (item_type == VEC_ITEM_TYPE_FLOAT) {
VecFloat v = { .len = item.len, .buf = (VecFloatBufObject *)item.buf };
return VecFloat_Box(v);
} else if (item_type == VEC_ITEM_TYPE_I32) {
VecI32 v = { .len = item.len, .buf = (VecI32BufObject *)item.buf };
return VecI32_Box(v);
} else if (item_type == VEC_ITEM_TYPE_I16) {
VecI16 v = { .len = item.len, .buf = (VecI16BufObject *)item.buf };
return VecI16_Box(v);
} else if (item_type == VEC_ITEM_TYPE_BOOL) {
VecBool v = { .len = item.len, .buf = (VecBoolBufObject *)item.buf };
return VecBool_Box(v);
} else {
// Generic vec[t]
VecT v = { .len = item.len, .buf = (VecTBufObject *)item.buf };
return VecT_Box(v, item_type);
}
}
}
// Misc helpers
PyObject *Vec_TypeToStr(size_t item_type, size_t depth);
PyObject *Vec_GenericRepr(PyObject *vec, size_t item_type, size_t depth, int verbose);
PyObject *Vec_GenericRichcompare(Py_ssize_t *len, PyObject **items,
Py_ssize_t *other_len, PyObject **other_items,
int op);
int Vec_GenericRemove(Py_ssize_t *len, PyObject **items, PyObject *item);
PyObject *Vec_GenericPopWrapper(Py_ssize_t *len, PyObject **items, PyObject *args);
PyObject *Vec_GenericPop(Py_ssize_t *len, PyObject **items, Py_ssize_t index);
// Global API pointers initialized by import_librt_vecs()
static VecCapsule *VecApi;
static VecI64API VecI64Api;
static VecI32API VecI32Api;
static VecI16API VecI16Api;
static VecU8API VecU8Api;
static VecFloatAPI VecFloatApi;
static VecBoolAPI VecBoolApi;
static VecTAPI VecTApi;
static VecNestedAPI VecNestedApi;
static int
import_librt_vecs(void)
{
PyObject *mod = PyImport_ImportModule("librt.vecs");
if (mod == NULL)
return -1;
Py_DECREF(mod); // we import just for the side effect of making the below work.
VecApi = PyCapsule_Import("librt.vecs._C_API", 0);
if (!VecApi)
return -1;
VecI64Api = *VecApi->i64;
VecI32Api = *VecApi->i32;
VecI16Api = *VecApi->i16;
VecU8Api = *VecApi->u8;
VecFloatApi = *VecApi->float_;
VecBoolApi = *VecApi->bool_;
VecTApi = *VecApi->t;
VecNestedApi = *VecApi->nested;
return 0;
}
#endif // MYPYC_EXPERIMENTAL
#endif // VEC_H_INCL

View file

@ -0,0 +1,20 @@
#ifdef MYPYC_EXPERIMENTAL
#define VEC VecBool
#define VEC_TYPE VecBoolType
#define VEC_OBJECT VecBoolObject
#define BUF_OBJECT VecBoolBufObject
#define BUF_TYPE VecBoolBufType
#define NAME(suffix) VecBool##suffix
#define FUNC(suffix) VecBool_##suffix
#define ITEM_TYPE_STR "bool"
#define ITEM_TYPE_MAGIC VEC_ITEM_TYPE_BOOL
#define ITEM_C_TYPE char
#define FEATURES Vec_BoolAPI
#define BOX_ITEM VecBool_BoxItem
#define UNBOX_ITEM VecBool_UnboxItem
#define IS_UNBOX_ERROR VecBool_IsUnboxError
#include "vec_template.c"
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,20 @@
#ifdef MYPYC_EXPERIMENTAL
#define VEC VecFloat
#define VEC_TYPE VecFloatType
#define VEC_OBJECT VecFloatObject
#define BUF_OBJECT VecFloatBufObject
#define BUF_TYPE VecFloatBufType
#define NAME(suffix) VecFloat##suffix
#define FUNC(suffix) VecFloat_##suffix
#define ITEM_TYPE_STR "float"
#define ITEM_TYPE_MAGIC VEC_ITEM_TYPE_FLOAT
#define ITEM_C_TYPE double
#define FEATURES Vec_FloatAPI
#define BOX_ITEM VecFloat_BoxItem
#define UNBOX_ITEM VecFloat_UnboxItem
#define IS_UNBOX_ERROR VecFloat_IsUnboxError
#include "vec_template.c"
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,20 @@
#ifdef MYPYC_EXPERIMENTAL
#define VEC VecI16
#define VEC_TYPE VecI16Type
#define VEC_OBJECT VecI16Object
#define BUF_OBJECT VecI16BufObject
#define BUF_TYPE VecI16BufType
#define NAME(suffix) VecI16##suffix
#define FUNC(suffix) VecI16_##suffix
#define ITEM_TYPE_STR "i16"
#define ITEM_TYPE_MAGIC VEC_ITEM_TYPE_I16
#define ITEM_C_TYPE int16_t
#define FEATURES Vec_I16API
#define BOX_ITEM VecI16_BoxItem
#define UNBOX_ITEM VecI16_UnboxItem
#define IS_UNBOX_ERROR VecI16_IsUnboxError
#include "vec_template.c"
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,20 @@
#ifdef MYPYC_EXPERIMENTAL
#define VEC VecI32
#define VEC_TYPE VecI32Type
#define VEC_OBJECT VecI32Object
#define BUF_OBJECT VecI32BufObject
#define BUF_TYPE VecI32BufType
#define NAME(suffix) VecI32##suffix
#define FUNC(suffix) VecI32_##suffix
#define ITEM_TYPE_STR "i32"
#define ITEM_TYPE_MAGIC VEC_ITEM_TYPE_I32
#define ITEM_C_TYPE int32_t
#define FEATURES Vec_I32API
#define BOX_ITEM VecI32_BoxItem
#define UNBOX_ITEM VecI32_UnboxItem
#define IS_UNBOX_ERROR VecI32_IsUnboxError
#include "vec_template.c"
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,20 @@
#ifdef MYPYC_EXPERIMENTAL
#define VEC VecI64
#define VEC_TYPE VecI64Type
#define VEC_OBJECT VecI64Object
#define BUF_OBJECT VecI64BufObject
#define BUF_TYPE VecI64BufType
#define NAME(suffix) VecI64##suffix
#define FUNC(suffix) VecI64_##suffix
#define ITEM_TYPE_STR "i64"
#define ITEM_TYPE_MAGIC VEC_ITEM_TYPE_I64
#define ITEM_C_TYPE int64_t
#define FEATURES Vec_I64API
#define BOX_ITEM VecI64_BoxItem
#define UNBOX_ITEM VecI64_UnboxItem
#define IS_UNBOX_ERROR VecI64_IsUnboxError
#include "vec_template.c"
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,506 @@
#ifdef MYPYC_EXPERIMENTAL
// Implementation of nested vec[t], when t is a vec type.
//
// Examples of types supported:
// - vec[vec[i64]]
// - vec[vec[vec[str]]]
// - vec[vec[str | None]]
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "librt_vecs.h"
#include "vecs_internal.h"
static inline VecNested vec_error() {
VecNested v = { .len = -1 };
return v;
}
static inline PyObject *box_vec_item_by_index(VecNested v, Py_ssize_t index) {
return VecNested_BoxItem(v, v.buf->items[index]);
}
// Alloc a partially initialized vec. Caller *must* initialize len and buf->items of the
// return value.
static VecNested vec_alloc(Py_ssize_t size, size_t item_type, size_t depth) {
VecNestedBufObject *buf = PyObject_GC_NewVar(VecNestedBufObject, &VecNestedBufType, size);
if (buf == NULL)
return vec_error();
buf->item_type = item_type;
buf->depth = depth;
if (!Vec_IsMagicItemType(item_type))
Py_INCREF(VEC_BUF_ITEM_TYPE(buf));
VecNested res = { .buf = buf };
PyObject_GC_Track(buf);
return res;
}
// Box a nested vec value, stealing 'vec'. On error, decref 'vec'.
PyObject *VecNested_Box(VecNested vec) {
VecNestedObject *obj = PyObject_GC_New(VecNestedObject, &VecNestedType);
if (obj == NULL) {
VEC_DECREF(vec);
return NULL;
}
obj->vec = vec;
PyObject_GC_Track(obj);
return (PyObject *)obj;
}
VecNested VecNested_Unbox(PyObject *obj, size_t item_type, size_t depth) {
if (obj->ob_type == &VecNestedType) {
VecNested result = ((VecNestedObject *)obj)->vec;
if (result.buf->item_type == item_type && result.buf->depth == depth) {
VEC_INCREF(result); // TODO: Should we borrow instead?
return result;
}
}
// TODO: Better error message, with name of type
PyErr_SetString(PyExc_TypeError, "vec[t] expected");
return vec_error();
}
VecNested VecNested_ConvertFromNested(VecNestedBufItem item) {
return (VecNested) { item.len, (VecNestedBufObject *)item.buf };
}
VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_t depth) {
if (cap < size)
cap = size;
VecNested vec = vec_alloc(cap, item_type, depth);
if (VEC_IS_ERROR(vec))
return vec;
for (Py_ssize_t i = 0; i < cap; i++) {
vec.buf->items[i].len = -1;
vec.buf->items[i].buf = NULL;
}
vec.len = size;
return vec;
}
static PyObject *vec_repr(PyObject *self) {
VecNested v = ((VecNestedObject *)self)->vec;
return Vec_GenericRepr(self, v.buf->item_type, v.buf->depth, 1);
}
static PyObject *vec_get_item(PyObject *o, Py_ssize_t i) {
VecNested v = ((VecNestedObject *)o)->vec;
if ((size_t)i < (size_t)v.len) {
return box_vec_item_by_index(v, i);
} else if ((size_t)i + (size_t)v.len < (size_t)v.len) {
return box_vec_item_by_index(v, i + v.len);
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
}
VecNested VecNested_Slice(VecNested vec, int64_t start, int64_t end) {
if (start < 0)
start += vec.len;
if (end < 0)
end += vec.len;
if (start < 0)
start = 0;
if (start >= vec.len)
start = vec.len;
if (end < start)
end = start;
if (end > vec.len)
end = vec.len;
int64_t slicelength = end - start;
VecNested res = vec_alloc(slicelength, vec.buf->item_type, vec.buf->depth);
if (VEC_IS_ERROR(res))
return res;
res.len = slicelength;
for (Py_ssize_t i = 0; i < slicelength; i++) {
VecNestedBufItem item = vec.buf->items[start + i];
Py_XINCREF(item.buf);
res.buf->items[i] = item;
}
return res;
}
static PyObject *vec_subscript(PyObject *self, PyObject *item) {
VecNested vec = ((VecNestedObject *)self)->vec;
if (PyIndex_Check(item)) {
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
if (i == -1 && PyErr_Occurred())
return NULL;
if ((size_t)i < (size_t)vec.len) {
return box_vec_item_by_index(vec, i);
} else if ((size_t)i + (size_t)vec.len < (size_t)vec.len) {
return box_vec_item_by_index(vec, i + vec.len);
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
} else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step;
if (PySlice_Unpack(item, &start, &stop, &step) < 0)
return NULL;
Py_ssize_t slicelength = PySlice_AdjustIndices(vec.len, &start, &stop, step);
VecNested res = vec_alloc(slicelength, vec.buf->item_type, vec.buf->depth);
if (VEC_IS_ERROR(res))
return NULL;
res.len = slicelength;
Py_ssize_t j = start;
for (Py_ssize_t i = 0; i < slicelength; i++) {
VecNestedBufItem item = vec.buf->items[j];
Py_INCREF(item.buf);
res.buf->items[i] = item;
j += step;
}
return VecNested_Box(res);
} else {
PyErr_Format(PyExc_TypeError, "vec indices must be integers or slices, not %.100s",
item->ob_type->tp_name);
return NULL;
}
}
static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) {
VecNested v = ((VecNestedObject *)self)->vec;
if ((size_t)i + (size_t)v.len < (size_t)v.len) {
i += v.len;
}
if ((size_t)i < (size_t)v.len) {
VecNestedBufItem item;
if (VecNested_UnboxItem(v, o, &item) < 0)
return -1;
VEC_INCREF(item);
VEC_DECREF(v.buf->items[i]);
v.buf->items[i] = item;
return 0;
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return -1;
}
}
static PyObject *compare_vec_eq(VecNested x, VecNested y, int op) {
int cmp = 1;
PyObject *res;
if (x.len != y.len
|| x.buf->item_type != y.buf->item_type
|| x.buf->depth != y.buf->depth) {
cmp = 0;
} else {
for (Py_ssize_t i = 0; i < x.len; i++) {
PyObject *x_item = box_vec_item_by_index(x, i);
PyObject *y_item = box_vec_item_by_index(y, i);
int itemcmp = PyObject_RichCompareBool(x_item, y_item, Py_EQ);
Py_DECREF(x_item);
Py_DECREF(y_item);
if (itemcmp < 0)
return NULL;
if (!itemcmp) {
cmp = 0;
break;
}
}
}
if (op == Py_NE)
cmp = cmp ^ 1;
res = cmp ? Py_True : Py_False;
Py_INCREF(res);
return res;
}
PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) {
PyObject *res;
if (op == Py_EQ || op == Py_NE) {
if (other->ob_type != &VecNestedType) {
res = op == Py_EQ ? Py_False : Py_True;
} else {
return compare_vec_eq(((VecNestedObject *)self)->vec,
((VecNestedObject *)other)->vec, op);
}
} else
res = Py_NotImplemented;
Py_INCREF(res);
return res;
}
// Append item to 'vec', stealing 'vec'. Return 'vec' with item appended.
VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) {
Py_ssize_t cap = VEC_CAP(vec);
VEC_INCREF(x);
if (vec.len < cap) {
// Slot may have duplicate ref from prior remove/pop
Py_XDECREF(vec.buf->items[vec.len].buf);
vec.buf->items[vec.len] = x;
vec.len++;
return vec;
} else {
Py_ssize_t new_size = 2 * cap + 1;
// TODO: Avoid initializing to zero here
VecNested new = vec_alloc(new_size, vec.buf->item_type, vec.buf->depth);
if (VEC_IS_ERROR(new)) {
VEC_DECREF(x);
// The input vec is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(vec);
return new;
}
// Copy items to new vec.
memcpy(new.buf->items, vec.buf->items, sizeof(VecNestedBufItem) * vec.len);
// TODO: How to safely represent deleted items?
memset(new.buf->items + vec.len, 0, sizeof(VecNestedBufItem) * (new_size - vec.len));
// Clear the items in the old vec. We avoid reference count manipulation.
memset(vec.buf->items, 0, sizeof(VecNestedBufItem) * vec.len);
new.buf->items[vec.len] = x;
new.len = vec.len + 1;
VEC_DECREF(vec);
return new;
}
}
// Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed.
VecNested VecNested_Remove(VecNested self, VecNestedBufItem arg) {
VecNestedBufItem *items = self.buf->items;
PyObject *boxed_arg = VecNested_BoxItem(self, arg);
if (boxed_arg == NULL) {
// The input self is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(self);
return vec_error();
}
for (Py_ssize_t i = 0; i < self.len; i++) {
int match = 0;
if (items[i].len == arg.len && items[i].buf == arg.buf)
match = 1;
else {
PyObject *item = box_vec_item_by_index(self, i);
if (item == NULL) {
Py_DECREF(boxed_arg);
// The input self is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(self);
return vec_error();
}
int itemcmp = PyObject_RichCompareBool(item, boxed_arg, Py_EQ);
Py_DECREF(item);
if (itemcmp < 0) {
Py_DECREF(boxed_arg);
// The input self is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(self);
return vec_error();
}
match = itemcmp;
}
if (match) {
if (i < self.len - 1) {
Py_CLEAR(items[i].buf);
for (; i < self.len - 1; i++) {
items[i] = items[i + 1];
}
Py_XINCREF(items[self.len - 1].buf);
}
self.len--;
Py_DECREF(boxed_arg);
// Return the stolen reference without INCREF
return self;
}
}
Py_DECREF(boxed_arg);
PyErr_SetString(PyExc_ValueError, "vec.remove(x): x not in vec");
// The input self is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(self);
return vec_error();
}
// Pop item from 'vec', stealing 'vec'. Return struct with modified 'vec' and the popped item.
VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index) {
VecNestedPopResult result;
if (index < 0)
index += v.len;
if (index < 0 || index >= v.len) {
PyErr_SetString(PyExc_IndexError, "index out of range");
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(v);
result.f0 = vec_error();
result.f1.len = 0;
result.f1.buf = NULL;
return result;
}
VecNestedBufItem *items = v.buf->items;
result.f1 = items[index];
for (Py_ssize_t i = index; i < v.len - 1; i++)
items[i] = items[i + 1];
if (v.len > 0)
Py_XINCREF(items[v.len - 1].buf);
v.len--;
// Return the stolen reference without INCREF
result.f0 = v;
return result;
}
static int
VecNested_traverse(VecNestedObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->vec.buf);
return 0;
}
static int
VecNested_clear(VecNestedObject *self)
{
Py_CLEAR(self->vec.buf);
return 0;
}
static void
VecNested_dealloc(VecNestedObject *self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_BEGIN(self, VecNested_dealloc)
Py_CLEAR(self->vec.buf);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END
}
static int
VecNestedBuf_traverse(VecNestedBufObject *self, visitproc visit, void *arg)
{
if (!Vec_IsMagicItemType(self->item_type))
Py_VISIT(VEC_BUF_ITEM_TYPE(self));
for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) {
Py_VISIT(self->items[i].buf);
}
return 0;
}
static inline int
VecNestedBuf_clear(VecNestedBufObject *self)
{
if (self->item_type && !Vec_IsMagicItemType(self->item_type)) {
Py_DECREF(VEC_BUF_ITEM_TYPE(self));
self->item_type = 0;
}
for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) {
Py_CLEAR(self->items[i].buf);
}
return 0;
}
static void
VecNestedBuf_dealloc(VecNestedBufObject *self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_BEGIN(self, VecNestedBuf_dealloc)
VecNestedBuf_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END
}
static Py_ssize_t vec_ext_length(PyObject *o) {
return ((VecNestedObject *)o)->vec.len;
}
static PyMappingMethods VecNestedMapping = {
.mp_length = vec_ext_length,
.mp_subscript = vec_subscript,
};
static PySequenceMethods VecNestedSequence = {
.sq_item = vec_get_item,
.sq_ass_item = vec_ass_item,
};
static PyMethodDef vec_methods[] = {
{NULL, NULL, 0, NULL}, /* Sentinel */
};
PyTypeObject VecNestedBufType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "vecbuf",
.tp_doc = "Internal data buffer used by vec objects",
.tp_basicsize = sizeof(VecNestedBufObject) - sizeof(VecNestedBufItem),
.tp_itemsize = sizeof(VecNestedBufItem),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)VecNestedBuf_traverse,
//.tp_new = vecbuf_i64_new, //??
.tp_free = PyObject_GC_Del,
.tp_clear = (inquiry)VecNestedBuf_clear,
.tp_dealloc = (destructor)VecNestedBuf_dealloc,
};
PyTypeObject VecNestedType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "vec",
.tp_doc = "Mutable sequence-like container optimized for compilation with mypyc",
.tp_basicsize = sizeof(VecNestedObject),
.tp_itemsize = 0,
.tp_base = &VecType, // Inherit from base vec type for isinstance() support
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)VecNested_traverse,
.tp_clear = (inquiry)VecNested_clear,
.tp_dealloc = (destructor)VecNested_dealloc,
//.tp_free = PyObject_GC_Del,
.tp_repr = (reprfunc)vec_repr,
.tp_as_sequence = &VecNestedSequence,
.tp_as_mapping = &VecNestedMapping,
.tp_richcompare = vec_richcompare,
.tp_methods = vec_methods,
// TODO: free
};
PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *iterable) {
VecNested v = vec_alloc(0, item_type, depth);
if (VEC_IS_ERROR(v))
return NULL;
v.len = 0;
PyObject *iter = PyObject_GetIter(iterable);
if (iter == NULL) {
VEC_DECREF(v);
return NULL;
}
PyObject *item;
while ((item = PyIter_Next(iter)) != NULL) {
VecNestedBufItem vecitem;
if (VecNested_UnboxItem(v, item, &vecitem) < 0) {
Py_DECREF(iter);
VEC_DECREF(v);
Py_DECREF(item);
return NULL;
}
v = VecNested_Append(v, vecitem);
Py_DECREF(item);
if (VEC_IS_ERROR(v)) {
Py_DECREF(iter);
VEC_DECREF(v);
return NULL;
}
}
Py_DECREF(iter);
if (PyErr_Occurred()) {
VEC_DECREF(v);
return NULL;
}
return VecNested_Box(v);
}
VecNestedAPI Vec_NestedAPI = {
&VecNestedType,
&VecNestedBufType,
VecNested_New,
VecNested_Box,
VecNested_Unbox,
VecNested_ConvertFromNested,
VecNested_Append,
VecNested_Pop,
VecNested_Remove,
VecNested_Slice,
};
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,501 @@
#ifdef MYPYC_EXPERIMENTAL
// Implementation of generic vec[t], when t is a plain type object (possibly optional).
//
// Examples of types supported:
//
// - vec[str]
// - vec[str | None]
// - vec[object]
// - vec[UserClass]
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "librt_vecs.h"
#include "vecs_internal.h"
static inline VecT vec_error() {
VecT v = { .len = -1 };
return v;
}
static inline VecTBufObject *alloc_buf(Py_ssize_t size, size_t item_type) {
VecTBufObject *buf = PyObject_GC_NewVar(VecTBufObject, &VecTBufType, size);
if (buf == NULL)
return NULL;
buf->item_type = item_type;
Py_INCREF(VEC_BUF_ITEM_TYPE(buf));
PyObject_GC_Track(buf);
return buf;
}
// Alloc a partially initialized vec. Caller *must* immediately initialize len, and buf->items
// if size > 0.
static VecT vec_alloc(Py_ssize_t size, size_t item_type) {
VecTBufObject *buf;
if (size == 0) {
buf = NULL;
} else {
buf = alloc_buf(size, item_type);
if (buf == NULL)
return vec_error();
}
return (VecT) { .buf = buf };
}
// Box a VecT value, stealing 'vec'. On failure, return NULL and decref 'vec'.
PyObject *VecT_Box(VecT vec, size_t item_type) {
// An unboxed empty vec may have a NULL buf, but a boxed vec must have it
// allocated, since it contains the item type
if (vec.buf == NULL) {
vec.buf = alloc_buf(0, item_type);
if (vec.buf == NULL)
return NULL;
}
VecTObject *obj = PyObject_GC_New(VecTObject, &VecTType);
if (obj == NULL) {
// vec.buf is always defined, so no need for a NULL check
Py_DECREF(vec.buf);
return NULL;
}
obj->vec = vec;
PyObject_GC_Track(obj);
return (PyObject *)obj;
}
VecT VecT_Unbox(PyObject *obj, size_t item_type) {
if (obj->ob_type == &VecTType) {
VecT result = ((VecTObject *)obj)->vec;
if (result.buf->item_type == item_type) {
VEC_INCREF(result); // TODO: Should we borrow instead?
return result;
}
}
// TODO: Better error message, with name of type
PyErr_SetString(PyExc_TypeError, "vec[t] expected");
return vec_error();
}
VecT VecT_ConvertFromNested(VecNestedBufItem item) {
return (VecT) { item.len, (VecTBufObject *)item.buf };
}
VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type) {
if (cap < size)
cap = size;
VecT vec = vec_alloc(cap, item_type);
if (VEC_IS_ERROR(vec))
return vec;
for (Py_ssize_t i = 0; i < cap; i++) {
vec.buf->items[i] = NULL;
}
vec.len = size;
return vec;
}
static PyObject *vec_repr(PyObject *self) {
VecTObject *v = (VecTObject *)self;
return Vec_GenericRepr(self, v->vec.buf->item_type, 0, 1);
}
static PyObject *vec_get_item(PyObject *o, Py_ssize_t i) {
VecT v = ((VecTObject *)o)->vec;
if ((size_t)i < (size_t)v.len) {
PyObject *item = v.buf->items[i];
Py_INCREF(item);
return item;
} else if ((size_t)i + (size_t)v.len < (size_t)v.len) {
PyObject *item = v.buf->items[i + v.len];
Py_INCREF(item);
return item;
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
}
VecT VecT_Slice(VecT vec, int64_t start, int64_t end) {
if (start < 0)
start += vec.len;
if (end < 0)
end += vec.len;
if (start < 0)
start = 0;
if (start >= vec.len)
start = vec.len;
if (end < start)
end = start;
if (end > vec.len)
end = vec.len;
int64_t slicelength = end - start;
if (slicelength == 0)
return (VecT) { .len = 0, .buf = NULL };
VecT res = vec_alloc(slicelength, vec.buf->item_type);
if (VEC_IS_ERROR(res))
return res;
res.len = slicelength;
for (Py_ssize_t i = 0; i < slicelength; i++) {
PyObject *item = vec.buf->items[start + i];
Py_INCREF(item);
res.buf->items[i] = item;
}
return res;
}
static PyObject *vec_subscript(PyObject *self, PyObject *item) {
VecT vec = ((VecTObject *)self)->vec;
if (PyIndex_Check(item)) {
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
if (i == -1 && PyErr_Occurred())
return NULL;
if ((size_t)i < (size_t)vec.len) {
PyObject *result = vec.buf->items[i];
Py_INCREF(result);
return result;
} else if ((size_t)i + (size_t)vec.len < (size_t)vec.len) {
PyObject *result = vec.buf->items[i + vec.len];
Py_INCREF(result);
return result;
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
} else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step;
if (PySlice_Unpack(item, &start, &stop, &step) < 0)
return NULL;
Py_ssize_t slicelength = PySlice_AdjustIndices(vec.len, &start, &stop, step);
VecT res = vec_alloc(slicelength, vec.buf->item_type);
if (VEC_IS_ERROR(res))
return NULL;
res.len = slicelength;
Py_ssize_t j = start;
for (Py_ssize_t i = 0; i < slicelength; i++) {
PyObject *item = vec.buf->items[j];
Py_INCREF(item);
res.buf->items[i] = item;
j += step;
}
PyObject *result = VecT_Box(res, vec.buf->item_type);
if (result == NULL) {
VEC_DECREF(res);
}
return result;
} else {
PyErr_Format(PyExc_TypeError, "vec indices must be integers or slices, not %.100s",
item->ob_type->tp_name);
return NULL;
}
}
static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) {
VecT v = ((VecTObject *)self)->vec;
if (!VecT_ItemCheck(v, o, v.buf->item_type))
return -1;
if ((size_t)i < (size_t)v.len) {
PyObject *old = v.buf->items[i];
Py_INCREF(o);
v.buf->items[i] = o;
Py_XDECREF(old);
return 0;
} else if ((size_t)i + (size_t)v.len < (size_t)v.len) {
PyObject *old = v.buf->items[i + v.len];
Py_INCREF(o);
v.buf->items[i + v.len] = o;
Py_XDECREF(old);
return 0;
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return -1;
}
}
static PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) {
PyObject *res;
if (op == Py_EQ || op == Py_NE) {
if (other->ob_type != &VecTType) {
res = op == Py_EQ ? Py_False : Py_True;
} else {
VecT x = ((VecTObject *)self)->vec;
VecT y = ((VecTObject *)other)->vec;
if (x.buf->item_type != y.buf->item_type) {
res = op == Py_EQ ? Py_False : Py_True;
} else {
// TODO: why pointers to len?
return Vec_GenericRichcompare(&x.len, x.buf->items, &y.len, y.buf->items, op);
}
}
} else
res = Py_NotImplemented;
Py_INCREF(res);
return res;
}
// Append item to 'vec', stealing 'vec'. Return 'vec' with item appended.
VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) {
if (vec.buf == NULL) {
VecT new = vec_alloc(1, item_type);
if (VEC_IS_ERROR(new))
return new;
Py_INCREF(x);
new.len = 1;
new.buf->items[0] = x;
return new;
}
Py_ssize_t cap = VEC_CAP(vec);
Py_INCREF(x);
if (vec.len < cap) {
// Slot may have duplicate ref from prior remove/pop
Py_XSETREF(vec.buf->items[vec.len], x);
vec.len++;
return vec;
} else {
Py_ssize_t new_size = 2 * cap + 1;
// TODO: Avoid initializing to zero here
VecT new = vec_alloc(new_size, vec.buf->item_type);
if (VEC_IS_ERROR(new)) {
Py_DECREF(x);
// The input vec is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(vec);
return new;
}
// Copy items to new vec.
memcpy(new.buf->items, vec.buf->items, sizeof(PyObject *) * vec.len);
memset(new.buf->items + vec.len, 0, sizeof(PyObject *) * (new_size - vec.len));
// Clear the items in the old vec. We avoid reference count manipulation.
memset(vec.buf->items, 0, sizeof(PyObject *) * vec.len);
new.buf->items[vec.len] = x;
new.len = vec.len + 1;
VEC_DECREF(vec);
return new;
}
}
// Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed.
VecT VecT_Remove(VecT v, PyObject *arg) {
PyObject **items = v.buf->items;
for (Py_ssize_t i = 0; i < v.len; i++) {
int match = 0;
if (items[i] == arg)
match = 1;
else {
int itemcmp = PyObject_RichCompareBool(items[i], arg, Py_EQ);
if (itemcmp < 0) {
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(v);
return vec_error();
}
match = itemcmp;
}
if (match) {
if (i < v.len - 1) {
Py_CLEAR(items[i]);
for (; i < v.len - 1; i++) {
items[i] = items[i + 1];
}
// Keep a duplicate item, since there could be another reference
// to the buffer with a longer length, and they expect a valid reference.
Py_XINCREF(items[v.len - 1]);
}
v.len--;
// Return the stolen reference without INCREF
return v;
}
}
PyErr_SetString(PyExc_ValueError, "vec.remove(x): x not in vec");
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(v);
return vec_error();
}
// Pop item from 'vec', stealing 'vec'. Return struct with modified 'vec' and the popped item.
VecTPopResult VecT_Pop(VecT v, Py_ssize_t index) {
VecTPopResult result;
if (index < 0)
index += v.len;
if (index < 0 || index >= v.len) {
PyErr_SetString(PyExc_IndexError, "index out of range");
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(v);
result.f0 = vec_error();
result.f1 = NULL;
return result;
}
PyObject **items = v.buf->items;
result.f1 = items[index];
for (Py_ssize_t i = index; i < v.len - 1; i++)
items[i] = items[i + 1];
// Keep duplicate item, since there could be another reference
// to the buffer with a longer length, and they expect a valid reference.
Py_XINCREF(items[v.len - 1]);
v.len--;
// Return the stolen reference without INCREF
result.f0 = v;
return result;
}
static int
VecT_traverse(VecTObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->vec.buf);
return 0;
}
static int
VecT_clear(VecTObject *self)
{
Py_CLEAR(self->vec.buf);
return 0;
}
static void
VecT_dealloc(VecTObject *self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_BEGIN(self, VecT_dealloc)
Py_CLEAR(self->vec.buf);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END
}
static int
VecTBuf_traverse(VecTBufObject *self, visitproc visit, void *arg)
{
Py_VISIT(VEC_BUF_ITEM_TYPE(self));
for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) {
Py_VISIT(self->items[i]);
}
return 0;
}
static inline int
VecTBuf_clear(VecTBufObject *self)
{
if (self->item_type) {
Py_DECREF(VEC_BUF_ITEM_TYPE(self));
self->item_type = 0;
}
for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) {
Py_CLEAR(self->items[i]);
}
return 0;
}
static void
VecTBuf_dealloc(VecTBufObject *self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_BEGIN(self, VecTBuf_dealloc)
VecTBuf_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END
}
static Py_ssize_t vec_length(PyObject *o) {
return ((VecTObject *)o)->vec.len;
}
static PyMappingMethods VecTMapping = {
.mp_length = vec_length,
.mp_subscript = vec_subscript,
};
static PySequenceMethods VecTSequence = {
.sq_item = vec_get_item,
.sq_ass_item = vec_ass_item,
};
static PyMethodDef vec_methods[] = {
{NULL, NULL, 0, NULL}, /* Sentinel */
};
PyTypeObject VecTBufType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "vecbuf",
.tp_doc = "Internal data buffer used by vec objects",
.tp_basicsize = sizeof(VecTBufObject) - sizeof(PyObject *),
.tp_itemsize = sizeof(PyObject *),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)VecTBuf_traverse,
//.tp_new = vecbuf_i64_new, //??
.tp_free = PyObject_GC_Del,
.tp_clear = (inquiry)VecTBuf_clear,
.tp_dealloc = (destructor)VecTBuf_dealloc,
};
PyTypeObject VecTType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "vec",
.tp_doc = "Mutable sequence-like container optimized for compilation with mypyc",
.tp_basicsize = sizeof(VecTObject),
.tp_itemsize = 0,
.tp_base = &VecType, // Inherit from base vec type for isinstance() support
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)VecT_traverse,
.tp_clear = (inquiry)VecT_clear,
.tp_dealloc = (destructor)VecT_dealloc,
//.tp_free = PyObject_GC_Del,
.tp_repr = (reprfunc)vec_repr,
.tp_as_sequence = &VecTSequence,
.tp_as_mapping = &VecTMapping,
.tp_richcompare = vec_richcompare,
.tp_methods = vec_methods,
// TODO: free
};
PyObject *VecT_FromIterable(size_t item_type, PyObject *iterable) {
VecT v = vec_alloc(0, item_type);
if (VEC_IS_ERROR(v))
return NULL;
v.len = 0;
PyObject *iter = PyObject_GetIter(iterable);
if (iter == NULL) {
VEC_DECREF(v);
return NULL;
}
PyObject *item;
while ((item = PyIter_Next(iter)) != NULL) {
if (!VecT_ItemCheck(v, item, item_type)) {
Py_DECREF(iter);
VEC_DECREF(v);
Py_DECREF(item);
return NULL;
}
v = VecT_Append(v, item, item_type);
Py_DECREF(item);
if (VEC_IS_ERROR(v)) {
Py_DECREF(iter);
VEC_DECREF(v);
return NULL;
}
}
Py_DECREF(iter);
if (PyErr_Occurred()) {
VEC_DECREF(v);
return NULL;
}
return VecT_Box(v, item_type);
}
VecTAPI Vec_TAPI = {
&VecTType,
&VecTBufType,
VecT_New,
VecT_Box,
VecT_Unbox,
VecT_ConvertFromNested,
VecT_Append,
VecT_Pop,
VecT_Remove,
VecT_Slice,
};
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,399 @@
#ifdef MYPYC_EXPERIMENTAL
// NOTE: This file can't be compiled on its own, it must be #included
// with certain #defines set, as described below.
//
// Implementation of a vec class specialized to a specific item type, such
// as vec[i64] or vec[float]. Assume that certain #defines are provided that
// provide all the item type specific definitions:
//
// VEC vec C type (e.g. VecI32)
// VEC_TYPE boxed vec type object (e.g. VecI32Type)
// VEC_OBJECT boxed Python object struct (e.g. VecI32Object)
// BUF_OBJECT buffer Python object struct (e.g. VecI32BufObject)
// BUF_TYPE buffer type object (e.g. VecI32BufType)
// NAME(suffix) macro to create prefixed name with given suffix (e.g. VecI32##suffix)
// FUNC(suffix) macro to create prefixed function name with suffix (e.g. VecI32_##suffix)
// ITEM_TYPE_STR vec item type as C string literal (e.g. "i32")
// ITEM_TYPE_MAGIC integer constant corresponding to the item type (e.g. VEC_ITEM_TYPE_I32)
// ITEM_C_TYPE C type used for items (e.g. int32_t)
// FEATURES capsule API struct name (e.g. Vec_I32API)
// BOX_ITEM C function to box item (e.g. VecI32_BoxItem)
// UNBOX_ITEM C function to unbox item (e.g. VecI32_UnboxItem)
// IS_UNBOX_ERROR C function to check for unbox error (e.g. VecI32_IsUnboxError)
#ifndef VEC
#error "VEC must be defined"
#endif
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "librt_vecs.h"
#include "vecs_internal.h"
inline static VEC vec_error() {
VEC v = { .len = -1 };
return v;
}
// Alloc a partially initialized vec. Caller *must* initialize len.
static VEC vec_alloc(Py_ssize_t size)
{
BUF_OBJECT *buf;
/* TODO: Check for overflow */
if (size == 0) {
buf = NULL;
} else {
buf = PyObject_NewVar(BUF_OBJECT, &BUF_TYPE, size);
if (buf == NULL)
return vec_error();
}
VEC res = { .buf = buf };
return res;
}
static void vec_dealloc(VEC_OBJECT *self) {
Py_CLEAR(self->vec.buf);
PyObject_Del(self);
}
// Box a vec[<itemtype>] value, stealing 'vec'. On error, decref 'vec'.
PyObject *FUNC(Box)(VEC vec) {
VEC_OBJECT *obj = PyObject_New(VEC_OBJECT, &VEC_TYPE);
if (obj == NULL) {
VEC_DECREF(vec);
return NULL;
}
obj->vec = vec;
return (PyObject *)obj;
}
VEC FUNC(Unbox)(PyObject *obj) {
if (obj->ob_type == &VEC_TYPE) {
VEC result = ((VEC_OBJECT *)obj)->vec;
VEC_INCREF(result); // TODO: Should we borrow instead?
return result;
} else {
PyErr_SetString(PyExc_TypeError, "vec[" ITEM_TYPE_STR "] expected");
return vec_error();
}
}
VEC FUNC(ConvertFromNested)(VecNestedBufItem item) {
return (VEC) { item.len, (BUF_OBJECT *)item.buf };
}
VEC FUNC(New)(Py_ssize_t size, Py_ssize_t cap) {
if (cap < size)
size = cap;
VEC vec = vec_alloc(cap);
if (VEC_IS_ERROR(vec))
return vec;
for (Py_ssize_t i = 0; i < cap; i++) {
vec.buf->items[i] = 0;
}
vec.len = size;
return vec;
}
PyObject *FUNC(FromIterable)(PyObject *iterable) {
VEC v = vec_alloc(0);
if (VEC_IS_ERROR(v))
return NULL;
v.len = 0;
PyObject *iter = PyObject_GetIter(iterable);
if (iter == NULL) {
VEC_DECREF(v);
return NULL;
}
PyObject *item;
while ((item = PyIter_Next(iter)) != NULL) {
ITEM_C_TYPE x = UNBOX_ITEM(item);
Py_DECREF(item);
if (IS_UNBOX_ERROR(x)) {
Py_DECREF(iter);
VEC_DECREF(v);
return NULL;
}
v = FUNC(Append)(v, x);
if (VEC_IS_ERROR(v)) {
Py_DECREF(iter);
VEC_DECREF(v);
return NULL;
}
}
Py_DECREF(iter);
if (PyErr_Occurred()) {
VEC_DECREF(v);
return NULL;
}
return FUNC(Box)(v);
}
static PyObject *vec_new(PyTypeObject *self, PyObject *args, PyObject *kw) {
static char *kwlist[] = {"", NULL};
PyObject *init = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:vec", kwlist, &init)) {
return NULL;
}
if (init == NULL) {
return FUNC(Box)(FUNC(New)(0, 0));
} else {
return (PyObject *)FUNC(FromIterable)(init);
}
}
static PyObject *vec_repr(PyObject *self) {
return Vec_GenericRepr(self, ITEM_TYPE_MAGIC, 0, 1);
}
static PyObject *vec_get_item(PyObject *o, Py_ssize_t i) {
VEC v = ((VEC_OBJECT *)o)->vec;
if ((size_t)i < (size_t)v.len) {
return BOX_ITEM(v.buf->items[i]);
} else if ((size_t)i + (size_t)v.len < (size_t)v.len) {
return BOX_ITEM(v.buf->items[i + v.len]);
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
}
VEC FUNC(Slice)(VEC vec, int64_t start, int64_t end) {
if (start < 0)
start += vec.len;
if (end < 0)
end += vec.len;
if (start < 0)
start = 0;
if (start >= vec.len)
start = vec.len;
if (end < start)
end = start;
if (end > vec.len)
end = vec.len;
int64_t slicelength = end - start;
VEC res = vec_alloc(slicelength);
if (VEC_IS_ERROR(res))
return res;
res.len = slicelength;
for (Py_ssize_t i = 0; i < slicelength; i++)
res.buf->items[i] = vec.buf->items[start + i];
return res;
}
static PyObject *vec_subscript(PyObject *self, PyObject *item) {
VEC vec = ((VEC_OBJECT *)self)->vec;
if (PyIndex_Check(item)) {
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
if (i == -1 && PyErr_Occurred())
return NULL;
if ((size_t)i < (size_t)vec.len) {
return BOX_ITEM(vec.buf->items[i]);
} else if ((size_t)i + (size_t)vec.len < (size_t)vec.len) {
return BOX_ITEM(vec.buf->items[i + vec.len]);
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
} else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step;
if (PySlice_Unpack(item, &start, &stop, &step) < 0)
return NULL;
Py_ssize_t slicelength = PySlice_AdjustIndices(vec.len, &start, &stop, step);
VEC res = vec_alloc(slicelength);
if (VEC_IS_ERROR(res))
return NULL;
res.len = slicelength;
Py_ssize_t j = start;
for (Py_ssize_t i = 0; i < slicelength; i++) {
res.buf->items[i] = vec.buf->items[j];
j += step;
}
return FUNC(Box)(res);
} else {
PyErr_Format(PyExc_TypeError, "vec indices must be integers or slices, not %.100s",
item->ob_type->tp_name);
return NULL;
}
}
static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) {
ITEM_C_TYPE x = UNBOX_ITEM(o);
if (IS_UNBOX_ERROR(x))
return -1;
VEC v = ((VEC_OBJECT *)self)->vec;
if ((size_t)i < (size_t)v.len) {
v.buf->items[i] = x;
return 0;
} else if ((size_t)i + (size_t)v.len < (size_t)v.len) {
v.buf->items[i + v.len] = x;
return 0;
} else {
PyErr_SetString(PyExc_IndexError, "index out of range");
return -1;
}
}
static Py_ssize_t vec_length(PyObject *o) {
return ((VEC_OBJECT *)o)->vec.len;
}
static PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) {
int cmp = 1;
PyObject *res;
if (op == Py_EQ || op == Py_NE) {
if (other->ob_type != &VEC_TYPE)
cmp = 0;
else {
VEC x = ((VEC_OBJECT *)self)->vec;
VEC y = ((VEC_OBJECT *)other)->vec;
if (x.len != y.len) {
cmp = 0;
} else {
for (Py_ssize_t i = 0; i < x.len; i++) {
if (x.buf->items[i] != y.buf->items[i]) {
cmp = 0;
break;
}
}
}
}
if (op == Py_NE)
cmp = cmp ^ 1;
res = cmp ? Py_True : Py_False;
} else
res = Py_NotImplemented;
Py_INCREF(res);
return res;
}
// Append item to 'vec', stealing 'vec'. Return 'vec' with item appended.
VEC FUNC(Append)(VEC vec, ITEM_C_TYPE x) {
if (vec.buf && vec.len < VEC_CAP(vec)) {
vec.buf->items[vec.len] = x;
vec.len++;
return vec;
} else {
Py_ssize_t cap = vec.buf ? VEC_CAP(vec) : 0;
Py_ssize_t new_size = 2 * cap + 1;
VEC new = vec_alloc(new_size);
if (VEC_IS_ERROR(new)) {
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(vec);
return vec_error();
}
new.len = vec.len + 1;
if (vec.len > 0)
memcpy(new.buf->items, vec.buf->items, sizeof(ITEM_C_TYPE) * vec.len);
new.buf->items[vec.len] = x;
Py_XDECREF(vec.buf);
return new;
}
}
// Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed.
VEC FUNC(Remove)(VEC v, ITEM_C_TYPE x) {
for (Py_ssize_t i = 0; i < v.len; i++) {
if (v.buf->items[i] == x) {
for (; i < v.len - 1; i++) {
v.buf->items[i] = v.buf->items[i + 1];
}
v.len--;
// Return the stolen reference without INCREF
return v;
}
}
PyErr_SetString(PyExc_ValueError, "vec.remove(x): x not in vec");
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(v);
return vec_error();
}
// Pop item from 'vec', stealing 'vec'. Return struct with modified 'vec' and the popped item.
NAME(PopResult) FUNC(Pop)(VEC v, Py_ssize_t index) {
NAME(PopResult) result;
if (index < 0)
index += v.len;
if (index < 0 || index >= v.len) {
PyErr_SetString(PyExc_IndexError, "index out of range");
// The input v is being consumed/stolen by this function, so on error
// we must decref it to avoid leaking the buffer.
VEC_DECREF(v);
result.f0 = vec_error();
result.f1 = 0;
return result;
}
result.f1 = v.buf->items[index];
for (Py_ssize_t i = index; i < v.len - 1; i++) {
v.buf->items[i] = v.buf->items[i + 1];
}
v.len--;
// Return the stolen reference without INCREF
result.f0 = v;
return result;
}
static PyMappingMethods vec_mapping_methods = {
.mp_length = vec_length,
.mp_subscript = vec_subscript,
};
static PySequenceMethods vec_sequence_methods = {
.sq_item = vec_get_item,
.sq_ass_item = vec_ass_item,
};
static PyMethodDef vec_methods[] = {
{NULL, NULL, 0, NULL}, /* Sentinel */
};
PyTypeObject BUF_TYPE = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "vecbuf[" ITEM_TYPE_STR "]",
.tp_doc = "Internal data buffer used by vec objects",
.tp_basicsize = sizeof(BUF_OBJECT) - sizeof(ITEM_C_TYPE),
.tp_itemsize = sizeof(ITEM_C_TYPE),
.tp_flags = Py_TPFLAGS_DEFAULT,
//.tp_new = ??
.tp_free = PyObject_Del,
};
PyTypeObject VEC_TYPE = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "vec[" ITEM_TYPE_STR "]",
.tp_doc = "Mutable sequence-like container optimized for compilation with mypyc",
.tp_basicsize = sizeof(VEC_OBJECT),
.tp_itemsize = 0,
.tp_base = &VecType, // Inherit from base vec type for isinstance() support
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = vec_new,
//.tp_free = PyObject_Del,
.tp_dealloc = (destructor)vec_dealloc,
.tp_repr = (reprfunc)vec_repr,
.tp_as_sequence = &vec_sequence_methods,
.tp_as_mapping = &vec_mapping_methods,
.tp_richcompare = vec_richcompare,
.tp_methods = vec_methods,
};
NAME(API) FEATURES = {
&VEC_TYPE,
&BUF_TYPE,
FUNC(New),
FUNC(Box),
FUNC(Unbox),
FUNC(ConvertFromNested),
FUNC(Append),
FUNC(Pop),
FUNC(Remove),
FUNC(Slice),
};
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,20 @@
#ifdef MYPYC_EXPERIMENTAL
#define VEC VecU8
#define VEC_TYPE VecU8Type
#define VEC_OBJECT VecU8Object
#define BUF_OBJECT VecU8BufObject
#define BUF_TYPE VecU8BufType
#define NAME(suffix) VecU8##suffix
#define FUNC(suffix) VecU8_##suffix
#define ITEM_TYPE_STR "u8"
#define ITEM_TYPE_MAGIC VEC_ITEM_TYPE_U8
#define ITEM_C_TYPE uint8_t
#define FEATURES Vec_U8API
#define BOX_ITEM VecU8_BoxItem
#define UNBOX_ITEM VecU8_UnboxItem
#define IS_UNBOX_ERROR VecU8_IsUnboxError
#include "vec_template.c"
#endif // MYPYC_EXPERIMENTAL

View file

@ -0,0 +1,11 @@
#ifndef VECS_INTERNAL_H_INCL
#define VECS_INTERNAL_H_INCL
// Internal header for mypyc/lib-rt/vecs implementation
#include <Python.h>
// The base vec type
extern PyTypeObject VecType;
#endif // VECS_INTERNAL_H_INCL