Position seek/tell.
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
#include "chacha20.h"
|
#include "charandom.h"
|
||||||
|
|
||||||
static uint64_t cha_uint64(void* st) {
|
static uint64_t cha_uint64(void* st) {
|
||||||
cha_ctx* ctx = (cha_ctx*)st;
|
cha_ctx* ctx = (cha_ctx*)st;
|
||||||
if (ctx->offset == ctx->end) {
|
if (ctx->offset + sizeof(uint64_t) > ctx->end) {
|
||||||
ctx->offset = 0;
|
ctx->offset -= ctx->end;
|
||||||
ctx->end =
|
ctx->end =
|
||||||
ctx->gen(ctx->unconsumed, BATCH_SIZE, ctx->state, ctx->rounds);
|
ctx->gen(ctx->unconsumed, BATCH_SIZE, ctx->state, ctx->rounds);
|
||||||
}
|
}
|
||||||
@@ -13,5 +13,6 @@ static uint64_t cha_uint64(void* st) {
|
|||||||
}
|
}
|
||||||
static uint32_t cha_uint32(void* st) { return cha_uint64(st); }
|
static uint32_t cha_uint32(void* st) { return cha_uint64(st); }
|
||||||
static double cha_double(void* st) {
|
static double cha_double(void* st) {
|
||||||
return cha_uint64(st) / (UINT64_MAX + 1.0);
|
// Fast uint64_to_double conversion from numpy/random/_common.pxd
|
||||||
|
return (cha_uint64(st) >> 11) * (1.0 / 9007199254740992.0);
|
||||||
}
|
}
|
||||||
@@ -75,11 +75,31 @@ void cha_init(
|
|||||||
void cha_wipe(cha_ctx* ctx) { memset(ctx, 0, sizeof(cha_ctx)); }
|
void cha_wipe(cha_ctx* ctx) { memset(ctx, 0, sizeof(cha_ctx)); }
|
||||||
|
|
||||||
/// @brief Advance or rewind the stream to any arbitrary location
|
/// @brief Advance or rewind the stream to any arbitrary location
|
||||||
|
/// Keeps byte offset inside batch untouched but clears the buffer
|
||||||
/// @param ctx ChaCha context
|
/// @param ctx ChaCha context
|
||||||
/// @param offset Offset in blocks of 64 bytes (counter change)
|
/// @param offset Offset in blocks of 64 bytes (counter change)
|
||||||
void cha_seek_blocks(cha_ctx* ctx, int64_t offset) {
|
void cha_seek_blocks(cha_ctx* ctx, int64_t offset) {
|
||||||
*(uint64_t*)(ctx->state + 12) += offset;
|
*(uint64_t*)(ctx->state + 12) += offset - (int64_t)ctx->end / 64;
|
||||||
ctx->offset = ctx->end = 0;
|
ctx->end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Seek a number of bytes forward or backward in stream
|
||||||
|
/// Supports seeking backwards and forwards, even beyond start, but only up to
|
||||||
|
/// 64 bit distance which does not cover the whole counter range.
|
||||||
|
/// @param ctx ChaCha context
|
||||||
|
/// @param offset Positive or negative offset from current byte position
|
||||||
|
void cha_seek(cha_ctx* ctx, int64_t offset) {
|
||||||
|
offset += (int64_t)ctx->offset;
|
||||||
|
ctx->offset = ((offset % 64) + 64) % 64;
|
||||||
|
cha_seek_blocks(ctx, (offset - (int64_t)ctx->offset) / 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Tell current byte position in stream (assuming initial counter 0).
|
||||||
|
/// Result is truncated to int64 range (positive or negative)
|
||||||
|
/// @param ctx ChaCha context
|
||||||
|
int64_t cha_tell(cha_ctx* ctx) {
|
||||||
|
int64_t counter = *(int64_t*)(ctx->state + 12);
|
||||||
|
return counter * CHA_BLOCK_SIZE + ctx->offset - ctx->end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Incremental generation, keeps state between calls
|
/// @brief Incremental generation, keeps state between calls
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "chacha20.h"
|
#include "charandom.h"
|
||||||
|
|
||||||
static volatile bool quit = false;
|
static volatile bool quit = false;
|
||||||
|
|
||||||
|
|||||||
+17
-6
@@ -1,8 +1,7 @@
|
|||||||
#cython: language_level=3
|
#cython: language_level=3
|
||||||
|
|
||||||
from libc.stdint cimport uint32_t, uint8_t, uint64_t
|
from libc.stdint cimport int64_t, uint32_t, uint8_t, uint64_t
|
||||||
from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer
|
from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
cimport numpy as np
|
cimport numpy as np
|
||||||
cimport cython
|
cimport cython
|
||||||
@@ -12,7 +11,7 @@ from numpy.random cimport BitGenerator
|
|||||||
|
|
||||||
np.import_array()
|
np.import_array()
|
||||||
|
|
||||||
cdef extern from "npbitgen.h":
|
cdef extern from "chanumpy.h":
|
||||||
struct cha_ctx:
|
struct cha_ctx:
|
||||||
uint32_t state[16]
|
uint32_t state[16]
|
||||||
uint8_t unconsumed[512]
|
uint8_t unconsumed[512]
|
||||||
@@ -20,15 +19,18 @@ cdef extern from "npbitgen.h":
|
|||||||
unsigned rounds;
|
unsigned rounds;
|
||||||
|
|
||||||
void cha_init(cha_ctx* ctx, const uint8_t* key, const uint8_t* iv, unsigned rounds) nogil
|
void cha_init(cha_ctx* ctx, const uint8_t* key, const uint8_t* iv, unsigned rounds) nogil
|
||||||
|
void cha_seek(cha_ctx* ctx, int64_t offset)
|
||||||
|
int64_t cha_tell(cha_ctx* ctx)
|
||||||
|
|
||||||
uint64_t cha_uint64(void *state) nogil
|
uint64_t cha_uint64(void *state) nogil
|
||||||
uint32_t cha_uint32(void *state) nogil
|
uint32_t cha_uint32(void *state) nogil
|
||||||
double cha_double(void *state) nogil
|
double cha_double(void *state) nogil
|
||||||
|
|
||||||
cdef class Cha(BitGenerator):
|
|
||||||
|
|
||||||
|
cdef class Cha(BitGenerator):
|
||||||
cdef cha_ctx rng_state
|
cdef cha_ctx rng_state
|
||||||
|
|
||||||
def __init__(self, seed=None, rounds=8):
|
def __init__(self, seed=None, *, rounds=20):
|
||||||
BitGenerator.__init__(self, seed)
|
BitGenerator.__init__(self, seed)
|
||||||
self._bitgen.state = <void *>&self.rng_state
|
self._bitgen.state = <void *>&self.rng_state
|
||||||
self._bitgen.next_uint64 = &cha_uint64
|
self._bitgen.next_uint64 = &cha_uint64
|
||||||
@@ -37,4 +39,13 @@ cdef class Cha(BitGenerator):
|
|||||||
self._bitgen.next_raw = &cha_uint64
|
self._bitgen.next_raw = &cha_uint64
|
||||||
# Generated state is ChaCha20 key
|
# Generated state is ChaCha20 key
|
||||||
key = self._seed_seq.generate_state(4, np.uint64)
|
key = self._seed_seq.generate_state(4, np.uint64)
|
||||||
cha_init(&self.rng_state, <uint8_t *>np.PyArray_DATA(key), b"NumpRand", rounds)
|
cha_init(&self.rng_state, <uint8_t *>np.PyArray_DATA(key), bytes(16) + b"NumpRand", rounds)
|
||||||
|
|
||||||
|
def advance(self, delta):
|
||||||
|
cha_seek(&self.rng_state, delta << 3)
|
||||||
|
|
||||||
|
def tell(self):
|
||||||
|
return cha_tell(&self.rng_state) >> 3;
|
||||||
|
|
||||||
|
def state(self):
|
||||||
|
return self.rng_state.state[12], self.rng_state.state[13]
|
||||||
|
|||||||
Reference in New Issue
Block a user