summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-epl-profile-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-epl-profile-parser.c')
-rw-r--r--epan/dissectors/packet-epl-profile-parser.c773
1 files changed, 773 insertions, 0 deletions
diff --git a/epan/dissectors/packet-epl-profile-parser.c b/epan/dissectors/packet-epl-profile-parser.c
new file mode 100644
index 00000000..61a7df87
--- /dev/null
+++ b/epan/dissectors/packet-epl-profile-parser.c
@@ -0,0 +1,773 @@
+/* packet-epl-profile-parser.c
+ * Routines for reading in Ethernet POWERLINK XDD and CANopen EDS profiles
+ * (Ethernet POWERLINK XML Device Description (DS301) Draft Standard v1.2.0)
+ *
+ * Copyright (c) 2017: Karlsruhe Institute of Technology (KIT)
+ * Institute for Anthropomatics and Robotics (IAR)
+ * Intelligent Process Control and Robotics (IPR)
+ * http://rob.ipr.kit.edu/
+ *
+ * - Ahmad Fatoum <ahmad[AT]a3f.at>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "packet-epl.h"
+#include "ws_attributes.h"
+
+#include <epan/ws_printf.h>
+#include <epan/range.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <wsutil/strtoi.h>
+#include <wsutil/str_util.h>
+#include <wsutil/wslog.h>
+#include <epan/wmem_scopes.h>
+
+#if defined HAVE_LIBXML2
+#include <libxml/xmlversion.h>
+
+#if defined LIBXML_XPATH_ENABLED \
+&& defined LIBXML_SAX1_ENABLED \
+&& defined LIBXML_TREE_ENABLED
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define PARSE_XDD 1
+
+typedef int xpath_handler(xmlNodeSetPtr, void*);
+static xpath_handler populate_object_list, populate_datatype_list, populate_profile_name;
+
+static struct xpath_namespace {
+ const xmlChar *prefix, *href;
+} namespaces[] = {
+ { BAD_CAST "x", BAD_CAST "http://www.ethernet-powerlink.org" },
+ { BAD_CAST "xsi", BAD_CAST "http://www.w3.org/2001/XMLSchema-instance" },
+ { NULL, NULL }
+};
+
+static struct xpath {
+ const xmlChar *expr;
+ xpath_handler *handler;
+} xpaths[] = {
+ {
+ BAD_CAST "//x:ISO15745Profile[x:ProfileHeader/x:ProfileIdentification='Powerlink_Communication_Profile']/x:ProfileHeader/x:ProfileName",
+ populate_profile_name
+ },
+ {
+ BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:DataTypeList/x:defType",
+ populate_datatype_list
+ },
+ {
+ BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:ObjectList/x:Object",
+ populate_object_list
+ },
+ { NULL, NULL }
+};
+
+
+#endif /* LIBXML_XPATH_ENABLED && LIBXML_SAX1_ENABLED && LIBXML_TREE_ENABLED */
+
+#endif /* HAVE_LIBXML2 */
+
+struct datatype {
+ guint16 id;
+ const struct epl_datatype *ptr;
+};
+
+static struct typemap_entry {
+ guint16 id;
+ const char *name;
+ struct epl_datatype *type;
+} epl_datatypes[] = {
+ {0x0001, "Boolean", NULL},
+ {0x0002, "Integer8", NULL},
+ {0x0003, "Integer16", NULL},
+ {0x0004, "Integer32", NULL},
+ {0x0005, "Unsigned8", NULL},
+ {0x0006, "Unsigned16", NULL},
+ {0x0007, "Unsigned32", NULL},
+ {0x0008, "Real32", NULL},
+ {0x0009, "Visible_String", NULL},
+ {0x0010, "Integer24", NULL},
+ {0x0011, "Real64", NULL},
+ {0x0012, "Integer40", NULL},
+ {0x0013, "Integer48", NULL},
+ {0x0014, "Integer56", NULL},
+ {0x0015, "Integer64", NULL},
+ {0x000A, "Octet_String", NULL},
+ {0x000B, "Unicode_String", NULL},
+ {0x000C, "Time_of_Day", NULL},
+ {0x000D, "Time_Diff", NULL},
+ {0x000F, "Domain", NULL},
+ {0x0016, "Unsigned24", NULL},
+ {0x0018, "Unsigned40", NULL},
+ {0x0019, "Unsigned48", NULL},
+ {0x001A, "Unsigned56", NULL},
+ {0x001B, "Unsigned64", NULL},
+ {0x0401, "MAC_ADDRESS", NULL},
+ {0x0402, "IP_ADDRESS", NULL},
+ {0x0403, "NETTIME", NULL},
+ {0x0000, NULL, NULL}
+};
+
+static wmem_map_t *eds_typemap;
+
+struct epl_wmem_iarray {
+ GEqualFunc equal;
+ wmem_allocator_t *scope;
+ GArray *arr;
+ guint cb_id;
+ guint8 is_sorted :1;
+};
+
+static epl_wmem_iarray_t *epl_wmem_iarray_new(wmem_allocator_t *allocator, const guint elem_size, GEqualFunc cmp) G_GNUC_MALLOC;
+static void epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data);
+static void epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr);
+
+static gboolean
+epl_ishex(const char *num)
+{
+ if (g_str_has_prefix(num, "0x"))
+ return TRUE;
+
+ for (; g_ascii_isxdigit(*num); num++)
+ ;
+
+ if (g_ascii_tolower(*num) == 'h')
+ return TRUE;
+
+ return FALSE;
+}
+
+static guint16
+epl_g_key_file_get_uint16(GKeyFile *gkf, const gchar *group_name, const gchar *key, GError **error)
+{
+ guint16 ret = 0;
+ const char *endptr;
+ char *val = g_key_file_get_string(gkf, group_name, key, error);
+
+ if (!val)
+ return 0;
+
+ if (epl_ishex(val)) /* We need to support XXh, but no octals (is that right?) */
+ ws_hexstrtou16(val, &endptr, &ret);
+ else
+ ws_strtou16(val, &endptr, &ret);
+
+ g_free(val);
+ return ret;
+}
+
+static void
+sort_subindices(void *key _U_, void *value, void *user_data _U_)
+{
+ epl_wmem_iarray_t *subindices = ((struct object*)value)->subindices;
+ if (subindices)
+ epl_wmem_iarray_sort_and_compact(subindices);
+}
+
+void
+epl_eds_init(void)
+{
+ struct typemap_entry *entry;
+ eds_typemap = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
+ for (entry = epl_datatypes; entry->name; entry++)
+ {
+ const struct epl_datatype *type = epl_type_to_hf(entry->name);
+ wmem_map_insert(eds_typemap, GUINT_TO_POINTER(entry->id), (void*)type);
+ }
+}
+
+struct profile *
+epl_eds_load(struct profile *profile, const char *eds_file)
+{
+ GKeyFile* gkf;
+ GError *err;
+ char **group, **groups;
+ char *val;
+ gsize groups_count;
+
+ gkf = g_key_file_new();
+
+ /* Load EDS document */
+ if (!g_key_file_load_from_file(gkf, eds_file, G_KEY_FILE_NONE, &err)){
+ ws_log(NULL, LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", eds_file);
+ profile = NULL;
+ goto cleanup;
+ }
+
+ profile->path = wmem_strdup(profile->scope, eds_file);
+
+ val = g_key_file_get_string(gkf, "FileInfo", "Description", NULL);
+ /* This leaves a trailing space, but that's ok */
+ profile->name = wmem_strndup(profile->scope, val, strcspn(val, "#"));
+ g_free(val);
+
+ groups = g_key_file_get_groups(gkf, &groups_count);
+ for (group = groups; *group; group++)
+ {
+ char *name;
+ const char *endptr;
+ guint16 idx = 0, datatype;
+ struct object *obj = NULL;
+ struct od_entry tmpobj = OD_ENTRY_INITIALIZER;
+ gboolean is_object = TRUE;
+
+ if (!g_ascii_isxdigit(**group))
+ continue;
+
+ ws_hexstrtou16(*group, &endptr, &idx);
+ if (*endptr == '\0')
+ { /* index */
+ tmpobj.idx = idx;
+ }
+ else if (g_str_has_prefix(endptr, "sub"))
+ { /* subindex */
+ if (!ws_hexstrtou16(endptr + 3, &endptr, &tmpobj.idx)
+ || tmpobj.idx > 0xFF)
+ continue;
+
+ is_object = FALSE;
+ }
+ else continue;
+
+ tmpobj.type_class = epl_g_key_file_get_uint16(gkf, *group, "ObjectType", NULL);
+ if (!tmpobj.type_class)
+ continue;
+
+ datatype = epl_g_key_file_get_uint16(gkf, *group, "DataType", NULL);
+ if (datatype)
+ tmpobj.type = (const struct epl_datatype*)wmem_map_lookup(eds_typemap, GUINT_TO_POINTER(datatype));
+
+ if ((name = g_key_file_get_string(gkf, *group, "ParameterName", NULL)))
+ {
+ gsize count = strcspn(name, "#") + 1;
+ (void) g_strlcpy(
+ tmpobj.name,
+ name,
+ count > sizeof tmpobj.name ? sizeof tmpobj.name : count
+ );
+ g_free(name);
+ }
+
+ obj = epl_profile_object_lookup_or_add(profile, idx);
+
+ if (is_object)
+ { /* Let's add a new object! Exciting! */
+ obj->info = tmpobj;
+ }
+ else
+ { /* Object already there, let's add subindices */
+ struct subobject subobj = SUBOBJECT_INITIALIZER;
+
+ if (!obj->subindices)
+ {
+ obj->subindices = epl_wmem_iarray_new(
+ profile->scope,
+ sizeof (struct subobject),
+ subobject_equal
+ );
+ }
+
+ subobj.info = tmpobj;
+ epl_wmem_iarray_insert(obj->subindices, subobj.info.idx, &subobj.range);
+ }
+ }
+ g_strfreev(groups);
+
+ /* Unlike with XDDs, subindices might interleave with others, so let's sort them now */
+ wmem_map_foreach(profile->objects, sort_subindices, NULL);
+
+ /* We don't read object mappings from EDS files */
+ /* epl_profile_object_mappings_update(profile); */
+
+cleanup:
+ g_key_file_free(gkf);
+ return profile;
+}
+
+#ifdef PARSE_XDD
+
+struct profile *
+epl_xdd_load(struct profile *profile, const char *xml_file)
+{
+ xmlXPathContextPtr xpathCtx = NULL;
+ xmlDoc *doc = NULL;
+ struct xpath_namespace *ns = NULL;
+ struct xpath *xpath = NULL;
+ GHashTable *typemap = NULL;
+
+ /* Load XML document */
+ doc = xmlParseFile(xml_file);
+ if (!doc)
+ {
+ ws_log(NULL, LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", xml_file);
+ profile = NULL;
+ goto cleanup;
+ }
+
+
+ /* Create xpath evaluation context */
+ xpathCtx = xmlXPathNewContext(doc);
+ if(!xpathCtx)
+ {
+ ws_log(NULL, LOG_LEVEL_WARNING, "Error: unable to create new XPath context\n");
+ profile = NULL;
+ goto cleanup;
+ }
+
+ /* Register namespaces from list */
+ for (ns = namespaces; ns->href; ns++)
+ {
+ if(xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href) != 0)
+ {
+ ws_log(NULL, LOG_LEVEL_WARNING, "Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
+ profile = NULL;
+ goto cleanup;
+ }
+ }
+
+ profile->path = wmem_strdup(profile->scope, xml_file);
+
+ /* mapping type ids to &hf_s */
+ profile->data = typemap = (GHashTable*)g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+ /* Evaluate xpath expressions */
+ for (xpath = xpaths; xpath->expr; xpath++)
+ {
+ xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpath->expr, xpathCtx);
+ if (!xpathObj || !xpathObj->nodesetval)
+ {
+ ws_log(NULL, LOG_LEVEL_WARNING, "Error: unable to evaluate xpath expression \"%s\"\n", xpath->expr);
+ xmlXPathFreeObject(xpathObj);
+ profile = NULL;
+ goto cleanup;
+ }
+
+ /* run handler */
+ if (xpath->handler && xpathObj->nodesetval->nodeNr)
+ xpath->handler(xpathObj->nodesetval, profile);
+ xmlXPathFreeObject(xpathObj);
+ }
+
+ /* We create ObjectMappings while reading the XML, this is makes it likely,
+ * that we won't be able to reference a mapped object in the ObjectMapping
+ * as we didn't reach its XML tag yet. Therefore, after reading the XDD
+ * completely, we update mappings in the profile
+ */
+ epl_profile_object_mappings_update(profile);
+
+cleanup:
+ if (typemap)
+ g_hash_table_destroy(typemap);
+
+ if (xpathCtx)
+ xmlXPathFreeContext(xpathCtx);
+ if (doc)
+ xmlFreeDoc(doc);
+
+ return profile;
+}
+
+static int
+populate_profile_name(xmlNodeSetPtr nodes, void *_profile)
+{
+ struct profile *profile = (struct profile*)_profile;
+ if (nodes->nodeNr == 1
+ && nodes->nodeTab[0]->type == XML_ELEMENT_NODE
+ && nodes->nodeTab[0]->children)
+ {
+ profile->name = wmem_strdup(profile->scope, (char*)nodes->nodeTab[0]->children->content);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+populate_datatype_list(xmlNodeSetPtr nodes, void *_profile)
+{
+ xmlNodePtr cur;
+ int i;
+ struct profile *profile = (struct profile*)_profile;
+
+ for(i = 0; i < nodes->nodeNr; ++i)
+ {
+ xmlAttrPtr attr;
+
+ if(!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE)
+ return -1;
+
+ cur = nodes->nodeTab[i];
+
+
+ for(attr = cur->properties; attr; attr = attr->next)
+ {
+ const char *endptr;
+ const char *key = (const char*)attr->name;
+ const char *val = (const char*)attr->children->content;
+
+ if (g_str_equal("dataType", key))
+ {
+ xmlNode *subnode;
+ guint16 idx = 0;
+
+ if (!ws_hexstrtou16(val, &endptr, &idx))
+ continue;
+
+ for (subnode = cur->children; subnode; subnode = subnode->next)
+ {
+ if (subnode->type == XML_ELEMENT_NODE)
+ {
+ struct datatype *type;
+ const struct epl_datatype *ptr = epl_type_to_hf((const char*)subnode->name);
+ if (!ptr)
+ {
+ ws_log(NULL, LOG_LEVEL_INFO, "Skipping unknown type '%s'\n", subnode->name);
+ continue;
+ }
+ type = g_new(struct datatype, 1);
+ type->id = idx;
+ type->ptr = ptr;
+ g_hash_table_insert((GHashTable*)profile->data, GUINT_TO_POINTER(type->id), type);
+ continue;
+ }
+ }
+
+ }
+ }
+ }
+
+ return 0;
+}
+
+static gboolean
+parse_obj_tag(xmlNode *cur, struct od_entry *out, struct profile *profile) {
+ xmlAttrPtr attr;
+ const char *defaultValue = NULL, *actualValue = NULL;
+ const char *endptr;
+
+ for(attr = cur->properties; attr; attr = attr->next)
+ {
+ const char *key = (const char*)attr->name,
+ *val = (const char*)attr->children->content;
+
+ if (g_str_equal("index", key))
+ {
+ if (!ws_hexstrtou16(val, &endptr, &out->idx))
+ return FALSE;
+
+ } else if (g_str_equal("subIndex", key)) {
+ if (!ws_hexstrtou16(val, &endptr, &out->idx))
+ return FALSE;
+
+ } else if (g_str_equal("name", key)) {
+ (void) g_strlcpy(out->name, val, sizeof out->name);
+
+ } else if (g_str_equal("objectType", key)) {
+ out->type_class = 0;
+ ws_hexstrtou16(val, &endptr, &out->type_class);
+
+ } else if (g_str_equal("dataType", key)) {
+ guint16 id;
+ if (ws_hexstrtou16(val, &endptr, &id))
+ {
+ struct datatype *type = (struct datatype*)g_hash_table_lookup((GHashTable*)profile->data, GUINT_TO_POINTER(id));
+ if (type) out->type = type->ptr;
+ }
+
+ } else if (g_str_equal("defaultValue", key)) {
+ defaultValue = val;
+
+ } else if (g_str_equal("actualValue", key)) {
+ actualValue = val;
+ }
+#if 0
+ else if (g_str_equal("PDOmapping", key)) {
+ obj.PDOmapping = get_index(ObjectPDOmapping_tostr, val);
+ assert(obj.PDOmapping >= 0);
+ }
+#endif
+ }
+
+ if (actualValue)
+ out->value = g_ascii_strtoull(actualValue, NULL, 0);
+ else if (defaultValue)
+ out->value = g_ascii_strtoull(defaultValue, NULL, 0);
+ else
+ out->value = 0;
+
+
+ return TRUE;
+}
+
+static int
+populate_object_list(xmlNodeSetPtr nodes, void *_profile)
+{
+ int i;
+ struct profile *profile = (struct profile*)_profile;
+
+ for(i = 0; i < nodes->nodeNr; ++i)
+ {
+ xmlNodePtr cur = nodes->nodeTab[i];
+ struct od_entry tmpobj = OD_ENTRY_INITIALIZER;
+
+ if (!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE)
+ continue;
+
+ parse_obj_tag(cur, &tmpobj, profile);
+
+ if (tmpobj.idx)
+ {
+ struct object *obj = epl_profile_object_add(profile, tmpobj.idx);
+ obj->info = tmpobj;
+
+ if (tmpobj.type_class == OD_ENTRY_ARRAY || tmpobj.type_class == OD_ENTRY_RECORD)
+ {
+ xmlNode *subcur;
+ struct subobject subobj = SUBOBJECT_INITIALIZER;
+
+ obj->subindices = epl_wmem_iarray_new(profile->scope, sizeof (struct subobject), subobject_equal);
+
+ for (subcur = cur->children; subcur; subcur = subcur->next)
+ {
+ if (subcur->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (parse_obj_tag(subcur, &subobj.info, profile))
+ {
+ epl_wmem_iarray_insert(obj->subindices,
+ subobj.info.idx, &subobj.range);
+ }
+ if (subobj.info.value && epl_profile_object_mapping_add(
+ profile, obj->info.idx, (guint8)subobj.info.idx, subobj.info.value))
+ {
+ ws_log(NULL, LOG_LEVEL_INFO,
+ "Loaded mapping from XDC %s:%s", obj->info.name, subobj.info.name);
+ }
+ }
+ epl_wmem_iarray_sort_and_compact(obj->subindices);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+#else /* ! PARSE_XDD */
+
+#ifdef HAVE_LIBXML2
+struct profile *
+epl_xdd_load(struct profile *profile _U_, const char *xml_file _U_)
+{
+ return NULL;
+}
+#endif /* HAVE_LIBXML2 */
+
+#endif /* ! PARSE_XDD */
+
+/**
+ * A sorted array keyed by intervals
+ * You keep inserting items, then sort the array.
+ * sorting also combines items that compare equal into one and adjusts
+ * the interval accordingly. find uses binary search to find the item
+ *
+ * This is particularly useful, if many similar items exist adjacent to each other
+ * e.g. ObjectMapping subindices in EPL XDD (packet-epl-profile-parser.c)
+ *
+ * Interval Trees wouldn't work for this scenario, because they don't allow
+ * expansion of existing intervals. Using an array instead of a tree,
+ * may additionally offer a possible performance advantage
+
+ * Much room for optimization in the creation process of the array,
+ * but we assume this to be an infrequent operation, with space utilization and
+ * finding speed being more important.
+ */
+
+
+static bool
+free_garray(wmem_allocator_t *scope _U_, wmem_cb_event_t event _U_, void *data)
+{
+ GArray *arr = (GArray*)data;
+ g_array_free(arr, TRUE);
+ return FALSE;
+}
+
+/**
+ * \param scope wmem pool to use
+ * \param elem_size size of elements to add into the iarray
+ * \param equal establishes whether two adjacent elements are equal and thus
+ * shall be combined at sort-time
+ *
+ * \returns a new interval array or NULL on failure
+ *
+ * Creates a new interval array.
+ * Elements must have a range_admin_t as their first element,
+ * which will be managed by the implementation.
+ * \NOTE The cmp parameter can be used to free resources. When combining,
+ * it's always the second argument that's getting removed.
+ */
+
+static epl_wmem_iarray_t *
+epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc equal)
+{
+ epl_wmem_iarray_t *iarr;
+
+ if (elem_size < sizeof(range_t)) return NULL;
+
+ iarr = wmem_new(scope, epl_wmem_iarray_t);
+ if (!iarr) return NULL;
+
+ iarr->equal = equal;
+ iarr->scope = scope;
+ iarr->arr = g_array_new(FALSE, FALSE, elem_size);
+ iarr->is_sorted = TRUE;
+
+ wmem_register_callback(scope, free_garray, iarr->arr);
+
+ return iarr;
+}
+
+
+/** Returns true if the iarr is empty. */
+gboolean
+epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr)
+{
+ return iarr->arr->len == 0;
+}
+
+/** Returns true if the iarr is sorted. */
+gboolean
+epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr)
+{
+ return iarr->is_sorted;
+}
+
+/** Inserts an element */
+static void
+epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data)
+{
+ if (iarr->arr->len)
+ iarr->is_sorted = FALSE;
+
+ data->high = data->low = where;
+ g_array_append_vals(iarr->arr, data, 1);
+}
+
+static int u32cmp(guint32 a, guint32 b)
+{
+ if (a < b) return -1;
+ if (a > b) return +1;
+
+ return 0;
+}
+
+static int
+epl_wmem_iarray_cmp(const void *a, const void *b)
+{
+ return u32cmp(*(const guint32*)a, *(const guint32*)b);
+}
+
+/** Makes array suitable for searching */
+static void
+epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr)
+{
+ range_admin_t *elem, *prev = NULL;
+ guint i, len;
+ len = iarr->arr->len;
+ if (iarr->is_sorted)
+ return;
+
+ g_array_sort(iarr->arr, epl_wmem_iarray_cmp);
+ prev = elem = (range_admin_t*)iarr->arr->data;
+
+ for (i = 1; i < len; i++) {
+ elem = (range_admin_t*)((char*)elem + g_array_get_element_size(iarr->arr));
+
+ /* neighbours' range must be within one of each other and their content equal */
+ while (i < len && elem->low - prev->high <= 1 && iarr->equal(elem, prev)) {
+ prev->high = elem->high;
+
+ g_array_remove_index(iarr->arr, i);
+ len--;
+ }
+ prev = elem;
+ }
+
+ iarr->is_sorted = 1;
+}
+
+static int
+find_in_range(const void *_a, const void *_b)
+{
+ const range_admin_t *a = (const range_admin_t*)_a,
+ *b = (const range_admin_t*)_b;
+
+ if (a->low <= b->high && b->low <= a->high) /* overlap */
+ return 0;
+
+ return u32cmp(a->low, b->low);
+}
+
+static void*
+bsearch_garray(const void *key, GArray *arr, int (*cmp)(const void*, const void*))
+{
+ return bsearch(key, arr->data, arr->len, g_array_get_element_size(arr), cmp);
+}
+
+/*
+ * Finds an element in the interval array. Returns NULL if it doesn't exist
+ * Calling this is unspecified if the array wasn't sorted before
+ */
+range_admin_t *
+epl_wmem_iarray_find(epl_wmem_iarray_t *iarr, guint32 value) {
+ epl_wmem_iarray_sort_and_compact(iarr);
+
+ range_admin_t needle;
+ needle.low = value;
+ needle.high = value;
+ return (range_admin_t*)bsearch_garray(&needle, iarr->arr, find_in_range);
+}
+
+#if 0
+void
+epl_wmem_print_iarr(epl_wmem_iarray_t *iarr)
+{
+ range_admin_t *elem;
+ guint i, len;
+ elem = (range_admin_t*)iarr->arr->data;
+ len = iarr->arr->len;
+ for (i = 0; i < len; i++)
+ {
+
+ ws_debug_printf("Range: low=%" PRIu32 " high=%" PRIu32 "\n", elem->low, elem->high);
+
+ elem = (range_admin_t*)((char*)elem + g_array_get_element_size(iarr->arr));
+ }
+}
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */