📄 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>#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>/**@defgroup su_tag Tag Item Lists * * Object-oriented tag routines for Sofia utility library. * * The <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 <su_tag_class.h>. See nta_tag.c or * su_tag_test.c for an example. * */#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); size_t n, m; n = snprintf(b, size, "%s::%s: ", tt->tt_ns ? tt->tt_ns : "", tt->tt_name ? tt->tt_name : "null"); if (n > (unsigned)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); assert(m != (size_t)-1); if (m == 0 && 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((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. */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. * * The function tl_dup() deep copies the tag list @a src to the buffer @a * dst. Memory areas associated with @a src are copied to buffer at @a **bb. * * 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 dst pointer to the destination buffer * @param src tag list to be duplicated * @param bb pointer to pointer to buffer * * @return * The function tl_dup() returns a pointer to the @a dst list after last * duplicated taglist element. * * The function tl_dup updates the pointer at @a *bb 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;} /** Allocate and duplicate a tag list. */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); return newlst;}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 a tag from given list. */tagi_t const *tl_find(tagi_t const lst[], tag_type_t tt){ if (tt) return t_find(tt, lst); return NULL;}static 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->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. * * The function tl_tgets() copies values of argument tag list into 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;}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;}/**Filter a tag list. * * The function tl_afilter() will build a tag list containing tags specified * in @a filter and extracted from @a src. It will allocate the memory used by * tag list via the specified memory @a home, which may be also @c NULL. */tagi_t *tl_afilter(su_home_t *home, tagi_t const filter[], tagi_t const src[]){ tagi_t *dst, *d, *t_end = NULL; void *b, *end = NULL; size_t len; /* Calculate length of the result */ t_end = tl_filter(NULL, filter, src, &end); len = ((char *)t_end - (char *)NULL) + ((char *)end - (char*)NULL); if (len == 0) return NULL; /* Allocate the result */ if (!(dst = su_alloc(home, len))) return NULL; /* Build the result */ b = (dst + (t_end - (tagi_t *)NULL)); d = tl_filter(dst, filter, src, (void **)&b); /* Ensure that everything is consistent */ assert(d == dst + (t_end - (tagi_t *)NULL)); assert(b == (char *)dst + len); return dst;}tagi_t *tl_tfilter(su_home_t *home, tagi_t const src[], tag_type_t tag, tag_value_t value, ...){ tagi_t *tl; ta_list ta; ta_start(ta, tag, value); tl = tl_afilter(home, ta_args(ta), src); ta_end(ta); return tl;}/** Create a filtered tag list. */tagi_t *tl_filtered_tlist(su_home_t *home, tagi_t const filter[], tag_type_t tag, tag_value_t value, ...){ tagi_t *tl; ta_list ta; ta_start(ta, tag, value); tl = tl_afilter(home, filter, ta_args(ta)); ta_end(ta); return tl;}/** Remove listed tags from the list @a lst. */int tl_tremove(tagi_t lst[], tag_type_t tag, tag_value_t value, ...){ tagi_t *l, *l_next; int retval = 0; ta_list ta; ta_start(ta, tag, value); for (l = lst; l; l = l_next) { if ((l_next = (tagi_t *)t_next(l))) { if (tl_find(ta_args(ta), l->t_tag)) l->t_tag = tag_skip; else retval++; } } ta_end(ta); return retval;}/** Calculate length of a tag list with a @c va_list. */size_t tl_vlen(va_list ap){ size_t len = 0; tagi_t tagi[2] = {{ NULL }}; do { tagi->t_tag = va_arg(ap, tag_type_t ); tagi->t_value = va_arg(ap, tag_value_t); len += sizeof(tagi_t); } while (!t_end(tagi)); return len;}/** Convert va_list to tag list */tagi_t *tl_vlist(va_list ap){ tagi_t *t, *rv; va_list aq; va_copy(aq, ap); rv = malloc(tl_vlen(aq)); va_end(aq); for (t = rv; t; t++) { t->t_tag = va_arg(ap, tag_type_t); t->t_value = va_arg(ap, tag_value_t); if (t_end(t)) break; } return rv;}tagi_t *tl_vlist2(tag_type_t tag, tag_value_t value, va_list ap){ tagi_t *t, *rv; tagi_t tagi[1]; int size; tagi->t_tag = tag, tagi->t_value = value; if (!t_end(tagi)) { va_list aq; va_copy(aq, ap); size = sizeof(tagi) + tl_vlen(aq); va_end(aq); } else size = sizeof(tagi); t = rv = malloc(size); for (;t;) { *t++ = *tagi; if (t_end(tagi)) break; tagi->t_tag = va_arg(ap, tag_type_t); tagi->t_value = va_arg(ap, tag_value_t); } assert((char *)rv + size == (char *)t); return rv;}/** Make a tag list until TAG_NEXT() or TAG_END() */tagi_t *tl_list(tag_type_t tag, tag_value_t value, ...){ va_list ap; tagi_t *t; va_start(ap, value); t = tl_vlist2(tag, value, ap); va_end(ap); return t;}/** Calculate length of a linear tag list. */size_t tl_vllen(tag_type_t tag, tag_value_t value, va_list ap){ size_t len = sizeof(tagi_t); tagi_t const *next; tagi_t tagi[3]; tagi[0].t_tag = tag; tagi[0].t_value = value; tagi[1].t_tag = tag_any; tagi[1].t_value = 0; for (;;) { next = tl_next(tagi); if (next != tagi + 1) break; if (tagi->t_tag != tag_skip) len += sizeof(tagi_t); tagi->t_tag = va_arg(ap, tag_type_t); tagi->t_value = va_arg(ap, tag_value_t); } for (; next; next = tl_next(next)) len += sizeof(tagi_t); return len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -