diff options
Diffstat (limited to 'os/port/random.c')
| -rw-r--r-- | os/port/random.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/os/port/random.c b/os/port/random.c new file mode 100644 index 00000000..720541c0 --- /dev/null +++ b/os/port/random.c @@ -0,0 +1,156 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +static struct +{ + QLock; + Rendez producer; + Rendez consumer; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar bits; + uchar wakeme; + uchar filled; + int target; + int kprocstarted; + ulong randn; +} rb; + +static int +rbnotfull(void*) +{ + int i; + + i = rb.wp - rb.rp; + if(i < 0) + i += sizeof(rb.buf); + return i < rb.target; +} + +static int +rbnotempty(void*) +{ + return rb.wp != rb.rp; +} + +static void +genrandom(void*) +{ + setpri(PriBackground); + + for(;;) { + for(;;) + if(++rb.randomcount > 100000) + break; + if(anyhigher()) + sched(); + if(!rbnotfull(0)) + sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void) +{ + uchar *p; + + if(rb.randomcount == 0) + return; + + if(!rbnotfull(0)) { + rb.filled = 1; + return; + } + + rb.bits = (rb.bits<<2) ^ (rb.randomcount&3); + rb.randomcount = 0; + + rb.next += 2; + if(rb.next != 8) + return; + + rb.next = 0; + *rb.wp ^= rb.bits ^ *rb.rp; + p = rb.wp+1; + if(p == rb.ep) + p = rb.buf; + rb.wp = p; + + if(rb.wakeme) + wakeup(&rb.consumer); +} + +void +randominit(void) +{ + /* Frequency close but not equal to HZ */ + addclock0link(randomclock, 13); + rb.target = 16; + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + int i, sofar; + uchar *e, *p; + + p = xp; + + qlock(&rb); + if(waserror()){ + qunlock(&rb); + nexterror(); + } + if(!rb.kprocstarted){ + rb.kprocstarted = 1; + kproc("genrand", genrandom, nil, 0); + } + + for(sofar = 0; sofar < n; sofar += i){ + i = rb.wp - rb.rp; + if(i == 0){ + rb.wakeme = 1; + wakeup(&rb.producer); + sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + if(i < 0) + i = rb.ep - rb.rp; + if((i+sofar) > n) + i = n - sofar; + memmove(p + sofar, rb.rp, i); + e = rb.rp + i; + if(e == rb.ep) + e = rb.buf; + rb.rp = e; + } + if(rb.filled && rb.wp == rb.rp){ + i = 2*rb.target; + if(i > sizeof(rb.buf) - 1) + i = sizeof(rb.buf) - 1; + rb.target = i; + rb.filled = 0; + } + poperror(); + qunlock(&rb); + + wakeup(&rb.producer); + + return n; +} |
