This commit is contained in:
2026-04-10 15:06:59 +02:00
parent 3031b7153b
commit e5a4711004
7806 changed files with 1918528 additions and 335 deletions

View File

@@ -0,0 +1,253 @@
"""
Expose top-level symbols that are safe for import *
"""
import platform
import re
import sys
import warnings
# ---------------------- WARNING WARNING WARNING ----------------------------
# THIS MUST RUN FIRST, DO NOT MOVE... SEE DOCSTRING IN _ensure_critical_deps
def _ensure_critical_deps():
"""
Make sure the Python, NumPy and SciPy present are supported versions.
This has to be done _before_ importing anything from Numba such that
incompatible versions can be reported to the user. If this occurs _after_
importing things from Numba and there's an issue in e.g. a Numba c-ext, a
SystemError might have occurred which prevents reporting the likely cause of
the problem (incompatible versions of critical dependencies).
"""
#NOTE THIS CODE SHOULD NOT IMPORT ANYTHING FROM NUMBA!
def extract_version(mod):
return tuple(map(int, mod.__version__.split('.')[:2]))
PYVERSION = sys.version_info[:2]
if PYVERSION < (3, 10):
msg = ("Numba needs Python 3.10 or greater. Got Python "
f"{PYVERSION[0]}.{PYVERSION[1]}.")
raise ImportError(msg)
import numpy as np
numpy_version = extract_version(np)
if numpy_version < (1, 22):
msg = (f"Numba needs NumPy 1.22 or greater. Got NumPy "
f"{numpy_version[0]}.{numpy_version[1]}.")
raise ImportError(msg)
if numpy_version > (2, 4):
msg = (f"Numba needs NumPy 2.4 or less. Got NumPy "
f"{numpy_version[0]}.{numpy_version[1]}.")
raise ImportError(msg)
try:
import scipy
except ImportError:
pass
else:
sp_version = extract_version(scipy)
if sp_version < (1, 0):
msg = ("Numba requires SciPy version 1.0 or greater. Got SciPy "
f"{scipy.__version__}.")
raise ImportError(msg)
_ensure_critical_deps()
# END DO NOT MOVE
# ---------------------- WARNING WARNING WARNING ----------------------------
from ._version import get_versions
from numba.misc.init_utils import generate_version_info
__version__ = get_versions()['version']
version_info = generate_version_info(__version__)
del get_versions
del generate_version_info
from numba.core import config
from numba.core import types, errors
# Re-export typeof
from numba.misc.special import (
typeof, prange, pndindex, gdb, gdb_breakpoint, gdb_init,
literally, literal_unroll,
)
# Re-export error classes
from numba.core.errors import *
# Re-export types itself
import numba.core.types as types
# Re-export all type names
from numba.core.types import *
# Re-export decorators
from numba.core.decorators import (cfunc, jit, njit, stencil,
jit_module)
# Re-export vectorize decorators and the thread layer querying function
from numba.np.ufunc import (vectorize, guvectorize, threading_layer,
get_num_threads, set_num_threads,
set_parallel_chunksize, get_parallel_chunksize,
get_thread_id)
# Re-export Numpy helpers
from numba.np.numpy_support import carray, farray, from_dtype
# Re-export experimental
from numba import experimental
# Initialize withcontexts
import numba.core.withcontexts
from numba.core.withcontexts import objmode_context as objmode
from numba.core.withcontexts import parallel_chunksize
# Initialize target extensions
import numba.core.target_extension
# Initialize typed containers
import numba.typed
# Keep this for backward compatibility.
def test(argv, **kwds):
# To speed up the import time, avoid importing `unittest` and other test
# dependencies unless the user is actually trying to run tests.
from numba.testing import _runtests as runtests
return runtests.main(argv, **kwds)
__all__ = """
cfunc
from_dtype
guvectorize
jit
experimental
njit
stencil
jit_module
typeof
prange
gdb
gdb_breakpoint
gdb_init
vectorize
objmode
literal_unroll
get_num_threads
set_num_threads
set_parallel_chunksize
get_parallel_chunksize
parallel_chunksize
""".split() + types.__all__ + errors.__all__
_min_llvmlite_version = (0, 47, 0)
_min_llvm_version = (14, 0, 0)
def _ensure_llvm():
"""
Make sure llvmlite is operational.
"""
import warnings
import llvmlite
# Only look at the major, minor and bugfix version numbers.
# Ignore other stuffs
regex = re.compile(r'(\d+)\.(\d+).(\d+)')
m = regex.match(llvmlite.__version__)
if m:
ver = tuple(map(int, m.groups()))
if ver < _min_llvmlite_version:
msg = ("Numba requires at least version %d.%d.%d of llvmlite.\n"
"Installed version is %s.\n"
"Please update llvmlite." %
(_min_llvmlite_version + (llvmlite.__version__,)))
raise ImportError(msg)
else:
# Not matching?
warnings.warn("llvmlite version format not recognized!")
from llvmlite.binding import llvm_version_info, check_jit_execution
if llvm_version_info < _min_llvm_version:
msg = ("Numba requires at least version %d.%d.%d of LLVM.\n"
"Installed llvmlite is built against version %d.%d.%d.\n"
"Please update llvmlite." %
(_min_llvm_version + llvm_version_info))
raise ImportError(msg)
check_jit_execution()
def _try_enable_svml():
"""
Tries to enable SVML if configuration permits use and the library is found.
"""
if not config.DISABLE_INTEL_SVML:
try:
if sys.platform.startswith('linux'):
llvmlite.binding.load_library_permanently("libsvml.so")
elif sys.platform.startswith('darwin'):
llvmlite.binding.load_library_permanently("libsvml.dylib")
elif sys.platform.startswith('win'):
llvmlite.binding.load_library_permanently("svml_dispmd")
else:
return False
# The SVML library is loaded, therefore SVML *could* be supported.
# Now see if LLVM has been compiled with the SVML support patch.
# If llvmlite has the checking function `has_svml` and it returns
# True, then LLVM was compiled with SVML support and the setup
# for SVML can proceed. We err on the side of caution and if the
# checking function is missing, regardless of that being fine for
# most 0.23.{0,1} llvmlite instances (i.e. conda or pip installed),
# we assume that SVML was not compiled in. llvmlite 0.23.2 is a
# bugfix release with the checking function present that will always
# produce correct behaviour. For context see: #3006.
try:
if not getattr(llvmlite.binding.targets, "has_svml")():
# has detection function, but no svml compiled in, therefore
# disable SVML
return False
except AttributeError:
if platform.machine() == 'x86_64' and config.DEBUG:
msg = ("SVML was found but llvmlite >= 0.23.2 is "
"needed to support it.")
warnings.warn(msg)
# does not have detection function, cannot detect reliably,
# disable SVML.
return False
# All is well, detection function present and reports SVML is
# compiled in, set the vector library to SVML.
llvmlite.binding.set_option('SVML', '-vector-library=SVML')
return True
except Exception:
if platform.machine() == 'x86_64' and config.DEBUG:
warnings.warn("SVML was not found/could not be loaded.")
return False
_ensure_llvm()
# we know llvmlite is working as the above tests passed, import it now as SVML
# needs to mutate runtime options (sets the `-vector-library`).
import llvmlite
"""
Is set to True if Intel SVML is in use.
"""
config.USING_SVML = _try_enable_svml()
# ---------------------- WARNING WARNING WARNING ----------------------------
# The following imports occur below here (SVML init) because somewhere in their
# import sequence they have a `@njit` wrapped function. This triggers too early
# a bind to the underlying LLVM libraries which then irretrievably sets the LLVM
# SVML state to "no SVML". See https://github.com/numba/numba/issues/4689 for
# context.
# ---------------------- WARNING WARNING WARNING ----------------------------

View File

@@ -0,0 +1,6 @@
"""Expose Numba command via ``python -m numba``."""
import sys
from numba.misc.numba_entry import main
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,21 @@
#ifndef NUMBA_ARYSTRUCT_H_
#define NUMBA_ARYSTRUCT_H_
/*
* Fill in the *arystruct* with information from the Numpy array *obj*.
* *arystruct*'s layout is defined in numba.targets.arrayobj (look
* for the ArrayTemplate class).
*/
typedef struct {
void *meminfo; /* see _nrt_python.c and nrt.h in numba/core/runtime */
PyObject *parent;
npy_intp nitems;
npy_intp itemsize;
void *data;
npy_intp shape_and_strides[];
} arystruct_t;
#endif /* NUMBA_ARYSTRUCT_H_ */

View File

@@ -0,0 +1,25 @@
#ifndef NUMBA_DEVICEARRAY_H_
#define NUMBA_DEVICEARRAY_H_
#ifdef __cplusplus
extern "C" {
#endif
/* These definitions should only be used by consumers of the Device Array API.
* Consumers access the API through the opaque pointer stored in
* _devicearray._DEVICEARRAY_API. We don't want these definitions in
* _devicearray.cpp itself because they would conflict with the actual
* implementations there.
*/
#ifndef NUMBA_IN_DEVICEARRAY_CPP_
extern void **DeviceArray_API;
#define DeviceArrayType (*(PyTypeObject*)DeviceArray_API[0])
#endif /* ndef NUMBA_IN_DEVICEARRAY_CPP */
#ifdef __cplusplus
}
#endif
#endif /* NUMBA_DEVICEARRAY_H_ */

View File

@@ -0,0 +1,552 @@
/*
* Definition of Environment and Closure objects.
* This module is included by _dynfuncmod.c and by pycc-compiled modules.
*/
#include "_pymodule.h"
#include <string.h>
// if python version is 3.13
#if ((PY_MAJOR_VERSION == 3) && ((PY_MINOR_VERSION == 13) || PY_MINOR_VERSION == 14))
#include "pythoncapi_compat.h"
#define _Py_IsFinalizing Py_IsFinalizing
#endif
/* NOTE: EnvironmentObject and ClosureObject must be kept in sync with
* the definitions in numba/targets/base.py (EnvBody and ClosureBody).
*/
/*
* EnvironmentObject hosts data needed for execution of compiled functions.
*/
typedef struct {
PyObject_HEAD
PyObject *globals;
/* Assorted "constants" that are needed at runtime to execute
the compiled function. This can include frozen closure variables,
lifted loops, etc. */
PyObject *consts;
} EnvironmentObject;
static PyMemberDef env_members[] = {
{"globals", T_OBJECT, offsetof(EnvironmentObject, globals), READONLY, NULL},
{"consts", T_OBJECT, offsetof(EnvironmentObject, consts), READONLY, NULL},
{NULL} /* Sentinel */
};
static int
env_traverse(EnvironmentObject *env, visitproc visit, void *arg)
{
Py_VISIT(env->globals);
Py_VISIT(env->consts);
return 0;
}
static int
env_clear(EnvironmentObject *env)
{
Py_CLEAR(env->globals);
Py_CLEAR(env->consts);
return 0;
}
static void
env_dealloc(EnvironmentObject *env)
{
PyObject_GC_UnTrack((PyObject *) env);
env_clear(env);
Py_TYPE(env)->tp_free((PyObject *) env);
}
static EnvironmentObject *
env_new_empty(PyTypeObject* type)
{
return (EnvironmentObject *) PyType_GenericNew(type, NULL, NULL);
}
static PyObject *
env_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
PyObject *globals;
EnvironmentObject *env;
static char *kwlist[] = {"globals", 0};
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!:function", kwlist,
&PyDict_Type, &globals))
return NULL;
env = env_new_empty(type);
if (env == NULL)
return NULL;
Py_INCREF(globals);
env->globals = globals;
env->consts = PyList_New(0);
if (!env->consts) {
Py_DECREF(env);
return NULL;
}
return (PyObject *) env;
}
static PyTypeObject EnvironmentType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_dynfunc.Environment", /* tp_name */
sizeof(EnvironmentObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor) env_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr*/
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc) env_traverse, /* tp_traverse */
(inquiry) env_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
env_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
env_new, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
0, /* tp_vectorcall */
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 12)
/* This was introduced first in 3.12
* https://github.com/python/cpython/issues/91051
*/
0, /* tp_watched */
#endif
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 13)
/* This was introduced in 3.13
* https://github.com/python/cpython/pull/114900
*/
0, /* tp_versions_used */
#endif
/* WARNING: Do not remove this, only modify it! It is a version guard to
* act as a reminder to update this struct on Python version update! */
#if (PY_MAJOR_VERSION == 3)
#if ! (NB_SUPPORTED_PYTHON_MINOR)
#error "Python minor version is not supported."
#endif
#else
#error "Python major version is not supported."
#endif
/* END WARNING*/
};
/* A closure object is created for each call to make_function(), and stored
as the resulting PyCFunction object's "self" pointer. It points to an
EnvironmentObject which is constructed during compilation. This allows
for two things:
- lifetime management of dependent data (e.g. lifted loop dispatchers)
- access to the execution environment by the compiled function
(for example the globals module)
*/
/* Closure is a variable-sized object for binary compatibility with
Generator (see below). */
#define CLOSURE_HEAD \
PyObject_VAR_HEAD \
EnvironmentObject *env;
typedef struct {
CLOSURE_HEAD
/* The dynamically-filled method definition for the PyCFunction object
using this closure. */
PyMethodDef def;
/* Arbitrary object to keep alive during the closure's lifetime.
(put a tuple to put several objects alive).
In practice, this helps keep the LLVM module and its generated
code alive. */
PyObject *keepalive;
PyObject *weakreflist;
} ClosureObject;
static int
closure_traverse(ClosureObject *clo, visitproc visit, void *arg)
{
Py_VISIT(clo->env);
Py_VISIT(clo->keepalive);
return 0;
}
static void
closure_dealloc(ClosureObject *clo)
{
PyObject_GC_UnTrack((PyObject *) clo);
if (clo->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) clo);
PyObject_Free((void *) clo->def.ml_name);
PyObject_Free((void *) clo->def.ml_doc);
Py_XDECREF(clo->env);
Py_XDECREF(clo->keepalive);
Py_TYPE(clo)->tp_free((PyObject *) clo);
}
static PyTypeObject ClosureType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_dynfunc._Closure", /* tp_name */
sizeof(ClosureObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor) closure_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc) closure_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
offsetof(ClosureObject, weakreflist), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
0, /* tp_vectorcall */
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 12)
/* This was introduced first in 3.12
* https://github.com/python/cpython/issues/91051
*/
0, /* tp_watched */
#endif
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 13)
/* This was introduced in 3.13
* https://github.com/python/cpython/pull/114900
*/
0, /* tp_versions_used */
#endif
/* WARNING: Do not remove this, only modify it! It is a version guard to
* act as a reminder to update this struct on Python version update! */
#if (PY_MAJOR_VERSION == 3)
#if ! (NB_SUPPORTED_PYTHON_MINOR)
#error "Python minor version is not supported."
#endif
#else
#error "Python major version is not supported."
#endif
/* END WARNING*/
};
/* Return an owned piece of character data duplicating a Python string
object's value. */
static char *
dup_string(PyObject *strobj)
{
const char *tmp = NULL;
char *str;
tmp = PyString_AsString(strobj);
if (tmp == NULL)
return NULL;
/* Using PyObject_Malloc allows this memory to be tracked for
leaks. */
str = PyObject_Malloc(strlen(tmp) + 1);
if (str == NULL) {
PyErr_NoMemory();
return NULL;
}
strcpy(str, tmp);
return str;
}
/* Create and initialize a new Closure object */
static ClosureObject *
closure_new(PyObject *name, PyObject *doc, PyCFunction fnaddr,
EnvironmentObject *env, PyObject *keepalive)
{
ClosureObject *clo = (ClosureObject *) PyType_GenericAlloc(&ClosureType, 0);
if (clo == NULL)
return NULL;
clo->def.ml_name = dup_string(name);
if (!clo->def.ml_name) {
Py_DECREF(clo);
return NULL;
}
clo->def.ml_meth = fnaddr;
clo->def.ml_flags = METH_VARARGS | METH_KEYWORDS;
clo->def.ml_doc = dup_string(doc);
if (!clo->def.ml_doc) {
Py_DECREF(clo);
return NULL;
}
Py_INCREF(env);
clo->env = env;
Py_XINCREF(keepalive);
clo->keepalive = keepalive;
return clo;
}
/* Create a new PyCFunction object wrapping a closure defined by
the given arguments. */
static PyObject *
pycfunction_new(PyObject *module, PyObject *name, PyObject *doc,
PyCFunction fnaddr, EnvironmentObject *env, PyObject *keepalive)
{
PyObject *funcobj;
PyObject *modname = NULL;
ClosureObject *closure = NULL;
closure = closure_new(name, doc, fnaddr, env, keepalive);
if (closure == NULL) goto FAIL;
modname = PyObject_GetAttrString(module, "__name__");
if (modname == NULL) goto FAIL;
funcobj = PyCFunction_NewEx(&closure->def, (PyObject *) closure, modname);
Py_DECREF(closure);
Py_DECREF(modname);
return funcobj;
FAIL:
Py_XDECREF(closure);
Py_XDECREF(modname);
return NULL;
}
/*
* Python-facing wrapper for Numba-compiled generator.
* Note the Environment's offset inside the struct is the same as in the
* Closure object. This is required to simplify generation of Python wrappers.
*/
typedef void (*gen_finalizer_t)(void *);
typedef struct {
CLOSURE_HEAD
PyCFunctionWithKeywords nextfunc;
gen_finalizer_t finalizer;
PyObject *weakreflist;
union {
double dummy; /* Force alignment */
char state[0];
};
} GeneratorObject;
static int
generator_traverse(GeneratorObject *gen, visitproc visit, void *arg)
{
/* XXX this doesn't traverse the state, which can own references to
PyObjects */
Py_VISIT(gen->env);
return 0;
}
static int
generator_clear(GeneratorObject *gen)
{
if (gen->finalizer != NULL) {
gen->finalizer(gen->state);
gen->finalizer = NULL;
}
Py_CLEAR(gen->env);
gen->nextfunc = NULL;
return 0;
}
static void
generator_dealloc(GeneratorObject *gen)
{
PyObject_GC_UnTrack((PyObject *) gen);
if (gen->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) gen);
/* XXX The finalizer may be called after the LLVM module has been
destroyed (typically at interpreter shutdown) */
if (!_Py_IsFinalizing())
if (gen->finalizer != NULL)
gen->finalizer(gen->state);
Py_XDECREF(gen->env);
Py_TYPE(gen)->tp_free((PyObject *) gen);
}
static PyObject *
generator_iternext(GeneratorObject *gen)
{
PyObject *res, *args;
if (gen->nextfunc == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"cannot call next() on finalized generator");
return NULL;
}
args = PyTuple_Pack(1, (PyObject *) gen);
if (args == NULL)
return NULL;
res = (*gen->nextfunc)((PyObject *) gen, args, NULL);
Py_DECREF(args);
return res;
}
static PyTypeObject GeneratorType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_dynfunc._Generator", /* tp_name*/
offsetof(GeneratorObject, state), /* tp_basicsize*/
1, /* tp_itemsize*/
(destructor) generator_dealloc, /* tp_dealloc*/
0, /* tp_vectorcall_offset*/
0, /* tp_getattr*/
0, /* tp_setattr*/
0, /* tp_as_async*/
0, /* tp_repr*/
0, /* tp_as_number*/
0, /* tp_as_sequence*/
0, /* tp_as_mapping*/
0, /* tp_hash */
0, /* tp_call*/
0, /* tp_str*/
0, /* tp_getattro*/
0, /* tp_setattro*/
0, /* tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC
| Py_TPFLAGS_BASETYPE, /* tp_flags*/
0, /* tp_doc */
(traverseproc) generator_traverse, /* tp_traverse */
(inquiry) generator_clear, /* tp_clear */
0, /* tp_richcompare */
offsetof(GeneratorObject, weakreflist), /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc) generator_iternext, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
0, /* tp_vectorcall */
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 12)
/* This was introduced first in 3.12
* https://github.com/python/cpython/issues/91051
*/
0, /* tp_watched */
#endif
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 13)
/* This was introduced in 3.13
* https://github.com/python/cpython/pull/114900
*/
0, /* tp_versions_used */
#endif
/* WARNING: Do not remove this, only modify it! It is a version guard to
* act as a reminder to update this struct on Python version update! */
#if (PY_MAJOR_VERSION == 3)
#if ! (NB_SUPPORTED_PYTHON_MINOR)
#error "Python minor version is not supported."
#endif
#else
#error "Python major version is not supported."
#endif
/* END WARNING*/
};
/* Dynamically create a new generator object */
static PyObject *
Numba_make_generator(Py_ssize_t gen_state_size,
void *initial_state,
PyCFunctionWithKeywords nextfunc,
gen_finalizer_t finalizer,
EnvironmentObject *env)
{
GeneratorObject *gen;
gen = (GeneratorObject *) PyType_GenericAlloc(&GeneratorType, gen_state_size);
if (gen == NULL)
return NULL;
memcpy(gen->state, initial_state, gen_state_size);
gen->nextfunc = nextfunc;
Py_XINCREF(env);
gen->env = env;
gen->finalizer = finalizer;
return (PyObject *) gen;
}
/* Initialization subroutine for use by modules including this */
static int
init_dynfunc_module(PyObject *module)
{
if (PyType_Ready(&ClosureType))
return -1;
if (PyType_Ready(&EnvironmentType))
return -1;
if (PyType_Ready(&GeneratorType))
return -1;
return 0;
}

View File

@@ -0,0 +1,93 @@
#include "_dynfunc.c"
/* Python-facing function to dynamically create a new C function object */
static PyObject*
make_function(PyObject *self, PyObject *args)
{
PyObject *module, *fname, *fdoc, *fnaddrobj;
void *fnaddr;
EnvironmentObject *env;
PyObject *keepalive;
if (!PyArg_ParseTuple(args, "OOOOO!|O",
&module, &fname, &fdoc, &fnaddrobj, &EnvironmentType, &env,
&keepalive)) {
return NULL;
}
fnaddr = PyLong_AsVoidPtr(fnaddrobj);
if (fnaddr == NULL && PyErr_Occurred())
return NULL;
return pycfunction_new(module, fname, fdoc, fnaddr, env, keepalive);
}
static PyMethodDef ext_methods[] = {
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
declmethod(make_function),
{ NULL },
#undef declmethod
};
static PyObject *
build_c_helpers_dict(void)
{
PyObject *dct = PyDict_New();
if (dct == NULL)
goto error;
#define _declpointer(name, value) do { \
PyObject *o = PyLong_FromVoidPtr(value); \
if (o == NULL) goto error; \
if (PyDict_SetItemString(dct, name, o)) { \
Py_DECREF(o); \
goto error; \
} \
Py_DECREF(o); \
} while (0)
#define declmethod(func) _declpointer(#func, &Numba_##func)
#define declpointer(ptr) _declpointer(#ptr, &ptr)
declmethod(make_generator);
#undef declmethod
return dct;
error:
Py_XDECREF(dct);
return NULL;
}
MOD_INIT(_dynfunc) {
PyObject *m, *impl_info;
MOD_DEF(m, "_dynfunc", "No docs", ext_methods)
if (m == NULL)
return MOD_ERROR_VAL;
if (init_dynfunc_module(m))
return MOD_ERROR_VAL;
impl_info = Py_BuildValue(
"{snsnsn}",
"offsetof_closure_body", offsetof(ClosureObject, env),
"offsetof_env_body", offsetof(EnvironmentObject, globals),
"offsetof_generator_state", offsetof(GeneratorObject, state)
);
if (impl_info == NULL)
return MOD_ERROR_VAL;
PyModule_AddObject(m, "_impl_info", impl_info);
Py_INCREF(&ClosureType);
PyModule_AddObject(m, "_Closure", (PyObject *) (&ClosureType));
Py_INCREF(&EnvironmentType);
PyModule_AddObject(m, "Environment", (PyObject *) (&EnvironmentType));
Py_INCREF(&GeneratorType);
PyModule_AddObject(m, "_Generator", (PyObject *) (&GeneratorType));
PyModule_AddObject(m, "c_helpers", build_c_helpers_dict());
return MOD_SUCCESS_VAL(m);
}

View File

@@ -0,0 +1,132 @@
/*
* See _hashtable.c for more information about this file.
*/
#ifndef Py_HASHTABLE_H
#define Py_HASHTABLE_H
/* The whole API is private */
#ifndef Py_LIMITED_API
typedef struct _Py_slist_item_s {
struct _Py_slist_item_s *next;
} _Py_slist_item_t;
typedef struct {
_Py_slist_item_t *head;
} _Py_slist_t;
#define _Py_SLIST_ITEM_NEXT(ITEM) (((_Py_slist_item_t *)ITEM)->next)
#define _Py_SLIST_HEAD(SLIST) (((_Py_slist_t *)SLIST)->head)
typedef struct {
/* used by _Numba_hashtable_t.buckets to link entries */
_Py_slist_item_t _Py_slist_item;
const void *key;
Py_uhash_t key_hash;
/* data follows */
} _Numba_hashtable_entry_t;
#define _Numba_HASHTABLE_ENTRY_DATA(ENTRY) \
((char *)(ENTRY) + sizeof(_Numba_hashtable_entry_t))
#define _Numba_HASHTABLE_ENTRY_DATA_AS_VOID_P(ENTRY) \
(*(void **)_Numba_HASHTABLE_ENTRY_DATA(ENTRY))
#define _Numba_HASHTABLE_ENTRY_READ_DATA(TABLE, DATA, DATA_SIZE, ENTRY) \
do { \
assert((DATA_SIZE) == (TABLE)->data_size); \
memcpy(DATA, _Numba_HASHTABLE_ENTRY_DATA(ENTRY), DATA_SIZE); \
} while (0)
typedef Py_uhash_t (*_Numba_hashtable_hash_func) (const void *key);
typedef int (*_Numba_hashtable_compare_func) (const void *key, const _Numba_hashtable_entry_t *he);
typedef void* (*_Numba_hashtable_copy_data_func)(void *data);
typedef void (*_Numba_hashtable_free_data_func)(void *data);
typedef size_t (*_Numba_hashtable_get_data_size_func)(void *data);
typedef struct {
/* allocate a memory block */
void* (*malloc) (size_t size);
/* release a memory block */
void (*free) (void *ptr);
} _Numba_hashtable_allocator_t;
typedef struct {
size_t num_buckets;
size_t entries; /* Total number of entries in the table. */
_Py_slist_t *buckets;
size_t data_size;
_Numba_hashtable_hash_func hash_func;
_Numba_hashtable_compare_func compare_func;
_Numba_hashtable_copy_data_func copy_data_func;
_Numba_hashtable_free_data_func free_data_func;
_Numba_hashtable_get_data_size_func get_data_size_func;
_Numba_hashtable_allocator_t alloc;
} _Numba_hashtable_t;
/* hash and compare functions for integers and pointers */
extern "C" PyAPI_FUNC(Py_uhash_t) _Numba_hashtable_hash_ptr(const void *key);
extern "C" PyAPI_FUNC(Py_uhash_t) _Numba_hashtable_hash_int(const void *key);
extern "C" PyAPI_FUNC(int) _Numba_hashtable_compare_direct(const void *key, const _Numba_hashtable_entry_t *entry);
extern "C" PyAPI_FUNC(_Numba_hashtable_t *) _Numba_hashtable_new(
size_t data_size,
_Numba_hashtable_hash_func hash_func,
_Numba_hashtable_compare_func compare_func);
extern "C" PyAPI_FUNC(_Numba_hashtable_t *) _Numba_hashtable_new_full(
size_t data_size,
size_t init_size,
_Numba_hashtable_hash_func hash_func,
_Numba_hashtable_compare_func compare_func,
_Numba_hashtable_copy_data_func copy_data_func,
_Numba_hashtable_free_data_func free_data_func,
_Numba_hashtable_get_data_size_func get_data_size_func,
_Numba_hashtable_allocator_t *allocator);
extern "C" PyAPI_FUNC(_Numba_hashtable_t *) _Numba_hashtable_copy(_Numba_hashtable_t *src);
extern "C" PyAPI_FUNC(void) _Numba_hashtable_clear(_Numba_hashtable_t *ht);
extern "C" PyAPI_FUNC(void) _Numba_hashtable_destroy(_Numba_hashtable_t *ht);
typedef int (*_Numba_hashtable_foreach_func) (_Numba_hashtable_entry_t *entry, void *arg);
extern "C" PyAPI_FUNC(int) _Numba_hashtable_foreach(
_Numba_hashtable_t *ht,
_Numba_hashtable_foreach_func func, void *arg);
extern "C" PyAPI_FUNC(size_t) _Numba_hashtable_size(_Numba_hashtable_t *ht);
extern "C" PyAPI_FUNC(_Numba_hashtable_entry_t*) _Numba_hashtable_get_entry(
_Numba_hashtable_t *ht,
const void *key);
extern "C" PyAPI_FUNC(int) _Numba_hashtable_set(
_Numba_hashtable_t *ht,
const void *key,
void *data,
size_t data_size);
extern "C" PyAPI_FUNC(int) _Numba_hashtable_get(
_Numba_hashtable_t *ht,
const void *key,
void *data,
size_t data_size);
extern "C" PyAPI_FUNC(int) _Numba_hashtable_pop(
_Numba_hashtable_t *ht,
const void *key,
void *data,
size_t data_size);
extern "C" PyAPI_FUNC(void) _Numba_hashtable_delete(
_Numba_hashtable_t *ht,
const void *key);
#define _Numba_HASHTABLE_SET(TABLE, KEY, DATA) \
_Numba_hashtable_set(TABLE, KEY, &(DATA), sizeof(DATA))
#define _Numba_HASHTABLE_GET(TABLE, KEY, DATA) \
_Numba_hashtable_get(TABLE, KEY, &(DATA), sizeof(DATA))
#endif /* Py_LIMITED_API */
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
def _import_cython_function(module_name: str, function_name: str):
...

View File

@@ -0,0 +1,277 @@
/*
Expose all functions as pointers in a dedicated C extension.
*/
#include "cext/cext.h"
/* Import _pymodule.h first, for a recent _POSIX_C_SOURCE */
#include "_pymodule.h"
#include <math.h>
#ifdef _MSC_VER
#define false 0
#define true 1
#define bool int
#else
#include <stdbool.h>
#endif
/*
Include C-extension here
*/
#include "cext/cext.h"
/* Numba C helpers */
#include "_helperlib.c"
static PyObject *
build_c_helpers_dict(void)
{
PyObject *dct = PyDict_New();
if (dct == NULL)
goto error;
#define _declpointer(name, value) do { \
PyObject *o = PyLong_FromVoidPtr(value); \
if (o == NULL) goto error; \
if (PyDict_SetItemString(dct, name, o)) { \
Py_DECREF(o); \
goto error; \
} \
Py_DECREF(o); \
} while (0)
#define declmethod(func) _declpointer(#func, &numba_##func)
#define declpointer(ptr) _declpointer(#ptr, &numba_##ptr)
declmethod(fixed_fmod);
declmethod(fixed_fmodf);
declmethod(set_fnclex);
declmethod(sdiv);
declmethod(srem);
declmethod(udiv);
declmethod(urem);
declmethod(frexp);
declmethod(frexpf);
declmethod(ldexp);
declmethod(ldexpf);
declmethod(cpow);
declmethod(cpowf);
declmethod(erf);
declmethod(erff);
declmethod(erfc);
declmethod(erfcf);
declmethod(gamma);
declmethod(gammaf);
declmethod(lgamma);
declmethod(lgammaf);
declmethod(nextafter);
declmethod(nextafterf);
declmethod(complex_adaptor);
declmethod(adapt_ndarray);
declmethod(ndarray_new);
declmethod(extract_record_data);
declmethod(get_buffer);
declmethod(adapt_buffer);
declmethod(release_buffer);
declmethod(extract_np_datetime);
declmethod(create_np_datetime);
declmethod(extract_np_timedelta);
declmethod(create_np_timedelta);
declmethod(recreate_record);
declmethod(fptoui);
declmethod(fptouif);
declmethod(gil_ensure);
declmethod(gil_release);
declmethod(fatal_error);
declmethod(py_type);
declmethod(unpack_slice);
declmethod(do_raise);
declmethod(unpickle);
declmethod(runtime_build_excinfo_struct);
declmethod(attempt_nocopy_reshape);
declmethod(get_pyobject_private_data);
declmethod(set_pyobject_private_data);
declmethod(reset_pyobject_private_data);
/* BLAS / LAPACK */
declmethod(xxgemm);
declmethod(xxgemv);
declmethod(xxdot);
declmethod(xxgetrf);
declmethod(ez_xxgetri);
declmethod(xxpotrf);
declmethod(ez_rgeev);
declmethod(ez_cgeev);
declmethod(ez_xxxevd);
declmethod(ez_gesdd);
declmethod(ez_geqrf);
declmethod(ez_xxgqr);
declmethod(ez_gelsd);
declmethod(xgesv);
declmethod(xxnrm2);
/* PRNG support */
declmethod(get_py_random_state);
declmethod(get_np_random_state);
declmethod(get_internal_random_state);
declmethod(rnd_shuffle);
declmethod(rnd_init);
declmethod(poisson_ptrs);
/* Unicode string support */
declmethod(extract_unicode);
declmethod(gettyperecord);
declmethod(get_PyUnicode_ExtendedCase);
/* for gdb breakpoint */
declmethod(gdb_breakpoint);
/* for dictionary support */
declmethod(test_dict);
declmethod(dict_new_sized);
declmethod(dict_set_method_table);
declmethod(dict_free);
declmethod(dict_length);
declmethod(dict_lookup);
declmethod(dict_insert);
declmethod(dict_insert_ez);
declmethod(dict_delitem);
declmethod(dict_popitem);
declmethod(dict_iter_sizeof);
declmethod(dict_iter);
declmethod(dict_iter_next);
declmethod(dict_dump);
/* for list support */
declmethod(test_list);
declmethod(list_new);
declmethod(list_set_method_table);
declmethod(list_free);
declmethod(list_base_ptr);
declmethod(list_size_address);
declmethod(list_length);
declmethod(list_allocated);
declmethod(list_is_mutable);
declmethod(list_set_is_mutable);
declmethod(list_setitem);
declmethod(list_getitem);
declmethod(list_append);
declmethod(list_delitem);
declmethod(list_delete_slice);
declmethod(list_iter_sizeof);
declmethod(list_iter);
declmethod(list_iter_next);
#define MATH_UNARY(F, R, A) declmethod(F);
#define MATH_BINARY(F, R, A, B) declmethod(F);
#include "mathnames.h"
#undef MATH_UNARY
#undef MATH_BINARY
#undef declmethod
return dct;
error:
Py_XDECREF(dct);
return NULL;
}
/*
* Helper to deal with flushing stdout
*/
PyAPI_FUNC(void) _numba_flush_stdout(void) ;
void
_numba_flush_stdout(void) {
fflush(stdout);
}
static PyMethodDef ext_methods[] = {
{ "rnd_get_state", (PyCFunction) _numba_rnd_get_state, METH_O, NULL },
{ "rnd_get_py_state_ptr", (PyCFunction) _numba_rnd_get_py_state_ptr, METH_NOARGS, NULL },
{ "rnd_get_np_state_ptr", (PyCFunction) _numba_rnd_get_np_state_ptr, METH_NOARGS, NULL },
{ "rnd_seed", (PyCFunction) _numba_rnd_seed, METH_VARARGS, NULL },
{ "rnd_set_state", (PyCFunction) _numba_rnd_set_state, METH_VARARGS, NULL },
{ "rnd_shuffle", (PyCFunction) _numba_rnd_shuffle, METH_O, NULL },
{ "_import_cython_function", (PyCFunction) _numba_import_cython_function, METH_VARARGS, NULL },
{ NULL },
};
/*
* These functions are exported by the module's DLL, to exercise ctypes / cffi
* without relying on libc availability (see https://bugs.python.org/issue23606)
*/
PyAPI_FUNC(double) _numba_test_sin(double x);
PyAPI_FUNC(double) _numba_test_cos(double x);
PyAPI_FUNC(double) _numba_test_exp(double x);
PyAPI_FUNC(void) _numba_test_vsquare(int n, double *x, double *out);
PyAPI_FUNC(double) _numba_test_funcptr(double (*func)(double));
PyAPI_FUNC(bool) _numba_test_boolean(void);
double _numba_test_sin(double x)
{
return sin(x);
}
double _numba_test_cos(double x)
{
return cos(x);
}
double _numba_test_exp(double x)
{
return exp(x);
}
void _numba_test_vsquare(int n, double *x, double *out)
{
int i;
for (i = 0; i < n; i++)
out[i] = pow(x[i], 2.0);
}
void _numba_test_vcube(int n, double *x, double *out)
{
int i;
for (i = 0; i < n; i++)
out[i] = pow(x[i], 3.0);
}
double _numba_test_funcptr(double (*func)(double))
{
return func(1.5);
}
bool _numba_test_boolean()
{
return true;
}
MOD_INIT(_helperlib) {
PyObject *m;
MOD_DEF(m, "_helperlib", "No docs", ext_methods)
if (m == NULL)
return MOD_ERROR_VAL;
import_array();
PyModule_AddObject(m, "c_helpers", build_c_helpers_dict());
PyModule_AddIntConstant(m, "long_min", LONG_MIN);
PyModule_AddIntConstant(m, "long_max", LONG_MAX);
PyModule_AddIntConstant(m, "py_buffer_size", sizeof(Py_buffer));
PyModule_AddIntConstant(m, "py_gil_state_size", sizeof(PyGILState_STATE));
PyModule_AddIntConstant(m, "py_unicode_1byte_kind", PyUnicode_1BYTE_KIND);
PyModule_AddIntConstant(m, "py_unicode_2byte_kind", PyUnicode_2BYTE_KIND);
PyModule_AddIntConstant(m, "py_unicode_4byte_kind", PyUnicode_4BYTE_KIND);
#if (PY_MAJOR_VERSION == 3)
#if ((PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11))
PyModule_AddIntConstant(m, "py_unicode_wchar_kind", PyUnicode_WCHAR_KIND);
#endif
#endif
numba_rnd_ensure_global_init();
return MOD_SUCCESS_VAL(m);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
#ifndef NUMBA_COMMON_H_
#define NUMBA_COMMON_H_
/* __has_attribute() is a clang / gcc-5 macro */
#ifndef __has_attribute
# define __has_attribute(x) 0
#endif
/* This attribute marks symbols that can be shared across C objects
* but are not exposed outside of a shared library or executable.
* Note this is default behaviour for global symbols under Windows.
*/
#if defined(_MSC_VER)
#define VISIBILITY_HIDDEN
#define VISIBILITY_GLOBAL __declspec(dllexport)
#elif (__has_attribute(visibility) || (defined(__GNUC__) && __GNUC__ >= 4))
#define VISIBILITY_HIDDEN __attribute__ ((visibility("hidden")))
#define VISIBILITY_GLOBAL __attribute__ ((visibility("default")))
#else
#define VISIBILITY_HIDDEN
#define VISIBILITY_GLOBAL
#endif
/*
* Numba's version of the PyArray_DescrCheck macro from NumPy, use it as a
* direct replacement of NumPy's PyArray_DescrCheck to ensure binary
* compatibility.
*
* Details of why this is needed:
* NumPy 1.18 changed the definition of the PyArray_DescrCheck macro here:
* https://github.com/numpy/numpy/commit/6108b5d1e138d07e3c9f2a4e3b1933749ad0e698
* the result of this being that building against NumPy <1.18 would prevent
* Numba running against NumPy >= 1.20 as noted here:
* https://github.com/numba/numba/issues/6041#issuecomment-665132199
*
* This macro definition is copied from:
* https://github.com/numpy/numpy/commit/6108b5d1e138d07e3c9f2a4e3b1933749ad0e698#diff-ad2213da23136c5fc5883d9eb2d88666R26
*
* NOTE: This is the NumPy 1.18 and above version of the macro.
*/
#define NUMBA_PyArray_DescrCheck(op) PyObject_TypeCheck(op, &PyArrayDescr_Type)
#endif /* NUMBA_COMMON_H_ */

View File

@@ -0,0 +1,51 @@
#ifndef NUMBA_PY_MODULE_H_
#define NUMBA_PY_MODULE_H_
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "structmember.h"
#include "frameobject.h"
/*
* Macro to handle GIL/free-threading at module level. Call on a newly created
* module returned by a call to PyModule_Create().
*/
#ifdef Py_GIL_DISABLED
#define MOD_NOGIL(m) \
do { \
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); \
} while(0)
#else
#define MOD_NOGIL(m) do {} while(0)
#endif
#define MOD_ERROR_VAL NULL
#define MOD_SUCCESS_VAL(val) val
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#define MOD_DEF(ob, name, doc, methods) { \
static struct PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, name, doc, -1, methods, NULL, NULL, NULL, NULL }; \
ob = PyModule_Create(&moduledef); \
if (ob == NULL) { return MOD_ERROR_VAL; } \
MOD_NOGIL(ob); \
}
#define MOD_INIT_EXEC(name) PyInit_##name();
#define PyString_AsString PyUnicode_AsUTF8
#define PyString_Check PyUnicode_Check
#define PyString_FromFormat PyUnicode_FromFormat
#define PyString_FromString PyUnicode_FromString
#define PyString_InternFromString PyUnicode_InternFromString
#define PyInt_Type PyLong_Type
#define PyInt_Check PyLong_Check
#define PyInt_CheckExact PyLong_CheckExact
#define SetAttrStringFromVoidPointer(m, name) do { \
PyObject *tmp = PyLong_FromVoidPtr((void *) &name); \
PyObject_SetAttrString(m, #name, tmp); \
Py_DECREF(tmp); } while (0)
#define NB_SUPPORTED_PYTHON_MINOR ((PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12) || (PY_MINOR_VERSION == 13) || (PY_MINOR_VERSION == 14))
#endif /* NUMBA_PY_MODULE_H_ */

View File

@@ -0,0 +1,492 @@
/*
* PRNG support.
*/
#ifdef _MSC_VER
#define HAVE_PTHREAD_ATFORK 0
#else
#define HAVE_PTHREAD_ATFORK 1
#include <pthread.h>
#endif
/* Magic Mersenne Twister constants */
#define MT_N 624
#define MT_M 397
#define MT_MATRIX_A 0x9908b0dfU
#define MT_UPPER_MASK 0x80000000U
#define MT_LOWER_MASK 0x7fffffffU
/*
* Note this structure is accessed in numba.targets.randomimpl,
* any changes here should be reflected there too.
*/
typedef struct {
int index;
/* unsigned int is sufficient on modern machines as we only need 32 bits */
unsigned int mt[MT_N];
int has_gauss;
double gauss;
int is_initialized;
} rnd_state_t;
/* Some code portions below from CPython's _randommodule.c, some others
from Numpy's and Jean-Sebastien Roy's randomkit.c. */
NUMBA_EXPORT_FUNC(void)
numba_rnd_shuffle(rnd_state_t *state)
{
int i;
unsigned int y;
for (i = 0; i < MT_N - MT_M; i++) {
y = (state->mt[i] & MT_UPPER_MASK) | (state->mt[i+1] & MT_LOWER_MASK);
state->mt[i] = state->mt[i+MT_M] ^ (y >> 1) ^
(-(int) (y & 1) & MT_MATRIX_A);
}
for (; i < MT_N - 1; i++) {
y = (state->mt[i] & MT_UPPER_MASK) | (state->mt[i+1] & MT_LOWER_MASK);
state->mt[i] = state->mt[i+(MT_M-MT_N)] ^ (y >> 1) ^
(-(int) (y & 1) & MT_MATRIX_A);
}
y = (state->mt[MT_N - 1] & MT_UPPER_MASK) | (state->mt[0] & MT_LOWER_MASK);
state->mt[MT_N - 1] = state->mt[MT_M - 1] ^ (y >> 1) ^
(-(int) (y & 1) & MT_MATRIX_A);
}
/* Initialize mt[] with an integer seed */
NUMBA_EXPORT_FUNC(void)
numba_rnd_init(rnd_state_t *state, unsigned int seed)
{
unsigned int pos;
seed &= 0xffffffffU;
/* Knuth's PRNG as used in the Mersenne Twister reference implementation */
for (pos = 0; pos < MT_N; pos++) {
state->mt[pos] = seed;
seed = (1812433253U * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffU;
}
state->index = MT_N;
state->has_gauss = 0;
state->gauss = 0.0;
state->is_initialized = 1;
}
/* Perturb mt[] with a key array */
static void
rnd_init_by_array(rnd_state_t *state, unsigned int init_key[], size_t key_length)
{
size_t i, j, k;
unsigned int *mt = state->mt;
numba_rnd_init(state, 19650218U);
i = 1; j = 0;
k = (MT_N > key_length ? MT_N : key_length);
for (; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525U))
+ init_key[j] + (unsigned int) j; /* non linear */
mt[i] &= 0xffffffffU;
i++; j++;
if (i >= MT_N) { mt[0] = mt[MT_N - 1]; i = 1; }
if (j >= key_length) j = 0;
}
for (k = MT_N - 1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941U))
- (unsigned int) i; /* non linear */
mt[i] &= 0xffffffffU;
i++;
if (i >= MT_N) { mt[0] = mt[MT_N - 1]; i=1; }
}
mt[0] = 0x80000000U; /* MSB is 1; ensuring non-zero initial array */
state->index = MT_N;
state->has_gauss = 0;
state->gauss = 0.0;
state->is_initialized = 1;
}
/*
* Management of thread-local random state.
*/
static int rnd_globally_initialized;
#ifdef _MSC_VER
#define THREAD_LOCAL(ty) __declspec(thread) ty
#else
/* Non-standard C99 extension that's understood by gcc and clang */
#define THREAD_LOCAL(ty) __thread ty
#endif
static THREAD_LOCAL(rnd_state_t) numba_py_random_state;
static THREAD_LOCAL(rnd_state_t) numba_np_random_state;
static THREAD_LOCAL(rnd_state_t) numba_internal_random_state;
/* Seed the state with random bytes */
static int
rnd_seed_with_bytes(rnd_state_t *state, Py_buffer *buf)
{
unsigned int *keys;
unsigned char *bytes;
size_t i, nkeys;
nkeys = buf->len / sizeof(unsigned int);
keys = (unsigned int *) PyMem_Malloc(nkeys * sizeof(unsigned int));
if (keys == NULL) {
PyBuffer_Release(buf);
return -1;
}
bytes = (unsigned char *) buf->buf;
/* Convert input bytes to int32 keys, without violating alignment
* constraints.
*/
for (i = 0; i < nkeys; i++, bytes += 4) {
keys[i] =
((unsigned int)bytes[3] << 24) +
((unsigned int)bytes[2] << 16) +
((unsigned int)bytes[1] << 8) +
((unsigned int)bytes[0] << 0);
}
PyBuffer_Release(buf);
rnd_init_by_array(state, keys, nkeys);
PyMem_Free(keys);
return 0;
}
#if HAVE_PTHREAD_ATFORK
/* After a fork(), the child should reseed its random states.
* Since only the main thread survives in the child, it's enough to mark
* the current thread-local states as uninitialized.
*/
static void
rnd_atfork_child(void)
{
numba_py_random_state.is_initialized = 0;
numba_np_random_state.is_initialized = 0;
numba_internal_random_state.is_initialized = 0;
}
#endif
/* Global initialization routine. It must be called as early as possible.
*/
NUMBA_EXPORT_FUNC(void)
numba_rnd_ensure_global_init(void)
{
if (!rnd_globally_initialized) {
#if HAVE_PTHREAD_ATFORK
pthread_atfork(NULL, NULL, rnd_atfork_child);
#endif
numba_py_random_state.is_initialized = 0;
numba_np_random_state.is_initialized = 0;
numba_internal_random_state.is_initialized = 0;
rnd_globally_initialized = 1;
}
}
/* First-time init a random state */
static void
rnd_implicit_init(rnd_state_t *state)
{
/* Initialize with random bytes. The easiest way to get good-quality
* cross-platform random bytes is still to call os.urandom()
* using the Python interpreter...
*/
PyObject *module, *bufobj;
Py_buffer buf;
PyGILState_STATE gilstate = PyGILState_Ensure();
module = PyImport_ImportModule("os");
if (module == NULL)
goto error;
/* Read as many bytes as necessary to get the full entropy
* exploitable by the MT generator.
*/
bufobj = PyObject_CallMethod(module, "urandom", "i",
(int) (MT_N * sizeof(unsigned int)));
Py_DECREF(module);
if (bufobj == NULL)
goto error;
if (PyObject_GetBuffer(bufobj, &buf, PyBUF_SIMPLE))
goto error;
Py_DECREF(bufobj);
if (rnd_seed_with_bytes(state, &buf))
goto error;
/* state->is_initialized is set now */
PyGILState_Release(gilstate);
return;
error:
/* In normal conditions, os.urandom() and PyMem_Malloc() shouldn't fail,
* and we don't want the caller to deal with errors, so just bail out.
*/
if (PyErr_Occurred())
PyErr_Print();
Py_FatalError(NULL);
}
/* Functions returning the thread-local random state pointer.
* The LLVM JIT doesn't support thread-local variables so we rely
* on the C compiler instead.
*/
NUMBA_EXPORT_FUNC(rnd_state_t *)
numba_get_py_random_state(void)
{
rnd_state_t *state = &numba_py_random_state;
if (!state->is_initialized)
rnd_implicit_init(state);
return state;
}
NUMBA_EXPORT_FUNC(rnd_state_t *)
numba_get_np_random_state(void)
{
rnd_state_t *state = &numba_np_random_state;
if (!state->is_initialized)
rnd_implicit_init(state);
return state;
}
NUMBA_EXPORT_FUNC(rnd_state_t *)
numba_get_internal_random_state(void)
{
rnd_state_t *state = &numba_internal_random_state;
if (!state->is_initialized)
rnd_implicit_init(state);
return state;
}
/*
* Python-exposed helpers for state management and testing.
*/
static int
rnd_state_converter(PyObject *obj, rnd_state_t **state)
{
*state = (rnd_state_t *) PyLong_AsVoidPtr(obj);
return (*state != NULL || !PyErr_Occurred());
}
NUMBA_EXPORT_FUNC(PyObject *)
_numba_rnd_get_py_state_ptr(PyObject *self)
{
return PyLong_FromVoidPtr(numba_get_py_random_state());
}
NUMBA_EXPORT_FUNC(PyObject *)
_numba_rnd_get_np_state_ptr(PyObject *self)
{
return PyLong_FromVoidPtr(numba_get_np_random_state());
}
NUMBA_EXPORT_FUNC(PyObject *)
_numba_rnd_shuffle(PyObject *self, PyObject *arg)
{
rnd_state_t *state;
if (!rnd_state_converter(arg, &state))
return NULL;
numba_rnd_shuffle(state);
Py_RETURN_NONE;
}
NUMBA_EXPORT_FUNC(PyObject *)
_numba_rnd_set_state(PyObject *self, PyObject *args)
{
int i, index;
rnd_state_t *state;
PyObject *tuplearg, *intlist;
if (!PyArg_ParseTuple(args, "O&O!:rnd_set_state",
rnd_state_converter, &state,
&PyTuple_Type, &tuplearg))
return NULL;
if (!PyArg_ParseTuple(tuplearg, "iO!", &index, &PyList_Type, &intlist))
return NULL;
if (PyList_GET_SIZE(intlist) != MT_N) {
PyErr_SetString(PyExc_ValueError, "list object has wrong size");
return NULL;
}
state->index = index;
for (i = 0; i < MT_N; i++) {
PyObject *v = PyList_GET_ITEM(intlist, i);
unsigned long x = PyLong_AsUnsignedLong(v);
if (x == (unsigned long) -1 && PyErr_Occurred())
return NULL;
state->mt[i] = (unsigned int) x;
}
state->has_gauss = 0;
state->gauss = 0.0;
state->is_initialized = 1;
Py_RETURN_NONE;
}
NUMBA_EXPORT_FUNC(PyObject *)
_numba_rnd_get_state(PyObject *self, PyObject *arg)
{
PyObject *intlist;
int i;
rnd_state_t *state;
if (!rnd_state_converter(arg, &state))
return NULL;
intlist = PyList_New(MT_N);
if (intlist == NULL)
return NULL;
for (i = 0; i < MT_N; i++) {
PyObject *v = PyLong_FromUnsignedLong(state->mt[i]);
if (v == NULL) {
Py_DECREF(intlist);
return NULL;
}
PyList_SET_ITEM(intlist, i, v);
}
return Py_BuildValue("iN", state->index, intlist);
}
NUMBA_EXPORT_FUNC(PyObject *)
_numba_rnd_seed(PyObject *self, PyObject *args)
{
unsigned int seed;
rnd_state_t *state;
if (!PyArg_ParseTuple(args, "O&I:rnd_seed",
rnd_state_converter, &state, &seed)) {
/* rnd_seed_*(bytes-like object) */
Py_buffer buf;
PyErr_Clear();
if (!PyArg_ParseTuple(args, "O&s*:rnd_seed",
rnd_state_converter, &state, &buf))
return NULL;
if (rnd_seed_with_bytes(state, &buf))
return NULL;
else
Py_RETURN_NONE;
}
else {
/* rnd_seed_*(int32) */
numba_rnd_init(state, seed);
Py_RETURN_NONE;
}
}
/*
* Random distribution helpers.
* Most code straight from Numpy's distributions.c.
*/
#ifndef M_PI
#define M_PI 3.14159265358979323846264338328
#endif
NUMBA_EXPORT_FUNC(unsigned int)
get_next_int32(rnd_state_t *state)
{
unsigned int y;
if (state->index == MT_N) {
numba_rnd_shuffle(state);
state->index = 0;
}
y = state->mt[state->index++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680U;
y ^= (y << 15) & 0xefc60000U;
y ^= (y >> 18);
return y;
}
NUMBA_EXPORT_FUNC(double)
get_next_double(rnd_state_t *state)
{
double a = get_next_int32(state) >> 5;
double b = get_next_int32(state) >> 6;
return (a * 67108864.0 + b) / 9007199254740992.0;
}
NUMBA_EXPORT_FUNC(double)
loggam(double x)
{
double x0, x2, xp, gl, gl0;
long k, n;
static double a[10] = {8.333333333333333e-02,-2.777777777777778e-03,
7.936507936507937e-04,-5.952380952380952e-04,
8.417508417508418e-04,-1.917526917526918e-03,
6.410256410256410e-03,-2.955065359477124e-02,
1.796443723688307e-01,-1.39243221690590e+00};
x0 = x;
n = 0;
if ((x == 1.0) || (x == 2.0))
{
return 0.0;
}
else if (x <= 7.0)
{
n = (long)(7 - x);
x0 = x + n;
}
x2 = 1.0/(x0*x0);
xp = 2*M_PI;
gl0 = a[9];
for (k=8; k>=0; k--)
{
gl0 *= x2;
gl0 += a[k];
}
gl = gl0/x0 + 0.5*log(xp) + (x0-0.5)*log(x0) - x0;
if (x <= 7.0)
{
for (k=1; k<=n; k++)
{
gl -= log(x0-1.0);
x0 -= 1.0;
}
}
return gl;
}
NUMBA_EXPORT_FUNC(int64_t)
numba_poisson_ptrs(rnd_state_t *state, double lam)
{
/* This method is invoked only if the parameter lambda of this
* distribution is big enough ( >= 10 ). The algorithm used is
* described in "Hörmann, W. 1992. 'The Transformed Rejection
* Method for Generating Poisson Random Variables'.
* The implementation comes straight from Numpy.
*/
int64_t k;
double U, V, slam, loglam, a, b, invalpha, vr, us;
slam = sqrt(lam);
loglam = log(lam);
b = 0.931 + 2.53*slam;
a = -0.059 + 0.02483*b;
invalpha = 1.1239 + 1.1328/(b-3.4);
vr = 0.9277 - 3.6224/(b-2);
while (1)
{
U = get_next_double(state) - 0.5;
V = get_next_double(state);
us = 0.5 - fabs(U);
k = (int64_t) floor((2*a/us + b)*U + lam + 0.43);
if ((us >= 0.07) && (V <= vr))
{
return k;
}
if ((k < 0) ||
((us < 0.013) && (V > us)))
{
continue;
}
if ((log(V) + log(invalpha) - log(a/(us*us)+b)) <=
(-lam + (double) k*loglam - loggam((double) k+1)))
{
return k;
}
}
}

View File

@@ -0,0 +1,16 @@
#ifndef NUMBA_TYPEOF_H_
#define NUMBA_TYPEOF_H_
#ifdef __cplusplus
extern "C" {
#endif
extern PyObject *typeof_init(PyObject *self, PyObject *args);
extern int typeof_typecode(PyObject *dispatcher, PyObject *val);
extern PyObject *typeof_compute_fingerprint(PyObject *val);
#ifdef __cplusplus
}
#endif
#endif /* NUMBA_TYPEOF_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
# This file was generated by 'versioneer.py' (0.28) from
# revision-control system data, or from the parent directory name of an
# unpacked source archive. Distribution tarballs contain a pre-generated copy
# of this file.
import json
version_json = '''
{
"date": "2026-03-31T12:27:00-0700",
"dirty": false,
"error": null,
"full-revisionid": "6ebcd9ef0a0372b6c124a6917c39498ffc923868",
"version": "0.65.0"
}
''' # END VERSION_JSON
def get_versions():
return json.loads(version_json)

View File

@@ -0,0 +1,23 @@
"""
Utilities for getting information about Numba C extensions
"""
import os
def get_extension_libs():
"""Return the .c files in the `numba.cext` directory.
"""
libs = []
base = get_path()
for fn in os.listdir(base):
if fn.endswith('.c'):
fn = os.path.join(base, fn)
libs.append(fn)
return libs
def get_path():
"""Returns the path to the directory for `numba.cext`.
"""
return os.path.abspath(os.path.join(os.path.dirname(__file__)))

View File

@@ -0,0 +1,21 @@
#ifndef NUMBA_EXTENSION_HELPER_H_
#define NUMBA_EXTENSION_HELPER_H_
#include "Python.h"
#include "../_numba_common.h"
/* Define all runtime-required symbols in this C module, but do not
export them outside the shared library if possible. */
#define NUMBA_EXPORT_FUNC(_rettype) VISIBILITY_HIDDEN _rettype
#define NUMBA_EXPORT_DATA(_vartype) VISIBILITY_HIDDEN _vartype
/* Use to declare a symbol as exported (global). */
#define NUMBA_GLOBAL_FUNC(_rettype) VISIBILITY_GLOBAL _rettype
NUMBA_EXPORT_FUNC(Py_ssize_t)
aligned_size(Py_ssize_t sz);
#include "dictobject.h"
#include "listobject.h"
#endif // end NUMBA_EXTENSION_HELPER_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,228 @@
/* Adapted from CPython3.7 Objects/dict-common.h */
#include "cext.h"
#ifndef NUMBA_DICT_COMMON_H
#define NUMBA_DICT_COMMON_H
typedef struct {
/* Uses Py_ssize_t instead of Py_hash_t to guarantee word size alignment */
Py_ssize_t hash;
char keyvalue[];
} NB_DictEntry;
typedef int (*dict_key_comparator_t)(const char *lhs, const char *rhs);
typedef void (*dict_refcount_op_t)(const void*);
typedef struct {
dict_key_comparator_t key_equal;
dict_refcount_op_t key_incref;
dict_refcount_op_t key_decref;
dict_refcount_op_t value_incref;
dict_refcount_op_t value_decref;
} type_based_methods_table;
typedef struct {
/* hash table size */
Py_ssize_t size;
/* Usable size of the hash table.
Also, size of the entries */
Py_ssize_t usable;
/* hash table used entries */
Py_ssize_t nentries;
/* Entry info
- key_size is the sizeof key type
- val_size is the sizeof value type
- entry_size is key_size + val_size + alignment
*/
Py_ssize_t key_size, val_size, entry_size;
/* Byte offset from indices to the first entry. */
Py_ssize_t entry_offset;
/* Method table for type-dependent operations. */
type_based_methods_table methods;
/* hash table */
char indices[];
} NB_DictKeys;
typedef struct {
/* num of elements in the hashtable */
Py_ssize_t used;
NB_DictKeys *keys;
} NB_Dict;
typedef struct {
/* parent dictionary */
NB_Dict *parent;
/* parent keys object */
NB_DictKeys *parent_keys;
/* dict size */
Py_ssize_t size;
/* iterator position; indicates the next position to read */
Py_ssize_t pos;
} NB_DictIter;
/* A test function for the dict
Returns 0 for OK; 1 for failure.
*/
NUMBA_EXPORT_FUNC(int)
numba_test_dict(void);
/* Allocate a new dict
Parameters
- NB_Dict **out
Output for the new dictionary.
- Py_ssize_t size
Hashtable size. Must be power of two.
- Py_ssize_t key_size
Size of a key entry.
- Py_ssize_t val_size
Size of a value entry.
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_new(NB_Dict **out, Py_ssize_t size, Py_ssize_t key_size, Py_ssize_t val_size);
/* Allocate a new dict with enough space to hold n_keys without resizing.
Parameters
- NB_Dict **out
Output for the new dictionary.
- Py_ssize_t n_keys
The number of keys to fit without needing resize.
- Py_ssize_t key_size
Size of a key entry.
- Py_ssize_t val_size
Size of a value entry.
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_new_sized(NB_Dict** out, Py_ssize_t n_keys, Py_ssize_t key_size, Py_ssize_t val_size);
/* Free a dict */
NUMBA_EXPORT_FUNC(void)
numba_dict_free(NB_Dict *d);
/* Returns length of a dict */
NUMBA_EXPORT_FUNC(Py_ssize_t)
numba_dict_length(NB_Dict *d);
/* Set the method table for type specific operations
*/
NUMBA_EXPORT_FUNC(void)
numba_dict_set_method_table(NB_Dict *d, type_based_methods_table *methods);
/* Lookup a key
Parameters
- NB_Dict *d
The dictionary object.
- const char *key_bytes
The key as a byte buffer.
- Py_hash_t hash
The precomputed hash of the key.
- char *oldval_bytes
An output parameter to store the associated value if the key is found.
Must point to memory of sufficient size to store the value.
*/
NUMBA_EXPORT_FUNC(Py_ssize_t)
numba_dict_lookup(NB_Dict *d, const char *key_bytes, Py_hash_t hash, char *oldval_bytes);
/* Resize the dict to at least *minsize*.
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_resize(NB_Dict *d, Py_ssize_t minsize);
/* Insert to the dict
Parameters
- NB_Dict *d
The dictionary object.
- const char *key_bytes
The key as a byte buffer.
- Py_hash_t hash
The precomputed hash of key.
- const char *val_bytes
The value as a byte buffer.
- char *oldval_bytes
An output buffer to store the replaced value.
Must point to memory of sufficient size to store the value.
Returns
- < 0 for error
- 0 for ok
- 1 for ok and oldval_bytes has a copy of the replaced value.
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_insert(NB_Dict *d, const char *key_bytes, Py_hash_t hash, const char *val_bytes, char *oldval_bytes);
/* Same as numba_dict_insert() but oldval_bytes is not needed */
NUMBA_EXPORT_FUNC(int)
numba_dict_insert_ez(NB_Dict *d, const char *key_bytes, Py_hash_t hash, const char *val_bytes);
/* Delete an entry from the dict
Parameters
- NB_Dict *d
The dictionary
- Py_hash_t hash
Precomputed hash of the key to be deleted
- Py_ssize_t ix
Precomputed entry index of the key to be deleted.
Usually results of numba_dict_lookup().
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_delitem(NB_Dict *d, Py_hash_t hash, Py_ssize_t ix);
/* Remove an item from the dict
Parameters
- NB_Dict *d
The dictionary
- char *key_bytes
Output. The key as a byte buffer
- char *val_bytes
Output. The value as a byte buffer
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_popitem(NB_Dict *d, char *key_bytes, char *val_bytes);
/* Returns the sizeof a dictionary iterator
*/
NUMBA_EXPORT_FUNC(size_t)
numba_dict_iter_sizeof(void);
/* Fill a NB_DictIter for a dictionary to begin iteration
Parameters
- NB_DictIter *it
Output. Must points to memory of size at least `numba_dict_iter_sizeof()`.
- NB_Dict *d
The dictionary to be iterated.
*/
NUMBA_EXPORT_FUNC(void)
numba_dict_iter(NB_DictIter *it, NB_Dict *d);
/* Advance the iterator
Parameters
- NB_DictIter *it
The iterator
- const char **key_ptr
Output pointer for the key. Points to data in the dictionary.
- const char **val_ptr
Output pointer for the key. Points to data in the dictionary.
Returns
- 0 for success; valid key_ptr and val_ptr
- ERR_ITER_EXHAUSTED for end of iterator.
- ERR_DICT_MUTATED for detected dictionary mutation.
*/
NUMBA_EXPORT_FUNC(int)
numba_dict_iter_next(NB_DictIter *it, const char **key_ptr, const char **val_ptr);
NUMBA_EXPORT_FUNC(void)
numba_dict_dump(NB_Dict *);
#endif

View File

@@ -0,0 +1,977 @@
#include "listobject.h"
/* This implements the C component of the Numba typed list. It is loosely
* inspired by the list implementation of the cpython list with some parts
* taken from the cpython slice implementation. The exact commit-id of the
* relevant files are:
*
* https://github.com/python/cpython/blob/51ddab8dae056867f3595ab3400bffc93f67c8d4/Objects/listobject.c
* https://github.com/python/cpython/blob/51ddab8dae056867f3595ab3400bffc93f67c8d4/Objects/sliceobject.c
*
* Algorithmically, this list is very similar to the cpython implementation so
* it should have the same performance (Big-O) characteristics for accessing,
* adding and removing elements/items. Specifically, it implements the same
* algorithms for list overallocation and growth. However, it never deals with
* PyObject types and instead must be typed with a type-size. As a result, the
* typed-list is type homogeneous and in contrast to the cpython version can
* not store a mixture of arbitrarily typed objects. Reference counting via the
* Numba Runtime (NRT) is supported and incrementing and decrementing functions
* are store as part of the struct and can be setup from the compiler level.
*
* Importantly, only a very limited subset of the cpython c functions have been
* ported over and the rest have been implemented (in Python) at the compiler
* level using the c functions provided. Additionally, initialization of, and
* iteration over, a ListIter is provided
*
* The following functions are implemented for the list:
*
* - Check valid index valid_index
* - Creation numba_list_new
* - Deletion numba_list_free
* - Accessing the length numba_list_length
* - Appending to the list numba_list_append
* - Getting an item numba_list_setitem
* - Setting an item numba_list_getitem
* - Resizing the list numba_list_resize
* - Deleting an item numba_list_delitem
* - Deleting a slice numba_list_delete_slice
*
* As you can see, only a single function for slices is implemented. The rest
* is all done entirely at the compiler level which then calls the c functions
* to mutate the list accordingly. Since slicing allows for replace, insert and
* delete operations over multiple items, we can simply implement those using
* the basic functions above.
*
* The following additional functions are implemented for the list, these are
* needed to make the list work within Numba.
*
* - Accessing the allocation numba_list_allocated
* - Copying an item copy_item
* - Calling incref on item list_incref_item
* - Calling decref on item list_decref_item
* - Set method table numba_list_set_method_table
*
* The following functions are implemented for the iterator:
*
* - Size of the iterator numba_list_iter_size
* - Initialization of iter numba_list_iter
* - Get next item from iter numba_list_iter_next
*
* Two methods are provided to query and set the 'is_mutable':
*
* - Query numba_list_is_mutable
* - Set numba_list_set_is_mutable
*
* Lastly a set of pure C level tests are provided which come in handy when
* needing to use valgrind and friends.
*
*/
/* Return status for the list functions.
*/
typedef enum {
LIST_OK = 0,
LIST_ERR_INDEX = -1,
LIST_ERR_NO_MEMORY = -2,
LIST_ERR_MUTATED = -3,
LIST_ERR_ITER_EXHAUSTED = -4,
LIST_ERR_IMMUTABLE = -5,
} ListStatus;
/* Copy an item from a list.
*
* lp: a list
* dst: destination pointer
* src: source pointer
*/
static void
copy_item(NB_List *lp, char *dst, const char *src){
memcpy(dst, src, lp->item_size);
}
/* Increment a reference to an item in a list.
*
* lp: a list
* item: the item to increment the reference for
*/
static void
list_incref_item(NB_List *lp, const char *item){
if (lp->methods.item_incref) {
lp->methods.item_incref(item);
}
}
/* Decrement a reference to an item in a list.
*
* lp: a list
* item: the item to decrement the reference for
*/
static void
list_decref_item(NB_List *lp, const char *item){
if (lp->methods.item_decref) {
lp->methods.item_decref(item);
}
}
/* Setup the method table for a list.
*
* This function is used from the compiler level to initialize the internal
* method table.
*
* lp: a list
* methods: the methods table to set up
*/
void
numba_list_set_method_table(NB_List *lp, list_type_based_methods_table *methods)
{
memcpy(&lp->methods, methods, sizeof(list_type_based_methods_table));
}
/* Check if a list index is valid.
*
* i: the index to check
* limit: the size of a list
*
* Adapted from CPython's valid_index().
*
* FIXME: need to find a way to inline this, even for Python 2.7 on Windows
*/
static int
valid_index(Py_ssize_t i, Py_ssize_t limit){
/* The cast to size_t lets us use just a single comparison
to check whether i is in the range: 0 <= i < limit.
See: Section 14.2 "Bounds Checking" in the Agner Fog
optimization manual found at:
https://www.agner.org/optimize/optimizing_cpp.pdf
*/
return (size_t) i < (size_t) limit;
}
/* Initialize a new list.
*
* out: pointer to hold an initialized list
* item_size: the size in bytes of the items in the list
* allocated: preallocation of the list in items
*
* This will allocate sufficient memory to hold the list structure and any
* items if requested (allocated != 0). See _listobject.h for more information
* on the NB_List struct.
*/
int
numba_list_new(NB_List **out, Py_ssize_t item_size, Py_ssize_t allocated){
NB_List *lp;
char *items;
// allocate memory to hold the struct
lp = malloc(aligned_size(sizeof(NB_List)));
if (lp == NULL) {
return LIST_ERR_NO_MEMORY;
}
// set up members
lp->size = 0;
lp->item_size = item_size;
lp->allocated = allocated;
lp->is_mutable = 1;
// set method table to zero */
memset(&lp->methods, 0x00, sizeof(list_type_based_methods_table));
// allocate memory to hold items, if requested
if (allocated != 0) {
items = malloc(aligned_size(lp->item_size * allocated));
// allocated was definitely not zero, if malloc returns NULL
// this is definitely an error
if (items == NULL) {
// free previously allocated struct to avoid leaking memory
free(lp);
return LIST_ERR_NO_MEMORY;
}
lp->items = items;
}
else {
// be explicit
lp->items = NULL;
}
*out = lp;
return LIST_OK;
}
/* Free the memory associated with a list.
*
* lp: a list
*/
void
numba_list_free(NB_List *lp) {
// decref all items, if needed
Py_ssize_t i;
if (lp->methods.item_decref) {
for (i = 0; i < lp->size; i++) {
char *item = lp->items + lp->item_size * i;
list_decref_item(lp, item);
}
}
// free items and list
if (lp->items != NULL) {
free(lp->items);
}
free(lp);
}
/* Return the base pointer of the list items.
*/
char *
numba_list_base_ptr(NB_List *lp)
{
return lp->items;
}
/* Return the address of the list size.
*/
Py_ssize_t
numba_list_size_address(NB_List *lp)
{
return (Py_ssize_t)&lp->size;
}
/* Return the length of a list.
*
* lp: a list
*/
Py_ssize_t
numba_list_length(NB_List *lp) {
return lp->size;
}
/* Return the current allocation of a list.
*
* lp: a list
*/
Py_ssize_t
numba_list_allocated(NB_List *lp) {
return lp->allocated;
}
/* Return the mutability status of the list
*
* lp: a list
*
*/
int
numba_list_is_mutable(NB_List *lp){
return lp->is_mutable;
}
/* Set the is_mutable attribute
*
* lp: a list
* is_mutable: an int, 0(False) or 1(True)
*
*/
void
numba_list_set_is_mutable(NB_List *lp, int is_mutable){
lp->is_mutable = is_mutable;
}
/* Set an item in a list.
*
* lp: a list
* index: the index of the item to set (must be in range 0 <= index < len(list))
* item: the item to set
*
* This assume there is already an element at the given index that will be
* overwritten and thereby have its reference decremented. DO NOT use this to
* write to an unassigned location.
*/
int
numba_list_setitem(NB_List *lp, Py_ssize_t index, const char *item) {
char *loc;
// check for mutability
if (!lp->is_mutable) {
return LIST_ERR_IMMUTABLE;
}
// check index is valid
// FIXME: this can be (and probably is) checked at the compiler level
if (!valid_index(index, lp->size)) {
return LIST_ERR_INDEX;
}
// set item at desired location
loc = lp->items + lp-> item_size * index;
list_decref_item(lp, loc);
copy_item(lp, loc, item);
list_incref_item(lp, loc);
return LIST_OK;
}
/* Get an item from a list.
*
* lp: a list
* index: the index of the item to get (must be in range 0 <= index < len(list))
* out: a pointer to hold the item
*/
int
numba_list_getitem(NB_List *lp, Py_ssize_t index, char *out) {
char *loc;
// check index is valid
// FIXME: this can be (and probably is) checked at the compiler level
if (!valid_index(index, lp->size)) {
return LIST_ERR_INDEX;
}
// get item at desired location
loc = lp->items + lp->item_size * index;
copy_item(lp, out, loc);
return LIST_OK;
}
/* Append an item to the end of a list.
*
* lp: a list
* item: the item to append.
*/
int
numba_list_append(NB_List *lp, const char *item) {
char *loc;
// check for mutability
if (!lp->is_mutable) {
return LIST_ERR_IMMUTABLE;
}
// resize by one, will change list size
int result = numba_list_resize(lp, lp->size + 1);
if(result < LIST_OK) {
return result;
}
// insert item at index: original size before resize
loc = lp->items + lp->item_size * (lp->size - 1);
copy_item(lp, loc, item);
list_incref_item(lp, loc);
return LIST_OK;
}
/* Resize a list.
*
* lp: a list
* newsize: the desired new size of the list.
*
* This will increase or decrease the size of the list, including reallocating
* the required memory and increasing the total allocation (additional free
* space to hold new items).
*
*
* Adapted from CPython's list_resize().
*
* Ensure lp->items has room for at least newsize elements, and set
* lp->size to newsize. If newsize > lp->size on entry, the content
* of the new slots at exit is undefined heap trash; it's the caller's
* responsibility to overwrite them with sane values.
* The number of allocated elements may grow, shrink, or stay the same.
* Failure is impossible if newsize <= lp->allocated on entry, although
* that partly relies on an assumption that the system realloc() never
* fails when passed a number of bytes <= the number of bytes last
* allocated (the C standard doesn't guarantee this, but it's hard to
* imagine a realloc implementation where it wouldn't be true).
* Note that lp->items may change, and even if newsize is less
* than lp->size on entry.
*/
int
numba_list_resize(NB_List *lp, Py_ssize_t newsize) {
char * items;
// check for mutability
if (!lp->is_mutable) {
return LIST_ERR_IMMUTABLE;
}
size_t new_allocated, num_allocated_bytes;
/* Bypass realloc() when a previous overallocation is large enough
to accommodate the newsize. If the newsize falls lower than half
the allocated size, then proceed with the realloc() to shrink the list.
*/
if (lp->allocated >= newsize && newsize >= (lp->allocated >> 1)) {
assert(lp->items != NULL || newsize == 0);
lp->size = newsize;
return LIST_OK;
}
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
* Note: new_allocated won't overflow because the largest possible value
* is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
*/
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
if (new_allocated > (size_t)PY_SSIZE_T_MAX / lp->item_size) {
return LIST_ERR_NO_MEMORY;
}
if (newsize == 0)
new_allocated = 0;
num_allocated_bytes = new_allocated * lp->item_size;
items = realloc(lp->items, aligned_size(num_allocated_bytes));
// realloc may return NULL if requested size is 0
if (num_allocated_bytes != 0 && items == NULL) {
return LIST_ERR_NO_MEMORY;
}
lp->items = items;
lp->size = newsize;
lp->allocated = (Py_ssize_t)new_allocated;
return LIST_OK;
}
/* Delete a single item.
*
* lp: a list
* index: the index of the item to delete
* (must be in range 0 <= index < len(list))
*
* */
int
numba_list_delitem(NB_List *lp, Py_ssize_t index) {
int result;
char *loc, *new_loc;
Py_ssize_t leftover_bytes;
// check for mutability
if (!lp->is_mutable) {
return LIST_ERR_IMMUTABLE;
}
// check index is valid
// FIXME: this can be (and probably is) checked at the compiler level
if (!valid_index(index, lp->size)) {
return LIST_ERR_INDEX;
}
// obtain item and decref if needed
loc = lp->items + lp->item_size * index;
list_decref_item(lp, loc);
if (index != lp->size - 1) {
// delitem from somewhere other than the end, incur the memory copy
leftover_bytes = (lp->size - 1 - index) * lp->item_size;
new_loc = lp->items + (lp->item_size * (index + 1));
// use memmove instead of memcpy since we may be dealing with
// overlapping regions of memory and the behaviour of memcpy is
// undefined in such situation (C99).
memmove(loc, new_loc, leftover_bytes);
}
// finally, shrink list by one
result = numba_list_resize(lp, lp->size - 1);
if(result < LIST_OK) {
// Since we are decreasing the size, this should never happen
return result;
}
return LIST_OK;
}
/* Delete a slice
*
* start: the start index of ths slice
* stop: the stop index of the slice (not included)
* step: the step to take
*
* This function assumes that the start and stop were clipped appropriately.
* I.e. if step > 0 start >= 0 and stop <= len(l) and
* if step < 0 start <= length and stop >= -1
* step != 0 and no Python negative indexing allowed.
*
* This code was copied and edited from the relevant section in
* list_ass_subscript from the cpython implementation, see the top of this file
* for the exact source
*/
int
numba_list_delete_slice(NB_List *lp,
Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step) {
int result, i, slicelength, new_length;
char *loc, *new_loc;
Py_ssize_t leftover_bytes, cur, lim;
// check for mutability
if (!lp->is_mutable) {
return LIST_ERR_IMMUTABLE;
}
// calculate the slicelength, taken from PySlice_AdjustIndices, see the top
// of this file for the exact source
if (step > 0) {
slicelength = start < stop ? (stop - start - 1) / step + 1 : 0;
} else {
slicelength = stop < start ? (start - stop - 1) / -step + 1 : 0;
}
if (slicelength <= 0){
return LIST_OK;
}
new_length = lp->size - slicelength;
// reverse step and indices
if (step < 0) {
stop = start + 1;
start = stop + step * (slicelength - 1) - 1;
step = -step;
}
if (step == 1) {
// decref if needed
if (lp->methods.item_decref) {
for (i = start ; i < stop ; i++){
loc = lp->items + lp->item_size * i;
lp->methods.item_decref(loc);
}
}
// memmove items into place
leftover_bytes = (lp->size - stop) * lp->item_size;
loc = lp->items + lp->item_size * start;
new_loc = lp->items + lp->item_size * stop;
memmove(loc, new_loc, leftover_bytes);
}
else { // step != 1
/* drawing pictures might help understand these for
* loops. Basically, we memmove the parts of the
* list that are *not* part of the slice: step-1
* items for each item that is part of the slice,
* and then tail end of the list that was not
* covered by the slice
*
* */
for (cur = start, // index of item to be deleted
i = 0; // counter of total items deleted so far
cur < stop;
cur += step,
i++) {
lim = step - 1; // number of leftover items after deletion of item
// clip limit, in case we are at the end of the slice, and there
// are now less than step-1 items to be moved
if (cur + step >= lp->size) {
lim = lp->size - cur - 1;
}
// decref item being removed
loc = lp->items + lp->item_size * cur;
list_decref_item(lp, loc);
/* memmove the aforementioned step-1 (or less) items
* dst : index of deleted item minus total deleted sofar
* src : index of deleted item plus one (next item)
*/
memmove(lp->items + lp->item_size * (cur - i),
lp->items + lp->item_size * (cur + 1),
lim * lp->item_size);
}
// memmove tail of the list
cur = start + slicelength * step;
if (cur < lp->size) {
memmove(lp->items + lp->item_size * (cur - slicelength),
lp->items + lp->item_size * cur,
(lp->size - cur) * lp->item_size);
}
}
// resize to correct size
result = numba_list_resize(lp, new_length);
if(result < LIST_OK) {
// Since we are decreasing the size, this should never happen
return result;
}
return LIST_OK;
}
/* Return the size of the list iterator (NB_ListIter) struct.
*/
size_t
numba_list_iter_sizeof() {
return sizeof(NB_ListIter);
}
/* Initialize a list iterator (NB_ListIter).
*
* it: an iterator
* lp: a list to iterate over
*/
void
numba_list_iter(NB_ListIter *it, NB_List *lp) {
// set members of iterator
it->parent = lp;
it->size = lp->size;
it->pos = 0;
}
/* Obtain the next item from a list iterator.
*
* it: an iterator
* item_ptr: pointer to hold the next item
*/
int
numba_list_iter_next(NB_ListIter *it, const char **item_ptr) {
NB_List *lp;
lp = it->parent;
/* FIXME: Detect list mutation during iteration */
if (lp->size != it->size) {
return LIST_ERR_MUTATED;
}
// get next element
if (it->pos < lp->size) {
*item_ptr = lp->items + lp->item_size * it->pos++;
return LIST_OK;
}else{
return LIST_ERR_ITER_EXHAUSTED;
}
}
#define CHECK(CASE) { \
if ( !(CASE) ) { \
printf("'%s' failed file %s:%d\n", #CASE, __FILE__, __LINE__); \
return -1; \
} \
}
/* Basic C based tests for the list.
*/
int
numba_test_list(void) {
NB_List *lp = NULL;
int status, i;
Py_ssize_t it_count;
const char *it_item = NULL;
NB_ListIter iter;
char got_item[4] = "\x00\x00\x00\x00";
const char *test_items_1 = NULL, *test_items_2 = NULL;
char *test_items_3 = NULL;
puts("test_list");
status = numba_list_new(&lp, 4, 0);
CHECK(status == LIST_OK);
CHECK(lp->item_size == 4);
CHECK(lp->size == 0);
CHECK(lp->allocated == 0);
CHECK(lp->is_mutable == 1);
// flip and check the is_mutable bit
CHECK(numba_list_is_mutable(lp) == 1);
numba_list_set_is_mutable(lp, 0);
CHECK(numba_list_is_mutable(lp) == 0);
numba_list_set_is_mutable(lp, 1);
CHECK(numba_list_is_mutable(lp) == 1);
// append 1st item, this will cause a realloc
status = numba_list_append(lp, "abc");
CHECK(status == LIST_OK);
CHECK(lp->size == 1);
CHECK(lp->allocated == 4);
status = numba_list_getitem(lp, 0, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "abc", 4) == 0);
// append 2nd item
status = numba_list_append(lp, "def");
CHECK(status == LIST_OK);
CHECK(lp->size == 2);
CHECK(lp->allocated == 4);
status = numba_list_getitem(lp, 1, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "def", 4) == 0);
// append 3rd item
status = numba_list_append(lp, "ghi");
CHECK(status == LIST_OK);
CHECK(lp->size == 3);
CHECK(lp->allocated == 4);
status = numba_list_getitem(lp, 2, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "ghi", 4) == 0);
// append 4th item
status = numba_list_append(lp, "jkl");
CHECK(status == LIST_OK);
CHECK(lp->size == 4);
CHECK(lp->allocated == 4);
status = numba_list_getitem(lp, 3, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "jkl", 4) == 0);
// append 5th item, this will cause another realloc
status = numba_list_append(lp, "mno");
CHECK(status == LIST_OK);
CHECK(lp->size == 5);
CHECK(lp->allocated == 8);
status = numba_list_getitem(lp, 4, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "mno", 4) == 0);
// overwrite 1st item
status = numba_list_setitem(lp, 0, "pqr");
CHECK(status == LIST_OK);
CHECK(lp->size == 5);
CHECK(lp->allocated == 8);
status = numba_list_getitem(lp, 0, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "pqr", 4) == 0);
// get and del 1st item, check item shift
status = numba_list_getitem(lp, 0, got_item);
status = numba_list_delitem(lp, 0);
CHECK(status == LIST_OK);
CHECK(lp->size == 4);
CHECK(lp->allocated == 8);
CHECK(memcmp(got_item, "pqr", 4) == 0);
CHECK(memcmp(lp->items, "def\x00ghi\x00jkl\x00mno\x00", 16) == 0);
// get and del last (4th) item, no shift since only last item affected
status = numba_list_getitem(lp, 3, got_item);
status = numba_list_delitem(lp, 3);
CHECK(status == LIST_OK);
CHECK(lp->size == 3);
CHECK(lp->allocated == 6); // this also shrinks the allocation
CHECK(memcmp(got_item, "mno", 4) == 0);
CHECK(memcmp(lp->items, "def\x00ghi\x00jkl\x00", 12) == 0);
// flip and check the is_mutable member
CHECK(numba_list_is_mutable(lp) == 1);
numba_list_set_is_mutable(lp, 0);
CHECK(numba_list_is_mutable(lp) == 0);
// ensure that any attempts to mutate an immutable list fail
CHECK(numba_list_setitem(lp, 0, "zzz") == LIST_ERR_IMMUTABLE);
CHECK(numba_list_append(lp, "zzz") == LIST_ERR_IMMUTABLE);
CHECK(numba_list_delitem(lp, 0) == LIST_ERR_IMMUTABLE);
CHECK(numba_list_resize(lp, 23) == LIST_ERR_IMMUTABLE);
CHECK(numba_list_delete_slice(lp, 0, 3, 1) == LIST_ERR_IMMUTABLE);
// ensure that all attempts to query/read from and immutable list succeed
CHECK(numba_list_length(lp) == 3);
status = numba_list_getitem(lp, 0, got_item);
CHECK(status == LIST_OK);
CHECK(memcmp(got_item, "def", 4) == 0);
// flip the is_mutable member back and check
numba_list_set_is_mutable(lp, 1);
CHECK(numba_list_is_mutable(lp) == 1);
// test iterator
CHECK(lp->size > 0);
numba_list_iter(&iter, lp);
it_count = 0;
CHECK(iter.parent == lp);
CHECK(iter.pos == it_count);
// current contents of list
test_items_1 = "def\x00ghi\x00jkl\x00";
while ( (status = numba_list_iter_next(&iter, &it_item)) == LIST_OK) {
it_count += 1;
CHECK(iter.pos == it_count); // check iterator position
CHECK(it_item != NULL); // quick check item is non-null
// go fishing in test_items_1
CHECK(memcmp((const char *)test_items_1 + ((it_count - 1) * 4), it_item, 4) == 0);
}
CHECK(status == LIST_ERR_ITER_EXHAUSTED);
CHECK(lp->size == it_count);
// free existing list
numba_list_free(lp);
// test growth upon append and shrink during delitem
status = numba_list_new(&lp, 1, 0);
CHECK(status == LIST_OK);
CHECK(lp->item_size == 1);
CHECK(lp->size == 0);
CHECK(lp->allocated == 0);
// first, grow the list
// Use exactly 17 elements, should go through the allocation pattern:
// 0, 4, 8, 16, 25
for (i = 0; i < 17 ; i++) {
switch(i) {
// Check the allocation before
case 0: CHECK(lp->allocated == 0); break;
case 4: CHECK(lp->allocated == 4); break;
case 8: CHECK(lp->allocated == 8); break;
case 16: CHECK(lp->allocated == 16); break;
}
status = numba_list_append(lp, (const char*)&i);
CHECK(status == LIST_OK);
switch(i) {
// Check that the growth happened accordingly
case 0: CHECK(lp->allocated == 4); break;
case 4: CHECK(lp->allocated == 8); break;
case 8: CHECK(lp->allocated == 16); break;
case 16: CHECK(lp->allocated == 25); break;
}
}
CHECK(lp->size == 17);
// Check current contents of list
test_items_2 = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10";
CHECK(memcmp(lp->items, test_items_2, 17) == 0);
// Now, delete them again and check that list shrinks
for (i = 17; i > 0 ; i--) {
switch(i) {
// Check the allocation before delitem
case 17: CHECK(lp->allocated == 25); break;
case 12: CHECK(lp->allocated == 25); break;
case 9: CHECK(lp->allocated == 18); break;
case 6: CHECK(lp->allocated == 12); break;
case 4: CHECK(lp->allocated == 8); break;
case 3: CHECK(lp->allocated == 6); break;
case 2: CHECK(lp->allocated == 5); break;
case 1: CHECK(lp->allocated == 4); break;
}
status = numba_list_getitem(lp, i-1, got_item);
status = numba_list_delitem(lp, i-1);
CHECK(status == LIST_OK);
switch(i) {
// Check that the shrink happened accordingly
case 17: CHECK(lp->allocated == 25); break;
case 12: CHECK(lp->allocated == 18); break;
case 9: CHECK(lp->allocated == 12); break;
case 6: CHECK(lp->allocated == 8); break;
case 4: CHECK(lp->allocated == 6); break;
case 3: CHECK(lp->allocated == 5); break;
case 2: CHECK(lp->allocated == 4); break;
case 1: CHECK(lp->allocated == 0); break;
}
}
// free existing list
numba_list_free(lp);
// Setup list for testing delete_slice
status = numba_list_new(&lp, 1, 0);
CHECK(status == LIST_OK);
CHECK(lp->item_size == 1);
CHECK(lp->size == 0);
CHECK(lp->allocated == 0);
for (i = 0; i < 17 ; i++) {
status = numba_list_append(lp, (const char*)&i);
CHECK(status == LIST_OK);
}
CHECK(lp->size == 17);
test_items_3 = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10";
CHECK(memcmp(lp->items, test_items_3, 17) == 0);
// delete multiple elements from the middle
status = numba_list_delete_slice(lp, 2, 5, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 14);
test_items_3 = "\x00\x01\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10";
CHECK(memcmp(lp->items, test_items_3, 14) == 0);
// delete single element from start
status = numba_list_delete_slice(lp, 0, 1, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 13);
test_items_3 = "\x01\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10";
CHECK(memcmp(lp->items, test_items_3, 13) == 0);
// delete single element from end
status = numba_list_delete_slice(lp, 12, 13, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 12);
test_items_3 = "\x01\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
CHECK(memcmp(lp->items, test_items_3, 12) == 0);
// delete single element from middle
status = numba_list_delete_slice(lp, 4, 5, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 11);
test_items_3 = "\x01\x05\x06\x07\x09\x0a\x0b\x0c\x0d\x0e\x0f";
CHECK(memcmp(lp->items, test_items_3, 11) == 0);
// delete all elements except first and last
status = numba_list_delete_slice(lp, 1, 10, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 2);
test_items_3 = "\x01\x0f";
CHECK(memcmp(lp->items, test_items_3, 2) == 0);
// delete all remaining elements
status = numba_list_delete_slice(lp, 0, lp->size, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 0);
test_items_3 = "";
CHECK(memcmp(lp->items, test_items_3, 0) == 0);
// free existing list
numba_list_free(lp);
// Setup list for testing delete_slice with non unary step
status = numba_list_new(&lp, 1, 0);
CHECK(status == LIST_OK);
CHECK(lp->item_size == 1);
CHECK(lp->size == 0);
CHECK(lp->allocated == 0);
for (i = 0; i < 17 ; i++) {
status = numba_list_append(lp, (const char*)&i);
CHECK(status == LIST_OK);
}
CHECK(lp->size == 17);
// delete all items with odd index
status = numba_list_delete_slice(lp, 0, 17, 2);
CHECK(status == LIST_OK);
CHECK(lp->size == 8);
test_items_3 = "\x01\x03\x05\x07\x09\x0b\x0d\x0f";
CHECK(memcmp(lp->items, test_items_3, 8) == 0);
// delete with a step of 4, starting at index 1
status = numba_list_delete_slice(lp, 1, 8, 4);
CHECK(status == LIST_OK);
CHECK(lp->size == 6);
test_items_3 = "\x01\x05\x07\x09\x0d\x0f";
CHECK(memcmp(lp->items, test_items_3, 6) == 0);
// delete with a step of 2, but finish before end of list
status = numba_list_delete_slice(lp, 0, 4, 2);
CHECK(status == LIST_OK);
CHECK(lp->size == 4);
test_items_3 = "\x05\x09\x0d\x0f";
CHECK(memcmp(lp->items, test_items_3, 4) == 0);
// no-op on empty slice
status = numba_list_delete_slice(lp, 0, 0, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 4);
test_items_3 = "\x05\x09\x0d\x0f";
CHECK(memcmp(lp->items, test_items_3, 4) == 0);
// no-op on empty slice, non-zero index
status = numba_list_delete_slice(lp, 2, 2, 1);
CHECK(status == LIST_OK);
CHECK(lp->size == 4);
test_items_3 = "\x05\x09\x0d\x0f";
CHECK(memcmp(lp->items, test_items_3, 4) == 0);
// free list and return 0
numba_list_free(lp);
// Setup list for testing delete_slice with negative step
status = numba_list_new(&lp, 1, 0);
CHECK(status == LIST_OK);
CHECK(lp->item_size == 1);
CHECK(lp->size == 0);
CHECK(lp->allocated == 0);
for (i = 0; i < 17 ; i++) {
status = numba_list_append(lp, (const char*)&i);
CHECK(status == LIST_OK);
}
CHECK(lp->size == 17);
// delete all items using unary negative slice
status = numba_list_delete_slice(lp, 16, -1, -1);
CHECK(status == LIST_OK);
CHECK(lp->size == 0);
// refill list
for (i = 0; i < 17 ; i++) {
status = numba_list_append(lp, (const char*)&i);
CHECK(status == LIST_OK);
}
// delete all items using unary negative slice
// need to start at index of last item (16) and
// go beyond first item, i.e. -1 in Cd
status = numba_list_delete_slice(lp, 16, -1, -2);
CHECK(status == LIST_OK);
CHECK(lp->size == 8);
test_items_3 = "\x01\x03\x05\x07\x09\x0b\x0d\x0f";
CHECK(memcmp(lp->items, test_items_3, 8) == 0);
// free list and return 0
numba_list_free(lp);
return 0;
}
#undef CHECK

