Skip to content

Commit 48809fc

Browse files
committed
Add MachineIDLCG
1 parent 61eded0 commit 48809fc

File tree

3 files changed

+109
-7
lines changed

3 files changed

+109
-7
lines changed

sonyflake_turbo.c

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <Python.h>
22
#include <pythread.h>
3+
#include <stdatomic.h>
34
#include <stdbool.h>
45
#include <stdint.h>
56
#include <time.h>
@@ -294,9 +295,81 @@ static struct PyModuleDef sonyflake_module = {
294295
.m_size = -1,
295296
};
296297

298+
inline uint16_t machine_id_lcg(uint32_t x) {
299+
return (32309 * x + 13799) % 65536;
300+
}
301+
302+
inline uint16_t machine_id_lcg_atomic(atomic_uint *x) {
303+
uint32_t old, new;
304+
do {
305+
old = atomic_load_explicit(x, memory_order_relaxed);
306+
new = machine_id_lcg(old);
307+
} while (!atomic_compare_exchange_weak_explicit(x, &old, new, memory_order_relaxed, memory_order_relaxed));
308+
return new;
309+
}
310+
311+
struct machine_id_lcg_state {
312+
PyObject_HEAD
313+
atomic_uint machine_id;
314+
};
315+
316+
static PyObject *machine_id_lcg_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
317+
unsigned int seed = 0;
318+
319+
if (!PyArg_ParseTuple(args, "I", &seed)) {
320+
return NULL;
321+
}
322+
323+
allocfunc tp_alloc = PyType_GetSlot(type, Py_tp_alloc);
324+
325+
assert(tp_alloc != NULL);
326+
327+
struct machine_id_lcg_state *self = (void *) tp_alloc(type, 0);
328+
329+
if (!self) {
330+
return NULL;
331+
}
332+
333+
atomic_init(&self->machine_id, machine_id_lcg((uint32_t) seed));
334+
335+
return (PyObject *) self;
336+
}
337+
338+
static void machine_id_lcg_dealloc(PyObject *self) {
339+
PyTypeObject *tp = Py_TYPE(self);
340+
freefunc tp_free = PyType_GetSlot(tp, Py_tp_free);
341+
342+
assert(tp_free != NULL);
343+
344+
tp_free(self);
345+
Py_DECREF(tp);
346+
}
347+
348+
static PyObject *machine_id_lcg_next(struct machine_id_lcg_state *self) {
349+
return PyLong_FromLong(machine_id_lcg_atomic(&self->machine_id));
350+
}
351+
352+
static PyType_Slot machine_id_lcg_slots[] = {
353+
{Py_tp_alloc, PyType_GenericAlloc},
354+
{Py_tp_dealloc, machine_id_lcg_dealloc},
355+
{Py_tp_iter, PyObject_SelfIter},
356+
{Py_tp_iternext, machine_id_lcg_next},
357+
{Py_tp_new, machine_id_lcg_new},
358+
{Py_tp_doc, "LCG with params a=32309, c=13799, m=65536"},
359+
{0, 0},
360+
};
361+
362+
static PyType_Spec machine_id_lcg_spec = {
363+
.name = "sonyflake_turbo.MachineIDLCG",
364+
.basicsize = sizeof(struct machine_id_lcg_state),
365+
.flags = Py_TPFLAGS_DEFAULT,
366+
.slots = machine_id_lcg_slots,
367+
};
368+
297369
PyMODINIT_FUNC
298370
PyInit_sonyflake_turbo(void)
299371
{
372+
PyObject *sonyflake_cls, *machine_id_lcg_cls;
300373
PyObject *module = PyModule_Create(&sonyflake_module);
301374

302375
if (!module) {
@@ -307,17 +380,24 @@ PyInit_sonyflake_turbo(void)
307380
PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED);
308381
#endif
309382

310-
PyObject *sonyflake_cls = PyType_FromSpec(&sonyflake_type_spec);
383+
sonyflake_cls = PyType_FromSpec(&sonyflake_type_spec);
311384

312385
if (!sonyflake_cls) {
313-
Py_DECREF(module);
314-
return NULL;
386+
goto err;
315387
}
316388

317389
if (PyModule_AddObject(module, "SonyFlake", sonyflake_cls) < 0) {
318-
Py_DECREF(sonyflake_cls);
319-
Py_DECREF(module);
320-
return NULL;
390+
goto err_sf;
391+
}
392+
393+
machine_id_lcg_cls = PyType_FromSpec(&machine_id_lcg_spec);
394+
395+
if (!machine_id_lcg_cls) {
396+
goto err_lcg;
397+
}
398+
399+
if (PyModule_AddObject(module, "MachineIDLCG", machine_id_lcg_cls) < 0) {
400+
goto err_lcg;
321401
}
322402

323403
PyModule_AddIntMacro(module, SONYFLAKE_EPOCH);
@@ -329,4 +409,12 @@ PyInit_sonyflake_turbo(void)
329409
PyModule_AddIntMacro(module, SONYFLAKE_TIME_OFFSET);
330410

331411
return module;
412+
413+
err_lcg:
414+
Py_DECREF(machine_id_lcg_cls);
415+
err_sf:
416+
Py_DECREF(sonyflake_cls);
417+
err:
418+
Py_DECREF(module);
419+
return NULL;
332420
}

sonyflake_turbo.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ class SonyFlake:
1717
def __init__(self, *machine_id: int, start_time: Optional[int] = None): ...
1818
def __iter__(self) -> Self: ...
1919
def __next__(self) -> int: ...
20+
21+
class MachineIDLCG:
22+
def __init__(self, x: int, /) -> None: ...
23+
def __iter__(self) -> Self: ...
24+
def __next__(self) -> int: ...

tests/test_sonyflake_turbo.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from pytest import mark, raises
66

7-
from sonyflake_turbo import SonyFlake
7+
from sonyflake_turbo import MachineIDLCG, SonyFlake
88

99

1010
def test_no_machine_ids() -> None:
@@ -83,3 +83,12 @@ def _thread(n: int, sf: SonyFlake) -> None:
8383
assert len(ids) == 250000
8484
assert len(set(ids)) == len(ids)
8585
assert sorted(ids) == ids
86+
87+
88+
def test_machine_id_lcg() -> None:
89+
lcg = MachineIDLCG(123)
90+
ids_seq = list(range(65536))
91+
ids_rng = [next(lcg) for _ in range(65536)]
92+
93+
assert not (set(ids_seq) - set(ids_rng))
94+
assert ids_seq != ids_rng

0 commit comments

Comments
 (0)