blob: 671d4158129bf00236df69e5b544e709c9307771 [file] [log] [blame]
Jens Wiklander02389a92016-12-16 11:13:38 +01001/*
2 * Copyright (c) 2016, Linaro Limited
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <stdint.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/param.h>
18#include <sys/types.h>
19
20#include "rand_stream.h"
21
22#define STREAM_BUF_MIN_SIZE 4
23
24struct rand_stream {
Jens Wiklander27867f52017-02-28 14:44:28 +010025 int32_t seed;
Jens Wiklander02389a92016-12-16 11:13:38 +010026 uint8_t word_buf[4];
27 size_t w_offs;
28 size_t sb_size;
29 size_t sb_offs;
30 uint8_t stream_buf[];
31};
32
33struct rand_stream *rand_stream_alloc(int seed, size_t stream_buffer_size)
34{
35 size_t sb_size = MAX(stream_buffer_size, STREAM_BUF_MIN_SIZE);
36 struct rand_stream *rs = calloc(1, sizeof(*rs) + sb_size);
37
38 if (!rs)
39 return NULL;
40
41 rs->sb_size = sb_size;;
42 rs->sb_offs = rs->sb_size;
43 rs->w_offs = sizeof(rs->word_buf);
Jens Wiklander27867f52017-02-28 14:44:28 +010044 rs->seed = seed;
Jens Wiklander02389a92016-12-16 11:13:38 +010045
46 return rs;
47}
48
49void rand_stream_free(struct rand_stream *rs)
50{
51 free(rs);
52}
53
54static void get_random(struct rand_stream *rs, uint8_t *buf, size_t blen)
55{
56 uint8_t *b = buf;
57 size_t l = blen;
58
Jens Wiklander27867f52017-02-28 14:44:28 +010059
60 /*
61 * This function uses an LCG,
62 * https://en.wikipedia.org/wiki/Linear_congruential_generator
63 * to generate the byte stream.
64 */
65
Jens Wiklander02389a92016-12-16 11:13:38 +010066 while (l) {
67 size_t t = MIN(sizeof(rs->word_buf) - rs->w_offs, l);
68
69 memcpy(b, rs->word_buf + rs->w_offs, t);
70 rs->w_offs += t;
71 l -= t;
72 b += t;
73
74 if (rs->w_offs == sizeof(rs->word_buf)) {
Jens Wiklander27867f52017-02-28 14:44:28 +010075 rs->seed = rs->seed * 1103515245 + 12345;
76 memcpy(rs->word_buf, &rs->seed, sizeof(rs->seed));
Jens Wiklander02389a92016-12-16 11:13:38 +010077 rs->w_offs = 0;
78 }
79 }
80}
81
82const void *rand_stream_peek(struct rand_stream *rs, size_t *num_bytes)
83{
84 if (rs->sb_offs == rs->sb_size) {
85 rs->sb_offs = 0;
86 get_random(rs, rs->stream_buf, rs->sb_size);
87 }
88
89 *num_bytes = MIN(*num_bytes, rs->sb_size - rs->sb_offs);
90 return rs->stream_buf + rs->sb_offs;
91}
92
93void rand_stream_read(struct rand_stream *rs, void *buf, size_t num_bytes)
94{
95 size_t peek_bytes = num_bytes;
96 const void *peek = rand_stream_peek(rs, &peek_bytes);
97
98 memcpy(buf, peek, peek_bytes);
99 rand_stream_advance(rs, peek_bytes);
100
101 if (num_bytes - peek_bytes)
102 get_random(rs, (uint8_t *)buf + peek_bytes,
103 num_bytes - peek_bytes);
104}
105
106void rand_stream_advance(struct rand_stream *rs, size_t num_bytes)
107{
108 size_t nb = num_bytes;
109
110 if (nb <= (rs->sb_size - rs->sb_offs)) {
111 rs->sb_offs += nb;
112 return;
113 }
114
115 nb -= rs->sb_size - rs->sb_offs;
116 rs->sb_offs = rs->sb_size;
117
118 while (nb > rs->sb_size) {
119 get_random(rs, rs->stream_buf, rs->sb_size);
120 nb -= rs->sb_size;
121 }
122
123 get_random(rs, rs->stream_buf, rs->sb_size);
124 rs->sb_offs = nb;
125}