View File

@@ -0,0 +1,137 @@
/* Adapted from CPython3.7 Include/listobject.h
*
* The exact commit-id of the relevant file is:
*
* https://github.com/python/cpython/blob/51ddab8dae056867f3595ab3400bffc93f67c8d4/Include/listobject.h
*
* WARNING:
* Most interfaces listed here are exported (global), but they are not
* supported, stable, or part of Numba's public API. These interfaces and their
* underlying implementations may be changed or removed in future without
* notice.
* */
#ifndef NUMBA_LIST_H
#define NUMBA_LIST_H
#include "cext.h"
typedef void (*list_refcount_op_t)(const void*);
typedef struct {
list_refcount_op_t item_incref;
list_refcount_op_t item_decref;
} list_type_based_methods_table;
/* This is the struct for the Numba typed list. It is largely inspired by the
* CPython list struct in listobject.h. In essence the list is a homogeneously
* typed container that can grow and shrink upon insertion and deletion. This
* means that appending an item to, or removing an item from, the end of the
* list, this will have a O(1) amortized runtime. This matches the
* behaviour of the CPython list type and it will grow with the same
* increments.
*
* 'items' contains space for 'allocated' elements. The number
* currently in use is 'size'. The size in bytes of the items stored in the
* list is given by 'item_size'.
*
* Invariants:
* 0 <= size <= allocated
* len(list) == size
* item == NULL implies size == allocated == 0
*
* FIXME: list.sort() temporarily sets allocated to -1 to detect mutations.
*
* Items must normally not be NULL, except during construction when
* the list is not yet visible outside the function that builds it.
*
* Additionally, this list has boolean member 'is_mutable' that can be used to
* set a list as immutable. Two functions to query and set this member are
* provided. Any attempt to mutate an immutable list will result in a status
* of LIST_ERR_IMMUTABLE.
*
*/
typedef struct {
/* size of the list in items */
Py_ssize_t size;
/* size of the list items in bytes */
Py_ssize_t item_size;
/* total allocated slots in items */
Py_ssize_t allocated;
/* is the list mutable */
int is_mutable;
/* method table for type-dependent operations */
list_type_based_methods_table methods;
/* array/pointer for items. Interpretation is governed by item_size */
char * items;
} NB_List;
typedef struct {
/* parent list */
NB_List *parent;
/* list size */
Py_ssize_t size;
/* iterator position; indicates the next position to read */
Py_ssize_t pos;
} NB_ListIter;
NUMBA_GLOBAL_FUNC(void)
numba_list_set_method_table(NB_List *lp, list_type_based_methods_table *methods);
NUMBA_GLOBAL_FUNC(int)
numba_list_new(NB_List **out, Py_ssize_t item_size, Py_ssize_t allocated);
NUMBA_GLOBAL_FUNC(void)
numba_list_free(NB_List *lp);
NUMBA_GLOBAL_FUNC(char *)
numba_list_base_ptr(NB_List *lp);
NUMBA_GLOBAL_FUNC(Py_ssize_t)
numba_list_size_address(NB_List *lp);
NUMBA_GLOBAL_FUNC(Py_ssize_t)
numba_list_length(NB_List *lp);
NUMBA_GLOBAL_FUNC(Py_ssize_t)
numba_list_allocated(NB_List *lp);
NUMBA_GLOBAL_FUNC(int)
numba_list_is_mutable(NB_List *lp);
NUMBA_GLOBAL_FUNC(void)
numba_list_set_is_mutable(NB_List *lp, int is_mutable);
NUMBA_GLOBAL_FUNC(int)
numba_list_setitem(NB_List *lp, Py_ssize_t index, const char *item);
NUMBA_GLOBAL_FUNC(int)
numba_list_getitem(NB_List *lp, Py_ssize_t index, char *out);
NUMBA_GLOBAL_FUNC(int)
numba_list_append(NB_List *lp, const char *item);
NUMBA_GLOBAL_FUNC(int)
numba_list_resize(NB_List *lp, Py_ssize_t newsize);
NUMBA_GLOBAL_FUNC(int)
numba_list_delitem(NB_List *lp, Py_ssize_t index);
NUMBA_GLOBAL_FUNC(int)
numba_list_delete_slice(NB_List *lp,
Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step);
NUMBA_GLOBAL_FUNC(size_t)
numba_list_iter_sizeof(void);
NUMBA_GLOBAL_FUNC(void)
numba_list_iter(NB_ListIter *it, NB_List *l);
NUMBA_GLOBAL_FUNC(int)
numba_list_iter_next(NB_ListIter *it, const char **item_ptr);
NUMBA_EXPORT_FUNC(int)
numba_test_list(void);
#endif

View File

@@ -0,0 +1,8 @@
#include "cext.h"
/* Align size *sz* to pointer width */
Py_ssize_t
aligned_size(Py_ssize_t sz) {
Py_ssize_t alignment = sizeof(void*);
return sz + (alignment - sz % alignment) % alignment;
}

View File

@@ -0,0 +1,18 @@
from . import cloudpickle
from .cloudpickle import * # noqa
__doc__ = cloudpickle.__doc__
__version__ = "3.1.1"
__all__ = [ # noqa
"__version__",
"Pickler",
"CloudPickler",
"dumps",
"loads",
"dump",
"load",
"register_pickle_by_value",
"unregister_pickle_by_value",
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
"""Compatibility module.
It can be necessary to load files generated by previous versions of cloudpickle
that rely on symbols being defined under the `cloudpickle.cloudpickle_fast`
namespace.
See: tests/test_backward_compat.py
"""
from . import cloudpickle
def __getattr__(name):
return getattr(cloudpickle, name)

Some files were not shown because too many files have changed in this diff Show More