xref: /aosp_15_r20/external/rappor/tests/_fastrand.c (revision 2abb31345f6c95944768b5222a9a5ed3fc68cc00)
1*2abb3134SXin Li /*
2*2abb3134SXin Li Copyright 2014 Google Inc. All rights reserved.
3*2abb3134SXin Li 
4*2abb3134SXin Li Licensed under the Apache License, Version 2.0 (the "License");
5*2abb3134SXin Li you may not use this file except in compliance with the License.
6*2abb3134SXin Li You may obtain a copy of the License at
7*2abb3134SXin Li 
8*2abb3134SXin Li     http://www.apache.org/licenses/LICENSE-2.0
9*2abb3134SXin Li 
10*2abb3134SXin Li Unless required by applicable law or agreed to in writing, software
11*2abb3134SXin Li distributed under the License is distributed on an "AS IS" BASIS,
12*2abb3134SXin Li WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*2abb3134SXin Li See the License for the specific language governing permissions and
14*2abb3134SXin Li limitations under the License.
15*2abb3134SXin Li */
16*2abb3134SXin Li 
17*2abb3134SXin Li /*
18*2abb3134SXin Li  * _fastrand.c -- Python extension module to generate random bit vectors
19*2abb3134SXin Li  * quickly.
20*2abb3134SXin Li  *
21*2abb3134SXin Li  * IMPORTANT: This module does not use crytographically strong randomness.  It
22*2abb3134SXin Li  * should be used ONLY be used to speed up the simulation.  Don't use it in
23*2abb3134SXin Li  * production.
24*2abb3134SXin Li  *
25*2abb3134SXin Li  * If an adversary can predict which random bits are flipped, then RAPPOR's
26*2abb3134SXin Li  * privacy is compromised.
27*2abb3134SXin Li  *
28*2abb3134SXin Li  */
29*2abb3134SXin Li 
30*2abb3134SXin Li #include <stdint.h>  // uint64_t
31*2abb3134SXin Li #include <stdio.h>  // printf
32*2abb3134SXin Li #include <stdlib.h>  // srand
33*2abb3134SXin Li #include <time.h>  // time
34*2abb3134SXin Li 
35*2abb3134SXin Li #include <Python.h>
36*2abb3134SXin Li 
randbits(float p1,int num_bits)37*2abb3134SXin Li uint64_t randbits(float p1, int num_bits) {
38*2abb3134SXin Li   uint64_t result = 0;
39*2abb3134SXin Li   // RAND_MAX is the maximum int returned by rand().
40*2abb3134SXin Li   //
41*2abb3134SXin Li   // When p1 == 1.0, we want to guarantee that all bits are 1.  The threshold
42*2abb3134SXin Li   // will be RAND_MAX + 1.  In the rare case that rand() returns RAND_MAX, the
43*2abb3134SXin Li   // "<" test succeeds, so we get 1.
44*2abb3134SXin Li   //
45*2abb3134SXin Li   // When p1 == 0.0, we want to guarantee that all bits are 0.  The threshold
46*2abb3134SXin Li   // will be 0.  In the rare case that rand() returns 0, the "<" test fails, so
47*2abb3134SXin Li   // we get 0.
48*2abb3134SXin Li 
49*2abb3134SXin Li   // NOTE: cast is necessary to do unsigned arithmetic rather than signed.
50*2abb3134SXin Li   // RAND_MAX is an int so adding 1 won't overflow a uint64_t.
51*2abb3134SXin Li   uint64_t max = (uint64_t)RAND_MAX + 1u;
52*2abb3134SXin Li   uint64_t threshold = p1 * max;
53*2abb3134SXin Li   int i;
54*2abb3134SXin Li   for (i = 0; i < num_bits; ++i) {
55*2abb3134SXin Li     // NOTE: The comparison is <= so that p1 = 1.0 implies that the bit is
56*2abb3134SXin Li     // ALWAYS set.  RAND_MAX is the maximum value returned by rand().
57*2abb3134SXin Li     uint64_t bit = (rand() < threshold);
58*2abb3134SXin Li     result |= (bit << i);
59*2abb3134SXin Li   }
60*2abb3134SXin Li   return result;
61*2abb3134SXin Li }
62*2abb3134SXin Li 
63*2abb3134SXin Li static PyObject *
func_randbits(PyObject * self,PyObject * args)64*2abb3134SXin Li func_randbits(PyObject *self, PyObject *args) {
65*2abb3134SXin Li   float p1;
66*2abb3134SXin Li   int num_bits;
67*2abb3134SXin Li 
68*2abb3134SXin Li   if (!PyArg_ParseTuple(args, "fi", &p1, &num_bits)) {
69*2abb3134SXin Li     return NULL;
70*2abb3134SXin Li   }
71*2abb3134SXin Li   if (p1 < 0.0 || p1 > 1.0) {
72*2abb3134SXin Li     printf("p1 must be between 0.0 and 1.0\n");
73*2abb3134SXin Li     // return None for now; easier than raising ValueError
74*2abb3134SXin Li     Py_INCREF(Py_None);
75*2abb3134SXin Li     return Py_None;
76*2abb3134SXin Li   }
77*2abb3134SXin Li   if (num_bits < 0 || num_bits > 64) {
78*2abb3134SXin Li     printf("num_bits must be 64 or less\n");
79*2abb3134SXin Li     // return None for now; easier than raising ValueError
80*2abb3134SXin Li     Py_INCREF(Py_None);
81*2abb3134SXin Li     return Py_None;
82*2abb3134SXin Li   }
83*2abb3134SXin Li 
84*2abb3134SXin Li   //printf("p: %f\n", p);
85*2abb3134SXin Li   uint64_t r = randbits(p1, num_bits);
86*2abb3134SXin Li   return PyLong_FromUnsignedLongLong(r);
87*2abb3134SXin Li }
88*2abb3134SXin Li 
89*2abb3134SXin Li PyMethodDef methods[] = {
90*2abb3134SXin Li   {"randbits", func_randbits, METH_VARARGS,
91*2abb3134SXin Li    "Return a number with N bits, where each bit is 1 with probability p."},
92*2abb3134SXin Li   {NULL, NULL},
93*2abb3134SXin Li };
94*2abb3134SXin Li 
init_fastrand(void)95*2abb3134SXin Li void init_fastrand(void) {
96*2abb3134SXin Li   Py_InitModule("_fastrand", methods);
97*2abb3134SXin Li 
98*2abb3134SXin Li   // Just seed it here; we don't give the application any control.
99*2abb3134SXin Li   int seed = time(NULL);
100*2abb3134SXin Li   srand(seed);
101*2abb3134SXin Li }
102