Added the Search API Synonym module to deal specifically with licence and license...
[yaffs-website] / vendor / paragonie / random_compat / lib / random_int.php
1 <?php
2
3 if (!is_callable('random_int')) {
4     /**
5      * Random_* Compatibility Library
6      * for using the new PHP 7 random_* API in PHP 5 projects
7      *
8      * The MIT License (MIT)
9      *
10      * Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
11      *
12      * Permission is hereby granted, free of charge, to any person obtaining a copy
13      * of this software and associated documentation files (the "Software"), to deal
14      * in the Software without restriction, including without limitation the rights
15      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16      * copies of the Software, and to permit persons to whom the Software is
17      * furnished to do so, subject to the following conditions:
18      *
19      * The above copyright notice and this permission notice shall be included in
20      * all copies or substantial portions of the Software.
21      *
22      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26      * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27      * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28      * SOFTWARE.
29      */
30
31     /**
32      * Fetch a random integer between $min and $max inclusive
33      *
34      * @param int $min
35      * @param int $max
36      *
37      * @throws Exception
38      *
39      * @return int
40      */
41     function random_int($min, $max)
42     {
43         /**
44          * Type and input logic checks
45          *
46          * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
47          * (non-inclusive), it will sanely cast it to an int. If you it's equal to
48          * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
49          * lose precision, so the <= and => operators might accidentally let a float
50          * through.
51          */
52
53         try {
54             /** @var int $min */
55             $min = RandomCompat_intval($min);
56         } catch (TypeError $ex) {
57             throw new TypeError(
58                 'random_int(): $min must be an integer'
59             );
60         }
61
62         try {
63             /** @var int $max */
64             $max = RandomCompat_intval($max);
65         } catch (TypeError $ex) {
66             throw new TypeError(
67                 'random_int(): $max must be an integer'
68             );
69         }
70
71         /**
72          * Now that we've verified our weak typing system has given us an integer,
73          * let's validate the logic then we can move forward with generating random
74          * integers along a given range.
75          */
76         if ($min > $max) {
77             throw new Error(
78                 'Minimum value must be less than or equal to the maximum value'
79             );
80         }
81
82         if ($max === $min) {
83             return (int) $min;
84         }
85
86         /**
87          * Initialize variables to 0
88          *
89          * We want to store:
90          * $bytes => the number of random bytes we need
91          * $mask => an integer bitmask (for use with the &) operator
92          *          so we can minimize the number of discards
93          */
94         $attempts = $bits = $bytes = $mask = $valueShift = 0;
95         /** @var int $attempts */
96         /** @var int $bits */
97         /** @var int $bytes */
98         /** @var int $mask */
99         /** @var int $valueShift */
100
101         /**
102          * At this point, $range is a positive number greater than 0. It might
103          * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
104          * a float and we will lose some precision.
105          *
106          * @var int|float $range
107          */
108         $range = $max - $min;
109
110         /**
111          * Test for integer overflow:
112          */
113         if (!is_int($range)) {
114
115             /**
116              * Still safely calculate wider ranges.
117              * Provided by @CodesInChaos, @oittaa
118              *
119              * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
120              *
121              * We use ~0 as a mask in this case because it generates all 1s
122              *
123              * @ref https://eval.in/400356 (32-bit)
124              * @ref http://3v4l.org/XX9r5  (64-bit)
125              */
126             $bytes = PHP_INT_SIZE;
127             /** @var int $mask */
128             $mask = ~0;
129
130         } else {
131
132             /**
133              * $bits is effectively ceil(log($range, 2)) without dealing with
134              * type juggling
135              */
136             while ($range > 0) {
137                 if ($bits % 8 === 0) {
138                     ++$bytes;
139                 }
140                 ++$bits;
141                 $range >>= 1;
142                 /** @var int $mask */
143                 $mask = $mask << 1 | 1;
144             }
145             $valueShift = $min;
146         }
147
148         /** @var int $val */
149         $val = 0;
150         /**
151          * Now that we have our parameters set up, let's begin generating
152          * random integers until one falls between $min and $max
153          */
154         /** @psalm-suppress RedundantCondition */
155         do {
156             /**
157              * The rejection probability is at most 0.5, so this corresponds
158              * to a failure probability of 2^-128 for a working RNG
159              */
160             if ($attempts > 128) {
161                 throw new Exception(
162                     'random_int: RNG is broken - too many rejections'
163                 );
164             }
165
166             /**
167              * Let's grab the necessary number of random bytes
168              */
169             $randomByteString = random_bytes($bytes);
170
171             /**
172              * Let's turn $randomByteString into an integer
173              *
174              * This uses bitwise operators (<< and |) to build an integer
175              * out of the values extracted from ord()
176              *
177              * Example: [9F] | [6D] | [32] | [0C] =>
178              *   159 + 27904 + 3276800 + 201326592 =>
179              *   204631455
180              */
181             $val &= 0;
182             for ($i = 0; $i < $bytes; ++$i) {
183                 $val |= ord($randomByteString[$i]) << ($i * 8);
184             }
185             /** @var int $val */
186
187             /**
188              * Apply mask
189              */
190             $val &= $mask;
191             $val += $valueShift;
192
193             ++$attempts;
194             /**
195              * If $val overflows to a floating point number,
196              * ... or is larger than $max,
197              * ... or smaller than $min,
198              * then try again.
199              */
200         } while (!is_int($val) || $val > $max || $val < $min);
201
202         return (int) $val;
203     }
204 }