aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tag.c4
-rw-r--r--src/tag_pool.c43
-rw-r--r--src/tag_pool.h2
3 files changed, 42 insertions, 7 deletions
diff --git a/src/tag.c b/src/tag.c
index 1dffeef41..d76ba5d95 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -313,9 +313,11 @@ struct tag *tag_dup(const struct tag *tag)
ret = tag_new();
ret->time = tag->time;
+ ret->numOfItems = tag->numOfItems;
+ ret->items = xmalloc(ret->numOfItems * sizeof(ret->items[0]));
for (i = 0; i < tag->numOfItems; i++) {
- tag_add_item(ret, tag->items[i]->type, tag->items[i]->value);
+ ret->items[i] = tag_pool_dup_item(tag->items[i]);
}
return ret;
diff --git a/src/tag_pool.c b/src/tag_pool.c
index 744a82fdb..89efef1fc 100644
--- a/src/tag_pool.c
+++ b/src/tag_pool.c
@@ -61,6 +61,19 @@ tag_item_to_slot(struct tag_item *item)
return (struct slot*)(((char*)item) - offsetof(struct slot, item));
}
+static struct slot *slot_alloc(struct slot *next,
+ enum tag_type type,
+ const char *value, int length)
+{
+ struct slot *slot = xmalloc(sizeof(*slot) + length);
+ slot->next = next;
+ slot->ref = 1;
+ slot->item.type = type;
+ memcpy(slot->item.value, value, length);
+ slot->item.value[length] = 0;
+ return slot;
+}
+
struct tag_item *tag_pool_get_item(enum tag_type type,
const char *value, int length)
{
@@ -76,16 +89,34 @@ struct tag_item *tag_pool_get_item(enum tag_type type,
}
}
- slot = xmalloc(sizeof(*slot) + length);
- slot->next = *slot_p;
- slot->ref = 1;
- slot->item.type = type;
- memcpy(slot->item.value, value, length);
- slot->item.value[length] = 0;
+ slot = slot_alloc(*slot_p, type, value, length);
*slot_p = slot;
return &slot->item;
}
+struct tag_item *tag_pool_dup_item(struct tag_item *item)
+{
+ struct slot *slot = tag_item_to_slot(item);
+
+ assert(slot->ref > 0);
+
+ if (slot->ref < 0xff) {
+ ++slot->ref;
+ return item;
+ } else {
+ /* the reference counter overflows above 0xff;
+ duplicate the item, and start with 1 */
+ size_t length = strlen(item->value);
+ struct slot **slot_p =
+ &slots[calc_hash_n(item->type, item->value,
+ length) % NUM_SLOTS];
+ slot = slot_alloc(*slot_p, item->type,
+ item->value, strlen(item->value));
+ *slot_p = slot;
+ return &slot->item;
+ }
+}
+
void tag_pool_put_item(struct tag_item *item)
{
struct slot **slot_p, *slot;
diff --git a/src/tag_pool.h b/src/tag_pool.h
index 4f063d1ed..e19b2f4b4 100644
--- a/src/tag_pool.h
+++ b/src/tag_pool.h
@@ -26,6 +26,8 @@ struct tag_item;
struct tag_item *tag_pool_get_item(enum tag_type type,
const char *value, int length);
+struct tag_item *tag_pool_dup_item(struct tag_item *item);
+
void tag_pool_put_item(struct tag_item *item);
#endif