summaryrefslogtreecommitdiffstats
path: root/Include/internal/pycore_long.h
diff options
context:
space:
mode:
Diffstat (limited to 'Include/internal/pycore_long.h')
-rw-r--r--Include/internal/pycore_long.h164
1 files changed, 146 insertions, 18 deletions
diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h
index 8c1d017..137a046 100644
--- a/Include/internal/pycore_long.h
+++ b/Include/internal/pycore_long.h
@@ -82,8 +82,6 @@ PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Subtract(PyLongObject *left, PyLongObject *right);
-int _PyLong_AssignValue(PyObject **target, Py_ssize_t value);
-
/* Used by Python/mystrtoul.c, _PyBytes_FromHex(),
_PyBytes_DecodeEscape(), etc. */
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
@@ -110,25 +108,155 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter(
int base,
int alternate);
-/* Return 1 if the argument is positive single digit int */
+/* Long value tag bits:
+ * 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
+ * 2: Reserved for immortality bit
+ * 3+ Unsigned digit count
+ */
+#define SIGN_MASK 3
+#define SIGN_ZERO 1
+#define SIGN_NEGATIVE 2
+#define NON_SIZE_BITS 3
+
+/* All *compact" values are guaranteed to fit into
+ * a Py_ssize_t with at least one bit to spare.
+ * In other words, for 64 bit machines, compact
+ * will be signed 63 (or fewer) bit values
+ */
+
+/* Return 1 if the argument is compact int */
+static inline int
+_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
+ assert(PyLong_Check(op));
+ return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
+}
+
+static inline int
+_PyLong_IsCompact(const PyLongObject* op) {
+ assert(PyLong_Check(op));
+ return op->long_value.lv_tag < (2 << NON_SIZE_BITS);
+}
+
static inline int
-_PyLong_IsPositiveSingleDigit(PyObject* sub) {
- /* For a positive single digit int, the value of Py_SIZE(sub) is 0 or 1.
-
- We perform a fast check using a single comparison by casting from int
- to uint which casts negative numbers to large positive numbers.
- For details see Section 14.2 "Bounds Checking" in the Agner Fog
- optimization manual found at:
- https://www.agner.org/optimize/optimizing_cpp.pdf
-
- The function is not affected by -fwrapv, -fno-wrapv and -ftrapv
- compiler options of GCC and clang
- */
- assert(PyLong_CheckExact(sub));
- Py_ssize_t signed_size = Py_SIZE(sub);
- return ((size_t)signed_size) <= 1;
+_PyLong_BothAreCompact(const PyLongObject* a, const PyLongObject* b) {
+ assert(PyLong_Check(a));
+ assert(PyLong_Check(b));
+ return (a->long_value.lv_tag | b->long_value.lv_tag) < (2 << NON_SIZE_BITS);
+}
+
+/* Returns a *compact* value, iff `_PyLong_IsCompact` is true for `op`.
+ *
+ * "Compact" values have at least one bit to spare,
+ * so that addition and subtraction can be performed on the values
+ * without risk of overflow.
+ */
+static inline Py_ssize_t
+_PyLong_CompactValue(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ assert(_PyLong_IsCompact(op));
+ Py_ssize_t sign = 1 - (op->long_value.lv_tag & SIGN_MASK);
+ return sign * (Py_ssize_t)op->long_value.ob_digit[0];
+}
+
+static inline bool
+_PyLong_IsZero(const PyLongObject *op)
+{
+ return (op->long_value.lv_tag & SIGN_MASK) == SIGN_ZERO;
+}
+
+static inline bool
+_PyLong_IsNegative(const PyLongObject *op)
+{
+ return (op->long_value.lv_tag & SIGN_MASK) == SIGN_NEGATIVE;
+}
+
+static inline bool
+_PyLong_IsPositive(const PyLongObject *op)
+{
+ return (op->long_value.lv_tag & SIGN_MASK) == 0;
+}
+
+static inline Py_ssize_t
+_PyLong_DigitCount(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ return op->long_value.lv_tag >> NON_SIZE_BITS;
}
+/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonCompactSign(op) */
+static inline Py_ssize_t
+_PyLong_SignedDigitCount(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ Py_ssize_t sign = 1 - (op->long_value.lv_tag & SIGN_MASK);
+ return sign * (Py_ssize_t)(op->long_value.lv_tag >> NON_SIZE_BITS);
+}
+
+static inline int
+_PyLong_CompactSign(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ assert(_PyLong_IsCompact(op));
+ return 1 - (op->long_value.lv_tag & SIGN_MASK);
+}
+
+static inline int
+_PyLong_NonCompactSign(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ assert(!_PyLong_IsCompact(op));
+ return 1 - (op->long_value.lv_tag & SIGN_MASK);
+}
+
+/* Do a and b have the same sign? */
+static inline int
+_PyLong_SameSign(const PyLongObject *a, const PyLongObject *b)
+{
+ return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK);
+}
+
+#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS))
+
+static inline void
+_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
+{
+ assert(size >= 0);
+ assert(-1 <= sign && sign <= 1);
+ assert(sign != 0 || size == 0);
+ op->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, (size_t)size);
+}
+
+static inline void
+_PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size)
+{
+ assert(size >= 0);
+ op->long_value.lv_tag = (((size_t)size) << NON_SIZE_BITS) | (op->long_value.lv_tag & SIGN_MASK);
+}
+
+#define NON_SIZE_MASK ~((1 << NON_SIZE_BITS) - 1)
+
+static inline void
+_PyLong_FlipSign(PyLongObject *op) {
+ unsigned int flipped_sign = 2 - (op->long_value.lv_tag & SIGN_MASK);
+ op->long_value.lv_tag &= NON_SIZE_MASK;
+ op->long_value.lv_tag |= flipped_sign;
+}
+
+#define _PyLong_DIGIT_INIT(val) \
+ { \
+ .ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \
+ .long_value = { \
+ .lv_tag = TAG_FROM_SIGN_AND_SIZE( \
+ (val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
+ (val) == 0 ? 0 : 1), \
+ { ((val) >= 0 ? (val) : -(val)) }, \
+ } \
+ }
+
+#define _PyLong_FALSE_TAG TAG_FROM_SIGN_AND_SIZE(0, 0)
+#define _PyLong_TRUE_TAG TAG_FROM_SIGN_AND_SIZE(1, 1)
+
#ifdef __cplusplus
}
#endif