TagBuilder.cxx 5.27 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * http://www.musicpd.org
 *
 * 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.
 *
 * 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.
 */

#include "config.h"
#include "TagBuilder.hxx"
#include "TagSettings.h"
#include "TagPool.hxx"
#include "TagString.hxx"
#include "Tag.hxx"

#include <assert.h>
#include <string.h>
29
#include <stdlib.h>
30

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
TagBuilder::TagBuilder(const Tag &other)
	:time(other.time), has_playlist(other.has_playlist)
{
	items.reserve(other.num_items);

	tag_pool_lock.lock();
	for (unsigned i = 0, n = other.num_items; i != n; ++i)
		items.push_back(tag_pool_dup_item(other.items[i]));
	tag_pool_lock.unlock();
}

TagBuilder::TagBuilder(Tag &&other)
	:time(other.time), has_playlist(other.has_playlist)
{
	/* move all TagItem pointers from the Tag object; we don't
	   need to contact the tag pool, because all we do is move
	   references */
48
	items.reserve(other.num_items);
49 50 51 52 53 54 55 56
	std::copy_n(other.items, other.num_items, std::back_inserter(items));

	/* discard the pointers from the Tag object */
	other.num_items = 0;
	delete[] other.items;
	other.items = nullptr;
}

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
TagBuilder &
TagBuilder::operator=(const TagBuilder &other)
{
	/* copy all attributes */
	time = other.time;
	has_playlist = other.has_playlist;
	items = other.items;

	/* increment the tag pool refcounters */
	tag_pool_lock.lock();
	for (auto i : items)
		tag_pool_dup_item(i);
	tag_pool_lock.unlock();

	return *this;
}

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
TagBuilder &
TagBuilder::operator=(TagBuilder &&other)
{
	time = other.time;
	has_playlist = other.has_playlist;
	items = std::move(other.items);

	return *this;
}

TagBuilder &
TagBuilder::operator=(Tag &&other)
{
	time = other.time;
	has_playlist = other.has_playlist;

	/* move all TagItem pointers from the Tag object; we don't
	   need to contact the tag pool, because all we do is move
	   references */
	items.clear();
	items.reserve(other.num_items);
	std::copy_n(other.items, other.num_items, std::back_inserter(items));

	/* discard the pointers from the Tag object */
	other.num_items = 0;
	delete[] other.items;
	other.items = nullptr;

	return *this;
}

105 106 107 108 109
void
TagBuilder::Clear()
{
	time = -1;
	has_playlist = false;
110
	RemoveAll();
111 112
}

113 114
void
TagBuilder::Commit(Tag &tag)
115
{
116 117 118 119
	tag.Clear();

	tag.time = time;
	tag.has_playlist = has_playlist;
120 121 122 123 124 125

	/* move all TagItem pointers to the new Tag object without
	   touching the TagPool reference counters; the
	   vector::clear() call is important to detach them from this
	   object */
	const unsigned n_items = items.size();
126
	tag.num_items = n_items;
127
	tag.items = new TagItem *[n_items];
128
	std::copy_n(items.begin(), n_items, tag.items);
129 130 131 132 133
	items.clear();

	/* now ensure that this object is fresh (will not delete any
	   items because we've already moved them out) */
	Clear();
134
}
135

136 137 138 139 140 141 142 143
Tag
TagBuilder::Commit()
{
	Tag tag;
	Commit(tag);
	return tag;
}

144
Tag *
145
TagBuilder::CommitNew()
146 147 148
{
	Tag *tag = new Tag();
	Commit(*tag);
149 150 151
	return tag;
}

152 153 154 155 156 157 158 159 160 161
bool
TagBuilder::HasType(TagType type) const
{
	for (auto i : items)
		if (i->type == type)
			return true;

	return false;
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
void
TagBuilder::Complement(const Tag &other)
{
	if (time <= 0)
		time = other.time;

	has_playlist |= other.has_playlist;

	items.reserve(items.size() + other.num_items);

	tag_pool_lock.lock();
	for (unsigned i = 0, n = other.num_items; i != n; ++i) {
		TagItem *item = other.items[i];
		if (!HasType(item->type))
			items.push_back(tag_pool_dup_item(item));
	}
	tag_pool_lock.unlock();
}

181
inline void
182
TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
183 184 185 186 187 188 189 190 191 192 193 194 195 196
{
	assert(value != nullptr);
	assert(length > 0);

	char *p = FixTagString(value, length);
	if (p != nullptr) {
		value = p;
		length = strlen(value);
	}

	tag_pool_lock.lock();
	auto i = tag_pool_get_item(type, value, length);
	tag_pool_lock.unlock();

197
	free(p);
198 199 200 201 202

	items.push_back(i);
}

void
203
TagBuilder::AddItem(TagType type, const char *value, size_t length)
204 205 206 207 208 209 210 211 212 213
{
	assert(value != nullptr);

	if (length == 0 || ignore_tag_items[type])
		return;

	AddItemInternal(type, value, length);
}

void
214
TagBuilder::AddItem(TagType type, const char *value)
215 216 217 218 219
{
	assert(value != nullptr);

	AddItem(type, value, strlen(value));
}
220

221 222 223 224 225 226 227 228 229 230
void
TagBuilder::AddEmptyItem(TagType type)
{
	tag_pool_lock.lock();
	auto i = tag_pool_get_item(type, "", 0);
	tag_pool_lock.unlock();

	items.push_back(i);
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
void
TagBuilder::RemoveAll()
{
	tag_pool_lock.lock();
	for (auto i : items)
		tag_pool_put_item(i);
	tag_pool_lock.unlock();

	items.clear();
}

void
TagBuilder::RemoveType(TagType type)
{
	const auto begin = items.begin(), end = items.end();

	items.erase(std::remove_if(begin, end,
				   [type](TagItem *item) {
					   if (item->type != type)
						   return false;
					   tag_pool_put_item(item);
					   return true;
				   }),
		    end);
}