merge changes from mpd-tree:

-use tree for tagTracker
-eliminate the master process

git-svn-id: https://svn.musicpd.org/mpd/trunk@4571 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
Warren Dukes 2006-08-06 06:40:11 +00:00
parent a8393d3937
commit 31de97a42b
12 changed files with 915 additions and 294 deletions

View File

@ -68,6 +68,7 @@ mpd_headers = \
stats.h \
tag.h \
tagTracker.h \
tree.h \
utf8.h \
utils.h \
volume.h
@ -116,6 +117,7 @@ mpd_SOURCES = \
stats.c \
tag.c \
tagTracker.c \
tree.c \
utils.c \
volume.c \
utf8.c

View File

@ -173,6 +173,7 @@ int updateInit(int fd, List * pathList)
if (directory_updatePid == 0) {
/* child */
int dbUpdated = 0;
clearPlayerPid();
unblockSignals();

View File

@ -54,14 +54,12 @@
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <unistd.h>
#define SYSTEM_CONFIG_FILE_LOCATION "/etc/mpd.conf"
#define USER_CONFIG_FILE_LOCATION "/.mpdconf"
volatile int masterPid = 0;
volatile int mainPid = 0;
typedef struct _Options {
int kill;
int daemon;
@ -311,47 +309,6 @@ static void openDB(Options * options, char *argv0)
}
}
static void startMainProcess(void)
{
int pid;
fflush(0);
pid = fork();
if (pid > 0) {
initInputStream();
initReplayGainState();
/* free stuff we don't need */
freeAllListenSockets();
mainPid = pid;
masterInitSigHandlers();
kill(mainPid, SIGUSR1);
while (masterHandlePendingSignals() != COMMAND_RETURN_KILL)
waitOnSignals();
/* we're killed */
playerKill();
finishPlaylist();
finishAudioConfig();
finishAudioDriver();
finishPaths();
kill(mainPid, SIGTERM);
waitpid(mainPid, NULL, 0);
finishConf();
close_log_files();
exit(EXIT_SUCCESS);
} else if (pid < 0) {
ERROR("problems fork'ing main process!\n");
exit(EXIT_FAILURE);
}
DEBUG("main process started!\n");
}
static void daemonize(Options * options)
{
FILE *fp = NULL;
@ -409,7 +366,6 @@ static void daemonize(Options * options)
DEBUG("writing pid file\n");
fprintf(fp, "%lu\n", (unsigned long)getpid());
fclose(fp);
masterPid = getpid();
}
}
@ -480,38 +436,30 @@ int main(int argc, char *argv[])
open_log_files(options.stdOutput);
initPlayerData();
initInputPlugins();
initPaths();
initPermissions();
initPlaylist();
initInputPlugins();
openDB(&options, argv[0]);
initCommands();
initPlayerData();
initAudioConfig();
initAudioDriver();
initVolume();
initInterfaces();
initReplayGainState();
initNormalization();
initPlaylist();
openDB(&options, argv[0]);
initInputStream();
daemonize(&options);
initSigHandlers();
setup_log_output(options.stdOutput);
startMainProcess();
/* This is the main process which has
* been forked from the master process.
*/
initPermissions();
initCommands();
initVolume();
initInterfaces();
printMemorySavedByTagTracker();
printSavedMemoryFromFilenames();
/* wait for the master process to get ready so we can start
* playing if readPlaylistState thinks we should*/
while (COMMAND_MASTER_READY != handlePendingSignals())
my_usleep(1);
initSigHandlers();
openVolumeDevice();
read_state_file();
@ -525,13 +473,14 @@ int main(int argc, char *argv[])
}
write_state_file();
playerKill();
freeAllInterfaces();
closeAllListenSockets();
/* This slows shutdown immensely, and doesn't really accomplish
* anything. Uncomment when we rewrite tagTracker to use a tree. */
/*closeMp3Directory(); */
clock_t start = clock();
closeMp3Directory();
DEBUG("closeMp3Directory took %f seconds\n",
((float)(clock()-start))/CLOCKS_PER_SEC);
finishPlaylist();
freePlayerData();

View File

@ -44,7 +44,12 @@
#include <errno.h>
#include <fcntl.h>
extern int masterPid;
volatile int player_pid = 0;
void clearPlayerPid()
{
player_pid = 0;
}
static void resetPlayerMetadata()
{
@ -59,7 +64,7 @@ void resetPlayer()
{
int pid;
setPlayerPid(0);
clearPlayerPid();
getPlayerData()->playerControl.stop = 0;
getPlayerData()->playerControl.play = 0;
getPlayerData()->playerControl.pause = 0;
@ -78,17 +83,23 @@ void resetPlayer()
void player_sigChldHandler(int pid, int status)
{
if (getPlayerPid() == pid) {
if (player_pid == pid)
{
DEBUG("SIGCHLD caused by player process\n");
if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM &&
WTERMSIG(status) != SIGINT) {
if (WIFSIGNALED(status) &&
WTERMSIG(status) != SIGTERM &&
WTERMSIG(status) != SIGINT)
{
ERROR("player process died from signal: %i\n",
WTERMSIG(status));
}
resetPlayer();
} else if (pid == getPlayerData()->playerControl.decode_pid
&& getPlayerPid() <= 0) {
if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
}
else if (pid == getPlayerData()->playerControl.decode_pid &&
player_pid <= 0)
{
if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM)
{
ERROR("(caught by master parent) "
"decode process died from a "
"non-TERM signal: %i\n", WTERMSIG(status));
@ -99,25 +110,29 @@ void player_sigChldHandler(int pid, int status)
int playerInit()
{
kill(masterPid, SIGUSR2);
/* we need to wait for the signal to take effect: */
while (getPlayerPid() == 0)
my_usleep(10000);
return 0;
}
int playerInitReal()
{
int player_pid;
blockSignals();
player_pid = fork();
if (player_pid == 0) {
if (player_pid==0)
{
clock_t start = clock();
PlayerControl *pc = &(getPlayerData()->playerControl);
unblockSignals();
setSigHandlersForDecoder();
closeAllListenSockets();
freeAllInterfaces();
closeMp3Directory();
finishPlaylist();
finishPermissions();
finishCommands();
finishVolume();
DEBUG("took %f to init player\n",
(float)(clock()-start)/CLOCKS_PER_SEC);
while (1) {
if (pc->play)
decode();
@ -143,14 +158,14 @@ int playerInitReal()
}
exit(EXIT_SUCCESS);
} else if (player_pid < 0) {
}
else if (player_pid < 0)
{
unblockSignals();
ERROR("player Problems fork()'ing\n");
setPlayerPid(0);
player_pid = 0;
return -1;
} else
setPlayerPid(player_pid);
}
unblockSignals();
@ -174,13 +189,13 @@ int playerPlay(int fd, Song * song)
pathcpy_trunc(pc->utf8url, getSongUrl(song));
pc->play = 1;
if (getPlayerPid() == 0 && playerInit() < 0) {
if (player_pid == 0 && playerInit() < 0) {
pc->play = 0;
return -1;
}
resetPlayerMetadata();
while (getPlayerPid() > 0 && pc->play)
while (player_pid > 0 && pc->play)
my_usleep(1000);
return 0;
@ -190,9 +205,9 @@ int playerStop(int fd)
{
PlayerControl *pc = &(getPlayerData()->playerControl);
if (getPlayerPid() > 0 && pc->state != PLAYER_STATE_STOP) {
if (player_pid > 0 && pc->state != PLAYER_STATE_STOP) {
pc->stop = 1;
while (getPlayerPid() > 0 && pc->stop)
while (player_pid > 0 && pc->stop)
my_usleep(1000);
}
@ -211,7 +226,7 @@ void playerKill()
playerCloseAudio(stderr);
if(player_pid>0 && pc->closeAudio) sleep(1); */
pid = getPlayerPid();
pid = player_pid;
if (pid > 0)
kill(pid, SIGTERM);
}
@ -220,9 +235,9 @@ int playerPause(int fd)
{
PlayerControl *pc = &(getPlayerData()->playerControl);
if (getPlayerPid() > 0 && pc->state != PLAYER_STATE_STOP) {
if (player_pid > 0 && pc->state != PLAYER_STATE_STOP) {
pc->pause = 1;
while (getPlayerPid() > 0 && pc->pause)
while (player_pid > 0 && pc->pause)
my_usleep(1000);
}
@ -233,7 +248,7 @@ int playerSetPause(int fd, int pause)
{
PlayerControl *pc = &(getPlayerData()->playerControl);
if (getPlayerPid() <= 0)
if (player_pid <= 0)
return 0;
switch (pc->state) {
@ -325,7 +340,7 @@ void playerCloseAudio()
{
PlayerControl *pc = &(getPlayerData()->playerControl);
if (getPlayerPid() > 0) {
if (player_pid > 0) {
if (playerStop(STDERR_FILENO) < 0)
return;
pc->closeAudio = 1;
@ -371,9 +386,9 @@ void playerQueueLock()
{
PlayerControl *pc = &(getPlayerData()->playerControl);
if (getPlayerPid() > 0 && pc->queueLockState == PLAYER_QUEUE_UNLOCKED) {
if (player_pid > 0 && pc->queueLockState == PLAYER_QUEUE_UNLOCKED) {
pc->lockQueue = 1;
while (getPlayerPid() > 0 && pc->lockQueue)
while (player_pid > 0 && pc->lockQueue)
my_usleep(1000);
}
}
@ -382,9 +397,9 @@ void playerQueueUnlock()
{
PlayerControl *pc = &(getPlayerData()->playerControl);
if (getPlayerPid() > 0 && pc->queueLockState == PLAYER_QUEUE_LOCKED) {
if (player_pid > 0 && pc->queueLockState == PLAYER_QUEUE_LOCKED) {
pc->unlockQueue = 1;
while (getPlayerPid() > 0 && pc->unlockQueue)
while (player_pid > 0 && pc->unlockQueue)
my_usleep(1000);
}
}
@ -414,7 +429,7 @@ int playerSeek(int fd, Song * song, float time)
resetPlayerMetadata();
pc->seekWhere = time;
pc->seek = 1;
while (getPlayerPid() > 0 && pc->seek)
while (player_pid > 0 && pc->seek)
my_usleep(1000);
}

View File

@ -89,8 +89,6 @@ typedef struct _PlayerControl {
MetadataChunk fileMetadataChunk;
} PlayerControl;
int playerInitReal();
void player_sigChldHandler(int pid, int status);
int playerPlay(int fd, Song * song);

View File

@ -95,15 +95,18 @@ void initPlayerData(void)
/* for audioDeviceStates[] */
allocationSize += device_array_size;
if ((shmid = shmget(IPC_PRIVATE, allocationSize, IPC_CREAT | 0600)) < 0) {
if ((shmid = shmget(IPC_PRIVATE, allocationSize, IPC_CREAT | 0600)) < 0)
{
ERROR("problems shmget'ing\n");
exit(EXIT_FAILURE);
}
if (!(playerData_pd = shmat(shmid, NULL, 0))) {
if (!(playerData_pd = shmat(shmid, NULL, 0)))
{
ERROR("problems shmat'ing\n");
exit(EXIT_FAILURE);
}
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
if (shmctl(shmid, IPC_RMID, NULL) < 0)
{
ERROR("problems shmctl'ing\n");
exit(EXIT_FAILURE);
}
@ -159,16 +162,6 @@ PlayerData *getPlayerData(void)
return playerData_pd;
}
int getPlayerPid(void)
{
return playerData_pd->pid;
}
void setPlayerPid(int pid)
{
playerData_pd->pid = pid;
}
void freePlayerData(void)
{
shmdt(playerData_pd);

View File

@ -38,14 +38,11 @@ typedef struct _PlayerData {
PlayerControl playerControl;
DecoderControl decoderControl;
mpd_uint8 *audioDeviceStates;
int pid;
} PlayerData;
void initPlayerData();
PlayerData *getPlayerData();
int getPlayerPid();
void setPlayerPid(int pid);
void freePlayerData();

View File

@ -39,34 +39,8 @@
extern volatile int masterPid;
extern volatile int mainPid;
int masterHandlePendingSignals()
{
if (signal_is_pending(SIGINT) || signal_is_pending(SIGTERM)) {
DEBUG("master process got SIGINT or SIGTERM, exiting\n");
return COMMAND_RETURN_KILL;
}
if (signal_is_pending(SIGHUP)) {
signal_clear(SIGHUP);
/* Forward it to the main process, which will update the DB */
kill(mainPid, SIGHUP);
}
return 0;
}
int handlePendingSignals()
{
/* this SIGUSR1 signal comes before the KILL signals, because there if the process is
* looping, waiting for this signal, it will not respond to the KILL signal. This might be
* better implemented by using bit-wise defines and or'ing of the COMMAND_FOO as return.
*/
if (signal_is_pending(SIGUSR1)) {
signal_clear(SIGUSR1);
DEBUG("The master process is ready to receive signals\n");
return COMMAND_MASTER_READY;
}
if (signal_is_pending(SIGINT) || signal_is_pending(SIGTERM)) {
DEBUG("main process got SIGINT or SIGTERM, exiting\n");
return COMMAND_RETURN_KILL;
@ -99,56 +73,11 @@ void chldSigHandler(int signal)
else
break;
}
player_sigChldHandler(pid, status);
directory_sigChldHandler(pid, status);
}
}
void masterChldSigHandler(int signal)
{
int status;
int pid;
DEBUG("master process got SIGCHLD\n");
while (0 != (pid = wait3(&status, WNOHANG, NULL))) {
if (pid < 0) {
if (errno == EINTR)
continue;
else
break;
}
DEBUG("PID: %d\n", pid);
if (pid == mainPid)
kill(getpid(), SIGTERM);
player_sigChldHandler(pid, status);
}
}
int playerInitReal();
void masterSigUsr2Handler(int signal)
{
DEBUG("Master process got SIGUSR2 starting a new player process\n");
if (getPlayerPid() <= 0)
playerInitReal();
}
void masterInitSigHandlers()
{
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_IGN;
while (sigaction(SIGPIPE, &sa, NULL) < 0 && errno == EINTR) ;
sa.sa_handler = masterChldSigHandler;
while (sigaction(SIGCHLD, &sa, NULL) < 0 && errno == EINTR) ;
sa.sa_handler = masterSigUsr2Handler;
while (sigaction(SIGUSR2, &sa, NULL) < 0 && errno == EINTR) ;
signal_handle(SIGUSR1);
signal_handle(SIGINT);
signal_handle(SIGTERM);
signal_handle(SIGHUP);
}
void initSigHandlers()
{
struct sigaction sa;
@ -159,7 +88,6 @@ void initSigHandlers()
while (sigaction(SIGPIPE, &sa, NULL) < 0 && errno == EINTR) ;
sa.sa_handler = chldSigHandler;
while (sigaction(SIGCHLD, &sa, NULL) < 0 && errno == EINTR) ;
signal_handle(SIGUSR2);
signal_handle(SIGUSR1);
signal_handle(SIGINT);
signal_handle(SIGTERM);
@ -202,26 +130,11 @@ void ignoreSignals()
while (sigaction(SIGPIPE, &sa, NULL) < 0 && errno == EINTR) ;
while (sigaction(SIGCHLD, &sa, NULL) < 0 && errno == EINTR) ;
while (sigaction(SIGUSR1, &sa, NULL) < 0 && errno == EINTR) ;
while (sigaction(SIGUSR2, &sa, NULL) < 0 && errno == EINTR) ;
while (sigaction(SIGINT, &sa, NULL) < 0 && errno == EINTR) ;
while (sigaction(SIGTERM, &sa, NULL) < 0 && errno == EINTR) ;
while (sigaction(SIGHUP, &sa, NULL) < 0 && errno == EINTR) ;
}
void waitOnSignals()
{
sigset_t sset;
sigfillset(&sset);
sigdelset(&sset, SIGCHLD);
sigdelset(&sset, SIGUSR1);
sigdelset(&sset, SIGUSR2);
sigdelset(&sset, SIGHUP);
sigdelset(&sset, SIGINT);
sigdelset(&sset, SIGTERM);
sigsuspend(&sset);
}
void blockSignals()
{
sigset_t sset;
@ -229,7 +142,6 @@ void blockSignals()
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigaddset(&sset, SIGUSR1);
sigaddset(&sset, SIGUSR2);
sigaddset(&sset, SIGHUP);
sigaddset(&sset, SIGINT);
sigaddset(&sset, SIGTERM);
@ -243,7 +155,6 @@ void unblockSignals()
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigaddset(&sset, SIGUSR1);
sigaddset(&sset, SIGUSR2);
sigaddset(&sset, SIGHUP);
sigaddset(&sset, SIGINT);
sigaddset(&sset, SIGTERM);

View File

@ -22,10 +22,8 @@
#include "../config.h"
int handlePendingSignals();
int masterHandlePendingSignals();
void initSigHandlers();
void masterInitSigHandlers();
void finishSigHandlers();
@ -33,8 +31,6 @@ void setSigHandlersForDecoder();
void ignoreSignals();
void waitOnSignals();
void blockSignals();
void unblockSignals();

View File

@ -18,12 +18,13 @@
#include "tagTracker.h"
#include "list.h"
#include "tree.h"
#include "log.h"
#include <assert.h>
#include <stdlib.h>
static List *tagLists[TAG_NUM_OF_ITEM_TYPES] = {
static Tree *tagTrees[TAG_NUM_OF_ITEM_TYPES] = {
NULL,
NULL,
NULL,
@ -40,129 +41,113 @@ typedef struct tagTrackerItem {
char *getTagItemString(int type, char *string)
{
ListNode *node;
int pos;
if (tagLists[type] == NULL) {
tagLists[type] = makeList(free, 1);
sortList(tagLists[type]);
TreeIterator iter;
if (tagTrees[type] == NULL)
{
tagTrees[type] = MakeTree((TreeCompareKeyFunction)strcmp,
(TreeFreeFunction)free,
(TreeFreeFunction)free);
}
if (findNodeInList(tagLists[type], string, &node, &pos)) {
((TagTrackerItem *) node->data)->count++;
} else {
if (FindInTree(tagTrees[type], string, &iter))
{
((TagTrackerItem *)GetTreeKeyData(&iter).data)->count++;
return (char *)GetTreeKeyData(&iter).key;
}
else
{
TagTrackerItem *item = malloc(sizeof(TagTrackerItem));
item->count = 1;
item->visited = 0;
node = insertInListBeforeNode(tagLists[type], node, pos,
string, item);
char * key= strdup(string);
InsertInTree(tagTrees[type], key, item);
return key;
}
return node->key;
}
void removeTagItemString(int type, char *string)
{
ListNode *node;
int pos;
TreeIterator iter;
assert(string);
assert(tagLists[type]);
if (tagLists[type] == NULL)
assert(tagTrees[type]);
if (tagTrees[type] == NULL)
return;
if (findNodeInList(tagLists[type], string, &node, &pos)) {
TagTrackerItem *item = node->data;
if (FindInTree(tagTrees[type], string, &iter))
{
TagTrackerItem * item =
(TagTrackerItem *)GetTreeKeyData(&iter).data;
item->count--;
if (item->count <= 0)
deleteNodeFromList(tagLists[type], node);
RemoveFromTreeByIterator(tagTrees[type], &iter);
}
if (tagLists[type]->numberOfNodes == 0) {
freeList(tagLists[type]);
tagLists[type] = NULL;
if (GetTreeSize(tagTrees[type]) == 0)
{
FreeTree(tagTrees[type]);
tagTrees[type] = NULL;
}
}
int getNumberOfTagItems(int type)
{
if (tagLists[type] == NULL)
if (tagTrees[type] == NULL)
return 0;
return tagLists[type]->numberOfNodes;
}
void printMemorySavedByTagTracker(void)
{
int i;
ListNode *node;
size_t sum = 0;
for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) {
if (!tagLists[i])
continue;
sum -= sizeof(List);
node = tagLists[i]->firstNode;
while (node != NULL) {
sum -= sizeof(ListNode);
sum -= sizeof(TagTrackerItem);
sum -= sizeof(node->key);
sum += (strlen(node->key) + 1) * (*((int *)node->data));
node = node->nextNode;
}
}
DEBUG("saved memory from tags: %li\n", (long)sum);
return GetTreeSize(tagTrees[type]);
}
void resetVisitedFlagsInTagTracker(int type)
{
ListNode *node;
TreeIterator iter;
if (!tagLists[type])
if (!tagTrees[type])
return;
node = tagLists[type]->firstNode;
while (node) {
((TagTrackerItem *) node->data)->visited = 0;
node = node->nextNode;
for (SetTreeIteratorToBegin(tagTrees[type], &iter);
!IsTreeIteratorAtEnd(&iter);
IncrementTreeIterator(&iter))
{
((TagTrackerItem *)GetTreeKeyData(&iter).data)->visited = 0;
}
}
void visitInTagTracker(int type, char *str)
{
void *item;
TreeIterator iter;
if (!tagLists[type])
if (!tagTrees[type])
return;
if (!findInList(tagLists[type], str, &item))
if (!FindInTree(tagTrees[type], str, &iter))
return;
((TagTrackerItem *) item)->visited = 1;
((TagTrackerItem *)GetTreeKeyData(&iter).data)->visited = 1;
}
void printVisitedInTagTracker(int fd, int type)
{
ListNode *node;
TagTrackerItem *item;
TreeIterator iter;
TagTrackerItem * item;
if (!tagLists[type])
if (!tagTrees[type])
return;
node = tagLists[type]->firstNode;
for (SetTreeIteratorToBegin(tagTrees[type], &iter);
!IsTreeIteratorAtEnd(&iter);
IncrementTreeIterator(&iter))
{
item = ((TagTrackerItem *)GetTreeKeyData(&iter).data);
while (node) {
item = node->data;
if (item->visited) {
fdprintf(fd, "%s: %s\n", mpdTagItemKeys[type],
node->key);
if (item->visited)
{
fdprintf(fd,
"%s: %s\n",
mpdTagItemKeys[type],
(char *)GetTreeKeyData(&iter).key);
}
node = node->nextNode;
}
}

712
src/tree.c Normal file
View File

@ -0,0 +1,712 @@
/* the Music Player Daemon (MPD)
* (c)2006 by Warren Dukes (warren.dukes@gmail.com)
* This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tree.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#ifndef CHILDREN_PER_NODE
#define CHILDREN_PER_NODE 25
#endif
#define DATA_PER_NODE (CHILDREN_PER_NODE-1)
#if CHILDREN_PER_NODE > 7
#define USE_BINARY_SEARCH 1
#endif
/************************* DATA STRUCTURES **********************************/
struct _TreeNode
{
TreeKeyData keyData[DATA_PER_NODE];
struct _TreeNode * parent;
short parentPos;
struct _TreeNode * children[CHILDREN_PER_NODE];
short count;
};
struct _Tree
{
TreeCompareKeyFunction compareKey;
TreeFreeFunction freeKey;
TreeFreeFunction freeData;
TreeNode * rootNode;
int size;
};
/************************* STATIC METHODS ***********************************/
static
TreeNode *
_MakeNode()
{
TreeNode * ret = malloc(sizeof(TreeNode));
memset(ret, 0, sizeof(TreeNode));
return ret;
}
static
void
_ClearKeyData(TreeKeyData * keyData)
{
memset(keyData, 0, sizeof(TreeKeyData));
}
static
int
_FindPosition(Tree * tree, TreeNode * node, void * key, int * pos)
{
#ifdef USE_BINARY_SEARCH
int low = 0;
int high = node->count;
int cmp = -1;
while (high > low)
{
int cur = (high + low) >> 1;
cmp = tree->compareKey(key, node->keyData[cur].key);
if (cmp > 0)
{
low = cur+1;
}
else if (cmp < 0)
{
high = cur;
}
else
{
low = cur;
break;
}
}
*pos = low;
return (cmp == 0);
#else
int i = 0;
int cmp = -1;
for (;
i < node->count &&
(cmp = tree->compareKey(key, node->keyData[i].key)) > 0;
i++);
*pos = i;
return (cmp == 0);
#endif
}
static
int
_Find(TreeIterator * iter, void * key)
{
while (1)
{
if (_FindPosition(iter->tree, iter->node, key, &iter->which))
{
iter->which++;
return 1;
}
if (iter->node->children[iter->which])
{
iter->node = iter->node->children[iter->which];
}
else
{
return 0;
}
}
}
static void _SetIteratorToRoot(Tree * tree, TreeIterator * iter)
{
iter->tree = tree;
iter->node = tree->rootNode;
iter->which = 0;
}
static
TreeNode *
_SplitNode(TreeNode * node)
{
assert(node->count == DATA_PER_NODE);
TreeNode * newNode = _MakeNode();
int i = DATA_PER_NODE/2;
int j = 0;
for (; i < DATA_PER_NODE; i++, j++)
{
newNode->keyData[j] = node->keyData[i];
newNode->children[j+1] = node->children[i+1];
if (newNode->children[j+1])
{
newNode->children[j+1]->parent = newNode;
newNode->children[j+1]->parentPos = j+1;
}
_ClearKeyData(&(node->keyData[i]));
node->children[i+1] = NULL;
}
newNode->count = (DATA_PER_NODE-DATA_PER_NODE/2);
node->count -= (DATA_PER_NODE-DATA_PER_NODE/2);
return newNode;
}
static
void
_InsertNodeAndData(Tree * tree,
TreeNode * node,
int pos,
TreeNode * newNode,
TreeKeyData keyData)
{
assert(node->count < DATA_PER_NODE);
int j = node->count;
for (; j > pos; j--)
{
node->keyData[j] = node->keyData[j-1];
node->children[j+1] = node->children[j];
if (node->children[j+1])
{
node->children[j+1]->parentPos = j+1;
}
}
node->keyData[pos] = keyData;
node->count++;
node->children[pos+1] = newNode;
if (newNode)
{
newNode->parent = node;
newNode->parentPos = pos+1;
}
}
static
TreeKeyData
_AddDataToSplitNodes(Tree * tree,
TreeNode * lessNode,
TreeNode * moreNode,
int pos,
TreeNode * newNode,
TreeKeyData keyData)
{
assert(moreNode->children[0] == NULL);
TreeKeyData retKeyData;
if (pos <= lessNode->count)
{
_InsertNodeAndData(tree, lessNode, pos, newNode, keyData);
lessNode->count--;
retKeyData = lessNode->keyData[lessNode->count];
_ClearKeyData(&(lessNode->keyData[lessNode->count]));
moreNode->children[0] =
lessNode->children[lessNode->count+1];
if (moreNode->children[0])
{
moreNode->children[0]->parent = moreNode;
moreNode->children[0]->parentPos = 0;
}
lessNode->children[lessNode->count+1] = NULL;
}
else
{
pos -= lessNode->count;
retKeyData = moreNode->keyData[0];
assert(!moreNode->children[0]);
int j = 0;
for (; j < pos; j++)
{
moreNode->keyData[j] = moreNode->keyData[j+1];
moreNode->children[j] = moreNode->children[j+1];
if (moreNode->children[j])
{
moreNode->children[j]->parentPos = j;
}
}
moreNode->keyData[pos-1] = keyData;
moreNode->children[pos] = newNode;
if (newNode)
{
newNode->parent = moreNode;
newNode->parentPos = pos;
}
}
return retKeyData;
}
static
void
_InsertAt(TreeIterator * iter, TreeKeyData keyData)
{
TreeNode * node = iter->node;
TreeNode * insertNode = NULL;
int pos = iter->which;
while (node != NULL)
{
// see if there's any NULL data in the current node
if (node->count == DATA_PER_NODE)
{
// no open data slots, split this node!
TreeNode * newNode = _SplitNode(node);
// insert data in split nodes
keyData = _AddDataToSplitNodes(iter->tree,
node,
newNode,
pos,
insertNode,
keyData);
if (node->parent == NULL)
{
assert(node == iter->tree->rootNode);
iter->tree->rootNode = _MakeNode();
iter->tree->rootNode->children[0] = node;
node->parent = iter->tree->rootNode;
node->parentPos = 0;
iter->tree->rootNode->children[1] = newNode;
newNode->parent = iter->tree->rootNode;
newNode->parentPos = 1;
iter->tree->rootNode->keyData[0] = keyData;
iter->tree->rootNode->count = 1;
return;
}
pos = node->parentPos;
node = node->parent;
insertNode = newNode;
}
else
{
// insert the data and newNode
_InsertNodeAndData(iter->tree,
node,
pos,
insertNode,
keyData);
return;
}
}
}
static
void
_MergeNodes(TreeNode * lessNode, TreeNode * moreNode)
{
int i = 0;
int j = lessNode->count;
assert((lessNode->count + moreNode->count) <= DATA_PER_NODE);
assert(lessNode->children[j] == NULL);
for(; i < moreNode->count; i++,j++)
{
assert(!lessNode->children[j]);
lessNode->keyData[j] = moreNode->keyData[i];
lessNode->children[j] = moreNode->children[i];
if (lessNode->children[j])
{
lessNode->children[j]->parent = lessNode;
lessNode->children[j]->parentPos = j;
}
}
lessNode->children[j] = moreNode->children[i];
if (lessNode->children[j])
{
lessNode->children[j]->parent = lessNode;
lessNode->children[j]->parentPos = j;
}
lessNode->count += i;
free(moreNode);
}
void
_DeleteAt(TreeIterator * iter)
{
TreeNode * node = iter->node;
int pos = iter->which - 1;
TreeKeyData * keyData = &(node->keyData[pos]);
TreeKeyData keyDataToFree = *keyData;
{
// find the least greater than data to fill the whole!
if (node->children[pos+1])
{
TreeNode * child = node->children[++pos];
while (child->children[0])
{
pos = 0;
child = child->children[0];
}
*keyData = child->keyData[0];
keyData = &(child->keyData[0]);
node = child;
}
// or the greatest lesser than data to fill the whole!
else if (node->children[pos])
{
TreeNode * child = node->children[pos];
while (child->children[child->count])
{
pos = child->count;
child = child->children[child->count];
}
*keyData = child->keyData[child->count-1];
keyData = &(child->keyData[child->count-1]);
node = child;
}
else
{
pos = node->parentPos;
}
}
// move data nodes over, we're at a leaf node, so we can ignore
// children
int i = keyData - node->keyData;;
for (; i < node->count-1; i++)
{
node->keyData[i] = node->keyData[i+1];
}
_ClearKeyData(&(node->keyData[--node->count]));
// merge the nodes from the bottom up which have too few data
while (node->count < (DATA_PER_NODE/2))
{
// if we're not the root
if (node->parent)
{
TreeNode ** child = &(node->parent->children[pos]);
assert(node->parent->children[pos] == node);
// check siblings for extra data
if (pos < node->parent->count &&
(*(child+1))->count > (DATA_PER_NODE/2))
{
child++;
node->keyData[node->count++] =
node->parent->keyData[pos];
node->children[node->count] =
(*child)->children[0];
if (node->children[node->count])
{
node->children[node->count]->
parent = node;
node->children[node->count]->
parentPos = node->count;
}
node->parent->keyData[pos] =
(*child)->keyData[0];
int i = 0;
for(; i < (*child)->count-1; i++)
{
(*child)->keyData[i] =
(*child)->keyData[i+1];
(*child)->children[i] =
(*child)->children[i+1];
if ((*child)->children[i])
{
(*child)->children[i]->
parentPos = i;
}
}
(*child)->children[i] = (*child)->children[i+1];
if ((*child)->children[i])
{
(*child)->children[i]->parentPos = i;
}
(*child)->children[i+1] =NULL;
_ClearKeyData(&((*child)->keyData[i]));
(*child)->count--;
}
else if (pos > 0 &&
(*(child-1))->count>(DATA_PER_NODE/2))
{
child--;
int i = node->count++;
for(; i > 0; i--)
{
node->keyData[i] = node->keyData[i-1];
node->children[i+1] = node->children[i];
if (node->children[i+1])
{
node->children[i+1]->parentPos =
i+1;
}
}
node->children[1] = node->children[0];
if (node->children[1])
{
node->children[1]->parentPos = 1;
}
node->keyData[0] = node->parent->keyData[pos-1];
node->children[0] =
(*child)->children[(*child)->count];
if (node->children[0])
{
node->children[0]->parent = node;
node->children[0]->parentPos = 0;
}
node->parent->keyData[pos-1] =
(*child)->keyData[(*child)->count-1];
(*child)->children[(*child)->count--] =
NULL;
_ClearKeyData(
&((*child)->keyData[(*child)->count]));
}
// merge with one of our siblings
else
{
if (pos < node->parent->count)
{
child++;
assert(*child);
node->keyData[node->count++] =
node->parent->keyData[pos];
_MergeNodes(node, *child);
}
else
{
assert(pos > 0);
child--;
assert(*child);
pos--;
(*child)->keyData[(*child)->count++] =
node->parent->keyData[pos];
_MergeNodes(*child, node);
node = *child;
}
int i = pos;
for(; i < node->parent->count-1; i++)
{
node->parent->keyData[i] =
node->parent->keyData[i+1];
node->parent->children[i+1] =
node->parent->children[i+2];
if (node->parent->children[i+1])
{
node->parent->children[i+1]->
parentPos = i+1;
}
}
_ClearKeyData(&(node->parent->keyData[i]));
node->parent->children[i+1] = NULL;
node->parent->count--;
node = node->parent;
pos = node->parentPos;
}
}
// this is a root node
else
{
if (node->count == 0)
{
if (node->children[0])
{
node->children[0]->parent = NULL;
node->children[0]->parentPos = 0;
}
iter->tree->rootNode = node->children[0];
free(node);
}
break;
}
}
if (iter->tree->freeKey)
{
iter->tree->freeData(keyDataToFree.key);
}
if (iter->tree->freeData)
{
iter->tree->freeData(keyDataToFree.data);
}
}
/************************* PUBLIC METHODS ***********************************/
Tree *
MakeTree(TreeCompareKeyFunction compareKey,
TreeFreeFunction freeKey,
TreeFreeFunction freeData)
{
Tree * ret = malloc(sizeof(Tree));
ret->compareKey = compareKey;
ret->freeKey = freeKey;
ret->freeData = freeData;
ret->rootNode = _MakeNode();
ret->size = 0;
return ret;
}
void
FreeTree(Tree * tree)
{
assert(tree->rootNode == NULL);
free(tree);
}
int
GetTreeSize(Tree * tree)
{
return tree->size;
}
void SetTreeIteratorToBegin(Tree * tree, TreeIterator * iter)
{
_SetIteratorToRoot(tree, iter);
IncrementTreeIterator(iter);
}
int IsTreeIteratorAtEnd(const TreeIterator * iter)
{
return (iter->node == NULL);
}
void IncrementTreeIterator(TreeIterator * iter)
{
while(iter->node)
{
if (iter->node->children[iter->which])
{
iter->node = iter->node->children[iter->which];
iter->which = 0;
}
else
{
iter->which++;
}
while (iter->node && iter->which > iter->node->count)
{
TreeNode * childNode = iter->node;
iter->node = childNode->parent;
if (iter->node)
{
for (iter->which = 0;
childNode !=
iter->node->children[iter->which];
iter->which++)
{
assert(iter->which <=
iter->node->count);
}
iter->which++;
}
}
if (iter->node &&
iter->which > 0 && iter->which <= iter->node->count)
{
return;
}
}
}
TreeKeyData
GetTreeKeyData(TreeIterator * iter)
{
assert(iter->node &&
iter->which > 0 &&
iter->which <= iter->node->count);
return iter->node->keyData[iter->which-1];
}
int
InsertInTree(Tree * tree, void * key, void * data)
{
TreeKeyData keyData = {key, data};
TreeIterator iter;
_SetIteratorToRoot(tree, &iter);
if (_Find(&iter, key))
{
return 0;
}
_InsertAt(&iter, keyData);
tree->size++;
return 1;
}
int
RemoveFromTreeByKey(Tree * tree, void * key)
{
TreeIterator iter;
_SetIteratorToRoot(tree, &iter);
if (_Find(&iter, key))
{
_DeleteAt(&iter);
tree->size--;
return 1;
}
return 0;
}
void
RemoveFromTreeByIterator(Tree * tree, TreeIterator * iter)
{
_DeleteAt(iter);
tree->size--;
}
int
FindInTree(Tree * tree, void * key, TreeIterator * iter)
{
TreeIterator i;
if (iter == NULL)
{
iter = &i;
}
_SetIteratorToRoot(tree, iter);
if (_Find(iter, key))
{
return 1;
}
return 0;
}

62
src/tree.h Normal file
View File

@ -0,0 +1,62 @@
/* the Music Player Daemon (MPD)
* (c)2006 by Warren Dukes (warren.dukes@gmail.com)
* This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef TREE_H
#define TREE_H
typedef struct _Tree Tree;
typedef struct _TreeNode TreeNode;
typedef struct _TreeIterator TreeIterator;
typedef struct _TreeKeyData TreeKeyData;
struct _TreeIterator
{
Tree * tree;
TreeNode * node;
int which;
};
struct _TreeKeyData
{
void * key;
void * data;
};
typedef int (*TreeCompareKeyFunction)(const void * key1, const void * key2);
typedef void (*TreeFreeFunction)(void * data);
Tree * MakeTree(TreeCompareKeyFunction compareFunc,
TreeFreeFunction freeKey,
TreeFreeFunction freeData);
void FreeTree(Tree * tree);
int GetTreeSize(Tree * tree);
void SetTreeIteratorToBegin(Tree * tree, TreeIterator * iter);
int IsTreeIteratorAtEnd(const TreeIterator * iter);
void IncrementTreeIterator(TreeIterator * iter);
TreeKeyData GetTreeKeyData(TreeIterator * iter);
int InsertInTree(Tree * tree, void * key, void * data);
int RemoveFromTreeByKey(Tree * tree, void * key);
void RemoveFromTreeByIterator(Tree * tree, TreeIterator * iter);
int FindInTree(Tree * tree, void * key, TreeIterator * iter /* can be NULL */);
#endif