-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
GH-101291: Rearrange the size bits in PyLongObject #102464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
0ec07e4
292b9d0
5c54894
029aaa4
b56e6da
91269fc
c48e825
449c0e2
c5ba601
4b3a3e8
9ef9d2c
9c408c1
548d656
3e3fefd
391fb51
df8c7d3
bc14fa6
54c6f1b
ce6bfb2
4c1956b
301158b
1aa1891
bf2a9af
169f521
90f9072
f143443
a0d661e
145a2e4
638a98f
7f5acc0
b06bb6f
a19b0a7
87f49b2
f764aa8
9843ac0
d6cb917
469d26f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,11 +118,13 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter( | |
#define SIGN_NEGATIVE 2 | ||
#define NON_SIZE_BITS 3 | ||
|
||
/* All "single digit" values are guaranteed to fit into | ||
/* 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 positive single digit int */ | ||
/* Return 1 if the argument is compact int */ | ||
static inline int | ||
_PyLong_IsNonNegativeCompact(const PyLongObject* op) { | ||
assert(PyLong_Check(op)); | ||
|
@@ -142,7 +144,9 @@ _PyLong_BothAreCompact(const PyLongObject* a, const PyLongObject* b) { | |
return (a->long_value.lv_tag | b->long_value.lv_tag) < (2 << NON_SIZE_BITS); | ||
} | ||
|
||
/* The value returned by this function will have at least one bit to spare, | ||
/* 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. | ||
*/ | ||
|
@@ -180,7 +184,7 @@ _PyLong_DigitCount(const PyLongObject *op) | |
return op->long_value.lv_tag >> NON_SIZE_BITS; | ||
} | ||
|
||
/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonZeroSign(op) */ | ||
/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonCompactSign(op) */ | ||
static inline Py_ssize_t | ||
_PyLong_SignedDigitCount(const PyLongObject *op) | ||
{ | ||
|
@@ -199,9 +203,10 @@ _PyLong_UnsignedDigitCount(const PyLongObject *op) | |
} | ||
|
||
static inline int | ||
_PyLong_NonZeroSign(const PyLongObject *op) | ||
_PyLong_NonCompactSign(const PyLongObject *op) | ||
{ | ||
assert(PyLong_Check(op)); | ||
assert(!_PyLong_IsCompact(op)); | ||
return 1 - (op->long_value.lv_tag & SIGN_MASK); | ||
} | ||
|
||
|
@@ -215,7 +220,7 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) | |
#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I also haven't checked the assembly here, but I don't really know what happens when OR-ing a signed 64-bit int with a signed 32-bit int, and if this is doing work that's not strictly necessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is only in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So maybe add a comment that this macro should only be used with literal or size_t arguments? |
||
|
||
static inline void | ||
_PyLong_SetSignAndSize(PyLongObject *op, int sign, Py_ssize_t size) | ||
_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) | ||
{ | ||
assert(size >= 0); | ||
assert(-1 <= sign && sign <= 1); | ||
|
@@ -224,16 +229,18 @@ _PyLong_SetSignAndSize(PyLongObject *op, int sign, Py_ssize_t size) | |
} | ||
|
||
static inline void | ||
_PyLong_SetSize(PyLongObject *op, Py_ssize_t size) | ||
_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 &= ~7; | ||
op->long_value.lv_tag &= NON_SIZE_MASK; | ||
op->long_value.lv_tag |= flipped_sign; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also add that compact values have at most one digit? I've seen some code depending on that (e.g.
_PyLong_Multiply
).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not with tagged ints. In theory a compact int could have 5 digits. (63 bit compact ints, and 15 bit digits).
For a sensible implementation, a compact int will be one or two digits.