blob: 60ad920c90b9bf4e920b84e7f61ea13036f3c5be [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 {
25 struct random_data random_data;
26 char state_buf[128];
27 uint8_t word_buf[4];
28 size_t w_offs;
29 size_t sb_size;
30 size_t sb_offs;
31 uint8_t stream_buf[];
32};
33
34struct rand_stream *rand_stream_alloc(int seed, size_t stream_buffer_size)
35{
36 size_t sb_size = MAX(stream_buffer_size, STREAM_BUF_MIN_SIZE);
37 struct rand_stream *rs = calloc(1, sizeof(*rs) + sb_size);
38
39 if (!rs)
40 return NULL;
41
42 rs->sb_size = sb_size;;
43 rs->sb_offs = rs->sb_size;
44 rs->w_offs = sizeof(rs->word_buf);
45
46 if (initstate_r(seed, rs->state_buf, sizeof(rs->state_buf),
47 &rs->random_data)) {
48 free(rs);
49 return NULL;
50 }
51
52 return rs;
53}
54
55void rand_stream_free(struct rand_stream *rs)
56{
57 free(rs);
58}
59
60static void get_random(struct rand_stream *rs, uint8_t *buf, size_t blen)
61{
62 uint8_t *b = buf;
63 size_t l = blen;
64
65 while (l) {
66 size_t t = MIN(sizeof(rs->word_buf) - rs->w_offs, l);
67
68 memcpy(b, rs->word_buf + rs->w_offs, t);
69 rs->w_offs += t;
70 l -= t;
71 b += t;
72
73 if (rs->w_offs == sizeof(rs->word_buf)) {
74 int32_t r;
75
76 random_r(&rs->random_data, &r);
77 memcpy(rs->word_buf, &r, sizeof(r));
78 rs->w_offs = 0;
79 }
80 }
81}
82
83const void *rand_stream_peek(struct rand_stream *rs, size_t *num_bytes)
84{
85 if (rs->sb_offs == rs->sb_size) {
86 rs->sb_offs = 0;
87 get_random(rs, rs->stream_buf, rs->sb_size);
88 }
89
90 *num_bytes = MIN(*num_bytes, rs->sb_size - rs->sb_offs);
91 return rs->stream_buf + rs->sb_offs;
92}
93
94void rand_stream_read(struct rand_stream *rs, void *buf, size_t num_bytes)
95{
96 size_t peek_bytes = num_bytes;
97 const void *peek = rand_stream_peek(rs, &peek_bytes);
98
99 memcpy(buf, peek, peek_bytes);
100 rand_stream_advance(rs, peek_bytes);
101
102 if (num_bytes - peek_bytes)
103 get_random(rs, (uint8_t *)buf + peek_bytes,
104 num_bytes - peek_bytes);
105}
106
107void rand_stream_advance(struct rand_stream *rs, size_t num_bytes)
108{
109 size_t nb = num_bytes;
110
111 if (nb <= (rs->sb_size - rs->sb_offs)) {
112 rs->sb_offs += nb;
113 return;
114 }
115
116 nb -= rs->sb_size - rs->sb_offs;
117 rs->sb_offs = rs->sb_size;
118
119 while (nb > rs->sb_size) {
120 get_random(rs, rs->stream_buf, rs->sb_size);
121 nb -= rs->sb_size;
122 }
123
124 get_random(rs, rs->stream_buf, rs->sb_size);
125 rs->sb_offs = nb;
126}