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.
kmplayer/src/triestring.cpp

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