diff options
author | Jim Meyering <meyering@redhat.com> | 2007-09-01 09:54:45 +0200 |
---|---|---|
committer | Jim Meyering <meyering@redhat.com> | 2007-10-07 19:43:35 +0200 |
commit | 58a7ead41d056909784bfab9323c5f44f577d3d0 (patch) | |
tree | ea0f1849f46529be4cac93d09f1b4f6f85f53255 /gl/lib/randread.c | |
parent | 696f4a8042e11f6caf479f729b09dcae2700e541 (diff) | |
download | coreutils-58a7ead41d056909784bfab9323c5f44f577d3d0.tar.xz |
Convert coreutils' rand*.{c,h,m4} into modules.
First step: move these files to gl/lib:
* lib/rand-isaac.c, lib/rand-isaac.h
* lib/randint.c, lib/randint.h
* lib/randperm.c, lib/randperm.h
* lib/randread.c, lib/randread.h
Step 2: add modules/rand* and remove now-unneeded .m4 files.
* gl/modules/randint: New file.
* gl/modules/randperm: New file.
* gl/modules/randread: New file.
* m4/randint.m4: Remove file.
* m4/randperm.m4: Remove file.
* m4/randread.m4: Remove file.
Step 3: use the new modules
* bootstrap.conf (gnulib_modules): Add randint and randperm.
* m4/prereq.m4 (gl_RANDINT, gl_RANDREAD, gl_RANDPERM): Don't require;
These have been removed.
(gl_ROOT_DEV_INO): Don't require; already handled via bootstrap.conf.
Diffstat (limited to 'gl/lib/randread.c')
-rw-r--r-- | gl/lib/randread.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/gl/lib/randread.c b/gl/lib/randread.c new file mode 100644 index 000000000..9f65db676 --- /dev/null +++ b/gl/lib/randread.c @@ -0,0 +1,297 @@ +/* Generate buffers of random data. + + Copyright (C) 2006 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert. */ + +#include <config.h> + +#include "randread.h" + +#include <errno.h> +#include <error.h> +#include <exitfail.h> +#include <quotearg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#include "rand-isaac.h" +#include "stdio-safer.h" +#include "unlocked-io.h" +#include "xalloc.h" + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ +# define __attribute__(x) +# endif +#endif + +#ifndef ATTRIBUTE_UNUSED +# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +#if _STRING_ARCH_unaligned +# define ALIGNED_POINTER(ptr, type) true +#else +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define ALIGNED_POINTER(ptr, type) ((size_t) (ptr) % alignof (type) == 0) +#endif + +#ifndef DEFAULT_RANDOM_FILE +# define DEFAULT_RANDOM_FILE "/dev/urandom" +#endif + +/* The maximum buffer size used for reads of random data. Using the + value 2 * ISAAC_BYTES makes this the largest power of two that + would not otherwise cause struct randread_source to grow. */ +#define RANDREAD_BUFFER_SIZE (2 * ISAAC_BYTES) + +/* A source of random data for generating random buffers. */ +struct randread_source +{ + /* Stream to read random bytes from. If null, the behavior is + undefined; the current implementation uses ISAAC in this case, + but this is for old-fashioned implementations that lack + /dev/urandom and callers should not rely on this. */ + FILE *source; + + /* Function to call, and its argument, if there is an input error or + end of file when reading from the stream; errno is nonzero if + there was an error. If this function returns, it should fix the + problem before returning. The default handler assumes that + handler_arg is the file name of the source. */ + void (*handler) (void const *); + void const *handler_arg; + + /* The buffer for SOURCE. It's kept here to simplify storage + allocation and to make it easier to clear out buffered random + data. */ + union + { + /* The stream buffer, if SOURCE is not null. */ + char c[RANDREAD_BUFFER_SIZE]; + + /* The buffered ISAAC pseudorandom buffer, if SOURCE is null. */ + struct isaac + { + /* The number of bytes that are buffered at the end of data.b. */ + size_t buffered; + + /* State of the ISAAC generator. */ + struct isaac_state state; + + /* Up to a buffer's worth of pseudorandom data. */ + union + { + uint32_t w[ISAAC_WORDS]; + unsigned char b[ISAAC_BYTES]; + } data; + } isaac; + } buf; +}; + + +/* The default error handler. */ + +static void +randread_error (void const *file_name) +{ + if (file_name) + error (exit_failure, errno, + _(errno == 0 ? "%s: end of file" : "%s: read error"), + quotearg_colon (file_name)); + abort (); +} + +/* Simply return a new randread_source object with the default error + handler. */ + +static struct randread_source * +simple_new (FILE *source, void const *handler_arg) +{ + struct randread_source *s = xmalloc (sizeof *s); + s->source = source; + s->handler = randread_error; + s->handler_arg = handler_arg; + return s; +} + +/* Create and initialize a random data source from NAME, or use a + reasonable default source if NAME is null. BYTES_BOUND is an upper + bound on the number of bytes that will be needed. If zero, it is a + hard bound; otherwise it is just an estimate. + + If NAME is not null, NAME is saved for use as the argument of the + default handler. Unless a non-default handler is used, NAME's + lifetime should be at least that of the returned value. + + Return NULL (setting errno) on failure. */ + +struct randread_source * +randread_new (char const *name, size_t bytes_bound) +{ + if (bytes_bound == 0) + return simple_new (NULL, NULL); + else + { + char const *file_name = (name ? name : DEFAULT_RANDOM_FILE); + FILE *source = fopen_safer (file_name, "rb"); + struct randread_source *s; + + if (! source) + { + if (name) + return NULL; + file_name = NULL; + } + + s = simple_new (source, file_name); + + if (source) + setvbuf (source, s->buf.c, _IOFBF, MIN (sizeof s->buf.c, bytes_bound)); + else + { + s->buf.isaac.buffered = 0; + isaac_seed (&s->buf.isaac.state); + } + + return s; + } +} + + +/* Set S's handler and its argument. HANDLER (HANDLER_ARG) is called + when there is a read error or end of file from the random data + source; errno is nonzero if there was an error. If HANDLER + returns, it should fix the problem before returning. The default + handler assumes that handler_arg is the file name of the source; it + does not return. */ + +void +randread_set_handler (struct randread_source *s, void (*handler) (void const *)) +{ + s->handler = handler; +} + +void +randread_set_handler_arg (struct randread_source *s, void const *handler_arg) +{ + s->handler_arg = handler_arg; +} + + +/* Place SIZE random bytes into the buffer beginning at P, using + the stream in S. */ + +static void +readsource (struct randread_source *s, unsigned char *p, size_t size) +{ + for (;;) + { + size_t inbytes = fread (p, sizeof *p, size, s->source); + int fread_errno = errno; + p += inbytes; + size -= inbytes; + if (size == 0) + break; + errno = (ferror (s->source) ? fread_errno : 0); + s->handler (s->handler_arg); + } +} + + +/* Place SIZE pseudorandom bytes into the buffer beginning at P, using + the buffered ISAAC generator in ISAAC. */ + +static void +readisaac (struct isaac *isaac, unsigned char *p, size_t size) +{ + size_t inbytes = isaac->buffered; + + for (;;) + { + if (size <= inbytes) + { + memcpy (p, isaac->data.b + ISAAC_BYTES - inbytes, size); + isaac->buffered = inbytes - size; + return; + } + + memcpy (p, isaac->data.b + ISAAC_BYTES - inbytes, inbytes); + p += inbytes; + size -= inbytes; + + /* If P is aligned, write to *P directly to avoid the overhead + of copying from the buffer. */ + if (ALIGNED_POINTER (p, uint32_t)) + { + uint32_t *wp = (uint32_t *) p; + while (ISAAC_BYTES <= size) + { + isaac_refill (&isaac->state, wp); + wp += ISAAC_WORDS; + size -= ISAAC_BYTES; + if (size == 0) + { + isaac->buffered = 0; + return; + } + } + p = (unsigned char *) wp; + } + + isaac_refill (&isaac->state, isaac->data.w); + inbytes = ISAAC_BYTES; + } +} + + +/* Consume random data from *S to generate a random buffer BUF of size + SIZE. */ + +void +randread (struct randread_source *s, void *buf, size_t size) +{ + if (s->source) + readsource (s, buf, size); + else + readisaac (&s->buf.isaac, buf, size); +} + + +/* Clear *S so that it no longer contains undelivered random data, and + deallocate any system resources associated with *S. Return 0 if + successful, a negative number (setting errno) if not (this is rare, + but can occur in theory if there is an input error). */ + +int +randread_free (struct randread_source *s) +{ + FILE *source = s->source; + memset (s, 0, sizeof *s); + free (s); + return (source ? fclose (source) : 0); +} |