From af2249a7c588a21e7a852fd630963eceb9f0035b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 7 May 2020 12:09:57 -0700 Subject: [PATCH] Add generic field cache and string field infrastructure This adds the infrastructure for the "field cache", which is just various helpers for the dc_get_field() interface. This includes the 'dc_field_cache_t' structure that a libdivecomputer backend can just add to its parser data structure, and a few macros to make it very easy to initialize the fields and then return them in the 'get_field()' callback. And part of it is the infrastructure support for the 'dc_field_string_t' type, which adds the support for string fields. That will be used to return various string-formatted data from the dive computer, like deco models, serial numbers, etc. And no, a serial number is most definitely not a "number". It's a string. Right now there are no users of this yet, that comes next. Signed-off-by: Linus Torvalds --- examples/output_xml.c | 18 +++++++++ include/libdivecomputer/parser.h | 11 +++++- msvc/libdivecomputer.vcproj | 4 ++ src/Makefile.am | 1 + src/field-cache.c | 64 ++++++++++++++++++++++++++++++++ src/field-cache.h | 63 +++++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 src/field-cache.c create mode 100644 src/field-cache.h diff --git a/examples/output_xml.c b/examples/output_xml.c index 9a5a9a8..58e045e 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -414,6 +414,24 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u convert_pressure(atmospheric, output->units)); } + message ("Parsing strings.\n"); + int idx; + for (idx = 0; idx < 100; idx++) { + dc_field_string_t str = { NULL }; + status = dc_parser_get_field(parser, DC_FIELD_STRING, idx, &str); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing strings"); + goto cleanup; + } + if (status == DC_STATUS_UNSUPPORTED) + break; + if (!str.desc || !str.value) + break; + fprintf (output->ostream, "\n", + str.desc, str.value); + + } + // Parse the sample data. message ("Parsing the sample data.\n"); status = dc_parser_samples_foreach (parser, sample_cb, &sampledata); diff --git a/include/libdivecomputer/parser.h b/include/libdivecomputer/parser.h index d12ca26..6868883 100644 --- a/include/libdivecomputer/parser.h +++ b/include/libdivecomputer/parser.h @@ -62,9 +62,13 @@ typedef enum dc_field_type_t { DC_FIELD_TEMPERATURE_MAXIMUM, DC_FIELD_TANK_COUNT, DC_FIELD_TANK, - DC_FIELD_DIVEMODE + DC_FIELD_DIVEMODE, + DC_FIELD_STRING, } dc_field_type_t; +// Make it easy to test support compile-time with "#ifdef DC_FIELD_STRING" +#define DC_FIELD_STRING DC_FIELD_STRING + typedef enum parser_sample_event_t { SAMPLE_EVENT_NONE, SAMPLE_EVENT_DECOSTOP, @@ -191,6 +195,11 @@ typedef struct dc_tank_t { double endpressure; /* End pressure (bar) */ } dc_tank_t; +typedef struct dc_field_string_t { + const char *desc; + const char *value; +} dc_field_string_t; + typedef union dc_sample_value_t { unsigned int time; double depth; diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 507140e..047e35b 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -498,6 +498,10 @@ RelativePath="..\src\tecdiving_divecomputereu_parser.c" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 4d19b48..b21ef3b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,6 +69,7 @@ libdivecomputer_la_SOURCES = \ rbstream.h rbstream.c \ checksum.h checksum.c \ array.h array.c \ + field-cache.h field-cache.c \ buffer.c \ cochran_commander.h cochran_commander.c cochran_commander_parser.c \ tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \ diff --git a/src/field-cache.c b/src/field-cache.c new file mode 100644 index 0000000..890320a --- /dev/null +++ b/src/field-cache.c @@ -0,0 +1,64 @@ +#include +#include +#include + +#include "parser-private.h" +#include "field-cache.h" + +/* + * The field cache 'string' interface has some simple rules: + * the "descriptor" part is assumed to be a static allocation, + * while the "value" is something that this interface will + * alway sallocate with 'strdup()', so you can generate it + * dynamically on the stack or whatever without having to + * worry about it. + */ +dc_status_t dc_field_add_string(dc_field_cache_t *cache, const char *desc, const char *value) +{ + int i; + + cache->initialized |= 1 << DC_FIELD_STRING; + for (i = 0; i < MAXSTRINGS; i++) { + dc_field_string_t *str = cache->strings+i; + if (str->desc) + continue; + str->value = strdup(value); + if (!str->value) + return DC_STATUS_NOMEMORY; + str->desc = desc; + return DC_STATUS_SUCCESS; + } + return DC_STATUS_INVALIDARGS; +} + +dc_status_t dc_field_add_string_fmt(dc_field_cache_t *cache, const char *desc, const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + + /* + * We ignore the return value from vsnprintf, and we + * always NUL-terminate the destination buffer ourselves. + * + * That way we don't have to worry about random bad legacy + * implementations. + */ + va_start(ap, fmt); + buffer[sizeof(buffer)-1] = 0; + (void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); + va_end(ap); + + return dc_field_add_string(cache, desc, buffer); +} + +dc_status_t dc_field_get_string(dc_field_cache_t *cache, unsigned idx, dc_field_string_t *value) +{ + if (idx < MAXSTRINGS) { + dc_field_string_t *res = cache->strings+idx; + if (res->desc && res->value) { + *value = *res; + return DC_STATUS_SUCCESS; + } + } + return DC_STATUS_UNSUPPORTED; +} diff --git a/src/field-cache.h b/src/field-cache.h new file mode 100644 index 0000000..ec8a7f4 --- /dev/null +++ b/src/field-cache.h @@ -0,0 +1,63 @@ +#include + +#define MAXGASES 16 +#define MAXSTRINGS 32 + +// dc_get_field() data +typedef struct dc_field_cache { + unsigned int initialized; + + // DC_GET_FIELD_xyz + unsigned int DIVETIME; + double MAXDEPTH; + double AVGDEPTH; + double ATMOSPHERIC; + dc_divemode_t DIVEMODE; + unsigned int GASMIX_COUNT; + dc_salinity_t SALINITY; + dc_gasmix_t GASMIX[MAXGASES]; + + // misc - clean me up! + double lowsetpoint; + double highsetpoint; + double customsetpoint; + + // This (slong with GASMIX) should be something like + // dc_tank_t TANK[MAXGASES] + // but that's for later + dc_tankinfo_t tankinfo[MAXGASES]; + double tanksize[MAXGASES]; + double tankworkingpressure[MAXGASES]; + + // DC_GET_FIELD_STRING + dc_field_string_t strings[MAXSTRINGS]; +} dc_field_cache_t; + +dc_status_t dc_field_add_string(dc_field_cache_t *, const char *desc, const char *data); +dc_status_t dc_field_add_string_fmt(dc_field_cache_t *, const char *desc, const char *fmt, ...); +dc_status_t dc_field_get_string(dc_field_cache_t *, unsigned idx, dc_field_string_t *value); + +/* + * Macro to make it easy to set DC_FIELD_xyz values. + * + * This explains why dc_field_cache member names are + * those odd all-capitalized names: they match the + * names of the DC_FIELD_xyz enums. + */ +#define DC_ASSIGN_FIELD(cache, name, value) do { \ + (cache).initialized |= 1u << DC_FIELD_##name; \ + (cache).name = (value); \ +} while (0) + +#define DC_ASSIGN_IDX(cache, name, idx, value) do { \ + (cache).initialized |= 1u << DC_FIELD_##name; \ + (cache).name[idx] = (value); \ +} while (0) + +// Ugly define thing makes the code much easier to read +// I'd love to use __typeof__, but that's a gcc'ism +#define DC_FIELD_VALUE(cache, p, NAME) \ + (memcpy((p), &(cache).NAME, sizeof((cache).NAME)), DC_STATUS_SUCCESS) + +#define DC_FIELD_INDEX(cache, p, NAME, idx) \ + (memcpy((p), (cache).NAME+idx, sizeof((cache).NAME[0])), DC_STATUS_SUCCESS)