Managed attribute spec memory properly.

This commit is contained in:
Pietro Gagliardi 2017-05-28 00:41:40 -04:00
parent 475ae4a4bf
commit 02020e676a
2 changed files with 54 additions and 41 deletions

View File

@ -24,6 +24,47 @@ struct attrlist {
struct attr *last; struct attr *last;
}; };
// We need to make local copies of any pointers in uiAttributeSpec.
// If we don't do this, we'll wind up leaking stuff, or worse, prematurely freeing stuff.
// TODO ensure this is docmented
static void attrSetSpec(struct attr *a, uiAttributeSpec *spec)
{
const char *family;
char *familyCopy;
a->spec = *spec;
switch (a->spec.Type) {
case uiAttributeFamily:
// TODO UTF-8 validate this?
family = a->spec.Family;
familyCopy = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttributeSpec.Family copy)");
strcpy(familyCopy, family);
a->spec.Family = familyCopy;
break;
case uiAttributeFeatures:
a->spec.Features = uiOpenTypeFeaturesClone(a->spec.Features);
break;
}
}
static void attrCopySpec(struct attr *dest, struct attr *src)
{
attrSetSpec(dest, &(src->spec));
}
// Likewise, this is needed to clean up a spec with a pointer when finished.
static void attrClearSpec(struct attr *a)
{
switch (a->spec.Type) {
case uiAttributeFamily:
uiFree((char *) (a->spec.Family));
break;
case uiAttributeFeatures:
uiOpenTypeFeaturesFree((uiOpenTypeFeatures *) (a->spec.Features));
break;
}
}
// if before is NULL, add to the end of the list // if before is NULL, add to the end of the list
static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before)
{ {
@ -128,6 +169,7 @@ static struct attr *attrDelete(struct attrlist *alist, struct attr *a)
struct attr *next; struct attr *next;
next = attrUnlink(alist, a); next = attrUnlink(alist, a);
attrClearSpec(a);
uiFree(a); uiFree(a);
return next; return next;
} }
@ -177,7 +219,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t
// we'll need to split the attribute into two // we'll need to split the attribute into two
b = uiNew(struct attr); b = uiNew(struct attr);
b->spec = a->spec; attrCopySpec(b, a);
b->start = end; b->start = end;
b->end = a->end; b->end = a->end;
*tail = b; *tail = b;
@ -221,7 +263,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a
return NULL; return NULL;
b = uiNew(struct attr); b = uiNew(struct attr);
b->spec = a->spec; attrCopySpec(b, a);
b->start = at; b->start = at;
b->end = a->end; b->end = a->end;
@ -284,39 +326,6 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size
return a->next; return a->next;
} }
static int boolsEqual(struct attr *attr, uiAttributeSpec *spec)
{
if (attr->spec.Value == 0 && spec->Value == 0)
return 1;
return attr->spec.Value != 0 && spec->Value != 0;
}
// BCP 47 is ASCII-only
static int asciiStringsEqualCaseFold(const char *a, const char *b)
{
char c, d;
for (;;) {
if (*a == *b) {
if (*a == '\0')
return 1;
a++;
b++;
continue;
}
c = *a;
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
d = *b;
if (d >= 'A' && d <= 'Z')
d += 'a' - 'A';
if (c != d)
return 0;
a++;
b++;
}
}
static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) static int specsIdentical(struct attr *attr, uiAttributeSpec *spec)
{ {
if (attr->spec.Type != spec->Type) if (attr->spec.Type != spec->Type)
@ -325,7 +334,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec)
case uiAttributeFamily: case uiAttributeFamily:
// TODO should we start copying these strings? // TODO should we start copying these strings?
// TODO should this be case-insensitive? // TODO should this be case-insensitive?
return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; return strcmp(attr->spec.Family, spec->Family) == 0;
case uiAttributeSize: case uiAttributeSize:
// TODO use a closest match? // TODO use a closest match?
return attr->spec.Double == spec->Double; return attr->spec.Double == spec->Double;
@ -342,9 +351,11 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec)
attr->spec.G == spec->G && attr->spec.G == spec->G &&
attr->spec.B == spec->B && attr->spec.B == spec->B &&
attr->spec.A == spec->A; attr->spec.A == spec->A;
case uiAttributeFeatures:
// TODO rename it to uiAttributeOpenTypeFeatures?
return uiOpenTypeFeaturesEqual(attr->spec.Features, spec->Features);
} }
// handles the rest, including pointer comparison for uiAttributeFeatures // handles the rest
// TODO rename it to uiAttributeOpenTypeFeatures?
return attr->spec.Value == spec->Value; return attr->spec.Value == spec->Value;
} }
@ -394,7 +405,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size
// if we got here, we know we have to add the attribute before before // if we got here, we know we have to add the attribute before before
a = uiNew(struct attr); a = uiNew(struct attr);
a->spec = *spec; attrSetSpec(a, spec);
a->start = start; a->start = start;
a->end = end; a->end = end;
attrInsertBefore(alist, a, before); attrInsertBefore(alist, a, before);
@ -661,6 +672,7 @@ void attrlistFree(struct attrlist *alist)
a = alist->first; a = alist->first;
while (a != NULL) { while (a != NULL) {
next = a->next; next = a->next;
attrClearSpec(a);
uiFree(a); uiFree(a);
a = next; a = next;
} }

View File

@ -33,7 +33,7 @@ _UI_ENUM(uiAttribute) {
// TODO document that the color in the case we don't specify it is the text color // TODO document that the color in the case we don't specify it is the text color
uiAttributeUnderlineColor, // enum uiDrawUnderlineColor uiAttributeUnderlineColor, // enum uiDrawUnderlineColor
// TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout // TODO note these are copied
uiAttributeFeatures, // use Features uiAttributeFeatures, // use Features
}; };
@ -65,7 +65,7 @@ _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, c
_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d);
_UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value);
_UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); _UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data);
_UI_EXTERN int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b); _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b);
typedef struct uiAttributeSpec uiAttributeSpec; typedef struct uiAttributeSpec uiAttributeSpec;
@ -82,6 +82,7 @@ struct uiAttributeSpec {
}; };
// TODO name the foreach return values // TODO name the foreach return values
// TODO make the spec const in a way that doesn't allow fields to be modified?
typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data);
// @role uiAttributedString constructor // @role uiAttributedString constructor