diff options
Diffstat (limited to 'src/triestring.cpp')
-rw-r--r-- | src/triestring.cpp | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/src/triestring.cpp b/src/triestring.cpp new file mode 100644 index 0000000..bf41506 --- /dev/null +++ b/src/triestring.cpp @@ -0,0 +1,516 @@ +/** + 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) { + QString indent (QString ().fill (QChar ('.'), 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 QString & 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; +} + +QString TrieString::toString () const { + QString s; + if (node) { + int len = 0; + char *utf8 = trieRetrieveString (node, len); + s = QString::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) { + qWarning ("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$QTDIR/include -L$QTDIR/lib -lqt-mt -g -DTEST_TRIE + +int main (int, char **) { + StringPool::init(); + { + TrieString s1; + TrieString s1_1(QString ("region")); + s1 = s1_1; + TrieString s2 (QString ("regionName")); + TrieString s3 (QString ("regPoint")); + TrieString s4 (QString ("regAlign")); + TrieString s6 (QString ("freeze")); + TrieString s7 (QString ("fit")); + { + TrieString s7_1 (QString ("fit")); + TrieString s5 (QString ("fill")); + dump (root_trie, 0); + } + dump (root_trie, 0); + TrieString s5 (QString ("fill")); + TrieString s8 (QString ("fontPtSize")); + TrieString s9 (QString ("fontSize")); + TrieString s10 (QString ("fontFace")); + TrieString s11 (QString ("fontColor")); + TrieString s12 (QString ("hAlign")); + TrieString s13 (QString ("region")); + TrieString s14 (QString ("ref")); + TrieString s15 (QString ("head")); + dump (root_trie, 0); + QString qs1 = s1.toString (); + QString 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 |