Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright  2012 Siarhei Siamashka <siarhei.siamashka (at) gmail.com>
      3  *
      4  * Based on the public domain implementation of small noncryptographic PRNG
      5  * authored by Bob Jenkins: http://burtleburtle.net/bob/rand/smallprng.html
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a
      8  * copy of this software and associated documentation files (the "Software"),
      9  * to deal in the Software without restriction, including without limitation
     10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     11  * and/or sell copies of the Software, and to permit persons to whom the
     12  * Software is furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the next
     15  * paragraph) shall be included in all copies or substantial portions of the
     16  * Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     24  * DEALINGS IN THE SOFTWARE.
     25  */
     26 
     27 #include "utils.h"
     28 #include "utils-prng.h"
     29 
     30 #if defined(GCC_VECTOR_EXTENSIONS_SUPPORTED) && defined(__SSE2__)
     31 #include <xmmintrin.h>
     32 #endif
     33 
     34 void smallprng_srand_r (smallprng_t *x, uint32_t seed)
     35 {
     36     uint32_t i;
     37     x->a = 0xf1ea5eed, x->b = x->c = x->d = seed;
     38     for (i = 0; i < 20; ++i)
     39         smallprng_rand_r (x);
     40 }
     41 
     42 /*
     43  * Set a 32-bit seed for PRNG
     44  *
     45  * LCG is used here for generating independent seeds for different
     46  * smallprng instances (in the case if smallprng is also used for
     47  * generating these seeds, "Big Crush" test from TestU01 detects
     48  * some problems in the glued 'prng_rand_128_r' output data).
     49  * Actually we might be even better using some cryptographic
     50  * hash for this purpose, but LCG seems to be also enough for
     51  * passing "Big Crush".
     52  */
     53 void prng_srand_r (prng_t *x, uint32_t seed)
     54 {
     55 #ifdef GCC_VECTOR_EXTENSIONS_SUPPORTED
     56     int i;
     57     prng_rand_128_data_t dummy;
     58     smallprng_srand_r (&x->p0, seed);
     59     x->a[0] = x->a[1] = x->a[2] = x->a[3] = 0xf1ea5eed;
     60     x->b[0] = x->c[0] = x->d[0] = (seed = seed * 1103515245 + 12345);
     61     x->b[1] = x->c[1] = x->d[1] = (seed = seed * 1103515245 + 12345);
     62     x->b[2] = x->c[2] = x->d[2] = (seed = seed * 1103515245 + 12345);
     63     x->b[3] = x->c[3] = x->d[3] = (seed = seed * 1103515245 + 12345);
     64     for (i = 0; i < 20; ++i)
     65         prng_rand_128_r (x, &dummy);
     66 #else
     67     smallprng_srand_r (&x->p0, seed);
     68     smallprng_srand_r (&x->p1, (seed = seed * 1103515245 + 12345));
     69     smallprng_srand_r (&x->p2, (seed = seed * 1103515245 + 12345));
     70     smallprng_srand_r (&x->p3, (seed = seed * 1103515245 + 12345));
     71     smallprng_srand_r (&x->p4, (seed = seed * 1103515245 + 12345));
     72 #endif
     73 }
     74 
     75 static force_inline void
     76 store_rand_128_data (void *addr, prng_rand_128_data_t *d, int aligned)
     77 {
     78 #ifdef GCC_VECTOR_EXTENSIONS_SUPPORTED
     79     if (aligned)
     80     {
     81         *(uint8x16 *)addr = d->vb;
     82         return;
     83     }
     84     else
     85     {
     86 #ifdef __SSE2__
     87         /* workaround for http://gcc.gnu.org/PR55614 */
     88         _mm_storeu_si128 (addr, _mm_loadu_si128 ((__m128i *)d));
     89         return;
     90 #endif
     91     }
     92 #endif
     93     /* we could try something better for unaligned writes (packed attribute),
     94      * but GCC is not very reliable: http://gcc.gnu.org/PR55454 */
     95     memcpy (addr, d, 16);
     96 }
     97 
     98 /*
     99  * Helper function and the actual code for "prng_randmemset_r" function
    100  */
    101 static force_inline void
    102 randmemset_internal (prng_t                  *prng,
    103                      uint8_t                 *buf,
    104                      size_t                   size,
    105                      prng_randmemset_flags_t  flags,
    106                      int                      aligned)
    107 {
    108     prng_t local_prng = *prng;
    109     prng_rand_128_data_t randdata;
    110     size_t i;
    111 
    112     while (size >= 16)
    113     {
    114         prng_rand_128_data_t t;
    115         if (flags == 0)
    116         {
    117             prng_rand_128_r (&local_prng, &randdata);
    118         }
    119         else
    120         {
    121             prng_rand_128_r (&local_prng, &t);
    122             prng_rand_128_r (&local_prng, &randdata);
    123 #ifdef GCC_VECTOR_EXTENSIONS_SUPPORTED
    124             if (flags & RANDMEMSET_MORE_FF)
    125             {
    126                 const uint8x16 const_C0 =
    127                 {
    128                     0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
    129                     0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0
    130                 };
    131                 randdata.vb |= (t.vb >= const_C0);
    132             }
    133             if (flags & RANDMEMSET_MORE_00)
    134             {
    135                 const uint8x16 const_40 =
    136                 {
    137                     0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    138                     0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
    139                 };
    140                 randdata.vb &= (t.vb >= const_40);
    141             }
    142             if (flags & RANDMEMSET_MORE_FFFFFFFF)
    143             {
    144                 const uint32x4 const_C0000000 =
    145                 {
    146                     0xC0000000, 0xC0000000, 0xC0000000, 0xC0000000
    147                 };
    148                 randdata.vw |= ((t.vw << 30) >= const_C0000000);
    149             }
    150             if (flags & RANDMEMSET_MORE_00000000)
    151             {
    152                 const uint32x4 const_40000000 =
    153                 {
    154                     0x40000000, 0x40000000, 0x40000000, 0x40000000
    155                 };
    156                 randdata.vw &= ((t.vw << 30) >= const_40000000);
    157             }
    158 #else
    159             #define PROCESS_ONE_LANE(i)                                       \
    160                 if (flags & RANDMEMSET_MORE_FF)                               \
    161                 {                                                             \
    162                     uint32_t mask_ff = (t.w[i] & (t.w[i] << 1)) & 0x80808080; \
    163                     mask_ff |= mask_ff >> 1;                                  \
    164                     mask_ff |= mask_ff >> 2;                                  \
    165                     mask_ff |= mask_ff >> 4;                                  \
    166                     randdata.w[i] |= mask_ff;                                 \
    167                 }                                                             \
    168                 if (flags & RANDMEMSET_MORE_00)                               \
    169                 {                                                             \
    170                     uint32_t mask_00 = (t.w[i] | (t.w[i] << 1)) & 0x80808080; \
    171                     mask_00 |= mask_00 >> 1;                                  \
    172                     mask_00 |= mask_00 >> 2;                                  \
    173                     mask_00 |= mask_00 >> 4;                                  \
    174                     randdata.w[i] &= mask_00;                                 \
    175                 }                                                             \
    176                 if (flags & RANDMEMSET_MORE_FFFFFFFF)                         \
    177                 {                                                             \
    178                     int32_t mask_ff = ((t.w[i] << 30) & (t.w[i] << 31)) &     \
    179                                        0x80000000;                            \
    180                     randdata.w[i] |= mask_ff >> 31;                           \
    181                 }                                                             \
    182                 if (flags & RANDMEMSET_MORE_00000000)                         \
    183                 {                                                             \
    184                     int32_t mask_00 = ((t.w[i] << 30) | (t.w[i] << 31)) &     \
    185                                        0x80000000;                            \
    186                     randdata.w[i] &= mask_00 >> 31;                           \
    187                 }
    188 
    189             PROCESS_ONE_LANE (0)
    190             PROCESS_ONE_LANE (1)
    191             PROCESS_ONE_LANE (2)
    192             PROCESS_ONE_LANE (3)
    193 #endif
    194         }
    195         if (is_little_endian ())
    196         {
    197             store_rand_128_data (buf, &randdata, aligned);
    198             buf += 16;
    199         }
    200         else
    201         {
    202 #ifdef GCC_VECTOR_EXTENSIONS_SUPPORTED
    203             const uint8x16 bswap_shufflemask =
    204             {
    205                 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12
    206             };
    207             randdata.vb = __builtin_shuffle (randdata.vb, bswap_shufflemask);
    208             store_rand_128_data (buf, &randdata, aligned);
    209             buf += 16;
    210 #else
    211             uint8_t t1, t2, t3, t4;
    212             #define STORE_ONE_LANE(i)                                         \
    213                 t1 = randdata.b[i * 4 + 3];                                   \
    214                 t2 = randdata.b[i * 4 + 2];                                   \
    215                 t3 = randdata.b[i * 4 + 1];                                   \
    216                 t4 = randdata.b[i * 4 + 0];                                   \
    217                 *buf++ = t1;                                                  \
    218                 *buf++ = t2;                                                  \
    219                 *buf++ = t3;                                                  \
    220                 *buf++ = t4;
    221 
    222             STORE_ONE_LANE (0)
    223             STORE_ONE_LANE (1)
    224             STORE_ONE_LANE (2)
    225             STORE_ONE_LANE (3)
    226 #endif
    227         }
    228         size -= 16;
    229     }
    230     i = 0;
    231     while (i < size)
    232     {
    233         uint8_t randbyte = prng_rand_r (&local_prng) & 0xFF;
    234         if (flags != 0)
    235         {
    236             uint8_t t = prng_rand_r (&local_prng) & 0xFF;
    237             if ((flags & RANDMEMSET_MORE_FF) && (t >= 0xC0))
    238                 randbyte = 0xFF;
    239             if ((flags & RANDMEMSET_MORE_00) && (t < 0x40))
    240                 randbyte = 0x00;
    241             if (i % 4 == 0 && i + 4 <= size)
    242             {
    243                 t = prng_rand_r (&local_prng) & 0xFF;
    244                 if ((flags & RANDMEMSET_MORE_FFFFFFFF) && (t >= 0xC0))
    245                 {
    246                     memset(&buf[i], 0xFF, 4);
    247                     i += 4;
    248                     continue;
    249                 }
    250                 if ((flags & RANDMEMSET_MORE_00000000) && (t < 0x40))
    251                 {
    252                     memset(&buf[i], 0x00, 4);
    253                     i += 4;
    254                     continue;
    255                 }
    256             }
    257         }
    258         buf[i] = randbyte;
    259         i++;
    260     }
    261     *prng = local_prng;
    262 }
    263 
    264 /*
    265  * Fill memory buffer with random data. Flags argument may be used
    266  * to tweak some statistics properties:
    267  *    RANDMEMSET_MORE_00        - set ~25% of bytes to 0x00
    268  *    RANDMEMSET_MORE_FF        - set ~25% of bytes to 0xFF
    269  *    RANDMEMSET_MORE_00000000  - ~25% chance for 00000000 4-byte clusters
    270  *    RANDMEMSET_MORE_FFFFFFFF  - ~25% chance for FFFFFFFF 4-byte clusters
    271  */
    272 void prng_randmemset_r (prng_t                  *prng,
    273                         void                    *voidbuf,
    274                         size_t                   size,
    275                         prng_randmemset_flags_t  flags)
    276 {
    277     uint8_t *buf = (uint8_t *)voidbuf;
    278     if ((uintptr_t)buf & 15)
    279     {
    280         /* unaligned buffer */
    281         if (flags == 0)
    282             randmemset_internal (prng, buf, size, 0, 0);
    283         else if (flags == RANDMEMSET_MORE_00_AND_FF)
    284             randmemset_internal (prng, buf, size, RANDMEMSET_MORE_00_AND_FF, 0);
    285         else
    286             randmemset_internal (prng, buf, size, flags, 0);
    287     }
    288     else
    289     {
    290         /* aligned buffer */
    291         if (flags == 0)
    292             randmemset_internal (prng, buf, size, 0, 1);
    293         else if (flags == RANDMEMSET_MORE_00_AND_FF)
    294             randmemset_internal (prng, buf, size, RANDMEMSET_MORE_00_AND_FF, 1);
    295         else
    296             randmemset_internal (prng, buf, size, flags, 1);
    297     }
    298 }
    299