Tag.cxx 4.71 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
3
 * http://www.musicpd.org
Warren Dukes's avatar
Warren Dukes committed
4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Warren Dukes's avatar
Warren Dukes committed
18 19
 */

20
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
21
#include "Tag.hxx"
Max Kellermann's avatar
Max Kellermann committed
22
#include "TagPool.hxx"
23
#include "TagString.hxx"
24
#include "TagSettings.h"
25
#include "util/ASCII.hxx"
Warren Dukes's avatar
Warren Dukes committed
26

27
#include <glib.h>
28
#include <assert.h>
Max Kellermann's avatar
Max Kellermann committed
29
#include <string.h>
30

31
TagType
32 33
tag_name_parse(const char *name)
{
Max Kellermann's avatar
Max Kellermann committed
34
	assert(name != nullptr);
35 36

	for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
Max Kellermann's avatar
Max Kellermann committed
37
		assert(tag_item_names[i] != nullptr);
38 39

		if (strcmp(name, tag_item_names[i]) == 0)
40
			return (TagType)i;
41 42 43 44 45
	}

	return TAG_NUM_OF_ITEM_TYPES;
}

46
TagType
47 48
tag_name_parse_i(const char *name)
{
Max Kellermann's avatar
Max Kellermann committed
49
	assert(name != nullptr);
50 51

	for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
Max Kellermann's avatar
Max Kellermann committed
52
		assert(tag_item_names[i] != nullptr);
53

54
		if (StringEqualsCaseASCII(name, tag_item_names[i]))
55
			return (TagType)i;
56 57 58 59 60
	}

	return TAG_NUM_OF_ITEM_TYPES;
}

Max Kellermann's avatar
Max Kellermann committed
61 62
static size_t
items_size(const Tag &tag)
63
{
Max Kellermann's avatar
Max Kellermann committed
64
	return tag.num_items * sizeof(TagItem *);
65 66
}

Max Kellermann's avatar
Max Kellermann committed
67 68 69 70 71 72 73 74 75 76 77
void
Tag::Clear()
{
	time = -1;
	has_playlist = false;

	tag_pool_lock.lock();
	for (unsigned i = 0; i < num_items; ++i)
		tag_pool_put_item(items[i]);
	tag_pool_lock.unlock();

78
	g_free(items);
Max Kellermann's avatar
Max Kellermann committed
79 80 81 82
	items = nullptr;
	num_items = 0;
}

Max Kellermann's avatar
Max Kellermann committed
83
Tag::~Tag()
Avuton Olrich's avatar
Avuton Olrich committed
84
{
85
	tag_pool_lock.lock();
Max Kellermann's avatar
Max Kellermann committed
86 87
	for (int i = num_items; --i >= 0; )
		tag_pool_put_item(items[i]);
88
	tag_pool_lock.unlock();
89

90
	g_free(items);
Warren Dukes's avatar
Warren Dukes committed
91 92
}

Max Kellermann's avatar
Max Kellermann committed
93 94 95 96
Tag::Tag(const Tag &other)
	:time(other.time), has_playlist(other.has_playlist),
	 items(nullptr),
	 num_items(other.num_items)
Avuton Olrich's avatar
Avuton Olrich committed
97
{
Max Kellermann's avatar
Max Kellermann committed
98 99
	if (num_items > 0) {
		items = (TagItem **)g_malloc(items_size(other));
Warren Dukes's avatar
Warren Dukes committed
100

Max Kellermann's avatar
Max Kellermann committed
101 102 103 104 105
		tag_pool_lock.lock();
		for (unsigned i = 0; i < num_items; i++)
			items[i] = tag_pool_dup_item(other.items[i]);
		tag_pool_lock.unlock();
	}
106 107
}

