Upgrade htsmsg code from Showtime's codebase

We do this to get access to floating point in JSON
This commit is contained in:
Andreas Öman 2012-11-19 13:16:58 +01:00
parent 5a07328a9a
commit 01db78aea0
12 changed files with 1255 additions and 498 deletions

View file

@ -94,6 +94,8 @@ SRCS = src/main.c \
src/htsmsg_binary.c \
src/htsmsg_json.c \
src/htsmsg_xml.c \
src/misc/dbl.c \
src/misc/json.c \
src/settings.c \
src/htsbuf.c \
src/trap.c \

View file

@ -1,6 +1,6 @@
/*
* Buffer management functions
* Copyright (C) 2008 Andreas Öman
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,17 +22,9 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "htsbuf.h"
#include "tvheadend.h"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#include "htsbuf.h"
/**
*
@ -206,6 +198,7 @@ htsbuf_peek(htsbuf_queue_t *hq, void *buf, size_t len)
c = MIN(hd->hd_data_len - hd->hd_data_off, len);
memcpy(buf, hd->hd_data + hd->hd_data_off, c);
r += c;
buf += c;
len -= c;
@ -233,7 +226,7 @@ htsbuf_drop(htsbuf_queue_t *hq, size_t len)
len -= c;
hd->hd_data_off += c;
hq->hq_size -= c;
r += c;
if(hd->hd_data_off == hd->hd_data_len)
htsbuf_data_free(hq, hd);
}
@ -314,19 +307,162 @@ htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src)
}
/**
*
*/
uint32_t
htsbuf_crc32(htsbuf_queue_t *hq, uint32_t crc)
void
htsbuf_dump_raw_stderr(htsbuf_queue_t *hq)
{
htsbuf_data_t *hd;
TAILQ_FOREACH(hd, &hq->hq_q, hd_link)
crc = tvh_crc32(hd->hd_data + hd->hd_data_off,
hd->hd_data_len - hd->hd_data_off,
crc);
return crc;
char n = '\n';
TAILQ_FOREACH(hd, &hq->hq_q, hd_link) {
if(write(2, hd->hd_data + hd->hd_data_off,
hd->hd_data_len - hd->hd_data_off)
!= hd->hd_data_len - hd->hd_data_off)
break;
}
if(write(2, &n, 1) != 1)
return;
}
void
htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix)
{
void *r = malloc(hq->hq_size);
htsbuf_peek(hq, r, hq->hq_size);
hexdump(prefix, r, hq->hq_size);
free(r);
}
/**
*
*/
void
htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *s)
{
const char *c = s;
const char *e = s + strlen(s);
if(e == s)
return;
while(1) {
const char *esc;
switch(*c++) {
case '<': esc = "&lt;"; break;
case '>': esc = "&gt;"; break;
case '&': esc = "&amp;"; break;
case '\'': esc = "&apos;"; break;
case '"': esc = "&quot;"; break;
default: esc = NULL; break;
}
if(esc != NULL) {
htsbuf_append(hq, s, c - s - 1);
htsbuf_append(hq, esc, strlen(esc));
s = c;
}
if(c == e) {
htsbuf_append(hq, s, c - s);
break;
}
}
}
/**
*
*/
void
htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s)
{
const char *c = s;
const char *e = s + strlen(s);
char C;
if(e == s)
return;
while(1) {
const char *esc;
C = *c++;
if((C >= '0' && C <= '9') ||
(C >= 'a' && C <= 'z') ||
(C >= 'A' && C <= 'Z') ||
C == '_' ||
C == '~' ||
C == '.' ||
C == '-') {
esc = NULL;
} else {
static const char hexchars[16] = "0123456789ABCDEF";
char buf[4];
buf[0] = '%';
buf[1] = hexchars[(C >> 4) & 0xf];
buf[2] = hexchars[C & 0xf];;
buf[3] = 0;
esc = buf;
}
if(esc != NULL) {
htsbuf_append(hq, s, c - s - 1);
htsbuf_append(hq, esc, strlen(esc));
s = c;
}
if(c == e) {
htsbuf_append(hq, s, c - s);
break;
}
}
}
/**
*
*/
void
htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *str)
{
const char *s = str;
htsbuf_append(hq, "\"", 1);
while(*s != 0) {
if(*s == '"' || *s == '\\' || *s == '\n' || *s == '\r' || *s == '\t') {
htsbuf_append(hq, str, s - str);
if(*s == '"')
htsbuf_append(hq, "\\\"", 2);
else if(*s == '\n')
htsbuf_append(hq, "\\n", 2);
else if(*s == '\r')
htsbuf_append(hq, "\\r", 2);
else if(*s == '\t')
htsbuf_append(hq, "\\t", 2);
else
htsbuf_append(hq, "\\\\", 2);
s++;
str = s;
} else {
s++;
}
}
htsbuf_append(hq, str, s - str);
htsbuf_append(hq, "\"", 1);
}
/**
*
*/
char *
htsbuf_to_string(htsbuf_queue_t *hq)
{
char *r = malloc(hq->hq_size + 1);
r[hq->hq_size] = 0;
htsbuf_read(hq, r, hq->hq_size);
return r;
}

View file

@ -1,6 +1,6 @@
/*
* Buffer management functions
* Copyright (C) 2008 Andreas Öman
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,10 +20,11 @@
#define HTSBUF_H__
#include <stdarg.h>
#include <stddef.h>
#include "queue.h"
#include <inttypes.h>
#include "queue.h"
TAILQ_HEAD(htsbuf_data_queue, htsbuf_data);
typedef struct htsbuf_data {
@ -48,15 +49,12 @@ void htsbuf_queue_flush(htsbuf_queue_t *hq);
void htsbuf_vqprintf(htsbuf_queue_t *hq, const char *fmt, va_list ap);
void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...)
__attribute__((format(printf,2,3)));
void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...);
void htsbuf_append(htsbuf_queue_t *hq, const void *buf, size_t len);
void htsbuf_append_prealloc(htsbuf_queue_t *hq, const void *buf, size_t len);
void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src);
void htsbuf_data_free(htsbuf_queue_t *hq, htsbuf_data_t *hd);
size_t htsbuf_read(htsbuf_queue_t *hq, void *buf, size_t len);
@ -67,6 +65,21 @@ size_t htsbuf_drop(htsbuf_queue_t *hq, size_t len);
size_t htsbuf_find(htsbuf_queue_t *hq, uint8_t v);
uint32_t htsbuf_crc32(htsbuf_queue_t *q, uint32_t crc);
void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src);
void htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *str);
void htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s);
void htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *s);
void htsbuf_dump_raw_stderr(htsbuf_queue_t *hq);
char *htsbuf_to_string(htsbuf_queue_t *hq);
struct rstr;
struct rstr *htsbuf_to_rstr(htsbuf_queue_t *hq, const char *prefix);
void htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix);
#endif /* HTSBUF_H__ */

