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:
parent
086b0a799e
commit
af2249a7c5
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -498,6 +498,10 @@
|
||||
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\field-cache.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\timer.c"
|
||||
>
|
||||
|
||||
@ -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
64
src/field-cache.c
Normal 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
63
src/field-cache.h
Normal 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)
|
||||
Loading…
x
Reference in New Issue
Block a user