/*
 * Copyright © 2000 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "fcint.h"
#include "fcftint.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

static FcBool
FcHashOwnsName(const FcChar8 *name);

FcPattern *
FcPatternCreate (void)
{
    FcPattern	*p;

    p = (FcPattern *) malloc (sizeof (FcPattern));
    if (!p)
	return 0;
    FcMemAlloc (FC_MEM_PATTERN, sizeof (FcPattern));
    p->num = 0;
    p->size = 0;
    p->elts_offset = FcPtrToOffset (p, NULL);
    p->ref = 1;
    return p;
}

void
FcValueDestroy (FcValue v)
{
    switch (v.type) {
    case FcTypeString:
        if (!FcHashOwnsName(v.u.s))
            FcStrFree ((FcChar8 *) v.u.s);
	break;
    case FcTypeMatrix:
	FcMatrixFree ((FcMatrix *) v.u.m);
	break;
    case FcTypeCharSet:
	FcCharSetDestroy ((FcCharSet *) v.u.c);
	break;
    case FcTypeLangSet:
	FcLangSetDestroy ((FcLangSet *) v.u.l);
	break;
    default:
	break;
    }
}

FcValue
FcValueCanonicalize (const FcValue *v)
{
    FcValue new;

    switch (v->type)
    {
    case FcTypeString:
	new.u.s = fc_value_string(v);
	new.type = FcTypeString;
	break;
    case FcTypeCharSet:
	new.u.c = fc_value_charset(v);
	new.type = FcTypeCharSet;
	break;
    case FcTypeLangSet:
	new.u.l = fc_value_langset(v);
	new.type = FcTypeLangSet;
	break;
    default:
	new = *v;
	break;
    }
    return new;
}

FcValue
FcValueSave (FcValue v)
{
    switch (v.type) {
    case FcTypeString:
	v.u.s = FcStrStaticName (v.u.s);
	if (!v.u.s)
	    v.type = FcTypeVoid;
	break;
    case FcTypeMatrix:
	v.u.m = FcMatrixCopy (v.u.m);
	if (!v.u.m)
	    v.type = FcTypeVoid;
	break;
    case FcTypeCharSet:
	v.u.c = FcCharSetCopy ((FcCharSet *) v.u.c);
	if (!v.u.c)
	    v.type = FcTypeVoid;
	break;
    case FcTypeLangSet:
	v.u.l = FcLangSetCopy (v.u.l);
	if (!v.u.l)
	    v.type = FcTypeVoid;
	break;
    default:
	break;
    }
    return v;
}

void
FcValueListDestroy (FcValueListPtr l)
{
    FcValueListPtr next;
    for (; l; l = next)
    {
	switch (l->value.type) {
	case FcTypeString:
            if (!FcHashOwnsName((FcChar8 *)l->value.u.s))
                FcStrFree ((FcChar8 *)l->value.u.s);
	    break;
	case FcTypeMatrix:
	    FcMatrixFree ((FcMatrix *)l->value.u.m);
	    break;
	case FcTypeCharSet:
	    FcCharSetDestroy 
		((FcCharSet *) (l->value.u.c));
	    break;
	case FcTypeLangSet:
	    FcLangSetDestroy 
		((FcLangSet *) (l->value.u.l));
	    break;
	default:
	    break;
	}
	next = FcValueListNext(l);
	FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
	free(l);
    }
}

FcBool
FcValueEqual (FcValue va, FcValue vb)
{
    if (va.type != vb.type)
    {
	if (va.type == FcTypeInteger)
	{
	    va.type = FcTypeDouble;
	    va.u.d = va.u.i;
	}
	if (vb.type == FcTypeInteger)
	{
	    vb.type = FcTypeDouble;
	    vb.u.d = vb.u.i;
	}
	if (va.type != vb.type)
	    return FcFalse;
    }
    switch (va.type) {
    case FcTypeVoid:
	return FcTrue;
    case FcTypeInteger:
	return va.u.i == vb.u.i;
    case FcTypeDouble:
	return va.u.d == vb.u.d;
    case FcTypeString:
	return FcStrCmpIgnoreCase (va.u.s, vb.u.s) == 0;
    case FcTypeBool:
	return va.u.b == vb.u.b;
    case FcTypeMatrix:
	return FcMatrixEqual (va.u.m, vb.u.m);
    case FcTypeCharSet:
	return FcCharSetEqual (va.u.c, vb.u.c);
    case FcTypeFTFace:
	return va.u.f == vb.u.f;
    case FcTypeLangSet:
	return FcLangSetEqual (va.u.l, vb.u.l);
    }
    return FcFalse;
}

static FcChar32
FcDoubleHash (double d)
{
    if (d < 0)
	d = -d;
    if (d > 0xffffffff)
	d = 0xffffffff;
    return (FcChar32) d;
}

FcChar32
FcStringHash (const FcChar8 *s)
{
    FcChar8	c;
    FcChar32	h = 0;
    
    if (s)
	while ((c = *s++))
	    h = ((h << 1) | (h >> 31)) ^ c;
    return h;
}

static FcChar32
FcValueHash (const FcValue *v)
{
    switch (fc_storage_type(v)) {
    case FcTypeVoid:
	return 0;
    case FcTypeInteger:
	return (FcChar32) v->u.i;
    case FcTypeDouble:
	return FcDoubleHash (v->u.d);
    case FcTypeString:
	return FcStringHash (fc_value_string(v));
    case FcTypeBool:
	return (FcChar32) v->u.b;
    case FcTypeMatrix:
	return (FcDoubleHash (v->u.m->xx) ^ 
		FcDoubleHash (v->u.m->xy) ^ 
		FcDoubleHash (v->u.m->yx) ^ 
		FcDoubleHash (v->u.m->yy));
    case FcTypeCharSet:
	return (FcChar32) fc_value_charset(v)->num;
    case FcTypeFTFace:
	return FcStringHash ((const FcChar8 *) ((FT_Face) v->u.f)->family_name) ^
	       FcStringHash ((const FcChar8 *) ((FT_Face) v->u.f)->style_name);
    case FcTypeLangSet:
	return FcLangSetHash (fc_value_langset(v));
    }
    return FcFalse;
}

static FcBool
FcValueListEqual (FcValueListPtr la, FcValueListPtr lb)
{
    if (la == lb)
	return FcTrue;

    while (la && lb)
    {
	if (!FcValueEqual (la->value, lb->value))
	    return FcFalse;
	la = FcValueListNext(la);
	lb = FcValueListNext(lb);
    }
    if (la || lb)
	return FcFalse;
    return FcTrue;
}

static FcChar32
FcValueListHash (FcValueListPtr l)
{
    FcChar32	hash = 0;
    
    for (; l; l = FcValueListNext(l))
    {
	hash = ((hash << 1) | (hash >> 31)) ^ FcValueHash (&l->value);
    }
    return hash;
}

void
FcPatternDestroy (FcPattern *p)
{
    int		    i;
    FcPatternElt    *elts;
    
    if (p->ref == FC_REF_CONSTANT)
    {
	FcCacheObjectDereference (p);
	return;
    }
	
    if (--p->ref > 0)
	return;

    elts = FcPatternElts (p);
    for (i = 0; i < p->num; i++)
	FcValueListDestroy (FcPatternEltValues(&elts[i]));

    FcMemFree (FC_MEM_PATELT, p->size * sizeof (FcPatternElt));
    free (elts);
    FcMemFree (FC_MEM_PATTERN, sizeof (FcPattern));
    free (p);
}

static int
FcPatternObjectPosition (const FcPattern *p, FcObject object)
{
    int	    low, high, mid, c;
    FcPatternElt    *elts = FcPatternElts(p);

    low = 0;
    high = p->num - 1;
    c = 1;
    mid = 0;
    while (low <= high)
    {
	mid = (low + high) >> 1;
	c = elts[mid].object - object;
	if (c == 0)
	    return mid;
	if (c < 0)
	    low = mid + 1;
	else
	    high = mid - 1;
    }
    if (c < 0)
	mid++;
    return -(mid + 1);
}

FcPatternElt *
FcPatternObjectFindElt (const FcPattern *p, FcObject object)
{
    int	    i = FcPatternObjectPosition (p, object);
    if (i < 0)
	return 0;
    return &FcPatternElts(p)[i];
}

FcPatternElt *
FcPatternObjectInsertElt (FcPattern *p, FcObject object)
{
    int		    i;
    FcPatternElt   *e;
    
    i = FcPatternObjectPosition (p, object);
    if (i < 0)
    {
	i = -i - 1;
    
	/* reallocate array */
	if (p->num + 1 >= p->size)
	{
	    int s = p->size + 16;
	    if (p->size)
	    {
		FcPatternElt *e0 = FcPatternElts(p);
		e = (FcPatternElt *) realloc (e0, s * sizeof (FcPatternElt));
		if (!e) /* maybe it was mmapped */
		{
		    e = malloc(s * sizeof (FcPatternElt));
		    if (e)
			memcpy(e, e0, p->num * sizeof (FcPatternElt));
		}
	    }
	    else
		e = (FcPatternElt *) malloc (s * sizeof (FcPatternElt));
	    if (!e)
		return FcFalse;
	    p->elts_offset = FcPtrToOffset (p, e);
	    if (p->size)
		FcMemFree (FC_MEM_PATELT, p->size * sizeof (FcPatternElt));
	    FcMemAlloc (FC_MEM_PATELT, s * sizeof (FcPatternElt));
	    while (p->size < s)
	    {
		e[p->size].object = 0;
		e[p->size].values = NULL;
		p->size++;
	    }
	}
	
	e = FcPatternElts(p);
	/* move elts up */
	memmove (e + i + 1,
		 e + i,
		 sizeof (FcPatternElt) *
		 (p->num - i));
		 
	/* bump count */
	p->num++;
	
	e[i].object = object;
	e[i].values = NULL;
    }
    
    return FcPatternElts(p) + i;
}