Max Kellermann's avatar
Max Kellermann committed
108 109
Tag *
Tag::Merge(const Tag &base, const Tag &add)
110 111 112 113 114
{
	unsigned n;

	/* allocate new tag object */

Max Kellermann's avatar
Max Kellermann committed
115 116 117
	Tag *ret = new Tag();
	ret->time = add.time > 0 ? add.time : base.time;
	ret->num_items = base.num_items + add.num_items;
Max Kellermann's avatar
Max Kellermann committed
118
	ret->items = ret->num_items > 0
Max Kellermann's avatar
Max Kellermann committed
119
		? (TagItem **)g_malloc(items_size(*ret))
Max Kellermann's avatar
Max Kellermann committed
120
		: nullptr;
121

122
	tag_pool_lock.lock();
123 124 125

	/* copy all items from "add" */

Max Kellermann's avatar
Max Kellermann committed
126 127
	for (unsigned i = 0; i < add.num_items; ++i)
		ret->items[i] = tag_pool_dup_item(add.items[i]);
128

Max Kellermann's avatar
Max Kellermann committed
129
	n = add.num_items;
130 131 132

	/* copy additional items from "base" */

Max Kellermann's avatar
Max Kellermann committed
133 134 135
	for (unsigned i = 0; i < base.num_items; ++i)
		if (!add.HasType(base.items[i]->type))
			ret->items[n++] = tag_pool_dup_item(base.items[i]);
136

137
	tag_pool_lock.unlock();
138

Max Kellermann's avatar
Max Kellermann committed
139
	assert(n <= ret->num_items);
140

Max Kellermann's avatar
Max Kellermann committed
141
	if (n < ret->num_items) {
142 143 144
		/* some tags were not copied - shrink ret->items */
		assert(n > 0);

Max Kellermann's avatar
Max Kellermann committed
145
		ret->num_items = n;
Max Kellermann's avatar
Max Kellermann committed
146 147
		ret->items = (TagItem **)
			g_realloc(ret->items, items_size(*ret));
148 149 150 151 152
	}

	return ret;
}

Max Kellermann's avatar
Max Kellermann committed
153 154
Tag *
Tag::MergeReplace(Tag *base, Tag *add)
155
{
Max Kellermann's avatar
Max Kellermann committed
156
	if (add == nullptr)
157 158
		return base;

Max Kellermann's avatar
Max Kellermann committed
159
	if (base == nullptr)
160 161
		return add;

Max Kellermann's avatar
Max Kellermann committed
162 163 164
	Tag *tag = Merge(*base, *add);
	delete base;
	delete add;
165 166 167 168

	return tag;
}

169
const char *
170
Tag::GetValue(TagType type) const
171 172 173
{
	assert(type < TAG_NUM_OF_ITEM_TYPES);

Max Kellermann's avatar
Max Kellermann committed
174 175 176
	for (unsigned i = 0; i < num_items; i++)
		if (items[i]->type == type)
			return items[i]->value;
177

Max Kellermann's avatar
Max Kellermann committed
178
	return nullptr;
179
}
180

Max Kellermann's avatar
Max Kellermann committed
181
bool
182
Tag::HasType(TagType type) const
183
{
Max Kellermann's avatar
Max Kellermann committed
184
	return GetValue(type) != nullptr;
185 186
}

Max Kellermann's avatar
Max Kellermann committed
187
void
188
Tag::AddItemInternal(TagType type, const char *value, size_t len)
189
{
Max Kellermann's avatar
Max Kellermann committed
190
	unsigned int i = num_items;
191

192
	char *p = FixTagString(value, len);
Max Kellermann's avatar
Max Kellermann committed
193
	if (p != nullptr) {
194 195 196
		value = p;
		len = strlen(value);
	}
197

Max Kellermann's avatar
Max Kellermann committed
198
	num_items++;
199

200
	items = (TagItem **)g_realloc(items, items_size(*this));
201

202
	tag_pool_lock.lock();
Max Kellermann's avatar
Max Kellermann committed
203
	items[i] = tag_pool_get_item(type, value, len);
204
	tag_pool_lock.unlock();
205

206
	g_free(p);
207 208
}

Max Kellermann's avatar
Max Kellermann committed
209
void
210
Tag::AddItem(TagType type, const char *value, size_t len)
Avuton Olrich's avatar
Avuton Olrich committed
211
{
Max Kellermann's avatar
Max Kellermann committed
212
	if (ignore_tag_items[type])
Avuton Olrich's avatar
Avuton Olrich committed
213
		return;
Max Kellermann's avatar
Max Kellermann committed
214 215

	if (value == nullptr || len == 0)
Avuton Olrich's avatar
Avuton Olrich committed
216
		return;
217

Max Kellermann's avatar
Max Kellermann committed
218 219 220 221
	AddItemInternal(type, value, len);
}

void
222
Tag::AddItem(TagType type, const char *value)
Max Kellermann's avatar
Max Kellermann committed
223 224
{
	AddItem(type, value, strlen(value));
225
}
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

void
Tag::RemoveType(TagType type)
{
	auto dest = items, src = items, end = items + num_items;

	tag_pool_lock.lock();
	while (src != end) {
		TagItem *item = *src++;
		if (item->type == type)
			/* remove it */
			tag_pool_put_item(item);
		else
			/* keep it */
			*dest++ = item;
	}
	tag_pool_lock.unlock();

	num_items = dest - items;
}