+#define OBJECT_HASH_SIZE 31
+struct objectBucket {
+ struct objectBucket *next;
+ FcChar32 hash;
+ int id;
+};
+static struct objectBucket *FcObjectBuckets[OBJECT_HASH_SIZE];
+
+/* Design constraint: biggest_known_ntypes must never change
+ * after any call to FcNameRegisterObjectTypes. */
+static const FcObjectType *biggest_known_types = _FcBaseObjectTypes;
+static int biggest_known_ntypes = NUM_OBJECT_TYPES;
+
+
+static FcObject
+FcObjectToPtrLookup (const char * object)
+{
+ FcObject i = 0, n;
+ const FcObjectTypeList *l;
+ FcObjectType *t = _FcUserObjectNames;
+ FcBool replace;
+
+ for (l = _FcObjectTypes; l; l = l->next)
+ {
+ for (i = 0; i < l->ntypes; i++)
+ {
+ t = (FcObjectType *)&l->types[i];
+ if (!strcmp (object, t->object))
+ {
+ if (l->types == _FcUserObjectNames)
+ return -i;
+ else
+ return l->basic_offset + i;
+ }
+ }
+ }
+
+ /* We didn't match. Look for the application's FcObjectTypeList
+ * and replace it in-place. */
+ for (l = _FcObjectTypes; l; l = l->next)
+ {
+ if (l->types == _FcUserObjectNames)
+ break;
+ }
+
+ replace = l && l->types == _FcUserObjectNames;
+ if (!_FcUserObjectNames ||
+ (replace && user_obj_alloc <= l->ntypes))
+ {
+ int nt = user_obj_alloc + 4;
+ FcObjectType * tt = realloc (_FcUserObjectNames,
+ nt * sizeof (FcObjectType));
+ if (!tt)
+ return 0;
+ _FcUserObjectNames = tt;
+ user_obj_alloc = nt;
+ }
+
+ if (replace)
+ {
+ n = l->ntypes;
+ FcNameUnregisterObjectTypesFree (l->types, l->ntypes, FcFalse);
+ }
+ else
+ n = 0;
+
+ FcNameRegisterObjectTypes (_FcUserObjectNames, n+1);
+
+ if (!_FcUserObjectNames)
+ return 0;
+
+ _FcUserObjectNames[n].object = object;
+ _FcUserObjectNames[n].type = FcTypeVoid;
+
+ return -n;
+}
+
+FcBool
+FcObjectValidType (FcObject object, FcType type)
+{
+ if (object < NUM_OBJECT_TYPES && _FcBaseObjectTypes[object].type != type)
+ return FcFalse;
+ return FcTrue;
+}
+
+FcObject
+FcObjectFromName (const char * name)
+{
+ FcChar32 hash = FcStringHash ((const FcChar8 *) name);
+ struct objectBucket **p;
+ struct objectBucket *b;
+ int size;
+
+ for (p = &FcObjectBuckets[hash % OBJECT_HASH_SIZE]; (b = *p); p = &(b->next)
+)
+ if (b->hash == hash && !strcmp (name, (char *) (b + 1)))
+ return b->id;
+ size = sizeof (struct objectBucket) + strlen (name) + 1;
+ /* workaround glibc bug which reads strlen in groups of 4 */
+ b = malloc (size + sizeof (int));
+ FcMemAlloc (FC_MEM_STATICSTR, size + sizeof(int));
+ if (!b)
+ return 0;
+ b->next = 0;
+ b->hash = hash;
+ b->id = FcObjectToPtrLookup (name);
+ strcpy ((char *) (b + 1), name);
+ *p = b;
+ return b->id;
+}
+
+void
+FcObjectStaticNameFini (void)
+{
+ int i, size;
+ struct objectBucket *b, *next;
+ char *name;
+
+ for (i = 0; i < OBJECT_HASH_SIZE; i++)
+ {
+ for (b = FcObjectBuckets[i]; b; b = next)
+ {
+ next = b->next;
+ name = (char *) (b + 1);
+ size = sizeof (struct objectBucket) + strlen (name) + 1;
+ FcMemFree (FC_MEM_STATICSTR, size);
+ free (b);
+ }
+ FcObjectBuckets[i] = 0;
+ }
+}
+
+const char *
+FcObjectName (FcObject object)
+{
+ const FcObjectTypeList *l;
+ int i, j;
+
+ if (object > 0)
+ {
+ if (object < biggest_known_ntypes)
+ return biggest_known_types[object].object;
+
+ j = 0;
+ for (l = _FcObjectTypes; l; l = l->next)
+ for (i = 0; i < l->ntypes; i++, j++)
+ if (j == object)
+ return l->types[i].object;
+ }
+
+ return _FcUserObjectNames[-object].object;
+}
+