FcBool
FcPatternEqual (const FcPattern *pa, const FcPattern *pb)
{
    int	i;
    FcPatternElt   *pae, *pbe;

    if (pa == pb)
	return FcTrue;

    if (pa->num != pb->num)
	return FcFalse;
    pae = FcPatternElts(pa);
    pbe = FcPatternElts(pb);
    for (i = 0; i < pa->num; i++)
    {
	if (pae[i].object != pbe[i].object)
	    return FcFalse;
	if (!FcValueListEqual (FcPatternEltValues(&pae[i]),
			       FcPatternEltValues(&pbe[i])))
	    return FcFalse;
    }
    return FcTrue;
}

FcChar32
FcPatternHash (const FcPattern *p)
{
    int		i;
    FcChar32	h = 0;
    FcPatternElt    *pe = FcPatternElts(p);

    for (i = 0; i < p->num; i++)
    {
	h = (((h << 1) | (h >> 31)) ^ 
	     pe[i].object ^
	     FcValueListHash (FcPatternEltValues(&pe[i])));
    }
    return h;
}

FcBool
FcPatternEqualSubset (const FcPattern *pai, const FcPattern *pbi, const FcObjectSet *os)
{
    FcPatternElt    *ea, *eb;
    int		    i;
    
    for (i = 0; i < os->nobject; i++)
    {
	FcObject    object = FcObjectFromName (os->objects[i]);
	ea = FcPatternObjectFindElt (pai, object);
	eb = FcPatternObjectFindElt (pbi, object);
	if (ea)
	{
	    if (!eb)
		return FcFalse;
	    if (!FcValueListEqual (FcPatternEltValues(ea), FcPatternEltValues(eb)))
		return FcFalse;
	}
	else
	{
	    if (eb)
		return FcFalse;
	}
    }
    return FcTrue;
}

FcBool
FcPatternObjectAddWithBinding  (FcPattern	*p,
				FcObject	object,
				FcValue		value,
				FcValueBinding  binding,
				FcBool		append)
{
    FcPatternElt   *e;
    FcValueListPtr new, *prev;

    if (p->ref == FC_REF_CONSTANT)
	goto bail0;

    new = malloc (sizeof (FcValueList));
    if (!new)
	goto bail0;

    memset(new, 0, sizeof (FcValueList));
    FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
    value = FcValueSave (value);
    if (value.type == FcTypeVoid)
	goto bail1;

    /*
     * Make sure the stored type is valid for built-in objects
     */
    if (!FcObjectValidType (object, value.type))
    {
	if (FcDebug() & FC_DBG_OBJTYPES)
	{
	    printf ("FcPattern object %s does not accept value ",
		    FcObjectName (object));
	    FcValuePrint (value);
	}
	goto bail1;
    }

    new->value = value;
    new->binding = binding;
    new->next = NULL;
    
    e = FcPatternObjectInsertElt (p, object);
    if (!e)
	goto bail2;
    
    if (append)
    {
	for (prev = &e->values; *prev; prev = &(*prev)->next)
	    ;
	*prev = new;
    }
    else
    {
	new->next = e->values;
	e->values = new;
    }
    
    return FcTrue;

bail2:    
    FcValueDestroy (value);
bail1:
    FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
    free (new);
bail0:
    return FcFalse;
}

FcBool
FcPatternObjectAdd (FcPattern *p, FcObject object, FcValue value, FcBool append)
{
    return FcPatternObjectAddWithBinding (p, object,
					  value, FcValueBindingStrong, append);
}

FcBool
FcPatternAdd (FcPattern *p, const char *object, FcValue value, FcBool append)
{
    return FcPatternObjectAddWithBinding (p, FcObjectFromName (object),
					  value, FcValueBindingStrong, append);
}

FcBool
FcPatternAddWeak  (FcPattern *p, const char *object, FcValue value, FcBool append)
{
    return FcPatternObjectAddWithBinding (p, FcObjectFromName (object),
					  value, FcValueBindingWeak, append);
}

FcBool
FcPatternObjectDel (FcPattern *p, FcObject object)
{
    FcPatternElt   *e;

    e = FcPatternObjectFindElt (p, object);
    if (!e)
	return FcFalse;

    /* destroy value */
    FcValueListDestroy (e->values);
    
    /* shuffle existing ones down */
    memmove (e, e+1, 
	     (FcPatternElts(p) + p->num - (e + 1)) * 
	     sizeof (FcPatternElt));
    p->num--;
    e = FcPatternElts(p) + p->num;
    e->object = 0;
    e->values = NULL;
    return FcTrue;
}

FcBool
FcPatternDel (FcPattern *p, const char *object)
{
    return FcPatternObjectDel (p, FcObjectFromName (object));
}
    
FcBool
FcPatternRemove (FcPattern *p, const char *object, int id)
{
    FcPatternElt    *e;
    FcValueListPtr  *prev, l;

    e = FcPatternObjectFindElt (p, FcObjectFromName (object));
    if (!e)
	return FcFalse;
    for (prev = &e->values; (l = *prev); prev = &l->next)
    {
	if (!id)
	{
	    *prev = l->next;
	    l->next = NULL;
	    FcValueListDestroy (l);
	    if (!e->values)
		FcPatternDel (p, object);
	    return FcTrue;
	}
	id--;
    }
    return FcFalse;
}

FcBool
FcPatternObjectAddInteger (FcPattern *p, FcObject object, int i)
{
    FcValue	v;

    v.type = FcTypeInteger;
    v.u.i = i;
    return FcPatternObjectAdd (p, object, v, FcTrue);
}

FcBool
FcPatternAddInteger (FcPattern *p, const char *object, int i)
{
    return FcPatternObjectAddInteger (p, FcObjectFromName (object), i);
}

FcBool
FcPatternObjectAddDouble (FcPattern *p, FcObject object, double d)
{
    FcValue	v;

    v.type = FcTypeDouble;
    v.u.d = d;
    return FcPatternObjectAdd (p, object, v, FcTrue);
}


FcBool
FcPatternAddDouble (FcPattern *p, const char *object, double d)
{
    return FcPatternObjectAddDouble (p, FcObjectFromName (object), d);
}

FcBool
FcPatternObjectAddString (FcPattern *p, FcObject object, const FcChar8 *s)
{
    FcValue	v;

    if (!s)
    {
	v.type = FcTypeVoid;
	v.u.s = 0;
	return FcPatternObjectAdd (p, object, v, FcTrue);
    }

    v.type = FcTypeString;
    v.u.s = FcStrStaticName(s);
    return FcPatternObjectAdd (p, object, v, FcTrue);
}

FcBool
FcPatternAddString (FcPattern *p, const char *object, const FcChar8 *s)
{
    return FcPatternObjectAddString (p, FcObjectFromName (object), s);
}

FcBool
FcPatternAddMatrix (FcPattern *p, const char *object, const FcMatrix *s)
{
    FcValue	v;

    v.type = FcTypeMatrix;
    v.u.m = s;
    return FcPatternAdd (p, object, v, FcTrue);
}


FcBool
FcPatternObjectAddBool (FcPattern *p, FcObject object, FcBool b)
{
    FcValue	v;

    v.type = FcTypeBool;
    v.u.b = b;
    return FcPatternObjectAdd (p, object, v, FcTrue);
}

FcBool
FcPatternAddBool (FcPattern *p, const char *object, FcBool b)
{
    return FcPatternObjectAddBool (p, FcObjectFromName (object), b);
}

FcBool
FcPatternAddCharSet (FcPattern *p, const char *object, const FcCharSet *c)
{
    FcValue	v;

    v.type = FcTypeCharSet;
    v.u.c = (FcCharSet *)c;
    return FcPatternAdd (p, object, v, FcTrue);
}

FcBool
FcPatternAddFTFace (FcPattern *p, const char *object, const FT_Face f)
{
    FcValue	v;

    v.type = FcTypeFTFace;
    v.u.f = (void *) f;
    return FcPatternAdd (p, object, v, FcTrue);
}

FcBool
FcPatternAddLangSet (FcPattern *p, const char *object, const FcLangSet *ls)
{
    FcValue	v;

    v.type = FcTypeLangSet;
    v.u.l = (FcLangSet *)ls;
    return FcPatternAdd (p, object, v, FcTrue);
}

FcResult
FcPatternObjectGet (const FcPattern *p, FcObject object, int id, FcValue *v)
{
    FcPatternElt   *e;
    FcValueListPtr l;

    e = FcPatternObjectFindElt (p, object);
    if (!e)
	return FcResultNoMatch;
    for (l = FcPatternEltValues(e); l; l = FcValueListNext(l))
    {
	if (!id)
	{
	    *v = FcValueCanonicalize(&l->value);
	    return FcResultMatch;
	}
	id--;
    }
    return FcResultNoId;
}

FcResult
FcPatternGet (const FcPattern *p, const char *object, int id, FcValue *v)
{
    return FcPatternObjectGet (p, FcObjectFromName (object), id, v);
}

FcResult
FcPatternObjectGetInteger (const FcPattern *p, FcObject object, int id, int *i)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternObjectGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    switch (v.type) {
    case FcTypeDouble:
	*i = (int) v.u.d;
	break;
    case FcTypeInteger:
	*i = v.u.i;
	break;
    default:
        return FcResultTypeMismatch;
    }
    return FcResultMatch;
}

FcResult
FcPatternGetInteger (const FcPattern *p, const char *object, int id, int *i)
{
    return FcPatternObjectGetInteger (p, FcObjectFromName (object), id, i);
}
    
    
FcResult
FcPatternObjectGetDouble (const FcPattern *p, FcObject object, int id, double *d)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternObjectGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    switch (v.type) {
    case FcTypeDouble:
	*d = v.u.d;
	break;
    case FcTypeInteger:
	*d = (double) v.u.i;
	break;
    default:
        return FcResultTypeMismatch;
    }
    return FcResultMatch;
}

