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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ jobs:
steps:
- checkout
- run:
name: Install make
command: dnf install make -y
name: Install build tools
command: |
PYVER=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
dnf install -y make gcc "python${PYVER}-devel"
- run:
name: Install pip
command: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

*.so

# Cython-generated C source (rebuilt from .pyx by setup.py)
pynuodb/_fetch.c

/.virttemp
/.testtemp

Expand Down
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include README.rst LICENSE
include README.rst LICENSE
recursive-include pynuodb *.pyx *.pxd *.pxi *.c *.h
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ SUDO ?= sudo -n
NUODB_HOME ?= /opt/nuodb

_INSTALL_CMD = $(PIP) install '.[crypto]'
_BUILD_EXT_CMD = $(PIP) install 'setuptools>=40.8.0' 'Cython>=3.0' \
&& $(PYTHON) setup.py build_ext --inplace
_VERIFY_CMD = $(NUODB_HOME)/bin/nuocmd show domain
_PYTEST_CMD = $(MKDIR) $(ARTIFACTDIR) $(RESULTSDIR) \
&& TMPDIR='$(TMPDIR)' PATH="$(NUODB_HOME)/bin:$$PATH" \
Expand All @@ -43,11 +45,13 @@ all:

install:
$(_INSTALL_CMD)
$(_BUILD_EXT_CMD)

check: mypy pylint fulltest

fulltest:
$(_INSTALL_CMD)
$(_BUILD_EXT_CMD)
$(PIP) install -r test_requirements.txt
$(_VERIFY_CMD)
$(_PYTEST_CMD)
Expand Down
47 changes: 47 additions & 0 deletions pynuodb/_codes.pxi
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Wire-protocol type codes, mirrored from protocol.py as C ints so
# decode_next_batch's per-cell range checks in _fetch.pyx don't pay for a
# Python attribute lookup on every column.
from . import protocol as _protocol

cdef int NULL_V = _protocol.NULL
cdef int TRUE_V = _protocol.TRUE
cdef int FALSE_V = _protocol.FALSE
cdef int INTMINUS10 = _protocol.INTMINUS10
cdef int INT0 = _protocol.INT0 # inline value 0: end-of-batch marker
cdef int INT31 = _protocol.INT31
cdef int INTLEN0 = _protocol.INTLEN0 # == INT31; codes 52-59 carry 1-8 byte integers
cdef int INTLEN8 = _protocol.INTLEN8
cdef int SCALEDLEN0 = _protocol.SCALEDLEN0 # base for 1-8 byte scaled decimals (61-68)
cdef int SCALEDLEN8 = _protocol.SCALEDLEN8
cdef int UTF8COUNT0 = _protocol.UTF8COUNT0 # base for length-prefixed strings (69-72)
cdef int UTF8COUNT1 = _protocol.UTF8COUNT1
cdef int UTF8COUNT4 = _protocol.UTF8COUNT4
cdef int OPAQUECOUNT0 = _protocol.OPAQUECOUNT0 # base for length-prefixed binary (73-76)
cdef int OPAQUECOUNT1 = _protocol.OPAQUECOUNT1
cdef int OPAQUECOUNT4 = _protocol.OPAQUECOUNT4
cdef int DOUBLELEN0 = _protocol.DOUBLELEN0 # 77 == double 0.0; 78-85 carry 1-8 byte doubles
cdef int DOUBLELEN8 = _protocol.DOUBLELEN8
cdef int MILLISECLEN0 = _protocol.MILLISECLEN0 # base for 1-8 byte millisecond timestamps
cdef int MILLISECLEN8 = _protocol.MILLISECLEN8
cdef int NANOSECLEN0 = _protocol.NANOSECLEN0 # base for 1-8 byte nanosecond timestamps
cdef int NANOSECLEN8 = _protocol.NANOSECLEN8
cdef int TIMELEN0 = _protocol.TIMELEN0 # base for 1-4 byte ms-since-midnight
cdef int TIMELEN4 = _protocol.TIMELEN4
cdef int UTF8LEN0 = _protocol.UTF8LEN0 # base for 0-39 byte inline-length strings
cdef int UTF8LEN39 = _protocol.UTF8LEN39
cdef int OPAQUELEN0 = _protocol.OPAQUELEN0 # base for 0-39 byte inline-length binary
cdef int OPAQUELEN39 = _protocol.OPAQUELEN39
cdef int BLOBLEN0 = _protocol.BLOBLEN0 # base for 0-4 byte length-prefixed BLOB
cdef int BLOBLEN4 = _protocol.BLOBLEN4
cdef int CLOBLEN0 = _protocol.CLOBLEN0 # base for 0-4 byte length-prefixed CLOB
cdef int CLOBLEN4 = _protocol.CLOBLEN4
cdef int UUID_C = _protocol.UUID
cdef int SCALEDDATELEN0 = _protocol.SCALEDDATELEN0 # 201-208 carry 1-8 byte scaled dates
cdef int SCALEDDATELEN8 = _protocol.SCALEDDATELEN8
cdef int SCALEDTIMELEN0 = _protocol.SCALEDTIMELEN0 # 209-216 carry 1-8 byte scaled times
cdef int SCALEDTIMELEN8 = _protocol.SCALEDTIMELEN8
cdef int SCALEDTIMESTAMPLEN0 = _protocol.SCALEDTIMESTAMPLEN0 # 217-224 carry 1-8 byte scaled timestamps
cdef int SCALEDTIMESTAMPLEN8 = _protocol.SCALEDTIMESTAMPLEN8
# No LEN1..LEN8 range: 234-240 belong to ARRAYLEN/SCALEDCOUNT3/DEBUGBARRIER.
cdef int SCALEDTIMESTAMPNOTZLEN0 = _protocol.SCALEDTIMESTAMPNOTZLEN0
cdef int SCALEDTIMESTAMPNOTZ = _protocol.SCALEDTIMESTAMPNOTZ
135 changes: 135 additions & 0 deletions pynuodb/_cutil.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#ifndef PYNUODB_CUTIL_H
#define PYNUODB_CUTIL_H
/* C support for _fetch.pyx's decode loop: raw-PyObject* value builders for
the row tuple, and big-endian integer/double readers for the wire
protocol. */

#include <Python.h>
#include <string.h>
#include <stdint.h>

#if defined(_MSC_VER)
#include <stdlib.h>
#define NUODB_INLINE static __inline
#define NUODB_LIKELY(x) (x)
#define NUODB_UNLIKELY(x) (x)
#define NUODB_BSWAP16(x) _byteswap_ushort(x)
#define NUODB_BSWAP32(x) _byteswap_ulong(x)
#define NUODB_BSWAP64(x) _byteswap_uint64(x)
#else
#define NUODB_INLINE static inline
#define NUODB_LIKELY(x) __builtin_expect(!!(x), 1)
#define NUODB_UNLIKELY(x) __builtin_expect(!!(x), 0)
#define NUODB_BSWAP16(x) __builtin_bswap16(x)
#define NUODB_BSWAP32(x) __builtin_bswap32(x)
#define NUODB_BSWAP64(x) __builtin_bswap64(x)
#endif

/* Build a value as a raw PyObject* for decode_next_batch to steal into a
row tuple via _pynuodb_tuple_steal. Don't replace these with Cython's
cpython.* pxd bindings: those take/return `object`, not `PyObject*`,
which would route the value through Cython's own refcounting first. */
NUODB_INLINE void _pynuodb_tuple_steal(PyObject *t, Py_ssize_t i, PyObject *o)
{
PyTuple_SET_ITEM(t, i, o);
}

NUODB_INLINE PyObject *_pynuodb_long_from_long(long v)
{
return PyLong_FromLong(v);
}

NUODB_INLINE PyObject *_pynuodb_long_from_longlong(long long v)
{
return PyLong_FromLongLong(v);
}

NUODB_INLINE PyObject *_pynuodb_decode_utf8(const char *s, Py_ssize_t n)
{
return PyUnicode_DecodeUTF8(s, n, NULL);
}

NUODB_INLINE PyObject *_pynuodb_incref(PyObject *o)
{
Py_INCREF(o);
return o;
}

NUODB_INLINE PyObject *_pynuodb_none(void) { return Py_None; }
NUODB_INLINE PyObject *_pynuodb_true(void) { return Py_True; }
NUODB_INLINE PyObject *_pynuodb_false(void) { return Py_False; }

NUODB_INLINE unsigned long long _pynuodb_be_u64(const unsigned char *p, int n)
{
switch (n)
{
case 0: return 0;
case 1: return p[0];
case 2: {
uint16_t x;
memcpy(&x, p, 2);
return NUODB_BSWAP16(x);
}
case 4: {
uint32_t x;
memcpy(&x, p, 4);
return NUODB_BSWAP32(x);
}
case 8: {
uint64_t x;
memcpy(&x, p, 8);
return NUODB_BSWAP64(x);
}
default: {
unsigned long long v = 0;
for (int i = 0; i < n; i++) {
v = (v << 8) | p[i];
}
return v;
}
}
}

/* Sign-extends the n-byte big-endian unsigned value from _pynuodb_be_u64*/
NUODB_INLINE long long _pynuodb_be_i64(const unsigned char *p, int n)
{
if (n <= 0) {
return 0;
}
unsigned long long v = _pynuodb_be_u64(p, n);
unsigned long long m = (n < 8) ? (1ULL << ((n << 3) - 1)) : 0x8000000000000000ULL;
return (long long)((v ^ m) - m);
}

NUODB_INLINE double _pynuodb_be_double(const unsigned char *p, int n)
{
unsigned char buf[8] = {0};
if (n < 0) {
n = 0;
} else if (n > 8) {
n = 8;
}
memcpy(buf, p, n);
uint64_t v = _pynuodb_be_u64(buf, 8);
double d;
memcpy(&d, &v, 8);
return d;
}

/* Try to make boundary checking fast through inline and marking the happy path as likely*/
NUODB_INLINE int _pynuodb_avail_ok(Py_ssize_t pos, Py_ssize_t need, Py_ssize_t n)
{
return NUODB_LIKELY(need >= 0 && pos <= n - need);
}

NUODB_INLINE PyObject *_pynuodb_pylong_be_signed(const unsigned char *p, Py_ssize_t n)
{
if (NUODB_UNLIKELY(n > 8)) {
PyErr_SetString(PyExc_OverflowError,
"_pynuodb_pylong_be_signed: width > 8 bytes not supported");
return NULL;
}
return PyLong_FromLongLong(_pynuodb_be_i64(p, (int)n));
}

#endif /* PYNUODB_CUTIL_H */
Loading