From: E.Smith <31170571+azlm8t@users.noreply.github.com> Date: Wed, 20 Sep 2017 00:49:47 +0000 (+0100) Subject: xmltv: Add sorted string_list type. (#4441). X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bb823387b266b626fc17ab60e88384c0c3b20dac;p=thirdparty%2Ftvheadend.git xmltv: Add sorted string_list type. (#4441). It's useful to be able to keep lists of sorted strings. Although htsmsg allows us to keep lists, it is not designed for easy sorted lists. So, build a simple type on top of the existing RB trees. --- diff --git a/Makefile b/Makefile index a46a64d62..a7969c3e2 100644 --- a/Makefile +++ b/Makefile @@ -254,6 +254,7 @@ SRCS-1 = \ src/profile.c \ src/bouquet.c \ src/lock.c \ + src/string_list.c \ src/wizard.c \ src/memoryinfo.c SRCS = $(SRCS-1) diff --git a/src/string_list.c b/src/string_list.c new file mode 100644 index 000000000..09d3ff441 --- /dev/null +++ b/src/string_list.c @@ -0,0 +1,174 @@ +/* + * Sorted String List Functions + * Copyright (C) 2017 Tvheadend Foundation CIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "string_list.h" + +#include +#include "htsmsg.h" + +/// Sorted string list helper functions. +void +string_list_init(string_list_t *l) +{ + RB_INIT(l); +} + +string_list_t * +string_list_create(void) +{ + string_list_t *ret = calloc(1, sizeof(string_list_t)); + string_list_init(ret); + return ret; +} + + +void +string_list_destroy(string_list_t *l) +{ + if (!l) return; + + string_list_item_t *item; + while ((item = RB_FIRST(l))) { + RB_REMOVE(l, item, h_link); + free(item->id); + } +} + +static inline int +string_list_item_cmp(const void *a, const void *b) +{ + return strcmp(((const string_list_item_t*)a)->id, ((const string_list_item_t*)b)->id); +} + +void +string_list_insert(string_list_t *l, const char *id) +{ + if (!id) return; + + string_list_item_t *item = calloc(1, sizeof(string_list_item_t)); + item->id = strdup(id); + if (RB_INSERT_SORTED(l, item, h_link, string_list_item_cmp)) { + /* Duplicate, so not inserted. */ + free(item->id); + free(item); + } +} + +htsmsg_t * +string_list_to_htsmsg(const string_list_t *l) +{ + htsmsg_t *ret = NULL; + string_list_item_t *item; + RB_FOREACH(item, l, h_link) { + if (!ret) ret = htsmsg_create_list(); + const char *id = item->id; + htsmsg_add_str(ret, NULL, id); + } + return ret; +} + +string_list_t * +htsmsg_to_string_list(const htsmsg_t *m) +{ + string_list_t *ret = NULL; + htsmsg_field_t *f; + HTSMSG_FOREACH(f, m) { + if (f->hmf_type == HMF_STR) { + const char *str = f->hmf_str; + if (str && *str) { + if (!ret) ret = string_list_create(); + + string_list_insert(ret, str); + } + } + } + return ret; +} + +void +string_list_serialize(const string_list_t *l, htsmsg_t *m, const char *f) +{ + if (!l) return; + + htsmsg_t *msg = string_list_to_htsmsg(l); + if (msg) + htsmsg_add_msg(m, f, msg); +} + +string_list_t * +string_list_deserialize(const htsmsg_t *m, const char *n) +{ + htsmsg_t *sub = htsmsg_get_list(m, n); + if (!sub) return NULL; + return htsmsg_to_string_list(sub); +} + +char * +string_list_2_csv(const string_list_t *l, char delim, int human) +{ + if (!l) return NULL; + + htsmsg_t *msg = string_list_to_htsmsg(l); + if (!msg) return NULL; + + char *ret = htsmsg_list_2_csv(msg, delim, human); + htsmsg_destroy(msg); + return ret; +} + +int +string_list_cmp(const string_list_t *m1, const string_list_t *m2) +{ + /* Algorithm based on htsmsg_cmp */ + if (m1 == NULL && m2 == NULL) + return 0; + if (m1 == NULL || m2 == NULL) + return 1; + + string_list_item_t *i1; + string_list_item_t *i2 = RB_FIRST(m2); + RB_FOREACH(i1, m1, h_link) { + if (i2 == NULL) + return 1; + const int cmp = strcmp(i1->id, i2->id); + /* Not equal? */ + if (cmp) + return cmp; + + i2 = RB_NEXT(i2, h_link); + } + + if (i2) + return 1; + + return 0; +} + +string_list_t * +string_list_copy(const string_list_t *src) +{ + if (!src) return NULL; + string_list_t *ret = string_list_create(); + string_list_item_t *item; + RB_FOREACH(item, src, h_link) { + const char *id = item->id; + string_list_insert(ret, id); + } + + return ret; +} diff --git a/src/string_list.h b/src/string_list.h new file mode 100644 index 000000000..8657133ad --- /dev/null +++ b/src/string_list.h @@ -0,0 +1,75 @@ +/* + * Sorted String List Functions + * Copyright (C) 2017 Tvheadend Foundation CIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef STRING_LIST_H +#define STRING_LIST_H + +#include "redblack.h" + +/* + * External forward decls + */ +struct htsmsg; + +/// Simple _sorted_ string list type and helper functions. +/// The htsmsg implements lists and maps but they are unsorted. +/// This implements a simple api for keeping track of sorted +/// strings. Only one copy of the string is kept in the list +/// (duplicates are not stored). The list owns the memory +/// for the strings (takes a copy). +/// +/// Example: +/// string_list_create_t *l = string_list_create(); +/// string_list_insert(l, "str1"); +/// string_list_insert(l, "str2"); +/// string_list_destroy(l); + +typedef struct string_list_item +{ + char* id; + RB_ENTRY(string_list_item) h_link; +} string_list_item_t; +typedef RB_HEAD(string_list, string_list_item) string_list_t; + +/// Initialize the memory used by the list. +void string_list_init(string_list_t *l); +/// Dynamically allocate and initialize a list. +string_list_t *string_list_create(void); + +/// Free up the memory used by the list. +void string_list_destroy(string_list_t *l); + +/// Insert a copy of id in to the sorted string list. +void string_list_insert(string_list_t *l, const char *id); + +/// Conversion function from sorted string list to an htsmsg. +/// @return NULL if empty. +struct htsmsg *string_list_to_htsmsg(const string_list_t *l) + __attribute__((warn_unused_result)); +string_list_t * htsmsg_to_string_list(const struct htsmsg *m) + __attribute__((warn_unused_result)); +void string_list_serialize(const string_list_t *l, struct htsmsg *m, const char *f); +string_list_t *string_list_deserialize(const struct htsmsg *m, const char *f) + __attribute__((warn_unused_result)); +char *string_list_2_csv(const string_list_t *l, char delim, int human) + __attribute__((warn_unused_result)); +int string_list_cmp(const string_list_t *m1, const string_list_t *m2) + __attribute__((warn_unused_result)); +/// Deep clone (shares no pointers, so have to string_list_destroy both. +string_list_t *string_list_copy(const string_list_t *src) + __attribute__((warn_unused_result)); +#endif