FcResult
FcPatternGetDouble (const FcPattern *p, const char *object, int id, double *d)
{
    return FcPatternObjectGetDouble (p, FcObjectFromName (object), id, d);
}

FcResult
FcPatternObjectGetString (const FcPattern *p, FcObject object, int id, FcChar8 ** s)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternObjectGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    if (v.type != FcTypeString)
        return FcResultTypeMismatch;

    *s = (FcChar8 *) v.u.s;
    return FcResultMatch;
}

FcResult
FcPatternGetString (const FcPattern *p, const char *object, int id, FcChar8 ** s)
{
    return FcPatternObjectGetString (p, FcObjectFromName (object), id, s);
}
    
FcResult
FcPatternGetMatrix(const FcPattern *p, const char *object, int id, FcMatrix **m)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    if (v.type != FcTypeMatrix)
        return FcResultTypeMismatch;
    *m = (FcMatrix *)v.u.m;
    return FcResultMatch;
}


FcResult
FcPatternGetBool(const FcPattern *p, const char *object, int id, FcBool *b)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    if (v.type != FcTypeBool)
        return FcResultTypeMismatch;
    *b = v.u.b;
    return FcResultMatch;
}

FcResult
FcPatternGetCharSet(const FcPattern *p, const char *object, int id, FcCharSet **c)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    if (v.type != FcTypeCharSet)
        return FcResultTypeMismatch;
    *c = (FcCharSet *)v.u.c;
    return FcResultMatch;
}

FcResult
FcPatternGetFTFace(const FcPattern *p, const char *object, int id, FT_Face *f)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    if (v.type != FcTypeFTFace)
	return FcResultTypeMismatch;
    *f = (FT_Face) v.u.f;
    return FcResultMatch;
}

FcResult
FcPatternGetLangSet(const FcPattern *p, const char *object, int id, FcLangSet **ls)
{
    FcValue	v;
    FcResult	r;

    r = FcPatternGet (p, object, id, &v);
    if (r != FcResultMatch)
	return r;
    if (v.type != FcTypeLangSet)
        return FcResultTypeMismatch;
    *ls = (FcLangSet *)v.u.l;
    return FcResultMatch;
}

FcPattern *
FcPatternDuplicate (const FcPattern *orig)
{
    FcPattern	    *new;
    FcPatternElt    *e;
    int		    i;
    FcValueListPtr  l;

    new = FcPatternCreate ();
    if (!new)
	goto bail0;

    e = FcPatternElts(orig);

    for (i = 0; i < orig->num; i++)
    {
	for (l = FcPatternEltValues(e + i); l; l = FcValueListNext(l))
	{
	    if (!FcPatternObjectAddWithBinding (new, e[i].object,
						FcValueCanonicalize(&l->value),
						l->binding,
						FcTrue))
		goto bail1;
	    
	}
    }

    return new;

bail1:
    FcPatternDestroy (new);
bail0:
    return 0;
}

void
FcPatternReference (FcPattern *p)
{
    if (p->ref != FC_REF_CONSTANT)
	p->ref++;
    else
	FcCacheObjectReference (p);
}

FcPattern *
FcPatternVaBuild (FcPattern *p, va_list va)
{
    FcPattern	*ret;
    
    FcPatternVapBuild (ret, p, va);
    return ret;
}

FcPattern *
FcPatternBuild (FcPattern *p, ...)
{
    va_list	va;
    
    va_start (va, p);
    FcPatternVapBuild (p, p, va);
    va_end (va);
    return p;
}

/*
 * Add all of the elements in 's' to 'p'
 */
FcBool
FcPatternAppend (FcPattern *p, FcPattern *s)
{
    int		    i;
    FcPatternElt    *e;
    FcValueListPtr  v;
    
    for (i = 0; i < s->num; i++)
    {
	e = FcPatternElts(s)+i;
	for (v = FcPatternEltValues(e); v; v = FcValueListNext(v))
	{
	    if (!FcPatternObjectAddWithBinding (p, e->object,
						FcValueCanonicalize(&v->value), 
						v->binding, FcTrue))
		return FcFalse;
	}
    }
    return FcTrue;
}

FcPattern *
FcPatternFilter (FcPattern *p, const FcObjectSet *os)
{
    int		    i;
    FcPattern	    *ret;
    FcPatternElt    *e;
    FcValueListPtr  v;

    if (!os)
	return FcPatternDuplicate (p);

    ret = FcPatternCreate ();
    if (!ret)
	return NULL;

    for (i = 0; i < os->nobject; i++)
    {
	FcObject object = FcObjectFromName (os->objects[i]);
	e = FcPatternObjectFindElt (p, object);
	if (e)
	{
	    for (v = FcPatternEltValues(e); v; v = FcValueListNext(v))
	    {
		if (!FcPatternObjectAddWithBinding (ret, e->object,
						    FcValueCanonicalize(&v->value),
						    v->binding, FcTrue))
		    goto bail0;
	    }
	}
    }
    return ret;

bail0:
    FcPatternDestroy (ret);
    return NULL;
}

#define OBJECT_HASH_SIZE    31
static struct objectBucket {
    struct objectBucket	*next;
    FcChar32		hash;
} *FcObjectBuckets[OBJECT_HASH_SIZE];

static FcBool
FcHashOwnsName (const FcChar8 *name)
{
    FcChar32		hash = FcStringHash (name);
    struct objectBucket	**p;
    struct objectBucket	*b;

    for (p = &FcObjectBuckets[hash % OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
	if (b->hash == hash && ((char *)name == (char *) (b + 1)))
            return FcTrue;
    return FcFalse;
}

const FcChar8 *
FcStrStaticName (const FcChar8 *name)
{
    FcChar32		hash = FcStringHash (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 ((char *)name, (char *) (b + 1)))
	    return (FcChar8 *) (b + 1);
    size = sizeof (struct objectBucket) + strlen ((char *)name) + 1;
    b = malloc (size + sizeof (int));
    /* workaround glibc bug which reads strlen in groups of 4 */
    FcMemAlloc (FC_MEM_STATICSTR, size + sizeof (int));
    if (!b)
        return NULL;
    b->next = 0;
    b->hash = hash;
    strcpy ((char *) (b + 1), (char *)name);
    *p = b;
    return (FcChar8 *) (b + 1);
}

static void
FcStrStaticNameFini (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 + sizeof (int));
	    free (b);
	}
	FcObjectBuckets[i] = 0;
    }
}

void
FcPatternFini (void)
{
    FcStrStaticNameFini ();
    FcObjectFini ();
}

FcBool
FcPatternSerializeAlloc (FcSerialize *serialize, const FcPattern *pat)
{
    int	i;
    FcPatternElt    *elts = FcPatternElts(pat);
    
    if (!FcSerializeAlloc (serialize, pat, sizeof (FcPattern)))
	return FcFalse;
    if (!FcSerializeAlloc (serialize, elts, pat->num * sizeof (FcPatternElt)))
	return FcFalse;
    for (i = 0; i < pat->num; i++)
	if (!FcValueListSerializeAlloc (serialize, FcPatternEltValues(elts+i)))
	    return FcFalse;
    return FcTrue;
}

FcPattern *
FcPatternSerialize (FcSerialize *serialize, const FcPattern *pat)
{
    FcPattern	    *pat_serialized;
    FcPatternElt    *elts = FcPatternElts (pat);
    FcPatternElt    *elts_serialized;
    FcValueList	    *values_serialized;
    int		    i;

    pat_serialized = FcSerializePtr (serialize, pat);
    if (!pat_serialized)
	return NULL;
    *pat_serialized = *pat;
    pat_serialized->size = pat->num;
    pat_serialized->ref = FC_REF_CONSTANT;
    
    elts_serialized = FcSerializePtr (serialize, elts);
    if (!elts_serialized)
	return NULL;
    
    pat_serialized->elts_offset = FcPtrToOffset (pat_serialized,
						 elts_serialized);

    for (i = 0; i < pat->num; i++)
    {
	values_serialized = FcValueListSerialize (serialize, FcPatternEltValues (elts+i));
	if (!values_serialized)
	    return NULL;
	elts_serialized[i].object = elts[i].object;
	elts_serialized[i].values = FcPtrToEncodedOffset (&elts_serialized[i], 
							  values_serialized,
							  FcValueList);
    }
    if (FcDebug() & FC_DBG_CACHEV) {
	printf ("Raw pattern:\n");
	FcPatternPrint (pat);
	printf ("Serialized pattern:\n");
	FcPatternPrint (pat_serialized);
	printf ("\n");
    }
    return pat_serialized;
}

