From 44bac24ba5d22fe238bd96702707eb2029efec41 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 22 May 2012 10:32:02 -0700 Subject: [PATCH] simap: New data structure for string-to-integer maps. This commit adapts a couple of existing pieces of code to use the new data structure. The following commit will add another user (which is also the first use of the simap_increas() function). Signed-off-by: Ben Pfaff --- lib/automake.mk | 2 + lib/learning-switch.c | 7 +- lib/learning-switch.h | 6 +- lib/odp-util.c | 31 +++-- lib/odp-util.h | 6 +- lib/simap.c | 244 +++++++++++++++++++++++++++++++++++++ lib/simap.h | 70 +++++++++++ utilities/ovs-controller.c | 13 +- utilities/ovs-dpctl.c | 9 +- 9 files changed, 352 insertions(+), 36 deletions(-) create mode 100644 lib/simap.c create mode 100644 lib/simap.h diff --git a/lib/automake.mk b/lib/automake.mk index 52e112bc1..338d39cdf 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -135,6 +135,8 @@ lib_libopenvswitch_a_SOURCES = \ lib/sha1.h \ lib/shash.c \ lib/shash.h \ + lib/simap.c \ + lib/simap.h \ lib/signals.c \ lib/signals.h \ lib/socket-util.c \ diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 4e7cedaf7..595b7f022 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -37,6 +37,7 @@ #include "poll-loop.h" #include "rconn.h" #include "shash.h" +#include "simap.h" #include "timeval.h" #include "vconn.h" #include "vlog.h" @@ -125,12 +126,12 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) hmap_init(&sw->queue_numbers); shash_init(&sw->queue_names); if (cfg->port_queues) { - struct shash_node *node; + struct simap_node *node; - SHASH_FOR_EACH (node, cfg->port_queues) { + SIMAP_FOR_EACH (node, cfg->port_queues) { struct lswitch_port *port = xmalloc(sizeof *port); hmap_node_nullify(&port->hmap_node); - port->queue_id = (uintptr_t) node->data; + port->queue_id = node->data; shash_add(&sw->queue_names, node->name, port); } } diff --git a/lib/learning-switch.h b/lib/learning-switch.h index e42aec16a..c3c37f296 100644 --- a/lib/learning-switch.h +++ b/lib/learning-switch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 2011 Nicira, Inc. + * Copyright (c) 2008, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,8 +55,8 @@ struct lswitch_config { * specifying a particular queue. */ uint32_t default_queue; - /* Maps from a port name to a queue_id (cast to void *). */ - const struct shash *port_queues; + /* Maps from a port name to a queue_id. */ + const struct simap *port_queues; }; struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *); diff --git a/lib/odp-util.c b/lib/odp-util.c index ce4563536..13cdf8e58 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -32,7 +32,7 @@ #include "ofpbuf.h" #include "openvswitch/tunnel.h" #include "packets.h" -#include "shash.h" +#include "simap.h" #include "timeval.h" #include "util.h" #include "vlog.h" @@ -49,7 +49,7 @@ VLOG_DEFINE_THIS_MODULE(odp_util); * from another. */ static const char *delimiters = ", \t\r\n"; -static int parse_odp_key_attr(const char *, const struct shash *port_names, +static int parse_odp_key_attr(const char *, const struct simap *port_names, struct ofpbuf *); static void format_odp_key_attr(const struct nlattr *a, struct ds *ds); @@ -355,7 +355,7 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions, } static int -parse_odp_action(const char *s, const struct shash *port_names, +parse_odp_action(const char *s, const struct simap *port_names, struct ofpbuf *actions) { /* Many of the sscanf calls in this function use oversized destination @@ -380,12 +380,11 @@ parse_odp_action(const char *s, const struct shash *port_names, if (port_names) { int len = strcspn(s, delimiters); - struct shash_node *node; + struct simap_node *node; - node = shash_find_len(port_names, s, len); + node = simap_find_len(port_names, s, len); if (node) { - nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, - (uintptr_t) node->data); + nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data); return len; } } @@ -561,7 +560,7 @@ parse_odp_action(const char *s, const struct shash *port_names, * Netlink attributes. On failure, no data is appended to 'actions'. Either * way, 'actions''s data might be reallocated. */ int -odp_actions_from_string(const char *s, const struct shash *port_names, +odp_actions_from_string(const char *s, const struct simap *port_names, struct ofpbuf *actions) { size_t old_size; @@ -878,7 +877,7 @@ ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type) } static int -parse_odp_key_attr(const char *s, const struct shash *port_names, +parse_odp_key_attr(const char *s, const struct simap *port_names, struct ofpbuf *key) { /* Many of the sscanf calls in this function use oversized destination @@ -925,14 +924,14 @@ parse_odp_key_attr(const char *s, const struct shash *port_names, if (port_names && !strncmp(s, "in_port(", 8)) { const char *name; - const struct shash_node *node; + const struct simap_node *node; int name_len; name = s + 8; name_len = strcspn(s, ")"); - node = shash_find_len(port_names, name, name_len); + node = simap_find_len(port_names, name, name_len); if (node) { - nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, (uintptr_t) node->data); + nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data); return 8 + name_len + 1; } } @@ -1209,15 +1208,15 @@ parse_odp_key_attr(const char *s, const struct shash *port_names, * data is appended to 'key'. Either way, 'key''s data might be * reallocated. * - * If 'port_names' is nonnull, it points to an shash that maps from a port name - * to a port number cast to void *. (Port names may be used instead of port - * numbers in in_port.) + * If 'port_names' is nonnull, it points to an simap that maps from a port name + * to a port number. (Port names may be used instead of port numbers in + * in_port.) * * On success, the attributes appended to 'key' are individually syntactically * valid, but they may not be valid as a sequence. 'key' might, for example, * have duplicated keys. odp_flow_key_to_flow() will detect those errors. */ int -odp_flow_key_from_string(const char *s, const struct shash *port_names, +odp_flow_key_from_string(const char *s, const struct simap *port_names, struct ofpbuf *key) { const size_t old_size = key->size; diff --git a/lib/odp-util.h b/lib/odp-util.h index 08b0a2a41..d53f0836b 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -30,7 +30,7 @@ struct ds; struct flow; struct nlattr; struct ofpbuf; -struct shash; +struct simap; #define OVSP_NONE ((uint16_t) -1) @@ -62,7 +62,7 @@ odp_port_to_ofp_port(uint16_t odp_port) void format_odp_actions(struct ds *, const struct nlattr *odp_actions, size_t actions_len); -int odp_actions_from_string(const char *, const struct shash *port_names, +int odp_actions_from_string(const char *, const struct simap *port_names, struct ofpbuf *odp_actions); /* Upper bound on the length of a nlattr-formatted flow key. The longest @@ -92,7 +92,7 @@ struct odputil_keybuf { }; void odp_flow_key_format(const struct nlattr *, size_t, struct ds *); -int odp_flow_key_from_string(const char *s, const struct shash *port_names, +int odp_flow_key_from_string(const char *s, const struct simap *port_names, struct ofpbuf *); void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *); diff --git a/lib/simap.c b/lib/simap.c new file mode 100644 index 000000000..f6804aab3 --- /dev/null +++ b/lib/simap.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "simap.h" +#include +#include "hash.h" + +static size_t hash_name(const char *, size_t length); +static struct simap_node *simap_find__(const struct simap *, + const char *name, size_t name_len, + size_t hash); +static struct simap_node *simap_add_nocopy__(struct simap *, + char *name, unsigned int data, + size_t hash); +static int compare_nodes_by_name(const void *a_, const void *b_); + +/* Initializes 'simap' as an empty string-to-integer map. */ +void +simap_init(struct simap *simap) +{ + hmap_init(&simap->map); +} + +/* Frees all the data that 'simap' contains. */ +void +simap_destroy(struct simap *simap) +{ + if (simap) { + simap_clear(simap); + hmap_destroy(&simap->map); + } +} + +/* Exchanges the contents of 'a' and 'b'. */ +void +simap_swap(struct simap *a, struct simap *b) +{ + hmap_swap(&a->map, &b->map); +} + +/* Adjusts 'simap' so that it is still valid after it has been moved around in + * memory (e.g. due to realloc()). */ +void +simap_moved(struct simap *simap) +{ + hmap_moved(&simap->map); +} + +/* Removes all of the mappings from 'simap' and frees them. */ +void +simap_clear(struct simap *simap) +{ + struct simap_node *node, *next; + + SIMAP_FOR_EACH_SAFE (node, next, simap) { + hmap_remove(&simap->map, &node->node); + free(node->name); + free(node); + } +} + +/* Returns true if 'simap' contains no mappings, false if it contains at least + * one. */ +bool +simap_is_empty(const struct simap *simap) +{ + return hmap_is_empty(&simap->map); +} + +/* Returns the number of mappings in 'simap'. */ +size_t +simap_count(const struct simap *simap) +{ + return hmap_count(&simap->map); +} + +/* Inserts a mapping from 'name' to 'data' into 'simap', replacing any + * existing mapping for 'name'. Returns true if a new mapping was added, + * false if an existing mapping's value was replaced. + * + * The caller retains ownership of 'name'. */ +bool +simap_put(struct simap *simap, const char *name, unsigned int data) +{ + size_t length = strlen(name); + size_t hash = hash_name(name, length); + struct simap_node *node; + + node = simap_find__(simap, name, length, hash); + if (node) { + node->data = data; + return false; + } else { + simap_add_nocopy__(simap, xmemdup0(name, length), data, hash); + return true; + } +} + +/* Increases the data value in the mapping for 'name' by 'amt', or inserts a + * mapping from 'name' to 'amt' if no such mapping exists. Returns the + * new total data value for the mapping. + * + * If 'amt' is zero, this function does nothing and returns 0. That is, this + * function won't create a mapping with a initial value of 0. + * + * The caller retains ownership of 'name'. */ +unsigned int +simap_increase(struct simap *simap, const char *name, unsigned int amt) +{ + if (amt) { + size_t length = strlen(name); + size_t hash = hash_name(name, length); + struct simap_node *node; + + node = simap_find__(simap, name, length, hash); + if (node) { + node->data += amt; + } else { + node = simap_add_nocopy__(simap, xmemdup0(name, length), + amt, hash); + } + return node->data; + } else { + return 0; + } +} + +/* Deletes 'node' from 'simap' and frees its associated memory. */ +void +simap_delete(struct simap *simap, struct simap_node *node) +{ + hmap_remove(&simap->map, &node->node); + free(node->name); + free(node); +} + +/* Searches 'simap' for a mapping with the given 'name'. Returns it, if found, + * or a null pointer if not. */ +struct simap_node * +simap_find(const struct simap *simap, const char *name) +{ + return simap_find_len(simap, name, strlen(name)); +} + +/* Searches 'simap' for a mapping whose name is the first 'name_len' bytes + * starting at 'name'. Returns it, if found, or a null pointer if not. */ +struct simap_node * +simap_find_len(const struct simap *simap, const char *name, size_t len) +{ + return simap_find__(simap, name, len, hash_name(name, len)); +} + +/* Searches 'simap' for a mapping with the given 'name'. Returns the + * associated data value, if found, otherwise zero. */ +unsigned int +simap_get(const struct simap *simap, const char *name) +{ + struct simap_node *node = simap_find(simap, name); + return node ? node->data : 0; +} + +/* Returns an array that contains a pointer to each mapping in 'simap', + * ordered alphabetically by name. The returned array has simap_count(simap) + * elements. + * + * The caller is responsible for freeing the returned array (with free()). It + * should not free the individual "simap_node"s in the array, because they are + * still part of 'simap'. */ +const struct simap_node ** +simap_sort(const struct simap *simap) +{ + if (simap_is_empty(simap)) { + return NULL; + } else { + const struct simap_node **nodes; + struct simap_node *node; + size_t i, n; + + n = simap_count(simap); + nodes = xmalloc(n * sizeof *nodes); + i = 0; + SIMAP_FOR_EACH (node, simap) { + nodes[i++] = node; + } + assert(i == n); + + qsort(nodes, n, sizeof *nodes, compare_nodes_by_name); + + return nodes; + } +} + +static size_t +hash_name(const char *name, size_t length) +{ + return hash_bytes(name, length, 0); +} + +static struct simap_node * +simap_find__(const struct simap *simap, const char *name, size_t name_len, + size_t hash) +{ + struct simap_node *node; + + HMAP_FOR_EACH_WITH_HASH (node, node, hash, &simap->map) { + if (!strncmp(node->name, name, name_len) && !node->name[name_len]) { + return node; + } + } + return NULL; +} + +static struct simap_node * +simap_add_nocopy__(struct simap *simap, char *name, unsigned int data, + size_t hash) +{ + struct simap_node *node = xmalloc(sizeof *node); + node->name = name; + node->data = data; + hmap_insert(&simap->map, &node->node, hash); + return node; +} + +static int +compare_nodes_by_name(const void *a_, const void *b_) +{ + const struct simap_node *const *a = a_; + const struct simap_node *const *b = b_; + return strcmp((*a)->name, (*b)->name); +} diff --git a/lib/simap.h b/lib/simap.h new file mode 100644 index 000000000..e7bf80b77 --- /dev/null +++ b/lib/simap.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SIMAP_H +#define SIMAP_H 1 + +#include "hmap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* A map from strings to unsigned integers. */ +struct simap { + struct hmap map; /* Contains "struct simap_node"s. */ +}; + +struct simap_node { + struct hmap_node node; /* In struct simap's 'map' hmap. */ + char *name; + unsigned int data; +}; + +#define SIMAP_INITIALIZER(SIMAP) { HMAP_INITIALIZER(&(SIMAP)->map) } + +#define SIMAP_FOR_EACH(SIMAP_NODE, SIMAP) \ + HMAP_FOR_EACH (SIMAP_NODE, node, &(SIMAP)->map) + +#define SIMAP_FOR_EACH_SAFE(SIMAP_NODE, NEXT, SIMAP) \ + HMAP_FOR_EACH_SAFE (SIMAP_NODE, NEXT, node, &(SIMAP)->map) + +void simap_init(struct simap *); +void simap_destroy(struct simap *); +void simap_swap(struct simap *, struct simap *); +void simap_moved(struct simap *); +void simap_clear(struct simap *); + +bool simap_is_empty(const struct simap *); +size_t simap_count(const struct simap *); + +bool simap_put(struct simap *, const char *, unsigned int); +unsigned int simap_increase(struct simap *, const char *, unsigned int); + +unsigned int simap_get(const struct simap *, const char *); +struct simap_node *simap_find(const struct simap *, const char *); +struct simap_node *simap_find_len(const struct simap *, + const char *, size_t len); + +void simap_delete(struct simap *, struct simap_node *); + +const struct simap_node **simap_sort(const struct simap *); + +#ifdef __cplusplus +} +#endif + +#endif /* simap.h */ diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c index d70b63051..aa4cf4eaa 100644 --- a/utilities/ovs-controller.c +++ b/utilities/ovs-controller.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ #include "openflow/openflow.h" #include "poll-loop.h" #include "rconn.h" -#include "shash.h" +#include "simap.h" #include "stream-ssl.h" #include "timeval.h" #include "unixctl.h" @@ -76,8 +76,8 @@ static bool mute = false; /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */ static uint32_t default_queue = UINT32_MAX; -/* -Q, --port-queue: map from port name to port number (cast to void *). */ -static struct shash port_queues = SHASH_INITIALIZER(&port_queues); +/* -Q, --port-queue: map from port name to port number. */ +static struct simap port_queues = SIMAP_INITIALIZER(&port_queues); /* --with-flows: Flows to send to switch. */ static struct ofputil_flow_mod *default_flows; @@ -274,8 +274,7 @@ add_port_queue(char *s) "\":\""); } - if (!shash_add_once(&port_queues, port_name, - (void *) (uintptr_t) atoi(queue_id))) { + if (!simap_put(&port_queues, port_name, atoi(queue_id))) { ovs_fatal(0, " arguments for -Q or --port-queue must " "be unique"); } @@ -398,7 +397,7 @@ parse_options(int argc, char *argv[]) } free(short_options); - if (!shash_is_empty(&port_queues) || default_queue != UINT32_MAX) { + if (!simap_is_empty(&port_queues) || default_queue != UINT32_MAX) { if (action_normal) { ovs_error(0, "queue IDs are incompatible with -N or --normal; " "not using OFPP_NORMAL"); diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c index 7c191164b..6cb05b887 100644 --- a/utilities/ovs-dpctl.c +++ b/utilities/ovs-dpctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ #include "ofpbuf.h" #include "packets.h" #include "shash.h" +#include "simap.h" #include "sset.h" #include "timeval.h" #include "util.h" @@ -826,7 +827,7 @@ sort_output_actions(struct nlattr *actions, size_t length) static void do_normalize_actions(int argc, char *argv[]) { - struct shash port_names; + struct simap port_names; struct ofpbuf keybuf; struct flow flow; struct ofpbuf odp_actions; @@ -841,7 +842,7 @@ do_normalize_actions(int argc, char *argv[]) ds_init(&s); - shash_init(&port_names); + simap_init(&port_names); for (i = 3; i < argc; i++) { char name[16]; int number; @@ -849,7 +850,7 @@ do_normalize_actions(int argc, char *argv[]) if (sscanf(argv[i], "%15[^=]=%d%n", name, &number, &n) > 0 && n > 0) { uintptr_t n = number; - shash_add(&port_names, name, (void *) n); + simap_put(&port_names, name, n); } else { ovs_fatal(0, "%s: expected NAME=NUMBER", argv[i]); } -- 2.43.0