summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2015-05-20 18:47:21 -0700
committerJesse Gross <jesse@nicira.com>2015-05-28 18:34:21 -0700
commite7ae59f9900ede275edfffc82d3a0d0110fd826d (patch)
tree3ce3f8eb25c4fe447d100f2f7c74348a98847659 /lib
parent65da723b40a5fdeff6c63c94758fb4121d89fe8a (diff)
downloadopenvswitch-e7ae59f9900ede275edfffc82d3a0d0110fd826d.tar.gz
util: Library routines for printing and scanning large hex integers.
Geneve options are variable length and up to 124 bytes long, which means that they can't be easily manipulated by the integer string functions like we do for other fields. This adds a few helper routines to make these operations easier. Signed-off-by: Jesse Gross <jesse@nicira.com> Acked-by: Andy Zhou <azhou@nicira.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/dynamic-string.c23
-rw-r--r--lib/dynamic-string.h1
-rw-r--r--lib/learn.c23
-rw-r--r--lib/meta-flow.c13
-rw-r--r--lib/util.c81
-rw-r--r--lib/util.h3
6 files changed, 113 insertions, 31 deletions
diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c
index 914af64c1..a6c8f6c76 100644
--- a/lib/dynamic-string.c
+++ b/lib/dynamic-string.c
@@ -361,6 +361,29 @@ ds_swap(struct ds *a, struct ds *b)
*b = temp;
}
+void
+ds_put_hex(struct ds *ds, const void *buf_, size_t size)
+{
+ const uint8_t *buf = buf_;
+ bool printed = false;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ uint8_t val = buf[i];
+ if (val || printed) {
+ if (!printed) {
+ ds_put_format(ds, "0x%"PRIx8, val);
+ } else {
+ ds_put_format(ds, "%02"PRIx8, val);
+ }
+ printed = true;
+ }
+ }
+ if (!printed) {
+ ds_put_char(ds, '0');
+ }
+}
+
/* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per
* line. Numeric offsets are also included, starting at 'ofs' for the first
* byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters
diff --git a/lib/dynamic-string.h b/lib/dynamic-string.h
index dc5981ac2..95172d102 100644
--- a/lib/dynamic-string.h
+++ b/lib/dynamic-string.h
@@ -55,6 +55,7 @@ void ds_put_format(struct ds *, const char *, ...) OVS_PRINTF_FORMAT(2, 3);
void ds_put_format_valist(struct ds *, const char *, va_list)
OVS_PRINTF_FORMAT(2, 0);
void ds_put_printable(struct ds *, const char *, size_t);
+void ds_put_hex(struct ds *ds, const void *buf, size_t size);
void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
uintptr_t ofs, bool ascii);
int ds_get_line(struct ds *, FILE *);
diff --git a/lib/learn.c b/lib/learn.c
index 99d56e601..8ff1e0a89 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -190,29 +190,14 @@ static char * OVS_WARN_UNUSED_RESULT
learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
{
const char *full_s = s;
- const char *arrow = strstr(s, "->");
struct mf_subfield dst;
union mf_subvalue imm;
char *error;
+ int err;
- memset(&imm, 0, sizeof imm);
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && arrow) {
- const char *in = arrow - 1;
- uint8_t *out = imm.u8 + sizeof imm.u8 - 1;
- int n = arrow - (s + 2);
- int i;
-
- for (i = 0; i < n; i++) {
- int hexit = hexit_value(in[-i]);
- if (hexit < 0) {
- return xasprintf("%s: bad hex digit in value", full_s);
- }
- out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit;
- }
- s = arrow;
- } else {
- ovs_be64 *last_be64 = &imm.be64[ARRAY_SIZE(imm.be64) - 1];
- *last_be64 = htonll(strtoull(s, (char **) &s, 0));
+ err = parse_int_string(s, imm.u8, sizeof imm.u8, (char **) &s);
+ if (err) {
+ return xasprintf("%s: bad hex digit in value", full_s);
}
if (strncmp(s, "->", 2)) {
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 124b5256c..757843dfb 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -2300,18 +2300,7 @@ mf_get_subfield(const struct mf_subfield *sf, const struct flow *flow)
void
mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
- if (subvalue->u8[i]) {
- ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
- for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
- ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
- }
- return;
- }
- }
- ds_put_char(s, '0');
+ ds_put_hex(s, subvalue->u8, sizeof subvalue->u8);
}
void
diff --git a/lib/util.c b/lib/util.c
index bcf770051..c7e2b77f5 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -738,6 +738,87 @@ hexits_value(const char *s, size_t n, bool *ok)
return value;
}
+/* Parses the string in 's' as an integer in either hex or decimal format and
+ * puts the result right justified in the array 'valuep' that is 'field_width'
+ * big. If the string is in hex format, the value may be arbitrarily large;
+ * integers are limited to 64-bit values. (The rationale is that decimal is
+ * likely to represent a number and 64 bits is a reasonable maximum whereas
+ * hex could either be a number or a byte string.)
+ *
+ * On return 'tail' points to the first character in the string that was
+ * not parsed as part of the value. ERANGE is returned if the value is too
+ * large to fit in the given field. */
+int
+parse_int_string(const char *s, uint8_t *valuep, int field_width, char **tail)
+{
+ unsigned long long int integer;
+ int i;
+
+ if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) {
+ uint8_t *hexit_str;
+ int len = 0;
+ int val_idx;
+ int err = 0;
+
+ s += 2;
+ hexit_str = xmalloc(field_width * 2);
+
+ for (;;) {
+ uint8_t hexit;
+ bool ok;
+
+ s += strspn(s, " \t\r\n");
+ hexit = hexits_value(s, 1, &ok);
+ if (!ok) {
+ *tail = CONST_CAST(char *, s);
+ break;
+ }
+
+ if (hexit != 0 || len) {
+ if (DIV_ROUND_UP(len + 1, 2) > field_width) {
+ err = ERANGE;
+ goto free;
+ }
+
+ hexit_str[len] = hexit;
+ len++;
+ }
+ s++;
+ }
+
+ val_idx = field_width;
+ for (i = len - 1; i >= 0; i -= 2) {
+ val_idx--;
+ valuep[val_idx] = hexit_str[i];
+ if (i > 0) {
+ valuep[val_idx] += hexit_str[i - 1] << 4;
+ }
+ }
+
+ memset(valuep, 0, val_idx);
+
+free:
+ free(hexit_str);
+ return err;
+ }
+
+ errno = 0;
+ integer = strtoull(s, tail, 0);
+ if (errno) {
+ return errno;
+ }
+
+ for (i = field_width - 1; i >= 0; i--) {
+ valuep[i] = integer;
+ integer >>= 8;
+ }
+ if (integer) {
+ return ERANGE;
+ }
+
+ return 0;
+}
+
/* Returns the current working directory as a malloc()'d string, or a null
* pointer if the current working directory cannot be determined. */
char *
diff --git a/lib/util.h b/lib/util.h
index 276edb569..78abfd388 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -314,6 +314,9 @@ bool str_to_double(const char *, double *);
int hexit_value(int c);
uintmax_t hexits_value(const char *s, size_t n, bool *ok);
+int parse_int_string(const char *s, uint8_t *valuep, int field_width,
+ char **tail);
+
const char *english_list_delimiter(size_t index, size_t total);
char *get_cwd(void);