+
+typedef unsigned char byte;
+
+
+typedef struct {
+ int istty2;
+} state_t;
+
+// cstr_argf: for byte vectors without null characters (e.g. paths)
+// rbuf_argf: for read-only byte vectors
+// wbuf_argf: for mutable byte vectors
+
+#if PY_MAJOR_VERSION < 3
+static state_t state;
+# define get_state(x) (&state)
+# define cstr_argf "s"
+# define rbuf_argf "s#"
+# define wbuf_argf "s*"
+#else
+# define get_state(x) ((state_t *) PyModule_GetState(x))
+# define cstr_argf "y"
+# define rbuf_argf "y#"
+# define wbuf_argf "y*"
+#endif // PY_MAJOR_VERSION >= 3
+
+
+static void *checked_calloc(size_t n, size_t size)
+{
+ void *result = calloc(n, size);
+ if (!result)
+ PyErr_NoMemory();
+ return result;
+}
+
+#ifndef BUP_HAVE_BUILTIN_MUL_OVERFLOW
+
+#define checked_malloc checked_calloc
+
+#else // defined BUP_HAVE_BUILTIN_MUL_OVERFLOW
+
+static void *checked_malloc(size_t n, size_t size)
+{
+ size_t total;
+ if (__builtin_mul_overflow(n, size, &total))
+ {
+ PyErr_Format(PyExc_OverflowError,
+ "request to allocate %lu items of size %lu is too large",
+ n, size);
+ return NULL;
+ }
+ void *result = malloc(total);
+ if (!result)
+ return PyErr_NoMemory();
+ return result;
+}
+
+#endif // defined BUP_HAVE_BUILTIN_MUL_OVERFLOW
+
+
+#ifndef htonll
+// This function should technically be macro'd out if it's going to be used
+// more than ocasionally. As of this writing, it'll actually never be called
+// in real world bup scenarios (because our packs are < MAX_INT bytes).
+static uint64_t htonll(uint64_t value)
+{
+ static const int endian_test = 42;
+
+ if (*(char *)&endian_test == endian_test) // LSB-MSB
+ return ((uint64_t)htonl(value & 0xFFFFFFFF) << 32) | htonl(value >> 32);
+ return value; // already in network byte order MSB-LSB
+}
+#endif
+
+
+// Disabling sign-compare here should be fine since we're explicitly
+// checking for a sign mismatch, i.e. if the signs don't match, then
+// it doesn't matter what the value comparison says.
+// FIXME: ... so should we reverse the order?
+#define INTEGRAL_ASSIGNMENT_FITS(dest, src) \
+ ({ \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wsign-compare\""); \
+ *(dest) = (src); \
+ int result = *(dest) == (src) && (*(dest) < 1) == ((src) < 1); \
+ _Pragma("GCC diagnostic pop"); \
+ result; \
+ })
+
+
+// At the moment any code that calls INTEGER_TO_PY() will have to
+// disable -Wtautological-compare for clang. See below.
+
+#define INTEGER_TO_PY(x) \
+ (((x) >= 0) ? PyLong_FromUnsignedLongLong(x) : PyLong_FromLongLong(x))
+
+
+
+#if PY_MAJOR_VERSION < 3
+static int bup_ulong_from_pyint(unsigned long *x, PyObject *py,
+ const char *name)
+{
+ const long tmp = PyInt_AsLong(py);
+ if (tmp == -1 && PyErr_Occurred())
+ {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_Format(PyExc_OverflowError, "%s too big for unsigned long",
+ name);
+ return 0;
+ }
+ if (tmp < 0)
+ {
+ PyErr_Format(PyExc_OverflowError,
+ "negative %s cannot be converted to unsigned long", name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+#endif
+
+
+static int bup_ulong_from_py(unsigned long *x, PyObject *py, const char *name)
+{
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(py))
+ return bup_ulong_from_pyint(x, py, name);
+#endif
+
+ if (!PyLong_Check(py))
+ {
+ PyErr_Format(PyExc_TypeError, "expected integer %s", name);
+ return 0;
+ }
+
+ const unsigned long tmp = PyLong_AsUnsignedLong(py);
+ if (PyErr_Occurred())
+ {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_Format(PyExc_OverflowError, "%s too big for unsigned long",
+ name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+
+static int bup_uint_from_py(unsigned int *x, PyObject *py, const char *name)
+{
+ unsigned long tmp;
+ if (!bup_ulong_from_py(&tmp, py, name))
+ return 0;
+
+ if (tmp > UINT_MAX)
+ {
+ PyErr_Format(PyExc_OverflowError, "%s too big for unsigned int", name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py,
+ const char *name)
+{
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(py))
+ {
+ unsigned long tmp;
+ if (bup_ulong_from_pyint(&tmp, py, name))
+ {
+ *x = tmp;
+ return 1;
+ }
+ return 0;
+ }
+#endif
+
+ if (!PyLong_Check(py))
+ {
+ PyErr_Format(PyExc_TypeError, "integer argument expected for %s", name);
+ return 0;
+ }
+
+ const unsigned PY_LONG_LONG tmp = PyLong_AsUnsignedLongLong(py);
+ if (tmp == (unsigned long long) -1 && PyErr_Occurred())
+ {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ PyErr_Format(PyExc_OverflowError,
+ "%s too big for unsigned long long", name);
+ return 0;
+ }
+ *x = tmp;
+ return 1;
+}
+
+
+static PyObject *bup_bytescmp(PyObject *self, PyObject *args)
+{
+ PyObject *py_s1, *py_s2; // This is really a PyBytes/PyString
+ if (!PyArg_ParseTuple(args, "SS", &py_s1, &py_s2))
+ return NULL;
+ char *s1, *s2;
+ Py_ssize_t s1_len, s2_len;
+ if (PyBytes_AsStringAndSize(py_s1, &s1, &s1_len) == -1)
+ return NULL;
+ if (PyBytes_AsStringAndSize(py_s2, &s2, &s2_len) == -1)
+ return NULL;
+ const Py_ssize_t n = (s1_len < s2_len) ? s1_len : s2_len;
+ const int cmp = memcmp(s1, s2, n);
+ if (cmp != 0)
+ return PyLong_FromLong(cmp);
+ if (s1_len == s2_len)
+ return PyLong_FromLong(0);;
+ return PyLong_FromLong((s1_len < s2_len) ? -1 : 1);
+}
+
+
+static PyObject *bup_cat_bytes(PyObject *self, PyObject *args)
+{
+ unsigned char *bufx = NULL, *bufy = NULL;
+ Py_ssize_t bufx_len, bufx_ofs, bufx_n;
+ Py_ssize_t bufy_len, bufy_ofs, bufy_n;
+ if (!PyArg_ParseTuple(args,
+ rbuf_argf "nn"
+ rbuf_argf "nn",
+ &bufx, &bufx_len, &bufx_ofs, &bufx_n,
+ &bufy, &bufy_len, &bufy_ofs, &bufy_n))
+ return NULL;
+ if (bufx_ofs < 0)
+ return PyErr_Format(PyExc_ValueError, "negative x offset");
+ if (bufx_n < 0)
+ return PyErr_Format(PyExc_ValueError, "negative x extent");
+ if (bufx_ofs > bufx_len)
+ return PyErr_Format(PyExc_ValueError, "x offset greater than length");
+ if (bufx_n > bufx_len - bufx_ofs)
+ return PyErr_Format(PyExc_ValueError, "x extent past end of buffer");
+
+ if (bufy_ofs < 0)
+ return PyErr_Format(PyExc_ValueError, "negative y offset");
+ if (bufy_n < 0)
+ return PyErr_Format(PyExc_ValueError, "negative y extent");
+ if (bufy_ofs > bufy_len)
+ return PyErr_Format(PyExc_ValueError, "y offset greater than length");
+ if (bufy_n > bufy_len - bufy_ofs)
+ return PyErr_Format(PyExc_ValueError, "y extent past end of buffer");
+
+ if (bufy_n > PY_SSIZE_T_MAX - bufx_n)
+ return PyErr_Format(PyExc_OverflowError, "result length too long");
+
+ PyObject *result = PyBytes_FromStringAndSize(NULL, bufx_n + bufy_n);
+ if (!result)
+ return PyErr_NoMemory();
+ char *buf = PyBytes_AS_STRING(result);
+ memcpy(buf, bufx + bufx_ofs, bufx_n);
+ memcpy(buf + bufx_n, bufy + bufy_ofs, bufy_n);
+ return result;
+}
+
+