From d38d2bc3533dcfb661a3cbb8bd6cd9574c31ab69 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 29 Aug 2008 15:04:49 +0200
Subject: tag: optimize tag_dup(), copy item references

Don't call tag_pool_get_item() for duplicating tags, just increase the
item's reference counter instead.
---
 src/tag.c      |  4 +++-
 src/tag_pool.c | 43 +++++++++++++++++++++++++++++++++++++------
 src/tag_pool.h |  2 ++
 3 files changed, 42 insertions(+), 7 deletions(-)

(limited to 'src')

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
-- 
cgit v1.2.3