json2cbor: Add the tool to convert from JSON to CBOR

Conversion works without metadata interpretation yet. That's coming
next.

Signed-off-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Thiago Macieira
2015-09-29 18:50:27 -07:00
parent 2a5fb79dae
commit d4c9ecb91f
4 changed files with 265 additions and 3 deletions
+18 -1
View File
@@ -71,8 +71,21 @@ endif
-include .config
# json2cbor depends on an external library (cJSON)
ifneq ($(cjson-pass)$(system-cjson-pass),)
JSON2CBOR_SOURCES = tools/json2cbor/json2cbor.c
INSTALL_TARGETS += $(bindir)/json2cbor
ifeq ($(system-cjson-pass),1)
LDFLAGS_CJSON = -lcJSON
else
JSON2CBOR_SOURCES += src/cjson/cJSON.c
json2cbor_CCFLAGS = -I$(SRCDIR)src/cjson
endif
endif
# Rules
all: .config lib/libtinycbor.a bin/cbordump tinycbor.pc
all: $(if $(JSON2CBOR_SOURCES),bin/json2cbor)
check: tests/Makefile | lib/libtinycbor.a
$(MAKE) -C tests check
silentcheck: | lib/libtinycbor.a
@@ -90,6 +103,9 @@ lib/libtinycbor.a: $(TINYCBOR_SOURCES:.c=.o) | lib
bin/cbordump: $(CBORDUMP_SOURCES:.c=.o) lib/libtinycbor.a | bin
$(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS)
bin/json2cbor: $(JSON2CBOR_SOURCES:.c=.o) lib/libtinycbor.a | bin
$(CC) -o $@ $(LDFLAGS) $(LDFLAGS_CJSON) $^ $(LDLIBS) -lm
tinycbor.pc: tinycbor.pc.in
$(SED) > $@ < $< \
-e 's,@prefix@,$(prefix),' \
@@ -130,6 +146,7 @@ mostlyclean:
clean: mostlyclean
$(RM) bin/cbordump
$(RM) bin/json2cbor
$(RM) lib/libtinycbor.a
$(RM) tinycbor.pc
test -e tests/Makefile && $(MAKE) -C tests clean || :
@@ -162,5 +179,5 @@ cflags += -DTINYCBOR_VERSION=\"$(VERSION)$(DIRTYSRC)\"
cflags += -std=c99 $(CFLAGS)
%.o: %.c
@test -d $(@D) || $(MKDIR) $(@D)
$(CC) $(cflags) -c -o $@ $<
$(CC) $(cflags) $($(basename $(notdir $@))_CCFLAGS) -c -o $@ $<
+10 -2
View File
@@ -1,4 +1,5 @@
ALLTESTS = open_memstream funopen gc_sections
ALLTESTS = open_memstream funopen gc_sections \
system-cjson cjson
MAKEFILE := $(lastword $(MAKEFILE_LIST))
OUT :=
@@ -7,6 +8,13 @@ PROGRAM-funopen = extern int funopen(); int main() { return funopen(); }
PROGRAM-gc_sections = int main() {}
CCFLAGS-gc_sections = -Wl,--gc-sections
PROGRAM-cjson = \#include <stdlib.h>\n
PROGRAM-cjson += \#include <cJSON.h>\n
PROGRAM-cjson += int main() { return cJSON_False; }
CCFLAGS-cjson = -I$(dir $(MAKEFILE))src/cjson
PROGRAM-system-cjson = $(PROGRAM-cjson)
CCLFAGS-system-cjson = -lcJSON
sink:
@echo >&2 Please run from the top-level Makefile.
@@ -14,7 +22,7 @@ configure: $(foreach it,$(ALLTESTS),check-$(it))
check-%:
@echo $(subst check-,,$@)-tested := 1 $(OUT)
$(if $(V),,@)if echo "$($(subst check-,PROGRAM-,$@))" | \
$(if $(V),,@)if echo -e "$($(subst check-,PROGRAM-,$@))" | \
$(CC) -xc $($(subst check-,CCFLAGS-,$@)) -o /dev/null - $(if $(V),,>/dev/null 2>&1); \
then \
echo $(subst check-,,$@)-pass := 1 $(OUT); \
+217
View File
@@ -0,0 +1,217 @@
/****************************************************************************
**
** Copyright (C) 2015 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#define _POSIX_C_SOURCE 200809L
#include "cbor.h"
#include <cJSON.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
uint8_t *buffer;
size_t buffersize;
bool usingMetaData = false;
size_t get_cjson_size_limited(cJSON *container)
{
// cJSON_GetArraySize is O(n), so don't go too far
unsigned s = 0;
cJSON *item;
for (item = container->child; item; item = item->next) {
if (++s > 255)
return CborIndefiniteLength;
}
return s;
}
CborError decode_json(cJSON *json, CborEncoder *encoder)
{
CborEncoder container;
CborError err;
cJSON *item;
switch (json->type) {
case cJSON_False:
case cJSON_True:
return cbor_encode_boolean(encoder, json->type == cJSON_True);
case cJSON_NULL:
return cbor_encode_null(encoder);
case cJSON_Number:
if ((double)json->valueint == json->valuedouble)
return cbor_encode_int(encoder, json->valueint);
encode_double:
// the only exception that JSON is larger: floating point numbers
container = *encoder; // save the state
err = cbor_encode_floating_point(encoder, CborDoubleType, &json->valuedouble);
if (err == CborErrorOutOfMemory) {
buffersize += 1024;
uint8_t *newbuffer = realloc(buffer, buffersize);
if (newbuffer == NULL)
return err;
*encoder = container; // restore state
encoder->ptr = newbuffer + (container.ptr - buffer);
encoder->end = newbuffer + buffersize;
buffer = newbuffer;
goto encode_double;
}
return err;
case cJSON_String:
return cbor_encode_text_stringz(encoder, json->valuestring);
default:
return CborErrorUnknownType;
case cJSON_Array:
err = cbor_encoder_create_array(encoder, &container, get_cjson_size_limited(json));
if (err)
return err;
for (item = json->child; item; item = item->next) {
err = decode_json(item, &container);
if (err)
return err;
}
return cbor_encoder_close_container_checked(encoder, &container);
case cJSON_Object:
err = cbor_encoder_create_map(encoder, &container, get_cjson_size_limited(json));
if (err)
return err;
for (item = json->child ; item; item = item->next) {
err = cbor_encode_text_stringz(&container, item->string);
if (err)
return err;
err = decode_json(item, &container);
if (err)
return err;
}
return cbor_encoder_close_container_checked(encoder, &container);
}
}
int main(int argc, char **argv)
{
int c;
while ((c = getopt(argc, argv, "M")) != -1) {
switch (c) {
case 'M':
usingMetaData = true;
break;
case '?':
fprintf(stderr, "Unknown option -%c.\n", optopt);
// fall through
case 'h':
puts("Usage: json2cbor [OPTION]... [FILE]...\n"
"Reads JSON content from FILE and convert to CBOR.\n"
"\n"
"Options:\n"
" -M Interpret metadata added by cbordump tool\n"
"");
return c == '?' ? EXIT_FAILURE : EXIT_SUCCESS;
}
}
FILE *in;
const char *fname = argv[optind];
if (fname && strcmp(fname, "-") != 0) {
in = fopen(fname, "r");
if (!in) {
perror("open");
return EXIT_FAILURE;
}
} else {
in = stdin;
fname = "-";
}
/* 1. read the file */
off_t fsize;
if (fseeko(in, 0, SEEK_END) == 0 && (fsize = ftello(in)) >= 0) {
buffersize = fsize + 1;
buffer = malloc(buffersize);
if (buffer == NULL) {
perror("malloc");
return EXIT_FAILURE;
}
rewind(in);
fsize = fread(buffer, 1, fsize, in);
buffer[fsize] = '\0';
} else {
const unsigned chunk = 16384;
buffersize = 0;
buffer = NULL;
do { // it the hard way
buffer = realloc(buffer, buffersize + chunk);
if (buffer == NULL)
perror("malloc");
buffersize += fread(buffer + buffersize, 1, chunk, in);
} while (!feof(in) && !ferror(in));
buffer[buffersize] = '\0';
}
if (ferror(in)) {
perror("read");
return EXIT_FAILURE;
}
if (in != stdin)
fclose(in);
/* 2. parse as JSON */
cJSON *doc = cJSON_ParseWithOpts((char *)buffer, NULL, true);
if (doc == NULL) {
fprintf(stderr, "json2cbor: %s: could not parse.\n", fname);
return EXIT_FAILURE;
}
/* 3. encode as CBOR */
// We're going to reuse the buffer, as CBOR is usually shorter than the equivalent JSON
CborEncoder encoder;
cbor_encoder_init(&encoder, buffer, buffersize, 0);
CborError err = decode_json(doc, &encoder);
cJSON_Delete(doc);
if (err) {
fprintf(stderr, "json2cbor: %s: error encoding to CBOR: %s\n", fname,
cbor_error_string(err));
return EXIT_FAILURE;
}
fwrite(buffer, 1, encoder.ptr - buffer, stdout);
free(buffer);
return EXIT_SUCCESS;
}
+20
View File
@@ -0,0 +1,20 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
DESTDIR = ../../bin
CBORDIR = $$PWD/../../src
INCLUDEPATH += $$CBORDIR
SOURCES += json2cbor.c
LIBS += ../../lib/libtinycbor.a
CJSONDIR = .
!exists($$CJSONDIR/cJSON.h): CJSONDIR = $$CBORDIR/cjson
exists($$CJSONDIR/cJSON.h) {
INCLUDEPATH += $$CJSONDIR
SOURCES += $$CJSONDIR/cJSON.c
} else {
message("cJSON not found, not building json2cbor.")
TEMPLATE = aux
}