View file

@ -1,6 +1,6 @@
/*
* Functions for manipulating HTS messages
* Copyright (C) 2007 Andreas Öman
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,10 +27,10 @@
static void htsmsg_clear(htsmsg_t *msg);
/*
/**
*
*/
static void
void
htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f)
{
TAILQ_REMOVE(&msg->hm_fields, f, hmf_link);
@ -102,7 +102,7 @@ htsmsg_field_add(htsmsg_t *msg, const char *name, int type, int flags)
/*
*
*/
static htsmsg_field_t *
htsmsg_field_t *
htsmsg_field_find(htsmsg_t *msg, const char *name)
{
htsmsg_field_t *f;
@ -196,16 +196,6 @@ htsmsg_add_s64(htsmsg_t *msg, const char *name, int64_t s64)
f->hmf_s64 = s64;
}
/*
*
*/
void
htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64)
{
htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_ALLOCED);
f->hmf_s64 = u64;
}
/*
*
*/
@ -217,6 +207,17 @@ htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32)
}
/*
*
*/
void
htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl)
{
htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_DBL, HMF_NAME_ALLOCED);
f->hmf_dbl = dbl;
}
/*
*
@ -267,8 +268,8 @@ htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub)
HMF_NAME_ALLOCED);
assert(sub->hm_data == NULL);
TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link);
f->hmf_msg.hm_islist = sub->hm_islist;
TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link);
free(sub);
}
@ -312,10 +313,14 @@ htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p)
case HMF_S64:
*s64p = f->hmf_s64;
break;
case HMF_DBL:
*s64p = f->hmf_dbl;
break;
}
return 0;
}
/**
*
*/
@ -326,30 +331,6 @@ htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def)
return htsmsg_get_s64(msg, name, &s64) ? def : s64;
}
/**
*
*/
int
htsmsg_get_u64(htsmsg_t *msg, const char *name, uint64_t *u64p)
{
htsmsg_field_t *f;
if((f = htsmsg_field_find(msg, name)) == NULL)
return HTSMSG_ERR_FIELD_NOT_FOUND;
switch(f->hmf_type) {
default:
return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
case HMF_STR:
*u64p = strtoull(f->hmf_str, NULL, 0);
break;
case HMF_S64:
*u64p = f->hmf_s64;
break;
}
return 0;
}
/*
*
@ -373,13 +354,26 @@ htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p)
/**
*
*/
uint32_t
int
htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def)
{
uint32_t u32;
return htsmsg_get_u32(msg, name, &u32) ? def : u32;
}
/**
*
*/
int32_t
htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name, int32_t def)
{
int32_t s32;
return htsmsg_get_s32(msg, name, &s32) ? def : s32;
}
/*
*
*/
@ -400,6 +394,25 @@ htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p)
}
/*
*
*/
int
htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp)
{
htsmsg_field_t *f;
if((f = htsmsg_field_find(msg, name)) == NULL)
return HTSMSG_ERR_FIELD_NOT_FOUND;
if(f->hmf_type != HMF_DBL)
return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
*dblp = f->hmf_dbl;
return 0;
}
/*
*
*/
@ -457,13 +470,6 @@ htsmsg_get_str(htsmsg_t *msg, const char *name)
}
const char *
htsmsg_get_str_or_default(htsmsg_t *msg, const char *name, const char *def)
{
const char *str = htsmsg_get_str(msg, name);
return str ?: def;
}
/*
*
*/
@ -493,6 +499,32 @@ htsmsg_get_map_multi(htsmsg_t *msg, ...)
return msg;
}
/**
*
*/
const char *
htsmsg_get_str_multi(htsmsg_t *msg, ...)
{
va_list ap;
const char *n;
htsmsg_field_t *f;
va_start(ap, msg);
while((n = va_arg(ap, char *)) != NULL) {
if((f = htsmsg_field_find(msg, n)) == NULL)
return NULL;
else if(f->hmf_type == HMF_STR)
return f->hmf_str;
else if(f->hmf_type == HMF_MAP)
msg = &f->hmf_msg;
else
return NULL;
}
return NULL;
}
/*
*
*/
@ -565,6 +597,10 @@ htsmsg_print0(htsmsg_t *msg, int indent)
case HMF_S64:
printf("S64) = %" PRId64 "\n", f->hmf_s64);
break;
case HMF_DBL:
printf("DBL) = %f\n", f->hmf_dbl);
break;
}
}
}
@ -611,6 +647,10 @@ htsmsg_copy_i(htsmsg_t *src, htsmsg_t *dst)
case HMF_BIN:
htsmsg_add_bin(dst, f->hmf_name, f->hmf_bin, f->hmf_binsize);
break;
case HMF_DBL:
htsmsg_add_dbl(dst, f->hmf_name, f->hmf_dbl);
break;
}
}
}
@ -623,6 +663,45 @@ htsmsg_copy(htsmsg_t *src)
return dst;
}
/**
*
*/
htsmsg_t *
htsmsg_get_map_in_list(htsmsg_t *m, int num)
{
htsmsg_field_t *f;
HTSMSG_FOREACH(f, m) {
if(!--num)
return htsmsg_get_map_by_field(f);
}
return NULL;
}
/**
*
*/
htsmsg_t *
htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name)
{
if(f->hmf_type != HMF_MAP)
return NULL;
if(strcmp(f->hmf_name, name))
return NULL;
return &f->hmf_msg;
}
/**
*
*/
const char *
htsmsg_get_cdata(htsmsg_t *m, const char *field)
{
return htsmsg_get_str_multi(m, field, "cdata", NULL);
}
/**
*
@ -633,3 +712,4 @@ htsmsg_dtor(htsmsg_t **mp)
if(*mp != NULL)
htsmsg_destroy(*mp);
}

View file

@ -1,6 +1,6 @@
/*
* Functions for manipulating HTS messages
* Copyright (C) 2007 Andreas Öman
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,9 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HTSMSG_H_
#define HTSMSG_H_
#pragma once
#include <stdlib.h>
#include <inttypes.h>
#include "queue.h"
@ -50,6 +49,7 @@ typedef struct htsmsg {
#define HMF_STR 3
#define HMF_BIN 4
#define HMF_LIST 5
#define HMF_DBL 6
typedef struct htsmsg_field {
TAILQ_ENTRY(htsmsg_field) hmf_link;
@ -68,6 +68,7 @@ typedef struct htsmsg_field {
size_t len;
} bin;
htsmsg_t msg;
double dbl;
} u;
} htsmsg_field_t;
@ -76,11 +77,12 @@ typedef struct htsmsg_field {
#define hmf_str u.str
#define hmf_bin u.bin.data
#define hmf_binsize u.bin.len
#define hmf_dbl u.dbl
#define htsmsg_get_map_by_field(f) \
((f)->hmf_type == HMF_MAP ? &(f)->hmf_msg : NULL)
#define htsmsg_get_list_by_field(f) \
((f)->hmf_type == HMF_LIST ? &(f)->hmf_msg : NULL)
((f)->hmf_type == HMF_LIST ? &(f)->hmf_msg : NULL)
#define HTSMSG_FOREACH(f, msg) TAILQ_FOREACH(f, &(msg)->hm_fields, hmf_link)
@ -94,6 +96,11 @@ htsmsg_t *htsmsg_create_map(void);
*/
htsmsg_t *htsmsg_create_list(void);
/**
* Remove a given field from a msg
*/
void htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f);
/**
* Destroys a message (map or list)
*/
@ -109,11 +116,6 @@ void htsmsg_add_u32(htsmsg_t *msg, const char *name, uint32_t u32);
*/
void htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32);
/**
* Add an integer field where source is unsigned 64 bit.
*/
void htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64);
/**
* Add an integer field where source is signed 64 bit.
*/
@ -129,6 +131,11 @@ void htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str);
*/
void htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub);
/**
* Add an field where source is a double
*/
void htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl);
/**
* Add an field where source is a list or map message.
*
@ -160,15 +167,6 @@ void htsmsg_add_binptr(htsmsg_t *msg, const char *name, const void *bin,
*/
int htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p);
/**
* Return the field \p name as an u32.
*
* @return An unsigned 32 bit integer or def if the field can not be found
* or if conversion is not possible.
*/
uint32_t htsmsg_get_u32_or_default
(htsmsg_t *msg, const char *name, uint32_t def);
/**
* Get an integer as an signed 32 bit integer.
*
@ -187,24 +185,11 @@ int htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p);
*/
int htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p);
/**
/*
* Return the field \p name as an s64.
*
* @return A signed 64 bit integer or def if the field can not be found
* or if conversion is not possible.
*/
int64_t htsmsg_get_s64_or_default
(htsmsg_t *msg, const char *name, int64_t def);
int64_t htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def);
/**
* Get an integer as an unsigned 64 bit integer.
*
* @return HTSMSG_ERR_FIELD_NOT_FOUND - Field does not exist
* HTSMSG_ERR_CONVERSION_IMPOSSIBLE - Field is not an integer or
* out of range for the requested storage.
*/
int htsmsg_get_u64(htsmsg_t *msg, const char *name, uint64_t *u64p);
/**
* Get pointer to a binary field. No copying of data is performed.
@ -236,15 +221,6 @@ htsmsg_t *htsmsg_get_list(htsmsg_t *msg, const char *name);
*/
const char *htsmsg_get_str(htsmsg_t *msg, const char *name);
/**
* Get a field of type 'string'. No copying is done.
*
* @return def if the field can not be found or not of string type.
* Otherwise a pointer to the data is returned.
*/
const char *htsmsg_get_str_or_default
(htsmsg_t *msg, const char *name, const char *def);
/**
* Get a field of type 'map'. No copying is done.
*
@ -256,7 +232,23 @@ htsmsg_t *htsmsg_get_map(htsmsg_t *msg, const char *name);
/**
* Traverse a hierarchy of htsmsg's to find a specific child.
*/
htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...);
htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...)
__attribute__((__sentinel__(0)));
/**
* Traverse a hierarchy of htsmsg's to find a specific child.
*/
const char *htsmsg_get_str_multi(htsmsg_t *msg, ...)
__attribute__((__sentinel__(0)));
/**
* Get a field of type 'double'.
*
* @return HTSMSG_ERR_FIELD_NOT_FOUND - Field does not exist
* HTSMSG_ERR_CONVERSION_IMPOSSIBLE - Field is not an integer or
* out of range for the requested storage.
*/
int htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp);
/**
* Given the field \p f, return a string if it is of type string, otherwise
@ -264,6 +256,23 @@ htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...);
*/
const char *htsmsg_field_get_string(htsmsg_field_t *f);
/**
* Return the field \p name as an u32.
*
* @return An unsigned 32 bit integer or NULL if the field can not be found
* or if conversion is not possible.
*/
int htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def);
/**
* Return the field \p name as an s32.
*
* @return A signed 32 bit integer or NULL if the field can not be found
* or if conversion is not possible.
*/
int32_t htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name,
int32_t def);
/**
* Remove the given field called \p name from the message \p msg.
*/
@ -288,6 +297,12 @@ void htsmsg_print(htsmsg_t *msg);
htsmsg_field_t *htsmsg_field_add(htsmsg_t *msg, const char *name,
int type, int flags);
/**
* Get a field, return NULL if it does not exist
*/
htsmsg_field_t *htsmsg_field_find(htsmsg_t *msg, const char *name);
/**
* Clone a message.
*/
@ -296,8 +311,15 @@ htsmsg_t *htsmsg_copy(htsmsg_t *src);
#define HTSMSG_FOREACH(f, msg) TAILQ_FOREACH(f, &(msg)->hm_fields, hmf_link)
/**
* Misc
*/
htsmsg_t *htsmsg_get_map_in_list(htsmsg_t *m, int num);
htsmsg_t *htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name);
const char *htsmsg_get_cdata(htsmsg_t *m, const char *field);
extern void htsmsg_dtor(htsmsg_t **mp);
#define htsmsg_autodtor(n) htsmsg_t *n __attribute__((cleanup(htsmsg_dtor)))
#endif /* HTSMSG_H_ */

