From 05e8e9faa308223c86475e4ccb18ff19e5e47943 Mon Sep 17 00:00:00 2001 From: Carl Smith Date: Tue, 10 May 2016 12:47:25 +1200 Subject: [PATCH] New helper functions with printf/scanf format specifiers --- apteryx.c | 127 ++++++++++++++++-------------------------------------- apteryx.h | 116 ++++++++++++++++++++++++++++++++++++------------- test.c | 78 +++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 121 deletions(-) diff --git a/apteryx.c b/apteryx.c index 182ebb7..8c342e6 100644 --- a/apteryx.c +++ b/apteryx.c @@ -502,62 +502,37 @@ apteryx_set (const char *path, const char *value) } bool -apteryx_cas_string (const char *path, const char *key, const char *value, uint64_t ts) +_apteryx_casf (uint64_t ts, const char *format, va_list vl) { - char *full_path; - size_t len; - bool res = false; - - /* Create full path */ - if (key) - len = asprintf (&full_path, "%s/%s", path, key); - else - len = asprintf (&full_path, "%s", path); - if (len) - { - res = apteryx_cas (full_path, value, ts); - free (full_path); - } + if (!format) + return false; + char *path = g_strdup_vprintf (format, vl); + char *value = va_arg (vl, char *); + value = value ? g_strdup_vprintf (value, vl) : NULL; + bool res = apteryx_cas (path, value, ts); + g_free (value); + g_free (path); return res; } bool -apteryx_set_string (const char *path, const char *key, const char *value) -{ - return apteryx_cas_string (path, key, value, UINT64_MAX); -} - -bool -apteryx_cas_int (const char *path, const char *key, int32_t value, uint64_t ts) +apteryx_casf (uint64_t ts, const char *format, ...) { - char *full_path; - size_t len; - char *v; - bool res = false; - - /* Create full path */ - if (key) - len = asprintf (&full_path, "%s/%s", path, key); - else - len = asprintf (&full_path, "%s", path); - if (len) - { - /* Store as a string at the moment */ - len = asprintf ((char **) &v, "%d", value); - if (len) - { - res = apteryx_cas (full_path, v, ts); - free ((void *) v); - } - free (full_path); - } + va_list vl; + va_start (vl, format); + bool res = _apteryx_casf (ts, format, vl); + va_end (vl); return res; } bool -apteryx_set_int (const char *path, const char *key, int32_t value) +apteryx_setf (const char *format, ...) { - return apteryx_cas_int (path, key, value, UINT64_MAX); + va_list vl; + va_start (vl, format); + bool res = _apteryx_casf (UINT64_MAX, format, vl); + va_end (vl); + return res; } typedef struct _get_data_t @@ -636,53 +611,23 @@ apteryx_get (const char *path) return value; } -char * -apteryx_get_string (const char *path, const char *key) -{ - char *full_path; - size_t len; - char *value = NULL; - char *str = NULL; - - /* Create full path */ - if (key) - len = asprintf (&full_path, "%s/%s", path, key); - else - len = asprintf (&full_path, "%s", path); - if (len) - { - if ((value = apteryx_get ((const char *) full_path))) - { - str = (char *) value; - } - free (full_path); - } - return str; -} - -int32_t -apteryx_get_int (const char *path, const char *key) +int +apteryx_getf (const char *format, ...) { - char *full_path; - size_t len; - char *v = NULL; - int value = -1; - - /* Create full path */ - if (key) - len = asprintf (&full_path, "%s/%s", path, key); - else - len = asprintf (&full_path, "%s", path); - if (len) - { - if ((v = apteryx_get (full_path))) - { - value = atoi ((char *) v); - free (v); - } - free (full_path); - } - return value; + va_list vl; + if (!format) + return 0; + va_start (vl, format); + char *path = g_strdup_vprintf (format, vl); + char *value = apteryx_get (path); + free (path); + if (!value) + return 0; + format = va_arg (vl, char *); + int res = vsscanf (value, format, vl); + free (value); + va_end (vl); + return res; } GNode * diff --git a/apteryx.h b/apteryx.h index 9aee8c7..32c9ec7 100644 --- a/apteryx.h +++ b/apteryx.h @@ -37,6 +37,7 @@ #include #include #include +#include #include /** Apteryx configuration @@ -149,10 +150,28 @@ bool apteryx_dump (const char *path, FILE *fp); * @return false if the path is invalid */ bool apteryx_set (const char *path, const char *value); -/** Helper to extend the path with the specified key */ -bool apteryx_set_string (const char *path, const char *key, const char *value); -/** Helper to store a simple int at an extended path */ -bool apteryx_set_int (const char *path, const char *key, int32_t value); + +/** + * Set a path/value using standard printf format specifiers + * Example: + apteryx_setf ("/rules/%d/index", index, "%d", index); + apteryx_setf ("/rules/%d/name", index, "%s", name); + apteryx_setf ("/rules/%d/action", index, "%d", action); + * @param format variable list of path and value format specifiers + * @return true on a successful set + * @return false if the path is invalid or the path/value cannot be formatted + */ +bool apteryx_setf (const char *format, ...); + +/** Deprecated helpers */ +static inline bool apteryx_set_string (const char *path, const char *key, const char *value) +{ + return apteryx_setf ("%s%s%s", path, key ? "/" : "", key ? : "", value); +} +static inline bool apteryx_set_int (const char *path, const char *key, int32_t value) +{ + return apteryx_setf ("%s%s%s", path, key ? "/" : "", key ? : "", "%d", value); +} /** * Get a path/value from Apteryx @@ -161,10 +180,33 @@ bool apteryx_set_int (const char *path, const char *key, int32_t value); * @return NULL if the path is invalid */ char *apteryx_get (const char *path); -/** Helper to retrieve the value using an extended path based on the specified key */ -char *apteryx_get_string (const char *path, const char *key); -/** Helper to retrieve a simple integer from an extended path */ -int32_t apteryx_get_int (const char *path, const char *key); + +/** + * Get a path/value using standard scanf format specifiers + * Example: + apteryx_getf ("/rules/%d/name", index, "%15s", name); + apteryx_getf ("/rules/%d/description", index, "%31[^\\0]", description); + apteryx_getf ("/rules/%d/action", index, "%d", &action); + * @param format variable list of path and value format specifiers + * @return number of successfully parsed items (0 if path value is NULL) + */ +int apteryx_getf (const char *format, ...); + +/** Deprecated helpers */ +static inline char *apteryx_get_string (const char *path, const char *key) +{ + char _v[8192] = {}; + if (apteryx_getf ("%s%s%s", path, key ? "/" : "", key ? : "", "%8191[^\\0]", _v)) + return strdup(_v); + return NULL; +} +static inline int32_t apteryx_get_int (const char *path, const char *key) +{ + int32_t _v = -1; + if (apteryx_getf ("%s%s%s", path, key ? "/" : "", key ?: "", "%d", &_v)) + return _v; + return -1; +} /** * Get the last change timestamp of a given path @@ -177,42 +219,56 @@ uint64_t apteryx_timestamp (const char *path); * Set a path/value in Apteryx, but only if the existing * value has not changed since the specified timestamp. * Can be used for a Compare-And-Swap operation. + * Example: Set a value only if has not changed since read + uint64_t ts = apteryx_timestamp ("/interfaces/eth0/state"); + char *state = apteryx_get ("/interfaces/eth0/state"); + if (strcmp (state, "up") == 0 && + apteryx_cas (ts, "/interfaces/eth0/state", "down")) + return true; + return false; + * @param path path to the value to set + * @param value value to set at the specified path + * @param ts timestamp to be compared to the paths last change time + * @return true on a successful set + * @return false if the set failed (errno == -EBUSY if timestamp comparison failed) + */ +bool apteryx_cas (const char *path, const char *value, uint64_t ts); + +/** + * Set a path/value using standard printf format specifiers, + * but only if the existing value has not changed since + * the specified timestamp. * Example: Safely reserve the next free row in a table uint32_t index = 1; while (index > 0) { - if (apteryx_cas_int (path, key, index, 0)) + if (apteryx_casf (0, "/rules/%d/index", index)) break; index++; } * Example: Safely updating a 32-bit bitmap while (1) { uint64_t ts = apteryx_timestamp (path); - uint32_t bitmap = 0; - char *value = apteryx_get (path); - if (value) - { - sscanf (value, "%"PRIx32, &bitmap); - free (value); - } + uint32_t bitmap = apteryx_getf (path, "%"PRIx32, &bitmap) ? bitmap : 0; bitmap = (bitmap & ~clear) | set; - if (asprintf (&value, "%"PRIx32, bitmap) > 0) { - bool success = apteryx_cas (path, value, ts); - free (value); - if (success || errno != -EBUSY) - return success; - } + if (apteryx_casf (ts, "%"PRIx32, bitmap)) + return; } - * @param path path to the value to set - * @param value value to set at the specified path * @param ts timestamp to be compared to the paths last change time + * @param format variable list of path and value format specifiers * @return true on a successful set - * @return false if the set failed (errno == -EBUSY if timestamp comparison failed) + * @return false if the path is invalid or the path/value cannot be formatted */ -bool apteryx_cas (const char *path, const char *value, uint64_t ts); -/** Helper to extend the path with the specified key */ -bool apteryx_cas_string (const char *path, const char *key, const char *value, uint64_t ts); -/** Helper to store a simple int at an extended path */ -bool apteryx_cas_int (const char *path, const char *key, int32_t value, uint64_t ts); +bool apteryx_casf (uint64_t ts, const char *format, ...); + +/** Deprecated helpers */ +static inline bool apteryx_cas_string (const char *path, const char *key, const char *value, uint64_t ts) +{ + return apteryx_casf (ts, "%s%s%s", path, key ? "/" : "", key ? : "", value); +} +static inline bool apteryx_cas_int (const char *path, const char *key, int32_t value, uint64_t ts) +{ + return apteryx_casf (ts, "%s%s%s", path, key ? "/" : "", key ? : "", "%d", value); +} /** * Helpers for generating and parsing an Apteryx tree. diff --git a/test.c b/test.c index 9071fe2..dba6c44 100644 --- a/test.c +++ b/test.c @@ -545,6 +545,59 @@ test_set_get_string () CU_ASSERT (assert_apteryx_empty ()); } +void +test_setf_getf () +{ + const char *path = TEST_PATH"/entity/zone/%s/description"; + const char *index = "private"; + const char *value = "my private zone"; + char buffer[1024] = {}; + + CU_ASSERT (apteryx_setf (path, index, "%s", value)); + CU_ASSERT (apteryx_getf (path, index, "%1023[^\\0]", buffer) == 1); + CU_ASSERT (strcmp (buffer, value) == 0); + CU_ASSERT (apteryx_setf (path, index, NULL)); + CU_ASSERT (apteryx_getf (path, index) == 0); + CU_ASSERT (assert_apteryx_empty ()); +} + +void +test_setf_parameters () +{ + char *v; + CU_ASSERT (!apteryx_setf (NULL)); + CU_ASSERT (!apteryx_setf ("%s", "dog")); + CU_ASSERT (apteryx_setf (TEST_PATH"/animal", "dog")); + CU_ASSERT (apteryx_setf (TEST_PATH"/animal", NULL)); + /* Bad things happen if we forget to specify the source */ + CU_ASSERT (apteryx_setf (TEST_PATH"/%s/%d", "animal", 1, "dog")); + CU_ASSERT (apteryx_setf (TEST_PATH"/%s/%d", "animal", 1, NULL)); + CU_ASSERT (apteryx_setf (TEST_PATH"/%s/%d", "animal", 1, "%s", "dog")); + CU_ASSERT ((v = apteryx_get (TEST_PATH"/animal/1")) != NULL); + CU_ASSERT (v && strcmp (v, "dog") == 0); + free (v); + CU_ASSERT (apteryx_setf (TEST_PATH"/%s/%d", "animal", 1, NULL)); + CU_ASSERT (assert_apteryx_empty ()); +} + +void +test_getf_parameters () +{ + char v[8192] = {}; + apteryx_set (TEST_PATH"/animal/2", "cat"); + CU_ASSERT (!apteryx_getf (NULL)); + CU_ASSERT (!apteryx_getf ("%s", "cat")); + CU_ASSERT (!apteryx_getf (TEST_PATH"/animal")); + CU_ASSERT (!apteryx_getf (TEST_PATH"/%s/%d", "animal", 2)); + CU_ASSERT (!apteryx_getf (TEST_PATH"/%s/%d", "animal", 1, "%s")); + CU_ASSERT (!apteryx_getf (TEST_PATH"/%s/%d", "animal", 1, "%d")); + /* Bad things happen if we forget to specify the destination */ + CU_ASSERT (apteryx_getf (TEST_PATH"/%s/%d", "animal", 2, "%s", v)); + CU_ASSERT (strcmp (v, "cat") == 0); + apteryx_set (TEST_PATH"/animal/2", NULL); + CU_ASSERT (assert_apteryx_empty ()); +} + void test_search_paths () { @@ -891,6 +944,27 @@ test_cas () CU_ASSERT (assert_apteryx_empty ()); } +void +test_casf () +{ + uint64_t ts; + char *v; + CU_ASSERT (!apteryx_casf (0, NULL)); + CU_ASSERT (!apteryx_casf (0, "%s", "dog")); + CU_ASSERT (apteryx_casf (0, TEST_PATH"/%s/%d", "animal", 1, "dog")); + CU_ASSERT (!apteryx_casf (0, TEST_PATH"/%s/%d", "animal", 1, "cat")); + CU_ASSERT (errno == -EBUSY); + CU_ASSERT ((ts = apteryx_timestamp (TEST_PATH"/animal/1")) != 0); + CU_ASSERT (apteryx_casf (ts, TEST_PATH"/%s/%d", "animal", 1, "mouse")); + CU_ASSERT (!apteryx_casf (ts, TEST_PATH"/%s/%d", "animal", 1, "frog")); + CU_ASSERT (errno == -EBUSY); + CU_ASSERT ((v = apteryx_get (TEST_PATH"/animal/1")) != NULL); + CU_ASSERT (v && strcmp (v, "mouse") == 0); + free (v); + CU_ASSERT (apteryx_set (TEST_PATH"/animal/1", NULL)); + CU_ASSERT (assert_apteryx_empty ()); +} + void test_cas_string () { @@ -3884,6 +3958,9 @@ static CU_TestInfo tests_api[] = { { "large value", test_set_get_large_value }, { "multiple leaves", test_multiple_leaves }, { "set/get string", test_set_get_string }, + { "setf/getf", test_setf_getf }, + { "setf parameters", test_setf_parameters }, + { "getf parameters", test_getf_parameters }, { "set/get int", test_set_get_int }, { "get no value", test_get_no_value }, { "overwrite", test_overwrite }, @@ -3894,6 +3971,7 @@ static CU_TestInfo tests_api[] = { { "multi processes writing to same table", test_process_multi_write }, { "prune", test_prune }, { "cas", test_cas }, + { "casf", test_casf }, { "cas string", test_cas_string }, { "cas int", test_cas_int }, { "bitmap", test_bitmap },