*.c: Publish algorithm and crypto-group tables as `mapping' objects.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 13 Nov 2019 00:59:07 +0000 (00:59 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 11:44:14 +0000 (12:44 +0100)
Previously, the algorithm tables were published as plain Python
dictionaries, and the group tables were published in a complicated way
through a read-only mapping object implemented in Python.

With the richer Python 3 mapping protocol, maintaining a separate custom
mapping object in Python seems much less attractive.  Also, the group
tables were read-only while the algorithm tables were mutable, which was
a rather unfortunate inconsistency.

Fix this mess by implementing a new simple mapping object, based on
Pyke's generic mapping machinery, and populating instances of it with
the necessary data.

algorithms.c
catacomb-python.h
catacomb.c
catacomb/__init__.py
ec.c
group.c
rand.c

index f792087..1b1545c 100644 (file)
@@ -3612,12 +3612,14 @@ void algorithms_pyinit(void)
   addmethods(methods);
 }
 
-GEN(gcciphers, cipher)
-GEN(gcaeads, aead)
-GEN(gchashes, hash)
-GEN(gcmacs, mac)
 #define gcprp prpinfo
-GEN(gcprps, prp)
+#define CLASS_TABLES(_) _(cipher) _(aead) _(hash) _(mac) _(prp)
+#define TABLE_FNS(pre)                                                 \
+  static const char *pre##_namefn(const void *p)                       \
+    { const gc##pre *const *cls = p; return (*cls ? (*cls)->name : 0); } \
+  static PyObject *pre##_valfn(const void *p)                          \
+    { gc##pre *const*cls = p; return (gc##pre##_pywrap(*cls)); }
+CLASS_TABLES(TABLE_FNS)
 
 void algorithms_pyinsert(PyObject *mod)
 {
@@ -3628,7 +3630,8 @@ void algorithms_pyinsert(PyObject *mod)
   INSERT("KeySZSet", keyszset_pytype);
   INSERT("GCCipher", gccipher_pytype);
   INSERT("GCipher", gcipher_pytype);
-  INSERT("gcciphers", gcciphers());
+  INSERT("gcciphers", make_algtab(gciphertab, sizeof(gccipher *),
+                                 cipher_namefn, cipher_valfn));
   INSERT("GCAEAD", gcaead_pytype);
   INSERT("GAEKey", gaeadkey_pytype);
   INSERT("GAEAADClass", gcaeadaad_pytype);
@@ -3637,16 +3640,19 @@ void algorithms_pyinsert(PyObject *mod)
   INSERT("GAEEnc", gaeadenc_pytype);
   INSERT("GAEDecClass", gcaeaddec_pytype);
   INSERT("GAEDec", gaeaddec_pytype);
-  INSERT("gcaeads", gcaeads());
+  INSERT("gcaeads", make_algtab(gaeadtab, sizeof(gcaead *),
+                               aead_namefn, aead_valfn));
   INSERT("GCHash", gchash_pytype);
   INSERT("GHash", ghash_pytype);
-  INSERT("gchashes", d = gchashes());
-  sha_pyobj = PyDict_GetItemString(d, "sha"); Py_INCREF(sha_pyobj);
-  has160_pyobj = PyDict_GetItemString(d, "has160"); Py_INCREF(has160_pyobj);
+  d = make_algtab(ghashtab, sizeof(gchash *), hash_namefn, hash_valfn);
+  INSERT("gchashes", d);
+  sha_pyobj = PyMapping_GetItemString(d, "sha"); Py_INCREF(sha_pyobj);
+  has160_pyobj = PyMapping_GetItemString(d, "has160"); Py_INCREF(has160_pyobj);
   INSERT("GCMAC", gcmac_pytype);
   INSERT("GMAC", gmac_pytype);
   INSERT("GMACHash", gmhash_pytype);
-  INSERT("gcmacs", gcmacs());
+  INSERT("gcmacs", make_algtab(gmactab, sizeof(gcmac *),
+                              mac_namefn, mac_valfn));
   INSERT("Poly1305Class", poly1305cls_pytype);
   INSERT("poly1305", poly1305key_pytype);
   INSERT("Poly1305Hash", poly1305hash_pytype);
@@ -3656,7 +3662,8 @@ void algorithms_pyinsert(PyObject *mod)
   INSERT("Shake256", shake256_pytype);
   INSERT("GCPRP", gcprp_pytype);
   INSERT("GPRP", gprp_pytype);
-  INSERT("gcprps", gcprps());
+  INSERT("gcprps", make_algtab(gprptab, sizeof(gcprp *),
+                              prp_namefn, prp_valfn));
   setconstants(mod, consts);
 }
 
index 4c7ced4..da6a8fe 100644 (file)
@@ -136,24 +136,14 @@ MODULES(DECLARE_MODINIT)
 /* Conversions. */
 extern int convmpw(PyObject *, void *);
 
-/* Make a dictionary of generic-crypto classes. */
-#define GEN(func, base)                                                        \
-  static PyObject *func(void)                                          \
-  {                                                                    \
-    PyObject *d = PyDict_New();                                                \
-    PyObject *o;                                                       \
-    int i;                                                             \
-                                                                       \
-    for (i = 0; g##base##tab[i]; i++) {                                        \
-      o = gc##base##_pywrap(CONVERT_CAREFULLY(gc##base *, const gc##base *, \
-                                             g##base##tab[i]));        \
-      PyDict_SetItemString(d,                                          \
-                          (/*unconst*/ char *)g##base##tab[i]->name,   \
-                          o);                                          \
-      Py_DECREF(o);                                                    \
-    }                                                                  \
-    return (d);                                                                \
-  }
+/* Building tables of things. */
+extern PyObject *make_algtab(const void *tab, size_t esz,
+                            const char *(*namefn)(const void *),
+                            PyObject *(*valfn)(const void *));
+extern PyObject *make_grouptab(const void *tab, size_t esz,
+                              const char *(*namefn)(const void *),
+                              int (*ixfn)(const void *),
+                              PyObject *(*valfn)(int));
 
 /* Common handling for simultaneous exponentiation. */
 extern PyObject *mexp_common(PyObject *, PyObject *, size_t,
index 60dae45..a914bff 100644 (file)
@@ -110,6 +110,175 @@ end:
   return (0);
 }
 
+static PyTypeObject *thingtab_pytype;
+
+typedef struct thingentry {
+  sym_base _b;
+  PyObject *val;
+} thingentry;
+#define THING_VAL(x) (((thingentry *)(x))->val)
+
+typedef struct thingtab_pyobj {
+  GMAP_PYOBJ_HEAD
+  sym_table t;
+} thingtab_pyobj;
+#define THINGTAB_T(x) (&((thingtab_pyobj *)(x))->t)
+
+static void *thingtab_gmlookup(PyObject *me, PyObject *key, unsigned *f)
+{
+  const char *p;
+
+  p = PyString_AsString(key); if (!p) return (0);
+  return (sym_find(THINGTAB_T(me), p, -1, 0, f));
+}
+
+static void thingtab_gmiterinit(PyObject *me, void *i)
+  { sym_mkiter(i, THINGTAB_T(me)); }
+
+static void *thingtab_gmiternext(PyObject *me, void *i)
+  { sym_iter *it = i; void *e; SYM_NEXT(it, e); return (e); }
+
+static PyObject *thingtab_gmentrykey(PyObject *me, void *e)
+  { return (PyString_FromString(SYM_NAME(e))); }
+
+static PyObject *thingtab_gmentryvalue(PyObject *me, void *e)
+  { PyObject *rc = THING_VAL(e); RETURN_OBJ(rc); }
+
+static const gmap_ops thingtab_gmops = {
+  sizeof(sym_iter),
+  thingtab_gmlookup,
+  thingtab_gmiterinit,
+  thingtab_gmiternext,
+  thingtab_gmentrykey,
+  thingtab_gmentryvalue
+};
+
+static Py_ssize_t thing_pysize(PyObject *me)
+  { return gmap_pysize_from_sym(THINGTAB_T(me)); }
+
+static const PyMappingMethods thingtab_pymapping = {
+  thing_pysize,
+  gmap_pylookup,
+  0
+};
+
+static thingtab_pyobj *make_thingtab(void)
+{
+  thingtab_pyobj *map = PyObject_NEW(thingtab_pyobj, thingtab_pytype);
+
+  map->gmops = &thingtab_gmops;
+  sym_create(&map->t);
+  return (map);
+}
+
+PyObject *make_algtab(const void *tab, size_t esz,
+                     const char *(*namefn)(const void *),
+                     PyObject *(*valfn)(const void *))
+{
+  thingtab_pyobj *map = make_thingtab();
+  const char *p = tab;
+  const char *name;
+  thingentry *e;
+  unsigned f;
+
+  for (;;) {
+    name = namefn(p); if (!name) break;
+    e = sym_find(&map->t, name, -1, sizeof(*e), &f); assert(!f);
+    e->val = valfn(p);
+    p += esz;
+  }
+  return ((PyObject *)map);
+}
+
+PyObject *make_grouptab(const void *tab, size_t esz,
+                       const char *(*namefn)(const void *),
+                       int (*ixfn)(const void *), PyObject *(*valfn)(int))
+{
+  thingtab_pyobj *map = make_thingtab();
+  struct { const char *name; int ix; } *ixtab = 0;
+  PyObject **valtab, **vv;
+  size_t i = 0, n = 0;
+  const char *p = tab;
+  const char *name;
+  thingentry *e;
+  unsigned f;
+
+  for (;;) {
+    name = namefn(p); if (!name) break;
+    if (i >= n) {
+      if (!n) n = 16;
+      else n *= 2;
+      ixtab = xrealloc(ixtab, n*sizeof(*ixtab), i*sizeof(*ixtab));
+    }
+    ixtab[i].name = name; ixtab[i].ix = ixfn(p); assert(ixtab[i].ix >= 0);
+    p += esz; i++;
+  }
+  n = i;
+
+  valtab = xmalloc(n*sizeof(*valtab));
+  for (i = 0; i < n; i++) valtab[i] = 0;
+
+  for (i = 0; i < n; i++) {
+    e = sym_find(&map->t, ixtab[i].name, -1, sizeof(*e), &f); assert(!f);
+    vv = &valtab[ixtab[i].ix];
+    if (*vv) Py_INCREF(*vv);
+    else *vv = valfn(ixtab[i].ix);
+    e->val = *vv;
+  }
+
+  xfree(ixtab); xfree(valtab);
+  return ((PyObject *)map);
+}
+
+static const PyTypeObject thingtab_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "_MiscTable",                                /* @tp_name@ */
+  sizeof(thingtab_pyobj),              /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  0,                                   /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  PYSEQUENCE(gmap),                    /* @tp_as_sequence@ */
+  PYMAPPING(thingtab),                 /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "Class for tables of algorithms and abstract-group data.\n"
+  "  Not instantiable by users.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  gmap_pyiter,                         /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  PYMETHODS(gmapro),                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
 static PyObject *smallprimes(void)
 {
   PyObject *v = PyList_New(NPRIME);
@@ -159,9 +328,11 @@ EXPORT void init_base(void)
   modname = PyString_FromString("catacomb");
   addmethods(methods);
   INIT_MODULES;
+  INITTYPE(thingtab, root);
   init_random();
   mod = Py_InitModule("catacomb._base", donemethods());
   INSERT_MODULES;
+  INSERT("_MiscTable", thingtab_pytype);
   INSERT("smallprimes", smallprimes());
 }
 
index 0996c5c..c810c42 100644 (file)
@@ -1002,14 +1002,9 @@ class Ed448Priv (_EdDSAPriv, Ed448Pub):
     return ed448_sign(me.priv, msg, pub = me.pub, **kw)
 
 ###--------------------------------------------------------------------------
-### Built-in named curves and prime groups.
-
-class _groupmap (object):
-  def __init__(me, map, nth):
-    me.map = map
-    me.nth = nth
-    me._n = max(map.values()) + 1
-    me.i = me._n*[None]
+### Built-in algorithm and group tables.
+
+class _tmp:
   def __repr__(me):
     return '{%s}' % ', '.join(['%r: %r' % kv for kv in me.iteritems()])
   def _repr_pretty_(me, pp, cyclep):
@@ -1017,36 +1012,7 @@ class _groupmap (object):
     if cyclep: pp.text('...')
     else: _pp_dict(pp, me.iteritems())
     pp.end_group(ind, ' }')
-  def __len__(me):
-    return me._n
-  def __contains__(me, k):
-    return k in me.map
-  def __getitem__(me, k):
-    i = me.map[k]
-    if me.i[i] is None:
-      me.i[i] = me.nth(i)
-    return me.i[i]
-  def __setitem__(me, k, v):
-    raise TypeError, "immutable object"
-  def __iter__(me):
-    return iter(me.map)
-  def iterkeys(me):
-    return iter(me.map)
-  def itervalues(me):
-    for k in me:
-      yield me[k]
-  def iteritems(me):
-    for k in me:
-      yield k, me[k]
-  def keys(me):
-    return [k for k in me]
-  def values(me):
-    return [me[k] for k in me]
-  def items(me):
-    return [(k, me[k]) for k in me]
-eccurves = _groupmap(_base._eccurves, ECInfo._curven)
-primegroups = _groupmap(_base._pgroups, DHInfo._groupn)
-bingroups = _groupmap(_base._bingroups, BinDHInfo._groupn)
+_augment(_base._MiscTable, _tmp)
 
 ###--------------------------------------------------------------------------
 ### Prime number generation.
diff --git a/ec.c b/ec.c
index ec4a98d..ff0cc03 100644 (file)
--- a/ec.c
+++ b/ec.c
@@ -1339,8 +1339,6 @@ static const PyTypeObject ecbinprojcurve_pytype_skel = {
 
 /*----- Curve info --------------------------------------------------------*/
 
-static int ncurves = -1;
-
 void ecinfo_copy(ec_info *eic, const ec_info *ei)
 {
   eic->c = eccurve_copy(ei->c);
@@ -1414,20 +1412,6 @@ end:
   return (rc);
 }
 
-static PyObject *eimeth__curven(PyObject *me, PyObject *arg)
-{
-  int i;
-  ec_info ei;
-  PyObject *rc = 0;
-
-  if (!PyArg_ParseTuple(arg, "i:_curven", &i)) goto end;
-  if (i < 0 || i >= ncurves) VALERR("curve index out of range");
-  ec_infofromdata(&ei, ectab[i].data);
-  rc = ecinfo_pywrap(&ei);
-end:
-  return (rc);
-}
-
 static PyObject *ecinfo_pyrichcompare(PyObject *x, PyObject *y, int op)
 {
   int b = ec_sameinfop(ECINFO_EI(x), ECINFO_EI(y));
@@ -1488,7 +1472,6 @@ static const PyMethodDef ecinfo_pymethods[] = {
 #define METHNAME(name) eimeth_##name
   KWMETH(check,                "I.check([rng = rand]) -> None")
   SMTH (parse,         "parse(STR) -> (I, REST)")
-  SMTH (_curven,       "_curven(N) -> I")
 #undef METHNAME
   { 0 }
 };
@@ -1561,29 +1544,25 @@ void ec_pyinit(void)
   INITTYPE(ecinfo, root);
 }
 
-static PyObject *namedcurves(void)
+static const char *ec_namefn(const void *p)
+  { const ecentry *ec = p; return (ec->name); }
+
+static int ec_ixfn(const void *p)
 {
-  int i, j;
-  const char *p;
-  PyObject *d, *c;
-
-  d = PyDict_New();
-  for (i = 0; ectab[i].name; i++) {
-    p = ectab[i].name;
-    for (j = 0; j < i; j++) {
-      if (ectab[i].data == ectab[j].data) {
-       c = PyDict_GetItemString(d, (/*unconst*/ char *)ectab[j].name);
-       Py_INCREF(c);
-       goto found;
-      }
-    }
-    c = PyInt_FromLong(i);
-  found:
-    PyDict_SetItemString(d, (/*unconst*/ char *)p, c);
-    Py_DECREF(c);
-  }
-  ncurves = i;
-  return (d);
+  const ecentry *ec = p;
+  int i;
+
+  for (i = 0; ectab[i].name; i++)
+    if (ectab[i].data == ec->data) return (i);
+  return (-1);
+}
+
+static PyObject *ec_valfn(int i)
+{
+  ec_info ei;
+
+  ec_infofromdata(&ei, ectab[i].data);
+  return (ecinfo_pywrap(&ei));
 }
 
 void ec_pyinsert(PyObject *mod)
@@ -1596,7 +1575,8 @@ void ec_pyinsert(PyObject *mod)
   INSERT("ECBinCurve", ecbincurve_pytype);
   INSERT("ECBinProjCurve", ecbinprojcurve_pytype);
   INSERT("ECInfo", ecinfo_pytype);
-  INSERT("_eccurves", namedcurves());
+  INSERT("eccurves", make_grouptab(ectab, sizeof(*ectab),
+                                  ec_namefn, ec_ixfn, ec_valfn));
   setconstants(mod, consts);
 }
 
diff --git a/group.c b/group.c
index d02f356..3ff0ad3 100644 (file)
--- a/group.c
+++ b/group.c
@@ -220,54 +220,6 @@ end:
   return (rc);
 }
 
-static int npgroups = -1, nbingroups = -1;
-
-static PyObject *namedgroups(const pentry *pp, int *ne)
-{
-  int i, j;
-  const char *p;
-  PyObject *d, *c;
-
-  d = PyDict_New();
-  for (i = 0; pp[i].name; i++) {
-    p = pp[i].name;
-    for (j = 0; j < i; j++) {
-      if (pp[i].data == pp[j].data) {
-       c = PyDict_GetItemString(d, (/*unconst*/ char *)pp[j].name);
-       Py_INCREF(c);
-       goto found;
-      }
-    }
-    c = PyInt_FromLong(i);
-  found:
-    PyDict_SetItemString(d, (/*unconst*/ char *)p, c);
-    Py_DECREF(c);
-  }
-  *ne = i;
-  return (d);
-}
-
-static PyObject *meth__groupn(PyObject *me, PyObject *arg,
-                             PyTypeObject *ty, const pentry *pp, int ne)
-{
-  int i;
-  gprime_param gp;
-  PyObject *rc = 0;
-
-  if (!PyArg_ParseTuple(arg, "i:_groupn", &i)) goto end;
-  if (i < 0 || i >= ne) VALERR("group index out of range");
-  dh_infofromdata(&gp, pp[i].data);
-  rc = fginfo_pywrap(&gp, ty);
-end:
-  return (rc);
-}
-
-static PyObject *dimeth__groupn(PyObject *me, PyObject *arg)
-  { return (meth__groupn(me, arg, dhinfo_pytype, ptab, npgroups)); }
-
-static PyObject *bimeth__groupn(PyObject *me, PyObject *arg)
-  { return (meth__groupn(me, arg, bindhinfo_pytype, bintab, nbingroups)); }
-
 static PyObject *meth__parse(PyObject *me, PyObject *arg, PyTypeObject *ty,
                             int (*parse)(qd_parse *, gprime_param *))
 {
@@ -308,7 +260,6 @@ static const PyGetSetDef dhinfo_pygetset[] = {
 static const PyMethodDef dhinfo_pymethods[] = {
 #define METHNAME(name) dimeth_##name
   SMTH (parse,         "parse(STR) -> D, REST")
-  SMTH (_groupn,       0)
   KWSMTH(generate,
        "generate(PBITS, [qbits = 0], [event = pgen_nullev],\n"
        "        [rng = rand], [nsteps = 0]) -> D")
@@ -340,7 +291,6 @@ static const PyGetSetDef bindhinfo_pygetset[] = {
 static const PyMethodDef bindhinfo_pymethods[] = {
 #define METHNAME(name) bimeth_##name
   SMTH (parse,         "parse(STR) -> D, REST")
-  SMTH (_groupn,       0)
 #undef METHNAME
   { 0 }
 };
@@ -1413,6 +1363,32 @@ void group_pyinit(void)
   INITTYPE(ecgroup, group);
 }
 
+static const char *grp_namefn(const void *p)
+  { const pentry *pt = p; return (pt->name); }
+
+static int grp_ixfn(const pentry *tab, const pentry *pt)
+{
+  int i;
+
+  for (i = 0; tab[i].name; i++)
+    if (tab[i].data == pt->data) return (i);
+  return (-1);
+}
+static int pgrp_ixfn(const void *p) { return (grp_ixfn(ptab, p)); }
+static int bgrp_ixfn(const void *p) { return (grp_ixfn(bintab, p)); }
+
+static PyObject *grp_valfn(const pentry *tab, PyTypeObject *ty, int i)
+{
+  gprime_param gp;
+
+  dh_infofromdata(&gp, tab[i].data);
+  return (fginfo_pywrap(&gp, ty));
+}
+static PyObject *pgrp_valfn(int i)
+  { return (grp_valfn(ptab, dhinfo_pytype, i)); }
+static PyObject *bgrp_valfn(int i)
+  { return (grp_valfn(bintab, bindhinfo_pytype, i)); }
+
 void group_pyinsert(PyObject *mod)
 {
   INSERT("FGInfo", fginfo_pytype);
@@ -1423,8 +1399,10 @@ void group_pyinsert(PyObject *mod)
   INSERT("PrimeGroup", primegroup_pytype);
   INSERT("BinGroup", bingroup_pytype);
   INSERT("ECGroup", ecgroup_pytype);
-  INSERT("_pgroups", namedgroups(ptab, &npgroups));
-  INSERT("_bingroups", namedgroups(bintab, &nbingroups));
+  INSERT("primegroups", make_grouptab(ptab, sizeof(*ptab),
+                                     grp_namefn, pgrp_ixfn, pgrp_valfn));
+  INSERT("bingroups", make_grouptab(bintab, sizeof(*bintab),
+                                   grp_namefn, bgrp_ixfn, bgrp_valfn));
 }
 
 /*----- That's all, folks -------------------------------------------------*/
diff --git a/rand.c b/rand.c
index 2c99f26..137be5c 100644 (file)
--- a/rand.c
+++ b/rand.c
@@ -65,7 +65,8 @@ PyObject *grand_pywrap(grand *r, unsigned f)
   else if (STRCMP(r->ops->name, ==, "sslprf")) ty = sslprf_pytype;
   else if (STRCMP(r->ops->name, ==, "tlsdx")) ty = tlsdx_pytype;
   else if (STRCMP(r->ops->name, ==, "tlsprf")) ty = tlsprf_pytype;
-  else if ((ob = PyDict_GetItemString(gccrands_dict, r->ops->name)) != 0)
+  else if ((ob = PyMapping_GetItemString
+                  (gccrands_dict, (/*unconst*/ char *)r->ops->name)) != 0)
     ty = (PyTypeObject *)ob;
   return (grand_dopywrap(ty, r, f));
 }
@@ -1513,8 +1514,10 @@ void rand_pyinit(void)
   rand_seed(RAND_GLOBAL, 160);
 }
 
-#define gccrand gccrand_info
-GEN(gccrands, crand)
+static const char *crand_namefn(const void *p)
+  { const gccrand_info *const *cls = p; return (*cls ? (*cls)->name : 0); }
+static PyObject *crand_valfn(const void *p)
+  { const gccrand_info *const *cls = p; return (gccrand_pywrap(*cls)); }
 
 void rand_pyinsert(PyObject *mod)
 {
@@ -1532,8 +1535,9 @@ void rand_pyinsert(PyObject *mod)
   INSERT("GCRand", gcrand_pytype);
   INSERT("GCLatinRand", gclatinrand_pytype);
   rand_pyobj = grand_pywrap(&rand_global, 0); Py_INCREF(rand_pyobj);
-  gccrands_dict = gccrands(); Py_INCREF(gccrands_dict);
-  INSERT("gccrands", gccrands_dict);
+  gccrands_dict = make_algtab(gcrandtab, sizeof(gccrand_info *),
+                             crand_namefn, crand_valfn);
+  INSERT("gccrands", gccrands_dict); Py_INCREF(gccrands_dict);
   INSERT("rand", rand_pyobj);
   setconstants(mod, consts);
 }