View file

@ -25,54 +25,19 @@
#include "htsmsg_json.h"
#include "htsbuf.h"
#include "misc/json.h"
#include "misc/dbl.h"
/**
*
*/
static void
htsmsg_json_encode_string(const char *str, htsbuf_queue_t *hq)
{
const char *s = str;
htsbuf_append(hq, "\"", 1);
while(*s != 0) {
if(*s == '"' || *s == '\\' || *s == '\n' || *s == '\t' || *s == '\r') {
htsbuf_append(hq, str, s - str);
if(*s == '"')
htsbuf_append(hq, "\\\"", 2);
else if(*s == '\n')
htsbuf_append(hq, "\\n", 2);
else if(*s == '\t')
htsbuf_append(hq, "\\t", 2);
else if(*s == '\r')
htsbuf_append(hq, "\\r", 2);
else
htsbuf_append(hq, "\\\\", 2);
s++;
str = s;
} else {
s++;
}
}
htsbuf_append(hq, str, s - str);
htsbuf_append(hq, "\"", 1);
}
/*
* Note to future:
* If your about to add support for numbers with decimal point,
* remember to always serialize them with '.' as decimal point character
* no matter what current locale says. This is according to the JSON spec.
*/
static void
htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
int indent, int pretty)
{
htsmsg_field_t *f;
char buf[30];
char buf[100];
static const char *indentor = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
htsbuf_append(hq, isarray ? "[" : "{", 1);
@ -83,7 +48,7 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
htsbuf_append(hq, indentor, indent < 16 ? indent : 16);
if(!isarray) {
htsmsg_json_encode_string(f->hmf_name ?: "noname", hq);
htsbuf_append_and_escape_jsonstr(hq, f->hmf_name ?: "noname");
htsbuf_append(hq, ": ", 2);
}
@ -97,11 +62,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
break;
case HMF_STR:
htsmsg_json_encode_string(f->hmf_str, hq);
htsbuf_append_and_escape_jsonstr(hq, f->hmf_str);
break;
case HMF_BIN:
htsmsg_json_encode_string("binary", hq);
htsbuf_append_and_escape_jsonstr(hq, "binary");
break;
case HMF_S64:
@ -109,6 +74,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
htsbuf_append(hq, buf, strlen(buf));
break;
case HMF_DBL:
my_double2str(buf, sizeof(buf), f->hmf_dbl);
htsbuf_append(hq, buf, strlen(buf));
break;
default:
abort();
}
@ -125,343 +95,109 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
/**
*
*/
int
void
htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty)
{
htsmsg_json_write(msg, hq, msg->hm_islist, 2, pretty);
if(pretty)
htsbuf_append(hq, "\n", 1);
return 0;
}
static const char *htsmsg_json_parse_value(const char *s,
htsmsg_t *parent, char *name);
/**
*
*/
static char *
htsmsg_json_parse_string(const char *s, const char **endp)
{
const char *start;
char *r, *a, *b;
int l, esc = 0;
while(*s > 0 && *s < 33)
s++;
if(*s != '"')
return NULL;
s++;
start = s;
while(1) {
if(*s == 0)
return NULL;
if(*s == '\\') {
esc = 1;
/* skip the escape */
s++;
if (*s == 'u') s += 4;
// Note: we could detect the lack of support here!
} else if(*s == '"') {
*endp = s + 1;
/* End */
l = s - start;
r = malloc(l + 1);
memcpy(r, start, l);
r[l] = 0;
if(esc) {
/* Do deescaping inplace */
a = b = r;
while(*a) {
if(*a == '\\') {
a++;
if(*a == 'b')
*b++ = '\b';
else if(*a == '\\')
*b++ = '\\';
else if(*a == 'f')
*b++ = '\f';
else if(*a == 'n')
*b++ = '\n';
else if(*a == 'r')
*b++ = '\r';
else if(*a == 't')
*b++ = '\t';
else if(*a == 'u') {
/* 4 hexdigits: Not supported */
free(r);
return NULL;
} else {
*b++ = *a;
}
a++;
} else {
*b++ = *a++;
}
}
*b = 0;
}
return r;
}
s++;
}
}
/**
*
*/
static htsmsg_t *
htsmsg_json_parse_object(const char *s, const char **endp)
char *
htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty)
{
char *name;
const char *s2;
htsmsg_t *r;
while(*s > 0 && *s < 33)
s++;
if(*s != '{')
return NULL;
s++;
r = htsmsg_create_map();
while(*s > 0 && *s < 33)
s++;
if(*s != '}') while(1) {
if((name = htsmsg_json_parse_string(s, &s2)) == NULL) {
htsmsg_destroy(r);
return NULL;
}
s = s2;
while(*s > 0 && *s < 33)
s++;
if(*s != ':') {
htsmsg_destroy(r);
free(name);
return NULL;
}
s++;
s2 = htsmsg_json_parse_value(s, r, name);
free(name);
if(s2 == NULL) {
htsmsg_destroy(r);
return NULL;
}
s = s2;
while(*s > 0 && *s < 33)
s++;
if(*s == '}')
break;
if(*s != ',') {
htsmsg_destroy(r);
return NULL;
}
s++;
}
s++;
*endp = s;
return r;
}
/**
*
*/
static htsmsg_t *
htsmsg_json_parse_array(const char *s, const char **endp)
{
const char *s2;
htsmsg_t *r;
while(*s > 0 && *s < 33)
s++;
if(*s != '[')
return NULL;
s++;
r = htsmsg_create_list();
while(*s > 0 && *s < 33)
s++;
if(*s != ']') {
while(1) {
s2 = htsmsg_json_parse_value(s, r, NULL);
if(s2 == NULL) {
htsmsg_destroy(r);
return NULL;
}
s = s2;
while(*s > 0 && *s < 33)
s++;
if(*s == ']')
break;
if(*s != ',') {
htsmsg_destroy(r);
return NULL;
}
s++;
}
}
s++;
*endp = s;
return r;
}
/*
* locale independent strtod.
* does not support hex floats as the standard strtod
*/
static double
_strntod(const char *s, char decimal_point_char, char **ep)
{
static char locale_decimal_point_char = 0;
char buf[64];
const char *c;
double d;
/* ugly but very portable way to get decimal point char */
if(locale_decimal_point_char == 0) {
snprintf(buf, sizeof(buf), "%f", 0.0);
locale_decimal_point_char = buf[1];
assert(locale_decimal_point_char != 0);
}
for(c = s;
*c != '\0' &&
((*c > 0 && *c < 33) || /* skip whitespace */
(*c == decimal_point_char || strchr("+-0123456789", *c) != NULL)); c++)
;
strncpy(buf, s, c - s);
buf[c - s] = '\0';
/* replace if specified char is not same as current locale */
if(decimal_point_char != locale_decimal_point_char) {
char *r = strchr(buf, decimal_point_char);
if(r != NULL)
*r = locale_decimal_point_char;
}
d = strtod(buf, ep);
/* figure out offset in original string */
if(ep != NULL)
*ep = (char *)s + (*ep - buf);
return d;
}
/**
*
*/
static char *
htsmsg_json_parse_number(const char *s, double *dp)
{
char *ep;
double d = _strntod(s, '.', &ep);
if(ep == s)
return NULL;
*dp = d;
return ep;
}
/**
*
*/
static const char *
htsmsg_json_parse_value(const char *s, htsmsg_t *parent, char *name)
{
const char *s2;
htsbuf_queue_t hq;
char *str;
double d = 0;
htsmsg_t *c;
if((c = htsmsg_json_parse_object(s, &s2)) != NULL) {
htsmsg_add_msg(parent, name, c);
return s2;
} else if((c = htsmsg_json_parse_array(s, &s2)) != NULL) {
htsmsg_add_msg(parent, name, c);
return s2;
} else if((str = htsmsg_json_parse_string(s, &s2)) != NULL) {
htsmsg_add_str(parent, name, str);
free(str);
return s2;
} else if((s2 = htsmsg_json_parse_number(s, &d)) != NULL) {
htsmsg_add_s64(parent, name, d);
return s2;
}
if(!strncmp(s, "true", 4)) {
htsmsg_add_u32(parent, name, 1);
return s + 4;
}
if(!strncmp(s, "false", 5)) {
htsmsg_add_u32(parent, name, 0);
return s + 5;
}
if(!strncmp(s, "null", 4)) {
/* Don't add anything */
return s + 4;
}
return NULL;
htsbuf_queue_init(&hq, 0);
htsmsg_json_serialize(msg, &hq, pretty);
str = htsbuf_to_string(&hq);
htsbuf_queue_flush(&hq);
return str;
}
/**
*
*/
static void *
create_map(void *opaque)
{
return htsmsg_create_map();
}
static void *
create_list(void *opaque)
{
return htsmsg_create_list();
}
static void
destroy_obj(void *opaque, void *obj)
{
return htsmsg_destroy(obj);
}
static void
add_obj(void *opaque, void *parent, const char *name, void *child)
{
htsmsg_add_msg(parent, name, child);
}
static void
add_string(void *opaque, void *parent, const char *name, char *str)
{
htsmsg_add_str(parent, name, str);
free(str);
}
static void
add_long(void *opaque, void *parent, const char *name, long v)
{
htsmsg_add_s64(parent, name, v);
}
static void
add_double(void *opaque, void *parent, const char *name, double v)
{
htsmsg_add_dbl(parent, name, v);
}
static void
add_bool(void *opaque, void *parent, const char *name, int v)
{
htsmsg_add_u32(parent, name, v);
}
static void
add_null(void *opaque, void *parent, const char *name)
{
}
/**
*
*/
static const json_deserializer_t json_to_htsmsg = {
.jd_create_map = create_map,
.jd_create_list = create_list,
.jd_destroy_obj = destroy_obj,
.jd_add_obj = add_obj,
.jd_add_string = add_string,
.jd_add_long = add_long,
.jd_add_double = add_double,
.jd_add_bool = add_bool,
.jd_add_null = add_null,
};
/**
*
*/
htsmsg_t *
htsmsg_json_deserialize(const char *src)
{
const char *end;
htsmsg_t *c;
if((c = htsmsg_json_parse_object(src, &end)) != NULL)
return c;
if((c = htsmsg_json_parse_array(src, &end)) != NULL) {
c->hm_islist = 1;
return c;
}
return NULL;
return json_deserialize(src, &json_to_htsmsg, NULL, NULL, 0);
}

View file

@ -1,6 +1,6 @@
/*
* Functions converting HTSMSGs to/from JSON
* Copyright (C) 2008 Andreas Öman
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,12 +21,16 @@
#include "htsmsg.h"
#include "htsbuf.h"
struct rstr;
/**
* htsmsg_binary_deserialize
*/
htsmsg_t *htsmsg_json_deserialize(const char *src);
int htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty);
void htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty);
char *htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty);
struct rstr *htsmsg_json_serialize_to_rstr(htsmsg_t *msg, const char *prefix);
#endif /* HTSMSG_JSON_H_ */

View file

@ -381,14 +381,14 @@ htsp_file_open(htsp_connection_t *htsp, const char *path)
hf->hf_fd = fd;
hf->hf_id = ++htsp->htsp_file_id;
hf->hf_path = strdup(path);
LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link);
LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link);
htsmsg_t *rep = htsmsg_create_map();
htsmsg_add_u32(rep, "id", hf->hf_id);
if(!fstat(hf->hf_fd, &st)) {
htsmsg_add_u64(rep, "size", st.st_size);
htsmsg_add_u64(rep, "mtime", st.st_mtime);
htsmsg_add_s64(rep, "size", st.st_size);
htsmsg_add_s64(rep, "mtime", st.st_mtime);
}
return rep;
@ -837,7 +837,7 @@ htsp_method_getEvent(htsp_connection_t *htsp, htsmsg_t *in)
if(htsmsg_get_u32(in, "eventId", &eventId))
return htsp_error("Missing argument 'eventId'");
lang = htsmsg_get_str_or_default(in, "language", htsp->htsp_language);
lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
if((e = epg_broadcast_find_by_id(eventId, NULL)) == NULL)
return htsp_error("Event does not exist");
@ -871,7 +871,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in)
maxTime
= htsmsg_get_s64_or_default(in, "maxTime", 0);
lang
= htsmsg_get_str_or_default(in, "language", htsp->htsp_language);
= htsmsg_get_str(in, "language") ?: htsp->htsp_language;
/* Use event as starting point */
if (e || ch) {
@ -940,7 +940,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
genre.code = u32;
eg = &genre;
}
lang = htsmsg_get_str_or_default(in, "language", htsp->htsp_language);
lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
full = htsmsg_get_u32_or_default(in, "full", 0);
//do the query
@ -1324,6 +1324,7 @@ htsp_method_change_weight(htsp_connection_t *htsp, htsmsg_t *in)
return NULL;
}
/**
* Open file
*/
@ -1358,17 +1359,17 @@ static htsmsg_t *
htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in)
{
htsp_file_t *hf = htsp_file_find(htsp, in);
uint64_t off;
uint64_t size;
int64_t off;
int64_t size;
if(hf == NULL)
return htsp_error("Unknown file id");
if(htsmsg_get_u64(in, "size", &size))
if(htsmsg_get_s64(in, "size", &size))
return htsp_error("Missing field 'size'");
/* Seek (optional) */
if (!htsmsg_get_u64(in, "offset", &off))
if (!htsmsg_get_s64(in, "offset", &off))
if(lseek(hf->hf_fd, off, SEEK_SET) != off)
return htsp_error("Seek error");
@ -1418,8 +1419,8 @@ htsp_method_file_stat(htsp_connection_t *htsp, htsmsg_t *in)
htsmsg_t *rep = htsmsg_create_map();
if(!fstat(hf->hf_fd, &st)) {
htsmsg_add_u64(rep, "size", st.st_size);
htsmsg_add_u64(rep, "mtime", st.st_mtime);
htsmsg_add_s64(rep, "size", st.st_size);
htsmsg_add_s64(rep, "mtime", st.st_mtime);
}
return rep;
}

295
src/misc/dbl.c Normal file
View file

@ -0,0 +1,295 @@
/*
* Floating point conversion functions.
* Not accurate but should be enough for Showtime's needs
*
* Copyright (C) 2011 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _ISOC99_SOURCE
#include <math.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "dbl.h"
double
my_str2double(const char *str, const char **endp)
{
double ret = 1.0f;
int n = 0, m = 0, o = 0, e = 0;
if(*str == '-') {
ret = -1.0f;
str++;
}
while(*str >= '0' && *str <= '9')
n = n * 10 + *str++ - '0';
if(*str != '.') {
ret *= n;
} else {
str++;
while(*str >= '0' && *str <= '9') {
o = o * 10 + *str++ - '0';
m--;
}
ret *= (n + pow(10, m) * o);
}
if(*str == 'e' || *str == 'E') {
int esign = 1;
str++;
if(*str == '+')
str++;
else if(*str == '-') {
str++;
esign = -1;
}
while(*str >= '0' && *str <= '9')
e = e * 10 + *str++ - '0';
ret *= pow(10, e * esign);
}
if(endp != NULL)
*endp = str;
return ret;
}
/*
** The code that follow is based on "printf" code that dates from the
** 1980s. It is in the public domain. The original comments are
** included here for completeness. They are very out-of-date but
** might be useful as an historical reference.
**
**************************************************************************
**
** The following modules is an enhanced replacement for the "printf" subroutines
** found in the standard C library. The following enhancements are
** supported:
**
** + Additional functions. The standard set of "printf" functions
** includes printf, fprintf, sprintf, vprintf, vfprintf, and
** vsprintf. This module adds the following:
**
** * snprintf -- Works like sprintf, but has an extra argument
** which is the size of the buffer written to.
**
** * mprintf -- Similar to sprintf. Writes output to memory
** obtained from malloc.
**
** * xprintf -- Calls a function to dispose of output.
**
** * nprintf -- No output, but returns the number of characters
** that would have been output by printf.
**
** * A v- version (ex: vsnprintf) of every function is also
** supplied.
**
** + A few extensions to the formatting notation are supported:
**
** * The "=" flag (similar to "-") causes the output to be
** be centered in the appropriately sized field.
**
** * The %b field outputs an integer in binary notation.
**
** * The %c field now accepts a precision. The character output
** is repeated by the number of times the precision specifies.
**
** * The %' field works like %c, but takes as its character the
** next character of the format string, instead of the next
** argument. For example, printf("%.78'-") prints 78 minus
** signs, the same as printf("%.78c",'-').
**
** + When compiled using GCC on a SPARC, this version of printf is
** faster than the library printf for SUN OS 4.1.
**
** + All functions are fully reentrant.
**
*/
static char
getdigit(double *val, int *cnt)
{
int digit;
double d;
if( (*cnt)++ >= 16 ) return '0';
digit = (int)*val;
d = digit;
digit += '0';
*val = (*val - d)*10.0;
return (char)digit;
}
#define xGENERIC 0
#define xFLOAT 1
#define xEXP 2
int
my_double2str(char *buf, size_t bufsize, double realvalue)
{
int precision = -1;
char *bufpt;
char prefix;
char xtype = xGENERIC;
int idx, exp, e2;
double rounder;
char flag_exp;
char flag_rtz;
char flag_dp;
char flag_alternateform = 0;
char flag_altform2 = 0;
int nsd;
if(bufsize < 8)
return -1;
if( precision<0 ) precision = 20; /* Set default precision */
if( precision>bufsize/2-10 ) precision = bufsize/2-10;
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
}else{
prefix = 0;
}
if( xtype==xGENERIC && precision>0 ) precision--;
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
if( xtype==xFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
if(isnan(realvalue)) {
strcpy(buf, "NaN");
return 0;
}
if( realvalue>0.0 ){
while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
if( prefix=='-' ){
strcpy(buf, "-Inf");
}else{
strcpy(buf, "Inf");
}
return 0;
}
}
bufpt = buf;
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
flag_exp = xtype==xEXP;
if( xtype != xFLOAT ){
realvalue += rounder;
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
}
if( xtype==xGENERIC ){
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = xEXP;
}else{
precision = precision - exp;
xtype = xFLOAT;
}
}else{
flag_rtz = 0;
}
if( xtype==xEXP ){
e2 = 0;
}else{
e2 = exp;
}
nsd = 0;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
*(bufpt++) = prefix;
}
/* Digits prior to the decimal point */
if( e2<0 ){
*(bufpt++) = '0';
}else{
for(; e2>=0; e2--){
*(bufpt++) = getdigit(&realvalue,&nsd);
}
}
/* The decimal point */
if( flag_dp ){
*(bufpt++) = '.';
}
/* "0" digits after the decimal point but before the first
** significant digit of the number */
for(e2++; e2<0; precision--, e2++){
assert( precision>0 );
*(bufpt++) = '0';
}
/* Significant digits after the decimal point */
while( (precision--)>0 ){
*(bufpt++) = getdigit(&realvalue,&nsd);
}
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
assert( bufpt>buf );
if( bufpt[-1]=='.' ){
if( flag_altform2 ){
*(bufpt++) = '0';
}else{
*(--bufpt) = 0;
}
}
}
/* Add the "eNNN" suffix */
if( flag_exp || xtype==xEXP ){
*(bufpt++) = 'e';
if( exp<0 ){
*(bufpt++) = '-'; exp = -exp;
}else{
*(bufpt++) = '+';
}
if( exp>=100 ){
*(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */
exp %= 100;
}
*(bufpt++) = (char)(exp/10+'0'); /* 10's digit */
*(bufpt++) = (char)(exp%10+'0'); /* 1's digit */
}
*bufpt = 0;
return 0;
}

5
src/misc/dbl.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
double my_str2double(const char *str, const char **endp);
int my_double2str(char *buf, size_t bufsize, double realvalue);

432
src/misc/json.c Normal file
View file

@ -0,0 +1,432 @@
/*
* JSON helpers
* Copyright (C) 2011 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include "json.h"
#include "dbl.h"
#include "tvheadend.h"
#define NOT_THIS_TYPE ((void *)-1)
static const char *json_parse_value(const char *s, void *parent,
const char *name,
const json_deserializer_t *jd,
void *opaque,
const char **failp, const char **failmsg);
/**
* Returns a newly allocated string
*/
static char *
json_parse_string(const char *s, const char **endp,
const char **failp, const char **failmsg)
{
const char *start;
char *r, *a, *b;
int l, esc = 0;
while(*s > 0 && *s < 33)
s++;
if(*s != '"')
return NOT_THIS_TYPE;
s++;
start = s;
while(1) {
if(*s == 0) {
*failmsg = "Unexpected end of JSON message";
*failp = s;
return NULL;
}
if(*s == '\\') {
esc = 1;
} else if(*s == '"' && s[-1] != '\\') {
*endp = s + 1;
/* End */
l = s - start;
r = malloc(l + 1);
memcpy(r, start, l);
r[l] = 0;
if(esc) {
/* Do deescaping inplace */
a = b = r;
while(*a) {
if(*a == '\\') {
a++;
if(*a == 'b')
*b++ = '\b';
else if(*a == 'f')
*b++ = '\f';
else if(*a == 'n')
*b++ = '\n';
else if(*a == 'r')
*b++ = '\r';
else if(*a == 't')
*b++ = '\t';
else if(*a == 'u') {
// Unicode character
int i, v = 0;
a++;
for(i = 0; i < 4; i++) {
v = v << 4;
switch(a[i]) {
case '0' ... '9':
v |= a[i] - '0';
break;
case 'a' ... 'f':
v |= a[i] - 'a' + 10;
break;
case 'A' ... 'F':
v |= a[i] - 'F' + 10;
break;
default:
free(r);
*failmsg = "Incorrect escape sequence";
*failp = (a - r) + start;
return NULL;
}
}
a+=3;
b += put_utf8(b, v);
} else {
*b++ = *a;
}
a++;
} else {
*b++ = *a++;
}
}
*b = 0;
}
return r;
}
s++;
}
}
/**
*
*/
static void *
json_parse_map(const char *s, const char **endp, const json_deserializer_t *jd,
void *opaque, const char **failp, const char **failmsg)
{
char *name;
const char *s2;
void *r;
while(*s > 0 && *s < 33)
s++;
if(*s != '{')
return NOT_THIS_TYPE;
s++;
r = jd->jd_create_map(opaque);
while(*s > 0 && *s < 33)
s++;
if(*s != '}') {
while(1) {
name = json_parse_string(s, &s2, failp, failmsg);
if(name == NOT_THIS_TYPE) {
*failmsg = "Expected string";
*failp = s;
return NULL;
}
if(name == NULL)
return NULL;
s = s2;
while(*s > 0 && *s < 33)
s++;
if(*s != ':') {
jd->jd_destroy_obj(opaque, r);
free(name);
*failmsg = "Expected ':'";
*failp = s;
return NULL;
}
s++;
s2 = json_parse_value(s, r, name, jd, opaque, failp, failmsg);
free(name);
if(s2 == NULL) {
jd->jd_destroy_obj(opaque, r);
return NULL;
}
s = s2;
while(*s > 0 && *s < 33)
s++;
if(*s == '}')
break;
if(*s != ',') {
jd->jd_destroy_obj(opaque, r);
*failmsg = "Expected ','";
*failp = s;
return NULL;
}
s++;
}
}
s++;
*endp = s;
return r;
}
/**
*
*/
static void *
json_parse_list(const char *s, const char **endp, const json_deserializer_t *jd,
void *opaque, const char **failp, const char **failmsg)
{
const char *s2;
void *r;
while(*s > 0 && *s < 33)
s++;
if(*s != '[')
return NOT_THIS_TYPE;
s++;
r = jd->jd_create_list(opaque);
while(*s > 0 && *s < 33)
s++;
if(*s != ']') {
while(1) {
s2 = json_parse_value(s, r, NULL, jd, opaque, failp, failmsg);
if(s2 == NULL) {
jd->jd_destroy_obj(opaque, r);
return NULL;
}
s = s2;
while(*s > 0 && *s < 33)
s++;
if(*s == ']')
break;
if(*s != ',') {
jd->jd_destroy_obj(opaque, r);
*failmsg = "Expected ','";
*failp = s;
return NULL;
}
s++;
}
}
s++;
*endp = s;
return r;
}
/**
*
*/
static const char *
json_parse_double(const char *s, double *dp)
{
const char *ep;
while(*s > 0 && *s < 33)
s++;
double d = my_str2double(s, &ep);
if(ep == s)
return NULL;
*dp = d;
return ep;
}
/**
*
*/
static char *
json_parse_integer(const char *s, long *lp)
{
char *ep;
while(*s > 0 && *s < 33)
s++;
const char *s2 = s;
if(*s2 == '-')
s2++;
while(*s2 >= '0' && *s2 <= '9')
s2++;
if(*s2 == 0)
return NULL;
if(s2[0] == '.' || s2[0] == 'e' || s2[0] == 'E')
return NULL; // Is floating point
long v = strtol(s, &ep, 10);
if(v == LONG_MIN || v == LONG_MAX)
return NULL;
if(ep == s)
return NULL;
*lp = v;
return ep;
}
/**
*
*/
static const char *
json_parse_value(const char *s, void *parent, const char *name,
const json_deserializer_t *jd, void *opaque,
const char **failp, const char **failmsg)
{
const char *s2;
char *str;
double d = 0;
long l = 0;
void *c;
if((c = json_parse_map(s, &s2, jd, opaque, failp, failmsg)) == NULL)
return NULL;
if(c != NOT_THIS_TYPE) {
jd->jd_add_obj(opaque, parent, name, c);
return s2;
}
if((c = json_parse_list(s, &s2, jd, opaque, failp, failmsg)) == NULL)
return NULL;
if(c != NOT_THIS_TYPE) {
jd->jd_add_obj(opaque, parent, name, c);
return s2;
}
if((str = json_parse_string(s, &s2, failp, failmsg)) == NULL)
return NULL;
if(str != NOT_THIS_TYPE) {
jd->jd_add_string(opaque, parent, name, str);
return s2;
}
if((s2 = json_parse_integer(s, &l)) != NULL) {
jd->jd_add_long(opaque, parent, name, l);
return s2;
} else if((s2 = json_parse_double(s, &d)) != NULL) {
jd->jd_add_double(opaque, parent, name, d);
return s2;
}
while(*s > 0 && *s < 33)
s++;
if(!strncmp(s, "true", 4)) {
jd->jd_add_bool(opaque, parent, name, 1);
return s + 4;
}
if(!strncmp(s, "false", 5)) {
jd->jd_add_bool(opaque, parent, name, 0);
return s + 5;
}
if(!strncmp(s, "null", 4)) {
jd->jd_add_null(opaque, parent, name);
return s + 4;
}
*failmsg = "Unknown token";
*failp = s;
return NULL;
}
/**
*
*/
void *
json_deserialize(const char *src, const json_deserializer_t *jd, void *opaque,
char *errbuf, size_t errlen)
{
const char *end;
void *c;
const char *errmsg;
const char *errp;
c = json_parse_map(src, &end, jd, opaque, &errp, &errmsg);
if(c == NOT_THIS_TYPE)
c = json_parse_list(src, &end, jd, opaque, &errp, &errmsg);
if(c == NOT_THIS_TYPE) {
snprintf(errbuf, errlen, "Invalid JSON, expected '{' or '['");
return NULL;
}
if(c == NULL) {
size_t len = strlen(src);
ssize_t offset = errp - src;
if(offset > len || offset < 0) {
snprintf(errbuf, errlen, "%s at (bad) offset %d", errmsg, (int)offset);
} else {
offset -= 10;
if(offset < 0)
offset = 0;
snprintf(errbuf, errlen, "%s at offset %d : '%.20s'", errmsg, (int)offset,
src + offset);
}
}
return c;
}

31
src/misc/json.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
typedef struct json_deserializer {
void *(*jd_create_map)(void *jd_opaque);
void *(*jd_create_list)(void *jd_opaque);
void (*jd_destroy_obj)(void *jd_opaque, void *obj);
void (*jd_add_obj)(void *jd_opaque, void *parent,
const char *name, void *child);
// str must be free'd by callee
void (*jd_add_string)(void *jd_opaque, void *parent,
const char *name, char *str);
void (*jd_add_long)(void *jd_opaque, void *parent,
const char *name, long v);
void (*jd_add_double)(void *jd_opaque, void *parent,
const char *name, double d);
void (*jd_add_bool)(void *jd_opaque, void *parent,
const char *name, int v);
void (*jd_add_null)(void *jd_opaque, void *parent,
const char *name);
} json_deserializer_t;
void *json_deserialize(const char *src, const json_deserializer_t *jd,
void *opaque, char *errbuf, size_t errlen);