FcBool
FcValueListSerializeAlloc (FcSerialize *serialize, const FcValueList *vl)
{
    while (vl)
    {
	if (!FcSerializeAlloc (serialize, vl, sizeof (FcValueList)))
	    return FcFalse;
	switch (vl->value.type) {
	case FcTypeString:
	    if (!FcStrSerializeAlloc (serialize, vl->value.u.s))
		return FcFalse;
	    break;
	case FcTypeCharSet:
	    if (!FcCharSetSerializeAlloc (serialize, vl->value.u.c))
		return FcFalse;
	    break;
	case FcTypeLangSet:
	    if (!FcLangSetSerializeAlloc (serialize, vl->value.u.l))
		return FcFalse;
	    break;
	default:
	    break;
	}
	vl = vl->next;
    }
    return FcTrue;
}

FcValueList *
FcValueListSerialize (FcSerialize *serialize, const FcValueList *vl)
{
    FcValueList	*vl_serialized;
    FcChar8	*s_serialized;
    FcCharSet	*c_serialized;
    FcLangSet	*l_serialized;
    FcValueList	*head_serialized = NULL;
    FcValueList	*prev_serialized = NULL;

    while (vl)
    {
	vl_serialized = FcSerializePtr (serialize, vl);
	if (!vl_serialized)
	    return NULL;
    
	if (prev_serialized)
	    prev_serialized->next = FcPtrToEncodedOffset (prev_serialized,
							  vl_serialized,
							  FcValueList);
	else
	    head_serialized = vl_serialized;
	
	vl_serialized->next = NULL;
	vl_serialized->value.type = vl->value.type;
	switch (vl->value.type) {
	case FcTypeInteger:
	    vl_serialized->value.u.i = vl->value.u.i;
	    break;
	case FcTypeDouble:
	    vl_serialized->value.u.d = vl->value.u.d;
	    break;
	case FcTypeString:
	    s_serialized = FcStrSerialize (serialize, vl->value.u.s);
	    if (!s_serialized)
		return NULL;
	    vl_serialized->value.u.s = FcPtrToEncodedOffset (&vl_serialized->value,
							     s_serialized,
							     FcChar8);
	    break;
	case FcTypeBool:
	    vl_serialized->value.u.b = vl->value.u.b;
	    break;
	case FcTypeMatrix:
	    /* can't happen */
	    break;
	case FcTypeCharSet:
	    c_serialized = FcCharSetSerialize (serialize, vl->value.u.c);
	    if (!c_serialized)
		return NULL;
	    vl_serialized->value.u.c = FcPtrToEncodedOffset (&vl_serialized->value,
							     c_serialized,
							     FcCharSet);
	    break;
	case FcTypeFTFace:
	    /* can't happen */
	    break;
	case FcTypeLangSet:
	    l_serialized = FcLangSetSerialize (serialize, vl->value.u.l);
	    if (!l_serialized)
		return NULL;
	    vl_serialized->value.u.l = FcPtrToEncodedOffset (&vl_serialized->value,
							     l_serialized,
							     FcLangSet);
	    break;
	default:
	    break;
	}
	prev_serialized = vl_serialized;
	vl = vl->next;
    }
    return head_serialized;
}
#define __fcpat__
#include "fcaliastail.h"
#include "fcftaliastail.h"
#undef __fcpat__
