📄 urlmap.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.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.1 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 St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@internal * @file urlmap.c * @brief Mapping with hierarchical URLs. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Wed Mar 10 17:05:23 2004 ppessi * */#include "config.h"#include <stddef.h>#include <string.h>#include <assert.h>#include <errno.h>#include <stdlib.h>#include "urlmap.h"/** Create map entry. */UrlMap *url_map_new(su_home_t *home, url_string_t const *url, unsigned size){ UrlMap *um; int xtra; url_t *u; xtra = url_xtra(url->us_url); um = su_zalloc(home, size + xtra); if (!um || url_dup((char *)um + size, xtra, um->um_url, url->us_url) < 0) { su_free(home, um); return NULL; } u = um->um_url; if (!u->url_path) u->url_path = ""; return um;}staticvoid left_rotate(UrlMap **top, UrlMap *x){ /* x c * / \ / \ * Convert a c into x d * / \ / \ * b d a b */ UrlMap *c = x->um_right, *dad = x->um_dad; assert(c); if ((x->um_right = c->um_left)) x->um_right->um_dad = x; if (!(c->um_dad = dad)) *top = c; else if (dad->um_left == x) dad->um_left = c; else assert(dad->um_right == x), dad->um_right = c; c->um_left = x; x->um_dad = c;}staticvoid right_rotate(UrlMap **top, UrlMap *x){ /* x c * / \ / \ * Convert c f into a x * / \ / \ * a d d f */ UrlMap *c = x->um_left, *dad = x->um_dad; assert(c); if ((x->um_left = c->um_right)) x->um_left->um_dad = x; if (!(c->um_dad = dad)) *top = c; else if (dad->um_right == x) dad->um_right = c; else assert(dad->um_left == x), dad->um_left = c; c->um_right = x; x->um_dad = c;}/** Balance Red-Black binary tree after inserting node @a um. * * The function red_black_balance_insert() balances a red-black tree after * insertion. */staticvoid red_black_balance_insert(UrlMap **top, UrlMap *um){ UrlMap *dad, *uncle, *granddad; um->um_black = 0; for (dad = um->um_dad; um != *top && !dad->um_black; dad = um->um_dad) { /* Repeat until we are parent or we have a black dad */ granddad = dad->um_dad; assert(granddad); if (dad == granddad->um_left) { uncle = granddad->um_right; if (uncle && !uncle->um_black) { dad->um_black = 1; uncle->um_black = 1; granddad->um_black = 0; um = granddad; } else { if (um == dad->um_right) { left_rotate(top, um = dad); dad = um->um_dad; assert(dad); granddad = dad->um_dad; assert(granddad); } dad->um_black = 1; granddad->um_black = 0; right_rotate(top, granddad); } } else { assert(dad == granddad->um_right); uncle = granddad->um_left; if (uncle && !uncle->um_black) { dad->um_black = 1; uncle->um_black = 1; granddad->um_black = 0; um = granddad; } else { if (um == dad->um_left) { right_rotate(top, um = dad); dad = um->um_dad; assert(dad); granddad = dad->um_dad; assert(granddad); } dad->um_black = 1; granddad->um_black = 0; left_rotate(top, granddad); } } } assert(*top); (*top)->um_black = 1;}static void red_black_balance_delete(UrlMap **top, UrlMap *um){ UrlMap *dad, *brother; for (dad = um->um_dad; um != *top && !dad->um_black; dad = um->um_dad) { if (um == dad->um_left) { brother = dad->um_right; if (!brother) { um = dad; continue; } assert(brother->um_black); if ((!brother->um_left || brother->um_left->um_black) && (!brother->um_right || brother->um_right->um_black)) { brother->um_black = 0; um = dad; continue; } if (!brother->um_right || brother->um_right->um_black) { brother->um_left->um_black = 1; brother->um_black = 0; right_rotate(top, brother); brother = dad->um_right; } brother->um_black = dad->um_black; dad->um_black = 1; if (brother->um_right) brother->um_right->um_black = 1; left_rotate(top, dad); um = *top; break; } else { assert(um == dad->um_right); brother = dad->um_left; if (!brother) { um = dad; continue; } assert(brother->um_black); if ((!brother->um_left || brother->um_left->um_black) && (!brother->um_right || brother->um_right->um_black)) { brother->um_black = 0; um = dad; continue; } if (!brother->um_left || brother->um_left->um_black) { brother->um_right->um_black = 1; brother->um_black = 0; left_rotate(top, brother); brother = dad->um_left; } brother->um_black = um->um_dad->um_black; um->um_dad->um_black = 1; if (brother->um_left) brother->um_left->um_black = 1; right_rotate(top, dad); um = *top; break; } } um->um_black = 1;}/** Compare paths. */su_inlineint urlmap_pathcmp(url_t const *a, url_t const *b, int *return_hostmatch){ int retval; retval = url_cmp(a, b); *return_hostmatch = !retval; if (retval) return retval; else return strcmp(a->url_path, b->url_path);}/** Insert URL into map. */int url_map_insert(UrlMap ** const tree, UrlMap * const um, UrlMap **return_old){ UrlMap *old, *dad, **tslot; url_t *u; int cmp, hostmatch; if (tree == NULL || um == NULL || um->um_inserted) return (errno = EINVAL), -1; u = um->um_url; /* Insert into red-black binary tree */ tslot = tree; for (old = *tree, dad = NULL; old; old = *tslot) { cmp = urlmap_pathcmp(u, old->um_url, &hostmatch); if (cmp < 0) dad = old, tslot = &old->um_left; else if (cmp > 0) dad = old, tslot = &old->um_right; else break; } assert(old != um); if (old) { if ((um->um_left = old->um_left)) um->um_left->um_dad = um; if ((um->um_right = old->um_right)) um->um_right->um_dad = um; if (!(um->um_dad = old->um_dad)) *tree = um; else if (um->um_dad->um_left == old) um->um_dad->um_left = um; else assert(um->um_dad->um_right == old), um->um_dad->um_right = um; um->um_black = old->um_black; old->um_left = NULL; old->um_right = NULL; old->um_dad = NULL; old->um_inserted = 0; } else { *tslot = um; um->um_dad = dad; if (tree != tslot) { red_black_balance_insert(tree, um); } else { um->um_black = 1; } } um->um_inserted = 1; if (return_old) *return_old = old; return 0;}/** Find a URL */UrlMap *url_map_find(UrlMap *root, url_string_t const *url, int relative){ UrlMap *um, *maybe = NULL; url_t u[1]; void *tbf = NULL; char *end; int cmp, hostmatch; if (root == NULL || url == NULL) return NULL; url = tbf = url_hdup(NULL, (url_t *)url); if (!url) return NULL; *u = *url->us_url; if (!u->url_path) u->url_path = ""; for (um = root; um; um = cmp < 0 ? um->um_left : um->um_right) { cmp = urlmap_pathcmp(u, um->um_url, &hostmatch); if (cmp == 0) break; if (hostmatch && !maybe) maybe = um; } while (!um && relative && u->url_path[0]) { end = strrchr(u->url_path, '/'); end = end ? end + 1 : (char *)u->url_path; if (*end) *end = '\0'; for (um = maybe; um; um = cmp < 0 ? um->um_left : um->um_right) { if ((cmp = urlmap_pathcmp(u, um->um_url, &hostmatch)) == 0) break; } } su_free(NULL, tbf); return um;}/** Remove URL. */void url_map_remove(UrlMap **top, UrlMap *um){ UrlMap *kid, *dad; int need_to_balance; if (top == NULL || um == NULL || !um->um_inserted) return; /* Make sure that node is in tree */ for (dad = um; dad; dad = dad->um_dad) if (dad == *top) break; assert(dad); if (!dad) return; /* Find a successor node with a free branch */ if (!um->um_left || !um->um_right) dad = um; else for (dad = um->um_right; dad->um_left; dad = dad->um_left) ; /* Dad has a free branch => kid is dad's only child */ kid = dad->um_left ? dad->um_left : dad->um_right; /* Remove dad from tree */ if (!(dad->um_dad)) *top = kid; else if (dad->um_dad->um_left == dad) dad->um_dad->um_left = kid; else assert(dad->um_dad->um_right == dad), dad->um_dad->um_right = kid; if (kid) kid->um_dad = dad->um_dad; need_to_balance = kid && dad->um_black; /* Put dad in place of um */ if (um != dad) { if (!(dad->um_dad = um->um_dad)) *top = dad; else if (dad->um_dad->um_left == um) dad->um_dad->um_left = dad; else assert(dad->um_dad->um_right == um), dad->um_dad->um_right = dad; dad->um_black = um->um_black; if ((dad->um_left = um->um_left)) dad->um_left->um_dad = dad; if ((dad->um_right = um->um_right)) dad->um_right->um_dad = dad; } um->um_left = NULL; um->um_right = NULL; um->um_dad = NULL; um->um_black = 0; um->um_inserted = 0; if (need_to_balance) red_black_balance_delete(top, kid);}#if TEST_URLMAP/* Functions currently used only by test cases *//** Return inorder successor of node @a um. */UrlMap *url_map_succ(UrlMap *um){ UrlMap *dad; if (um->um_right) { for (um = um->um_right; um->um_left; um = um->um_left) ; return um; } for (dad = um->um_dad; dad && um == dad->um_right; dad = um->um_dad) um = dad; return dad;}/** Return inorder precedessor of node @a um. */UrlMap *url_map_prec(UrlMap *um){ UrlMap *dad; if (um->um_left) { for (um = um->um_left; um->um_right; um = um->um_right) ; return um; } for (dad = um->um_dad; dad && um == dad->um_left; dad = um->um_dad) um = dad; return dad;}/** Return first node in tree @a um. */UrlMap *url_map_first(UrlMap *um){ while (um && um->um_left) um = um->um_left; return um;}/** Return last node in tree @a um. */UrlMap *url_map_last(UrlMap *um){ while (um && um->um_right) um = um->um_right; return um;}/** Return height of the tree */int url_map_height(UrlMap const *tree){ int left, right; if (!tree) return 0; left = tree->um_left ? url_map_height(tree->um_left) : 0; right = tree->um_right ? url_map_height(tree->um_right) : 0; if (left > right) return left + 1; else return right + 1;}/** Check consistency */staticint redblack_check(UrlMap const *n){ UrlMap const *l, *r; if (!n) return 1; l = n->um_left, r = n->um_right; if (n->um_black || ((!l || l->um_black) && (!r || r->um_black))) return (!l || redblack_check(l)) && (!r || redblack_check(r)); else return 0;}/* Testing functions */int tstflags;#define TSTFLAGS tstflags#include <stdio.h>#include <sofia-sip/tstdef.h>char const *name = "test_urlmap";int test_path(void){ su_home_t *home; UrlMap *tree = NULL, *o; UrlMap *um, *um1, *um2, *um3; BEGIN(); home = su_home_clone(NULL, sizeof(*home)); TEST_1(home); um1 = url_map_new(home, (void*)"http://host/aa/", sizeof *um1); TEST_1(um1); um2 = url_map_new(home, (void*)"http://host/aa/bb/", sizeof *um1); TEST_1(um2); um3 = url_map_new(home, (void*)"http://host/aa/bb/cc/", sizeof *um); TEST_1(um3); TEST_1(um1 != um2 && um1 != um3 && um2 != um3); o = (void *)-1; TEST(url_map_insert(&tree, um3, &o), 0); TEST_P(o, NULL); o = (void *)-1; TEST(url_map_insert(&tree, um2, &o), 0); TEST_P(o, NULL); o = (void *)-1; TEST(url_map_insert(&tree, um1, &o), 0); TEST_P(o, NULL); um = url_map_find(tree, (void*)"http://host/aa/bb/cc", 1); TEST_P(um, um2); um = url_map_find(tree, (void*)"http://host/aa/bb/cc/oo", 1); TEST_P(um, um3); um = url_map_find(tree, (void*)"http://host/aa/bb", 1); TEST_P(um, um1); um = url_map_find(tree, (void*)"http://host/aa/bb", 0); TEST_P(um, NULL); um = url_map_find(tree, (void*)"http://host/aa/bb/", 1); TEST_P(um, um2); su_home_check(home); su_home_zap(home);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -