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 <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2020-05-07 12:09:57 -07:00
parent 086b0a799e
commit af2249a7c5
6 changed files with 160 additions and 1 deletions

View File

@ -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, "<extradata key='%s' value='%s' />\n",
str.desc, str.value);
}
// Parse the sample data.
message ("Parsing the sample data.\n");
status = dc_parser_samples_foreach (parser, sample_cb, &sampledata);

View File

@ -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;

View File

@ -498,6 +498,10 @@
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
>
</File>
<File
RelativePath="..\src\field-cache.c"
>
</File>
<File
RelativePath="..\src\timer.c"
>

View File

@ -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 \

64
src/field-cache.c Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#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;
}

63
src/field-cache.h Normal file
View File

@ -0,0 +1,63 @@
#include <string.h>
#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)