mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-133489: Remove size restrictions on getrandbits() and randbytes() (GH-133658)
random.getrandbits() can now generate more that 2**31 bits. random.randbytes() can now generate more that 256 MiB.
This commit is contained in:
parent
c6e63d9d35
commit
68784fed78
5 changed files with 47 additions and 26 deletions
|
@ -392,6 +392,8 @@ class TestBasicOps:
|
||||||
self.assertRaises(TypeError, self.gen.getrandbits)
|
self.assertRaises(TypeError, self.gen.getrandbits)
|
||||||
self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
|
self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
|
||||||
self.assertRaises(ValueError, self.gen.getrandbits, -1)
|
self.assertRaises(ValueError, self.gen.getrandbits, -1)
|
||||||
|
self.assertRaises(OverflowError, self.gen.getrandbits, 1<<1000)
|
||||||
|
self.assertRaises(ValueError, self.gen.getrandbits, -1<<1000)
|
||||||
self.assertRaises(TypeError, self.gen.getrandbits, 10.1)
|
self.assertRaises(TypeError, self.gen.getrandbits, 10.1)
|
||||||
|
|
||||||
def test_pickling(self):
|
def test_pickling(self):
|
||||||
|
@ -435,6 +437,8 @@ class TestBasicOps:
|
||||||
self.assertRaises(TypeError, self.gen.randbytes)
|
self.assertRaises(TypeError, self.gen.randbytes)
|
||||||
self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
|
self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
|
||||||
self.assertRaises(ValueError, self.gen.randbytes, -1)
|
self.assertRaises(ValueError, self.gen.randbytes, -1)
|
||||||
|
self.assertRaises(OverflowError, self.gen.randbytes, 1<<1000)
|
||||||
|
self.assertRaises((ValueError, OverflowError), self.gen.randbytes, -1<<1000)
|
||||||
self.assertRaises(TypeError, self.gen.randbytes, 1.0)
|
self.assertRaises(TypeError, self.gen.randbytes, 1.0)
|
||||||
|
|
||||||
def test_mu_sigma_default_args(self):
|
def test_mu_sigma_default_args(self):
|
||||||
|
@ -806,6 +810,22 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
|
||||||
self.assertEqual(self.gen.getrandbits(100),
|
self.assertEqual(self.gen.getrandbits(100),
|
||||||
97904845777343510404718956115)
|
97904845777343510404718956115)
|
||||||
|
|
||||||
|
def test_getrandbits_2G_bits(self):
|
||||||
|
size = 2**31
|
||||||
|
self.gen.seed(1234567)
|
||||||
|
x = self.gen.getrandbits(size)
|
||||||
|
self.assertEqual(x.bit_length(), size)
|
||||||
|
self.assertEqual(x & (2**100-1), 890186470919986886340158459475)
|
||||||
|
self.assertEqual(x >> (size-100), 1226514312032729439655761284440)
|
||||||
|
|
||||||
|
@support.bigmemtest(size=2**32, memuse=1/8+2/15, dry_run=False)
|
||||||
|
def test_getrandbits_4G_bits(self, size):
|
||||||
|
self.gen.seed(1234568)
|
||||||
|
x = self.gen.getrandbits(size)
|
||||||
|
self.assertEqual(x.bit_length(), size)
|
||||||
|
self.assertEqual(x & (2**100-1), 287241425661104632871036099814)
|
||||||
|
self.assertEqual(x >> (size-100), 739728759900339699429794460738)
|
||||||
|
|
||||||
def test_randrange_uses_getrandbits(self):
|
def test_randrange_uses_getrandbits(self):
|
||||||
# Verify use of getrandbits by randrange
|
# Verify use of getrandbits by randrange
|
||||||
# Use same seed as in the cross-platform repeatability test
|
# Use same seed as in the cross-platform repeatability test
|
||||||
|
@ -962,6 +982,14 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
|
||||||
self.assertEqual(self.gen.randbytes(n),
|
self.assertEqual(self.gen.randbytes(n),
|
||||||
gen2.getrandbits(n * 8).to_bytes(n, 'little'))
|
gen2.getrandbits(n * 8).to_bytes(n, 'little'))
|
||||||
|
|
||||||
|
@support.bigmemtest(size=2**29, memuse=1+16/15, dry_run=False)
|
||||||
|
def test_randbytes_256M(self, size):
|
||||||
|
self.gen.seed(2849427419)
|
||||||
|
x = self.gen.randbytes(size)
|
||||||
|
self.assertEqual(len(x), size)
|
||||||
|
self.assertEqual(x[:12].hex(), 'f6fd9ae63855ab91ea238b4f')
|
||||||
|
self.assertEqual(x[-12:].hex(), '0e7af69a84ee99bf4a11becc')
|
||||||
|
|
||||||
def test_sample_counts_equivalence(self):
|
def test_sample_counts_equivalence(self):
|
||||||
# Test the documented strong equivalence to a sample with repeated elements.
|
# Test the documented strong equivalence to a sample with repeated elements.
|
||||||
# We run this test on random.Random() which makes deterministic selections
|
# We run this test on random.Random() which makes deterministic selections
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:func:`random.getrandbits` can now generate more that 2\ :sup:`31` bits.
|
||||||
|
:func:`random.randbytes` can now generate more that 256 MiB.
|
|
@ -497,34 +497,32 @@ _random_Random_setstate_impl(RandomObject *self, PyObject *state)
|
||||||
_random.Random.getrandbits
|
_random.Random.getrandbits
|
||||||
|
|
||||||
self: self(type="RandomObject *")
|
self: self(type="RandomObject *")
|
||||||
k: int
|
k: uint64
|
||||||
/
|
/
|
||||||
|
|
||||||
getrandbits(k) -> x. Generates an int with k random bits.
|
getrandbits(k) -> x. Generates an int with k random bits.
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_random_Random_getrandbits_impl(RandomObject *self, int k)
|
_random_Random_getrandbits_impl(RandomObject *self, uint64_t k)
|
||||||
/*[clinic end generated code: output=b402f82a2158887f input=87603cd60f79f730]*/
|
/*[clinic end generated code: output=c30ef8435f3433cf input=64226ac13bb4d2a3]*/
|
||||||
{
|
{
|
||||||
int i, words;
|
Py_ssize_t i, words;
|
||||||
uint32_t r;
|
uint32_t r;
|
||||||
uint32_t *wordarray;
|
uint32_t *wordarray;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
if (k < 0) {
|
|
||||||
PyErr_SetString(PyExc_ValueError,
|
|
||||||
"number of bits must be non-negative");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k == 0)
|
if (k == 0)
|
||||||
return PyLong_FromLong(0);
|
return PyLong_FromLong(0);
|
||||||
|
|
||||||
if (k <= 32) /* Fast path */
|
if (k <= 32) /* Fast path */
|
||||||
return PyLong_FromUnsignedLong(genrand_uint32(self) >> (32 - k));
|
return PyLong_FromUnsignedLong(genrand_uint32(self) >> (32 - k));
|
||||||
|
|
||||||
words = (k - 1) / 32 + 1;
|
if ((k - 1u) / 32u + 1u > PY_SSIZE_T_MAX / 4u) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
words = (k - 1u) / 32u + 1u;
|
||||||
wordarray = (uint32_t *)PyMem_Malloc(words * 4);
|
wordarray = (uint32_t *)PyMem_Malloc(words * 4);
|
||||||
if (wordarray == NULL) {
|
if (wordarray == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
|
10
Modules/clinic/_randommodule.c.h
generated
10
Modules/clinic/_randommodule.c.h
generated
|
@ -3,6 +3,7 @@ preserve
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
|
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
|
||||||
|
#include "pycore_long.h" // _PyLong_UInt64_Converter()
|
||||||
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
||||||
|
|
||||||
PyDoc_STRVAR(_random_Random_random__doc__,
|
PyDoc_STRVAR(_random_Random_random__doc__,
|
||||||
|
@ -124,16 +125,15 @@ PyDoc_STRVAR(_random_Random_getrandbits__doc__,
|
||||||
{"getrandbits", (PyCFunction)_random_Random_getrandbits, METH_O, _random_Random_getrandbits__doc__},
|
{"getrandbits", (PyCFunction)_random_Random_getrandbits, METH_O, _random_Random_getrandbits__doc__},
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_random_Random_getrandbits_impl(RandomObject *self, int k);
|
_random_Random_getrandbits_impl(RandomObject *self, uint64_t k);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_random_Random_getrandbits(PyObject *self, PyObject *arg)
|
_random_Random_getrandbits(PyObject *self, PyObject *arg)
|
||||||
{
|
{
|
||||||
PyObject *return_value = NULL;
|
PyObject *return_value = NULL;
|
||||||
int k;
|
uint64_t k;
|
||||||
|
|
||||||
k = PyLong_AsInt(arg);
|
if (!_PyLong_UInt64_Converter(arg, &k)) {
|
||||||
if (k == -1 && PyErr_Occurred()) {
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
Py_BEGIN_CRITICAL_SECTION(self);
|
Py_BEGIN_CRITICAL_SECTION(self);
|
||||||
|
@ -143,4 +143,4 @@ _random_Random_getrandbits(PyObject *self, PyObject *arg)
|
||||||
exit:
|
exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=4458b5a69201ebea input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=7ce97b2194eecaf7 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -971,16 +971,9 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n,
|
||||||
++numsignificantbytes;
|
++numsignificantbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* How many Python int digits do we need? We have
|
/* avoid integer overflow */
|
||||||
8*numsignificantbytes bits, and each Python int digit has
|
ndigits = numsignificantbytes / PyLong_SHIFT * 8
|
||||||
PyLong_SHIFT bits, so it's the ceiling of the quotient. */
|
+ (numsignificantbytes % PyLong_SHIFT * 8 + PyLong_SHIFT - 1) / PyLong_SHIFT;
|
||||||
/* catch overflow before it happens */
|
|
||||||
if (numsignificantbytes > (PY_SSIZE_T_MAX - PyLong_SHIFT) / 8) {
|
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
|
||||||
"byte array too long to convert to int");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ndigits = (numsignificantbytes * 8 + PyLong_SHIFT - 1) / PyLong_SHIFT;
|
|
||||||
v = long_alloc(ndigits);
|
v = long_alloc(ndigits);
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue