You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
517 lines
16 KiB
517 lines
16 KiB
/**
|
|
This file belong to the KMPlayer project, a movie player plugin for Konqueror
|
|
Copyright (C) 2007 Koos Vriezen <koos.vriezen@gmail.com>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
**/
|
|
|
|
#ifdef TEST_TRIE
|
|
# define KMPLAYER_NO_EXPORT
|
|
# define KMPLAYER_EXPORT
|
|
# define KDE_NO_EXPORT
|
|
# define KDE_NO_CDTOR_EXPORT
|
|
#else
|
|
# include <config.h>
|
|
# include "kmplayer_def.h"
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include "triestring.h"
|
|
|
|
namespace KMPlayer {
|
|
|
|
struct KMPLAYER_NO_EXPORT TrieNode {
|
|
TrieNode (const char * s);
|
|
~TrieNode ();
|
|
void unref ();
|
|
void removeChild (TrieNode *);
|
|
void dump (int lvl) {
|
|
TQString indent (TQString ().fill (TQChar ('.'), lvl));
|
|
printf("%s%s len:%4d rc:%4d\n", indent.ascii(), str, length, ref_count);
|
|
}
|
|
char * str;
|
|
unsigned short length;
|
|
unsigned short ref_count;
|
|
TrieNode * parent;
|
|
TrieNode * first_child;
|
|
TrieNode * next_sibling;
|
|
};
|
|
|
|
}
|
|
|
|
using namespace KMPlayer;
|
|
|
|
static TrieNode * root_trie;
|
|
|
|
void dump (TrieNode * node, int lvl) {
|
|
if (!node)
|
|
return;
|
|
node->dump (lvl);
|
|
dump (node->first_child, lvl+2);
|
|
if (node->next_sibling)
|
|
dump (node->next_sibling, lvl);
|
|
}
|
|
|
|
KDE_NO_CDTOR_EXPORT TrieNode::TrieNode (const char * s)
|
|
: str (s ? strdup (s) : 0L),
|
|
length (s ? strlen (s) : 0),
|
|
ref_count (1),
|
|
parent (0L),
|
|
first_child (0L),
|
|
next_sibling (0L) {}
|
|
|
|
KDE_NO_CDTOR_EXPORT TrieNode::~TrieNode () {
|
|
if (str)
|
|
free (str);
|
|
}
|
|
|
|
KDE_NO_EXPORT void TrieNode::unref () {
|
|
if (--ref_count <= 0 && !first_child)
|
|
parent->removeChild (this);
|
|
}
|
|
|
|
KDE_NO_EXPORT void TrieNode::removeChild (TrieNode * node) {
|
|
if (node == first_child) {
|
|
first_child = node->next_sibling;
|
|
} else {
|
|
for (TrieNode *tn = first_child; tn; tn = tn->next_sibling)
|
|
if (tn->next_sibling == node) {
|
|
tn->next_sibling = node->next_sibling;
|
|
break;
|
|
}
|
|
}
|
|
delete node;
|
|
if (!parent)
|
|
return;
|
|
if (!ref_count && !first_child)
|
|
parent->removeChild (this); // can this happen ?
|
|
else if (!ref_count && !first_child->next_sibling) { // merge with child
|
|
char * tmp = first_child->str;
|
|
first_child->length = first_child->length + length;
|
|
first_child->str = (char *) malloc (first_child->length + 1);
|
|
strcpy (first_child->str, str);
|
|
strcat (first_child->str, tmp);
|
|
free (tmp);
|
|
first_child->parent = parent;
|
|
first_child->next_sibling = next_sibling;
|
|
if (parent->first_child == this) {
|
|
parent->first_child = first_child;
|
|
} else {
|
|
for (TrieNode *n = parent->first_child; n; n = n->next_sibling)
|
|
if (n->next_sibling == this) {
|
|
n->next_sibling = first_child;
|
|
break;
|
|
}
|
|
}
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
static char * trieRetrieveString (TrieNode * node, int &len) {
|
|
char *buf;
|
|
if (node->parent) {
|
|
len += node->length;
|
|
buf = trieRetrieveString (node->parent, len);
|
|
strcat (buf, node->str);
|
|
} else {
|
|
buf = (char *) malloc (len + 1);
|
|
*buf = 0;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static int trieStringCompare (TrieNode * node, const char * s, int &len) {
|
|
int cmp = 0;
|
|
if (!node)
|
|
return !!s;
|
|
if (node->parent && node->parent != root_trie)
|
|
cmp = trieStringCompare (node->parent, s, len);
|
|
if (!cmp) {
|
|
#ifdef TEST_TRIE
|
|
printf( "compare %s %s %d\n", node->str, s + len, node->length);
|
|
#endif
|
|
cmp = s ? strncmp (node->str, s + len, node->length) : 1;
|
|
len += node->length;
|
|
}
|
|
return cmp;
|
|
}
|
|
|
|
static int trieStringCompare (TrieNode * n1, TrieNode * n2) {
|
|
// pre n1 && n2 on same depth and not NIL
|
|
int cmp = 0;
|
|
if (n1->parent && n1->parent != root_trie)
|
|
cmp = trieStringCompare (n1->parent, n2->parent);
|
|
if (!cmp && n1 != n2) {
|
|
#ifdef TEST_TRIE
|
|
printf( "compare %s %s", n1->str, n2->str);
|
|
#endif
|
|
if (!n1->str)
|
|
cmp = n2->str ? 1 : 0;
|
|
else if (!n2->str)
|
|
cmp = 1;
|
|
else
|
|
cmp = strcmp (n1->str, n2->str);
|
|
#ifdef TEST_TRIE
|
|
printf( "=> %d\n", cmp);
|
|
#endif
|
|
}
|
|
return cmp;
|
|
}
|
|
|
|
static int trieStringStarts (TrieNode * node, const char * s, int & pos) {
|
|
int cmp = -1; // -1 still matches, 0 no, 1 yes
|
|
if (node->parent && node->parent != root_trie)
|
|
cmp = trieStringStarts (node->parent, s, pos);
|
|
if (cmp == -1) {
|
|
for (int i = 0; i < node->length; i++)
|
|
if (node->str[i] != s[pos + i])
|
|
return !s[pos + i] ? 1 : 0;
|
|
pos += node->length;
|
|
}
|
|
return cmp;
|
|
}
|
|
|
|
static TrieNode * trieInsert (const char * s) {
|
|
if (!root_trie)
|
|
root_trie = new TrieNode (0L);
|
|
//printf("trieInsert %s\n", s);
|
|
//dumpTrie();
|
|
TrieNode * parent = root_trie;
|
|
for (TrieNode * c = parent->first_child; c; c = c->first_child) {
|
|
TrieNode * prev = c;
|
|
for (TrieNode * n = prev; n; n = n->next_sibling) {
|
|
if (n->str[0] == s[0]) { // insert here
|
|
int i = 1;
|
|
for (; i < n->length; i++) {
|
|
if (n->str[i] != s[i]) { // break here
|
|
// insert new node so strings to n remain valid
|
|
bool bigger = n->str[i] < s[i];
|
|
char *tmp = n->str;
|
|
n->str = strdup (tmp + i);
|
|
n->length -= i;
|
|
tmp[i] = 0;
|
|
TrieNode * node = new TrieNode (tmp);
|
|
free (tmp);
|
|
node->parent = parent;
|
|
node->next_sibling = n->next_sibling;
|
|
if (prev != n)
|
|
prev->next_sibling = node;
|
|
else
|
|
parent->first_child = node;
|
|
n->parent = node;
|
|
TrieNode * snode;
|
|
if (!s[i]) {
|
|
node->first_child = n;
|
|
n->next_sibling = 0L;
|
|
snode = node; // s is complete in node
|
|
} else {
|
|
snode = new TrieNode (s+i);
|
|
snode->parent = node;
|
|
if (bigger) { // set n before snode
|
|
node->first_child = n;
|
|
n->next_sibling = snode;
|
|
} else { // set snode before n
|
|
node->first_child = snode;
|
|
snode->next_sibling = n;
|
|
n->next_sibling = 0L;
|
|
}
|
|
node->ref_count--;
|
|
}
|
|
return snode;
|
|
}
|
|
}
|
|
if (s[i]) { // go one level deeper with s+i
|
|
s = s + i;
|
|
c = n;
|
|
prev = 0;
|
|
break;
|
|
} // else n and s are equal
|
|
n->ref_count++;
|
|
return n;
|
|
} else if (n->str[0] > s[0]) { // insert before
|
|
TrieNode * node = new TrieNode (s);
|
|
node->parent = parent;
|
|
node->next_sibling = n;
|
|
if (prev != n)
|
|
prev->next_sibling = node;
|
|
else
|
|
parent->first_child = node;
|
|
return node;
|
|
}
|
|
prev = n;
|
|
}
|
|
if (prev) { // insert after
|
|
TrieNode * node = new TrieNode (s);
|
|
node->parent = parent;
|
|
prev->next_sibling = node;
|
|
return node;
|
|
}
|
|
parent = c;
|
|
}
|
|
// hit an empty first_child, add s as first_child
|
|
TrieNode * node = new TrieNode (s);
|
|
parent->first_child = node;
|
|
node->parent = parent;
|
|
return node;
|
|
}
|
|
|
|
TrieString::TrieString (const TQString & s)
|
|
: node (s.isEmpty () ? 0L : trieInsert (s.utf8 ().data ()))
|
|
{}
|
|
|
|
TrieString::TrieString (const char * utf8)
|
|
: node (!utf8 ? 0L : trieInsert (utf8))
|
|
{}
|
|
|
|
TrieString::TrieString (const TrieString & s) : node (s.node) {
|
|
if (node)
|
|
node->ref_count++;
|
|
}
|
|
|
|
TrieString::~TrieString () {
|
|
if (node)
|
|
node->unref ();
|
|
}
|
|
|
|
bool TrieString::startsWith (const TrieString & s) const {
|
|
for (TrieNode * n = node; n; n = n->parent)
|
|
if (n == s.node)
|
|
return true;
|
|
return s.node ? false : true;
|
|
}
|
|
|
|
bool TrieString::startsWith (const char * str) const {
|
|
if (!node)
|
|
return !str ? true : false;
|
|
if (!str)
|
|
return true;
|
|
int pos = 0;
|
|
return trieStringStarts (node, str, pos) != 0;
|
|
}
|
|
|
|
void TrieString::clear () {
|
|
if (node)
|
|
node->unref ();
|
|
node = 0L;
|
|
}
|
|
|
|
TrieString & TrieString::operator = (const TrieString & s) {
|
|
if (s.node != node) {
|
|
if (s.node)
|
|
s.node->ref_count++;
|
|
if (node)
|
|
node->unref ();
|
|
node = s.node;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
TrieString & TrieString::operator = (const char * utf8) {
|
|
if (node)
|
|
node->unref ();
|
|
node = !utf8 ? 0L : trieInsert (utf8);
|
|
return *this;
|
|
}
|
|
|
|
TQString TrieString::toString () const {
|
|
TQString s;
|
|
if (node) {
|
|
int len = 0;
|
|
char *utf8 = trieRetrieveString (node, len);
|
|
s = TQString::fromUtf8 (utf8);
|
|
free (utf8);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
bool TrieString::operator < (const TrieString & s) const {
|
|
if (node == s.node)
|
|
return false;
|
|
int depth1 = 0, depth2 = 0;
|
|
for (TrieNode * n = node; n; n = n->parent)
|
|
depth1++;
|
|
if (!depth1)
|
|
return s.node ? true : false;
|
|
for (TrieNode * n = s.node; n; n = n->parent)
|
|
depth2++;
|
|
if (!depth2)
|
|
return false;
|
|
TrieNode * n1 = node;
|
|
TrieNode * n2 = s.node;
|
|
while (depth1 > depth2) {
|
|
if (n1 == n2)
|
|
return false;
|
|
n1 = n1->parent;
|
|
depth1--;
|
|
}
|
|
while (depth2 > depth1) {
|
|
if (n1 == n2)
|
|
return true;
|
|
n2 = n2->parent;
|
|
depth2--;
|
|
}
|
|
int cmp = trieStringCompare (n1, n2);
|
|
if (cmp)
|
|
return cmp < 0;
|
|
return depth1 < depth2;
|
|
}
|
|
|
|
bool KMPlayer::operator == (const TrieString & s1, const char * s2) {
|
|
int len = 0;
|
|
return !trieStringCompare (s1.node, s2, len);
|
|
}
|
|
|
|
|
|
TrieString StringPool::attr_id;
|
|
TrieString StringPool::attr_name;
|
|
TrieString StringPool::attr_src;
|
|
TrieString StringPool::attr_url;
|
|
TrieString StringPool::attr_href;
|
|
TrieString StringPool::attr_width;
|
|
TrieString StringPool::attr_height;
|
|
TrieString StringPool::attr_top;
|
|
TrieString StringPool::attr_left;
|
|
TrieString StringPool::attr_bottom;
|
|
TrieString StringPool::attr_right;
|
|
TrieString StringPool::attr_title;
|
|
TrieString StringPool::attr_begin;
|
|
TrieString StringPool::attr_dur;
|
|
TrieString StringPool::attr_end;
|
|
TrieString StringPool::attr_region;
|
|
TrieString StringPool::attr_target;
|
|
TrieString StringPool::attr_type;
|
|
TrieString StringPool::attr_value;
|
|
TrieString StringPool::attr_fill;
|
|
|
|
void StringPool::init() {
|
|
attr_width = "width";
|
|
attr_value = "value";
|
|
attr_url = "url";
|
|
attr_type = "type";
|
|
attr_top = "top";
|
|
attr_title = "title";
|
|
attr_target = "target";
|
|
attr_src = "src";
|
|
attr_right = "right";
|
|
attr_region = "region";
|
|
attr_name = "name";
|
|
attr_left = "left";
|
|
attr_id = "id";
|
|
attr_href = "href";
|
|
attr_height = "height";
|
|
attr_fill = "fill";
|
|
attr_end = "end";
|
|
attr_dur = "dur";
|
|
attr_bottom = "bottom";
|
|
attr_begin = "begin";
|
|
}
|
|
|
|
void StringPool::reset() {
|
|
attr_id.clear ();
|
|
attr_name.clear ();
|
|
attr_src.clear ();
|
|
attr_url.clear ();
|
|
attr_href.clear ();
|
|
attr_width.clear ();
|
|
attr_height.clear ();
|
|
attr_top.clear ();
|
|
attr_left.clear ();
|
|
attr_bottom.clear ();
|
|
attr_right.clear ();
|
|
attr_title.clear ();
|
|
attr_begin.clear ();
|
|
attr_dur.clear ();
|
|
attr_end.clear ();
|
|
attr_region.clear ();
|
|
attr_target.clear ();
|
|
attr_type.clear ();
|
|
attr_value.clear ();
|
|
attr_fill.clear ();
|
|
if (root_trie->first_child) {
|
|
tqWarning ("Trie not empty");
|
|
dumpTrie ();
|
|
} else {
|
|
delete root_trie;
|
|
root_trie = 0;
|
|
}
|
|
}
|
|
|
|
void KMPlayer::dumpTrie () {
|
|
dump (root_trie, 0);
|
|
}
|
|
|
|
#ifdef TEST_TRIE
|
|
// g++ triestring.cpp -o triestring -I$TQTDIR/include -L$TQTDIR/lib -lqt-mt -g -DTEST_TRIE
|
|
|
|
int main (int, char **) {
|
|
StringPool::init();
|
|
{
|
|
TrieString s1;
|
|
TrieString s1_1(TQString ("region"));
|
|
s1 = s1_1;
|
|
TrieString s2 (TQString ("regionName"));
|
|
TrieString s3 (TQString ("regPoint"));
|
|
TrieString s4 (TQString ("regAlign"));
|
|
TrieString s6 (TQString ("freeze"));
|
|
TrieString s7 (TQString ("fit"));
|
|
{
|
|
TrieString s7_1 (TQString ("fit"));
|
|
TrieString s5 (TQString ("fill"));
|
|
dump (root_trie, 0);
|
|
}
|
|
dump (root_trie, 0);
|
|
TrieString s5 (TQString ("fill"));
|
|
TrieString s8 (TQString ("fontPtSize"));
|
|
TrieString s9 (TQString ("fontSize"));
|
|
TrieString s10 (TQString ("fontFace"));
|
|
TrieString s11 (TQString ("fontColor"));
|
|
TrieString s12 (TQString ("hAlign"));
|
|
TrieString s13 (TQString ("region"));
|
|
TrieString s14 (TQString ("ref"));
|
|
TrieString s15 (TQString ("head"));
|
|
dump (root_trie, 0);
|
|
TQString qs1 = s1.toString ();
|
|
TQString qs2 = s2.toString ();
|
|
printf ("%s\n%s\n", qs1.ascii(), qs2.ascii());
|
|
printf("equal %s %s %d\n", qs2.ascii(), "regionName", s2 == "regionName");
|
|
printf("equal %s %s %d\n", qs2.ascii(), "zegionName", s2 == "zegionName");
|
|
printf("equal %s %s %d\n", qs2.ascii(), "reqionName", s2 == "reqionName");
|
|
printf("equal %s %s %d\n", qs2.ascii(), "regiinName", s2 == "regiinName");
|
|
printf("equal %s %s %d\n", qs2.ascii(), "regionNeme", s2 == "regionNeme");
|
|
printf("%s < %s %d\n", qs2.ascii(), "regionName", s2 < TrieString("regionName"));
|
|
printf("%s < %s %d\n", qs2.ascii(), "zegion", s2 < TrieString("zegion"));
|
|
printf("%s < %s %d\n", qs2.ascii(), "req", s2 < TrieString("req"));
|
|
printf("%s < %s %d\n", qs2.ascii(), "regiinName", s2 < TrieString("regiinName"));
|
|
printf("%s < %s %d\n", qs2.ascii(), "regionNeme", s2 < TrieString("regionNeme"));
|
|
printf("%s startsWith %s %d\n", s1.toString().ascii(), "region", s1.startsWith ("region"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "region", s2.startsWith ("region"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "regi", s2.startsWith ("regi"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "regian", s2.startsWith ("regian"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "regio", s2.startsWith ("regio"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "zegio", s2.startsWith ("zegio"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "r", s2.startsWith ("r"));
|
|
printf("%s startsWith %s %d\n", qs2.ascii(), "q", s2.startsWith ("q"));
|
|
TrieString fnt ("font");
|
|
printf("%s startsWith %s %d\n", s8.toString().ascii(), fnt.toString().ascii(), s8.startsWith(fnt));
|
|
printf("%s startsWith %s %d\n", s8.toString().ascii(), s14.toString().ascii(), s8.startsWith(s14));
|
|
}
|
|
dump (root_trie, 0);
|
|
StringPool::reset();
|
|
return 0;
|
|
}
|
|
#endif
|