summaryrefslogtreecommitdiff
path: root/os/port/random.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/port/random.c')
-rw-r--r--os/port/random.c156
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;
+}