📄 su_taglist.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 * *//**@SU_TAG * * @CFILE su_taglist.c * * Implementation of tag items and lists. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Tue Feb 20 20:03:38 2001 ppessi */#include "config.h"#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <stdio.h>#include <limits.h>#if defined(va_copy)/* Xyzzy */#elif defined(__va_copy)#define va_copy(dst, src) __va_copy((dst), (src))#else#define va_copy(dst, src) (memcpy(&(dst), &(src), sizeof (va_list)))#endif#include <assert.h>#include <sofia-sip/su_config.h>#include <sofia-sip/su_tag.h>#include <sofia-sip/su_tag_class.h>#include <sofia-sip/su_tag_inline.h>#include <sofia-sip/su_tagarg.h>#ifndef HAVE_STRTOULLunsigned longlong strtoull(const char *, char **, int);#endif/**@defgroup su_tag Tag Item Lists * * Object-oriented tag routines for Sofia utility library. * * The <sofia-sip/su_tag.h> defines a interface to object-oriented tag list routines. * A tag list is a linear list (array) of tag items, tagi_t structures, * terminated by a TAG_END() item. Each tag item has a label, tag, (@c * t_tag) and a value (@c t_value). The tag is a pointer (tag_type_t) to a * structure defining how the value should be interpreted, in other words, * the name and the type of the value. The value or pointer to the actual * value is stored as opaque data (tag_value_t). The tag item structure is * defined as follows: * * @code * typedef struct { * tag_type_t t_tag; * tag_value_t t_value; * } tagi_t; * @endcode * * The tag lists are central concept in the Sofia APIs. The tags lists can * be used to a list of named arguments to a @ref tagarg "@em tagarg" * function, to store variable amount of data in a memory area, and pass * data between processes and threads. * * The tagged argument lists can be used like named arguments in * higher-level languages. The argument list consists of tag-value pairs; * tags specify the name and type of the value. All the tag items are not * necessarily interpreted by the called function, but it can pass the list * to other functions. This feature is used also by the Sofia APIs, the * lower-layer settings and options are frequently passed through the * upper-layer API in the tag lists. * * The tagged argument lists are constructed using special macros that * expand to two function arguments, tag and value. Each tag item macro * checks its arguments type so the tagged argument lists are typesafe if * the list is correctly constructed. * * Each function documents the tags it accepts and also the tags it may pass * to the lower layers (at least in theory). * * @par Special Tags * * There are a new special tags that are used to control and modify the tag * list processing itself. These special tags are as follows: * - TAG_NULL() or TAG_END() - indicates the end of tag list * - TAG_SKIP() - indicates an empty (overwritten) tag item * - TAG_NEXT() - contains a pointer to the next tag list. * * The tag type structures are declared as tag_typedef_t. They can be * defined by the macros found in <sofia-sip/su_tag_class.h>. See nta_tag.c or * su_tag_test.c for an example. * *//**@class tag_class_s sofia-sip/su_tag_class.h <sofia-sip/su_tag_class.h> * * @brief Virtual function table for @ref su_tag "tags". * * The struct tag_class_s contains virtual function table for tags, * specifying non-default behaviour of different tags. It provides functions * for copying, matching, printing and converting the tagged values. */#ifdef longlongtypedef longlong unsigned llu;#elsetypedef long unsigned llu;#endif/** Print a tag. */int t_snprintf(tagi_t const *t, char b[], size_t size){ tag_type_t tt = TAG_TYPE_OF(t); int n, m; n = snprintf(b, size, "%s::%s: ", tt->tt_ns ? tt->tt_ns : "", tt->tt_name ? tt->tt_name : "null"); if (n < 0) return n; if ((size_t)n > size) size = n; if (tt->tt_snprintf) m = tt->tt_snprintf(t, b + n, size - n); else m = snprintf(b + n, size - n, "%llx", (llu)t->t_value); if (m < 0) return m; if (m == 0 && 0 < n && (size_t)n < size) b[--n] = '\0'; return n + m;}/** Get next tag item from list. */tagi_t *tl_next(tagi_t const *t){ tag_type_t tt; t = t_next(t); for (tt = TAG_TYPE_OF(t); t && tt->tt_next; tt = TAG_TYPE_OF(t)) { t = tt->tt_next(t); } return (tagi_t *)t;}/**Move a tag list. * * The function tl_tmove() moves the tag list arguments to @a dst. The @a * dst must have big enough for all arguments. * * @param dst pointer to the destination buffer * @param size sizeof @a dst * @param t_tag,t_value,... tag list * * @return * The function tl_tmove() returns number of tag list items initialized. */size_t tl_tmove(tagi_t *dst, size_t size, tag_type_t t_tag, tag_value_t t_value, ...){ size_t n = 0, N = size / sizeof(tagi_t); tagi_t tagi[1]; va_list ap; va_start(ap, t_value); tagi->t_tag = t_tag, tagi->t_value = t_value; for (;;) { assert((size_t)((char *)&dst[n] - (char *)dst) < size); if (n < N) dst[n] = *tagi; n++; if (t_end(tagi)) break; tagi->t_tag = va_arg(ap, tag_type_t); tagi->t_value = va_arg(ap, tag_value_t); } va_end(ap); return n;}/**Move a tag list. * * The function tl_move() copies the tag list @a src to the buffer @a * dst. The size of the @a dst list must be at least @c tl_len(src) bytes. * * @param dst pointer to the destination buffer * @param src tag list to be moved * * @return * The function tl_move() returns a pointer to the @a dst list after last * moved element. */tagi_t *tl_move(tagi_t *dst, tagi_t const src[]){ do { dst = t_move(dst, src); } while ((src = t_next(src))); return dst;}/** Calculate effective length of a tag list as bytes. */size_t tl_len(tagi_t const lst[]){ size_t len = 0; do { len += t_len(lst); } while ((lst = t_next(lst))); return len;}/** Calculate the size of extra memory areas associated with tag list. */size_t tl_xtra(tagi_t const lst[], size_t offset){ size_t xtra = offset; for (; lst; lst = t_next(lst)) xtra += t_xtra(lst, xtra); return xtra - offset;}/** Duplicate a tag list. * * Deep copy the tag list @a src to the buffer @a dst. Memory areas * associated with @a src are copied to buffer at @a **bb. * * This is a rather low-level function. See tl_adup() for a more convenient * functionality. * * The size of the @a dst buffer must be at least @c tl_len(src) bytes. The * size of buffer @a **bb must be at least @c tl_dup_xtra(src) bytes. * * @param[out] dst pointer to the destination buffer * @param[in] src tag list to be duplicated * @param[in,out] bb pointer to pointer to buffer * * @return * A pointer to the @a dst list after last * duplicated taglist element. * * The pointer at @a *bb is updated to the byte after last duplicated memory * area. */tagi_t *tl_dup(tagi_t dst[], tagi_t const src[], void **bb){ do { dst = t_dup(dst, src, bb); } while ((src = t_next(src))); return dst;} /** Free a tag list. * * The function tl_free() frees resources associated with a tag list. * In other words, it calls t_free on each tag item on the list. * */void tl_free(tagi_t list[]){ while (list) list = t_free(list);}/** Allocate and duplicate a tag list using memory home. */tagi_t *tl_adup(su_home_t *home, tagi_t const lst[]){ size_t len = tl_len(lst); size_t xtra = tl_xtra(lst, 0); void *b = su_alloc(home, len + xtra); tagi_t *d, *newlst = b; void *end = (char *)b + len + xtra; tagi_t *tend = (tagi_t*)((char *)b + len); b = (char *)b + len; d = tl_dup(newlst, lst, &b); assert(b == end); assert(tend == d); (void)end; (void)tend; return newlst;}/** Allocate and duplicate tagged arguments as a tag list using memory home. */tagi_t *tl_tlist(su_home_t *home, tag_type_t tag, tag_value_t value, ...){ tagi_t *tl; ta_list ta; ta_start(ta, tag, value); tl = tl_adup(home, ta_args(ta)); ta_end(ta); return tl;}/** Find first tag item with type @a tt from list. */tagi_t *tl_find(tagi_t const lst[], tag_type_t tt){ return (tagi_t *)t_find(tt, lst);}/** Find last tag item with type @a tt from list. */tagi_t *tl_find_last(tagi_t const lst[], tag_type_t tt){ tagi_t const *last, *next; for (next = last = t_find(tt, lst); next; next = t_find(tt, t_next(last))) last = next; return (tagi_t *)last;}su_inlineint t_ref_set(tag_type_t tt, void *ref, tagi_t const value[]){ if (value == NULL) return 0; if (tt->tt_class->tc_ref_set) return tt->tt_class->tc_ref_set(tt, ref, value); *(tag_value_t *)ref = value->t_value; return 1;}static int tl_get(tag_type_t tt, void *p, tagi_t const lst[]){ tagi_t const *t, *latest = NULL; assert(tt); if (tt == NULL || p == NULL) return 0; if (tt->tt_class == ref_tag_class) tt = (tag_type_t)tt->tt_magic; for (t = t_find(tt, lst); t; t = t_find(tt, t_next(t))) latest = t; return t_ref_set(tt, p, latest);}/** Find tags from given list. */int tl_gets(tagi_t const lst[], tag_type_t tag, tag_value_t value, ...){ int n = 0; tagi_t *t; ta_list ta; ta_start(ta, tag, value); for (t = ta_args(ta); t; t = (tagi_t *)t_next(t)) { tag_type_t tt = t->t_tag; if (!tt) continue; if (tt->tt_class == ref_tag_class) { assert(((tag_type_t)tt->tt_magic)->tt_class->tc_ref_set); n += tl_get(tt, (void *)t->t_value, lst); }#if !defined(NDEBUG) else if (tt->tt_class && tt->tt_class->tc_ref_set) { fprintf(stderr, "WARNING: tag %s::%s directly used by tl_gets()\n", tt->tt_ns, tt->tt_name); assert(tt->tt_class == ref_tag_class); }#endif } ta_end(ta); return n;}/** Find tags from given list. * * Copies values of argument tag list into the reference tags in the tag * list @a lst. * * @sa tl_gets() */int tl_tgets(tagi_t lst[], tag_type_t tag, tag_value_t value, ...){ int n = 0; tagi_t *t; ta_list ta; ta_start(ta, tag, value); for (t = lst; t; t = (tagi_t *)t_next(t)) { tag_type_t tt = t->t_tag; if (!tt) continue; if (tt->tt_class == ref_tag_class) { assert(((tag_type_t)tt->tt_magic)->tt_class->tc_ref_set); n += tl_get(tt, (void *)t->t_value, ta_args(ta)); }#if !defined(NDEBUG) else if (tt->tt_class->tc_ref_set) { fprintf(stderr, "WARNING: tag %s::%s used in tl_tgets(lst)\n", tt->tt_ns, tt->tt_name); assert(tt->tt_class == ref_tag_class); }#endif } ta_end(ta); return n;}/** Filter an element in tag list */tagi_t *t_filter(tagi_t *dst, tagi_t const filter[], tagi_t const *src, void **bb){ tag_type_t tt = TAG_TYPE_OF(src); tagi_t const *f; if (dst) { for (f = filter; f; f = t_next(f)) { if (TAG_TYPE_OF(f)->tt_filter) dst = TAG_TYPE_OF(f)->tt_filter(dst, f, src, bb); else if (f->t_tag == tt) dst = t_dup(dst, src, bb); } } else { size_t d = 0; for (f = filter; f; f = t_next(f)) { if (TAG_TYPE_OF(f)->tt_filter) d += (size_t)TAG_TYPE_OF(f)->tt_filter(NULL, f, src, bb); else if (tt == f->t_tag) { d += t_len(src); *bb = (char *)*bb + t_xtra(src, (size_t)*bb); } } dst = (tagi_t *)d; } return dst;}/** Make filtered copy of a tag list @a src with @a filter to @a dst. * * Each tag in @a src is checked against tags in list @a filter. If the tag * is in the @a filter list, or there is a special filter tag in the list * which matches with the tag in @a src, the tag is duplicated to @a dst using * memory buffer in @a b. * * When @a dst is NULL, this function calculates the size of the filtered list. * * @sa tl_afilter(), tl_tfilter(), tl_filtered_tlist(), * TAG_FILTER(), TAG_ANY(), #ns_tag_class */tagi_t *tl_filter(tagi_t dst[], tagi_t const filter[], tagi_t const src[], void **b){ tagi_t const *s; tagi_t *d; if (dst) { for (s = src, d = dst; s; s = t_next(s)) d = t_filter(d, filter, s, b); } else { size_t rv = 0; for (s = src, d = dst; s; s = t_next(s)) { d = t_filter(NULL, filter, s, b); rv += (char *)d - (char *)NULL; } d = (tagi_t *)rv; } return d;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -