Begin making the string interface and "field cache" generic

This starts with the deepblu code, which is the last one I touched.

The next step is to try to make some of the other backends use this too,
and see where the interface isn't quite generic enough.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2020-02-13 10:33:48 -08:00
parent 820a797c67
commit f73b6836ad
5 changed files with 127 additions and 104 deletions

View File

@ -514,6 +514,10 @@
RelativePath="..\src\deepblu_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 \

View File

@ -28,6 +28,7 @@
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#include "field-cache.h"
#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
@ -35,10 +36,6 @@
struct msg_desc;
#define MAXTYPE 16
#define MAXGASES 16
#define MAXSTRINGS 32
typedef struct deepblu_parser_t {
dc_parser_t base;
@ -48,37 +45,10 @@ typedef struct deepblu_parser_t {
// 20 sec for scuba, 1 sec for freedives
int sample_interval;
// Field cache
struct {
unsigned int initialized;
// dc_get_field() data
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];
dc_field_string_t strings[MAXSTRINGS];
} cache;
// Common fields
struct dc_field_cache cache;
} deepblu_parser_t;
// I *really* need to make this generic
static void add_string(deepblu_parser_t *deepblu, const char *desc, const char *data);
static void add_string_fmt(deepblu_parser_t *deepblu, const char *desc, const char *fmt, ...);
/*
* Macro to make it easy to set DC_FIELD_xyz values
*/
#define ASSIGN_FIELD(name, value) do { \
deepblu->cache.initialized |= 1u << DC_FIELD_##name; \
deepblu->cache.name = (value); \
} while (0)
static dc_status_t deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
@ -114,41 +84,6 @@ deepblu_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS;
}
/*
* FIXME! This should all be generic.
*
* Now it's just copied between all the different
* dive computers that support the strings..
*/
static void add_string(deepblu_parser_t *deepblu, const char *desc, const char *value)
{
int i;
deepblu->cache.initialized |= 1 << DC_FIELD_STRING;
for (i = 0; i < MAXSTRINGS; i++) {
dc_field_string_t *str = deepblu->cache.strings+i;
if (str->desc)
continue;
str->desc = desc;
str->value = strdup(value);
break;
}
}
static void add_string_fmt(deepblu_parser_t *deepblu, const char *desc, const char *fmt, ...)
{
char buffer[256];
va_list ap;
va_start(ap, fmt);
buffer[sizeof(buffer)-1] = 0;
(void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
va_end(ap);
add_string(deepblu, desc, buffer);
}
static double
pressure_to_depth(unsigned int mbar)
{
@ -169,6 +104,7 @@ deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsig
const unsigned char *hdr = data;
const unsigned char *profile = data + 256;
unsigned int divetime, maxpressure;
dc_gasmix_t gasmix = {0, };
if (size < 256)
return DC_STATUS_IO;
@ -189,19 +125,19 @@ deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsig
case 2:
// SCUBA - divetime in minutes
divetime *= 60;
deepblu->cache.gasmix[0].oxygen = data[3] / 100.0;
deepblu->cache.initialized |= 1u << DC_FIELD_GASMIX;
ASSIGN_FIELD(GASMIX_COUNT, 1);
ASSIGN_FIELD(DIVEMODE, DC_DIVEMODE_OC);
gasmix.oxygen = data[3] / 100.0;
DC_ASSIGN_IDX(deepblu->cache, GASMIX, 0, gasmix);
DC_ASSIGN_FIELD(deepblu->cache, GASMIX_COUNT, 1);
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_OC);
break;
case 3:
// GAUGE - divetime in minutes
divetime *= 60;
ASSIGN_FIELD(DIVEMODE, DC_DIVEMODE_GAUGE);
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_GAUGE);
break;
case 4:
// FREEDIVE - divetime in seconds
ASSIGN_FIELD(DIVEMODE, DC_DIVEMODE_FREEDIVE);
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_FREEDIVE);
deepblu->sample_interval = 1;
break;
default:
@ -214,8 +150,8 @@ deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsig
maxpressure = hdr[22] + 256*hdr[23]; // Maxpressure in millibar
ASSIGN_FIELD(DIVETIME, divetime);
ASSIGN_FIELD(MAXDEPTH, pressure_to_depth(maxpressure));
DC_ASSIGN_FIELD(deepblu->cache, DIVETIME, divetime);
DC_ASSIGN_FIELD(deepblu->cache, MAXDEPTH, pressure_to_depth(maxpressure));
return DC_STATUS_SUCCESS;
}
@ -263,25 +199,6 @@ deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
return DC_STATUS_SUCCESS;
}
static dc_status_t get_string_field(dc_field_string_t *strings, unsigned idx, dc_field_string_t *value)
{
if (idx < MAXSTRINGS) {
dc_field_string_t *res = strings+idx;
if (res->desc && res->value) {
*value = *res;
return DC_STATUS_SUCCESS;
}
}
return DC_STATUS_UNSUPPORTED;
}
// Ugly define thing makes the code much easier to read
// I'd love to use __typeof__, but that's a gcc'ism
#define field_value(p, NAME) \
(memcpy((p), &deepblu->cache.NAME, sizeof(deepblu->cache.NAME)), DC_STATUS_SUCCESS)
// Hacky hack hack
#define GASMIX gasmix[flags]
static dc_status_t
deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
@ -296,28 +213,28 @@ deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
switch (type) {
case DC_FIELD_DIVETIME:
return field_value(value, DIVETIME);
return DC_FIELD_VALUE(deepblu->cache, value, DIVETIME);
case DC_FIELD_MAXDEPTH:
return field_value(value, MAXDEPTH);
return DC_FIELD_VALUE(deepblu->cache, value, MAXDEPTH);
case DC_FIELD_AVGDEPTH:
return field_value(value, AVGDEPTH);
return DC_FIELD_VALUE(deepblu->cache, value, AVGDEPTH);
case DC_FIELD_GASMIX_COUNT:
case DC_FIELD_TANK_COUNT:
return field_value(value, GASMIX_COUNT);
return DC_FIELD_VALUE(deepblu->cache, value, GASMIX_COUNT);
case DC_FIELD_GASMIX:
if (flags >= MAXGASES)
return DC_STATUS_UNSUPPORTED;
return field_value(value, GASMIX);
return DC_FIELD_INDEX(deepblu->cache, value, GASMIX, flags);
case DC_FIELD_SALINITY:
return field_value(value, SALINITY);
return DC_FIELD_VALUE(deepblu->cache, value, SALINITY);
case DC_FIELD_ATMOSPHERIC:
return field_value(value, ATMOSPHERIC);
return DC_FIELD_VALUE(deepblu->cache, value, ATMOSPHERIC);
case DC_FIELD_DIVEMODE:
return field_value(value, DIVEMODE);
return DC_FIELD_VALUE(deepblu->cache, value, DIVEMODE);
case DC_FIELD_TANK:
return DC_STATUS_UNSUPPORTED;
case DC_FIELD_STRING:
return get_string_field(deepblu->cache.strings, flags, (dc_field_string_t *)value);
return dc_field_get_string(&deepblu->cache, flags, (dc_field_string_t *)value);
default:
return DC_STATUS_UNSUPPORTED;
}

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

@ -0,0 +1,54 @@
#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.
*/
void 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->desc = desc;
str->value = strdup(value);
break;
}
}
void dc_field_add_string_fmt(dc_field_cache_t *cache, const char *desc, const char *fmt, ...)
{
char buffer[256];
va_list ap;
va_start(ap, fmt);
buffer[sizeof(buffer)-1] = 0;
(void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
va_end(ap);
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;
}

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

@ -0,0 +1,47 @@
#define MAXGASES 16
#define MAXSTRINGS 32
// dc_get_field() data
typedef struct dc_field_cache {
unsigned int initialized;
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];
dc_field_string_t strings[MAXSTRINGS];
} dc_field_cache_t;
void dc_field_add_string(dc_field_cache_t *, const char *desc, const char *data);
void 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)