From a372823a14461c454feaa86373bd672fd518847a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Dec 2012 16:10:26 +0100 Subject: build: move qobject files to qobject/ and libqemuutil.a Signed-off-by: Paolo Bonzini --- Makefile | 2 +- Makefile.objs | 17 +- json-lexer.c | 373 ------------------------- json-parser.c | 704 ------------------------------------------------ json-streamer.c | 122 --------- qbool.c | 68 ----- qdict.c | 456 ------------------------------- qerror.c | 156 ----------- qfloat.c | 68 ----- qint.c | 67 ----- qjson.c | 294 -------------------- qlist.c | 170 ------------ qobject/Makefile.objs | 3 + qobject/json-lexer.c | 373 +++++++++++++++++++++++++ qobject/json-parser.c | 704 ++++++++++++++++++++++++++++++++++++++++++++++++ qobject/json-streamer.c | 122 +++++++++ qobject/qbool.c | 68 +++++ qobject/qdict.c | 456 +++++++++++++++++++++++++++++++ qobject/qerror.c | 156 +++++++++++ qobject/qfloat.c | 68 +++++ qobject/qint.c | 67 +++++ qobject/qjson.c | 294 ++++++++++++++++++++ qobject/qlist.c | 170 ++++++++++++ qobject/qstring.c | 141 ++++++++++ qstring.c | 141 ---------- tests/Makefile | 14 +- 26 files changed, 2633 insertions(+), 2641 deletions(-) delete mode 100644 json-lexer.c delete mode 100644 json-parser.c delete mode 100644 json-streamer.c delete mode 100644 qbool.c delete mode 100644 qdict.c delete mode 100644 qerror.c delete mode 100644 qfloat.c delete mode 100644 qint.c delete mode 100644 qjson.c delete mode 100644 qlist.c create mode 100644 qobject/Makefile.objs create mode 100644 qobject/json-lexer.c create mode 100644 qobject/json-parser.c create mode 100644 qobject/json-streamer.c create mode 100644 qobject/qbool.c create mode 100644 qobject/qdict.c create mode 100644 qobject/qerror.c create mode 100644 qobject/qfloat.c create mode 100644 qobject/qint.c create mode 100644 qobject/qjson.c create mode 100644 qobject/qlist.c create mode 100644 qobject/qstring.c delete mode 100644 qstring.c diff --git a/Makefile b/Makefile index 989cb1f..ee39b36 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,7 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) -qemu-ga$(EXESUF): $(qga-obj-y) $(trace-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) libqemuutil.a libqemustub.a +qemu-ga$(EXESUF): $(qga-obj-y) $(trace-obj-y) $(qapi-obj-y) $(version-obj-y) libqemuutil.a libqemustub.a $(call LINK, $^) clean: diff --git a/Makefile.objs b/Makefile.objs index 3b777c8..d412d8c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -1,6 +1,7 @@ ####################################################################### -# Stub library, linked in tools +# Common libraries for tools and emulators stub-obj-y = stubs/ +util-obj-y = util/ qobject/ ####################################################################### # Target-independent parts used in system and user emulation @@ -8,14 +9,6 @@ universal-obj-y = universal-obj-y += qemu-log.o ####################################################################### -# QObject -qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o -qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o -qobject-obj-y += qerror.o - -universal-obj-y += $(qobject-obj-y) - -####################################################################### # QOM qom-obj-y = qom/ @@ -28,10 +21,6 @@ hw-core-obj-y += hw/ universal-obj-y += $(hw-core-obj-y) ####################################################################### -# util-obj-y is code depending on the OS (win32 vs posix) -util-obj-y += util/ - -####################################################################### # coroutines coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o coroutine-obj-y += qemu-coroutine-sleep.o @@ -53,7 +42,7 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o block-obj-y = async.o thread-pool.o block-obj-y += nbd.o block.o blockjob.o -block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y) +block-obj-y += $(coroutine-obj-y) $(version-obj-y) block-obj-y += main-loop.o iohandler.o qemu-timer.o block-obj-$(CONFIG_POSIX) += aio-posix.o block-obj-$(CONFIG_WIN32) += aio-win32.o diff --git a/json-lexer.c b/json-lexer.c deleted file mode 100644 index 440df60..0000000 --- a/json-lexer.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * JSON lexer - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qint.h" -#include "qemu-common.h" -#include "qapi/qmp/json-lexer.h" - -#define MAX_TOKEN_SIZE (64ULL << 20) - -/* - * \"([^\\\"]|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*\" - * '([^\\']|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*' - * 0|([1-9][0-9]*(.[0-9]+)?([eE]([-+])?[0-9]+)) - * [{}\[\],:] - * [a-z]+ - * - */ - -enum json_lexer_state { - IN_ERROR = 0, - IN_DQ_UCODE3, - IN_DQ_UCODE2, - IN_DQ_UCODE1, - IN_DQ_UCODE0, - IN_DQ_STRING_ESCAPE, - IN_DQ_STRING, - IN_SQ_UCODE3, - IN_SQ_UCODE2, - IN_SQ_UCODE1, - IN_SQ_UCODE0, - IN_SQ_STRING_ESCAPE, - IN_SQ_STRING, - IN_ZERO, - IN_DIGITS, - IN_DIGIT, - IN_EXP_E, - IN_MANTISSA, - IN_MANTISSA_DIGITS, - IN_NONZERO_NUMBER, - IN_NEG_NONZERO_NUMBER, - IN_KEYWORD, - IN_ESCAPE, - IN_ESCAPE_L, - IN_ESCAPE_LL, - IN_ESCAPE_I, - IN_ESCAPE_I6, - IN_ESCAPE_I64, - IN_WHITESPACE, - IN_START, -}; - -#define TERMINAL(state) [0 ... 0x7F] = (state) - -/* Return whether TERMINAL is a terminal state and the transition to it - from OLD_STATE required lookahead. This happens whenever the table - below uses the TERMINAL macro. */ -#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \ - (json_lexer[(old_state)][0] == (terminal)) - -static const uint8_t json_lexer[][256] = { - /* double quote string */ - [IN_DQ_UCODE3] = { - ['0' ... '9'] = IN_DQ_STRING, - ['a' ... 'f'] = IN_DQ_STRING, - ['A' ... 'F'] = IN_DQ_STRING, - }, - [IN_DQ_UCODE2] = { - ['0' ... '9'] = IN_DQ_UCODE3, - ['a' ... 'f'] = IN_DQ_UCODE3, - ['A' ... 'F'] = IN_DQ_UCODE3, - }, - [IN_DQ_UCODE1] = { - ['0' ... '9'] = IN_DQ_UCODE2, - ['a' ... 'f'] = IN_DQ_UCODE2, - ['A' ... 'F'] = IN_DQ_UCODE2, - }, - [IN_DQ_UCODE0] = { - ['0' ... '9'] = IN_DQ_UCODE1, - ['a' ... 'f'] = IN_DQ_UCODE1, - ['A' ... 'F'] = IN_DQ_UCODE1, - }, - [IN_DQ_STRING_ESCAPE] = { - ['b'] = IN_DQ_STRING, - ['f'] = IN_DQ_STRING, - ['n'] = IN_DQ_STRING, - ['r'] = IN_DQ_STRING, - ['t'] = IN_DQ_STRING, - ['/'] = IN_DQ_STRING, - ['\\'] = IN_DQ_STRING, - ['\''] = IN_DQ_STRING, - ['\"'] = IN_DQ_STRING, - ['u'] = IN_DQ_UCODE0, - }, - [IN_DQ_STRING] = { - [1 ... 0xBF] = IN_DQ_STRING, - [0xC2 ... 0xF4] = IN_DQ_STRING, - ['\\'] = IN_DQ_STRING_ESCAPE, - ['"'] = JSON_STRING, - }, - - /* single quote string */ - [IN_SQ_UCODE3] = { - ['0' ... '9'] = IN_SQ_STRING, - ['a' ... 'f'] = IN_SQ_STRING, - ['A' ... 'F'] = IN_SQ_STRING, - }, - [IN_SQ_UCODE2] = { - ['0' ... '9'] = IN_SQ_UCODE3, - ['a' ... 'f'] = IN_SQ_UCODE3, - ['A' ... 'F'] = IN_SQ_UCODE3, - }, - [IN_SQ_UCODE1] = { - ['0' ... '9'] = IN_SQ_UCODE2, - ['a' ... 'f'] = IN_SQ_UCODE2, - ['A' ... 'F'] = IN_SQ_UCODE2, - }, - [IN_SQ_UCODE0] = { - ['0' ... '9'] = IN_SQ_UCODE1, - ['a' ... 'f'] = IN_SQ_UCODE1, - ['A' ... 'F'] = IN_SQ_UCODE1, - }, - [IN_SQ_STRING_ESCAPE] = { - ['b'] = IN_SQ_STRING, - ['f'] = IN_SQ_STRING, - ['n'] = IN_SQ_STRING, - ['r'] = IN_SQ_STRING, - ['t'] = IN_SQ_STRING, - ['/'] = IN_DQ_STRING, - ['\\'] = IN_DQ_STRING, - ['\''] = IN_SQ_STRING, - ['\"'] = IN_SQ_STRING, - ['u'] = IN_SQ_UCODE0, - }, - [IN_SQ_STRING] = { - [1 ... 0xBF] = IN_SQ_STRING, - [0xC2 ... 0xF4] = IN_SQ_STRING, - ['\\'] = IN_SQ_STRING_ESCAPE, - ['\''] = JSON_STRING, - }, - - /* Zero */ - [IN_ZERO] = { - TERMINAL(JSON_INTEGER), - ['0' ... '9'] = IN_ERROR, - ['.'] = IN_MANTISSA, - }, - - /* Float */ - [IN_DIGITS] = { - TERMINAL(JSON_FLOAT), - ['0' ... '9'] = IN_DIGITS, - }, - - [IN_DIGIT] = { - ['0' ... '9'] = IN_DIGITS, - }, - - [IN_EXP_E] = { - ['-'] = IN_DIGIT, - ['+'] = IN_DIGIT, - ['0' ... '9'] = IN_DIGITS, - }, - - [IN_MANTISSA_DIGITS] = { - TERMINAL(JSON_FLOAT), - ['0' ... '9'] = IN_MANTISSA_DIGITS, - ['e'] = IN_EXP_E, - ['E'] = IN_EXP_E, - }, - - [IN_MANTISSA] = { - ['0' ... '9'] = IN_MANTISSA_DIGITS, - }, - - /* Number */ - [IN_NONZERO_NUMBER] = { - TERMINAL(JSON_INTEGER), - ['0' ... '9'] = IN_NONZERO_NUMBER, - ['e'] = IN_EXP_E, - ['E'] = IN_EXP_E, - ['.'] = IN_MANTISSA, - }, - - [IN_NEG_NONZERO_NUMBER] = { - ['0'] = IN_ZERO, - ['1' ... '9'] = IN_NONZERO_NUMBER, - }, - - /* keywords */ - [IN_KEYWORD] = { - TERMINAL(JSON_KEYWORD), - ['a' ... 'z'] = IN_KEYWORD, - }, - - /* whitespace */ - [IN_WHITESPACE] = { - TERMINAL(JSON_SKIP), - [' '] = IN_WHITESPACE, - ['\t'] = IN_WHITESPACE, - ['\r'] = IN_WHITESPACE, - ['\n'] = IN_WHITESPACE, - }, - - /* escape */ - [IN_ESCAPE_LL] = { - ['d'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_L] = { - ['d'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_LL, - }, - - [IN_ESCAPE_I64] = { - ['d'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_I6] = { - ['4'] = IN_ESCAPE_I64, - }, - - [IN_ESCAPE_I] = { - ['6'] = IN_ESCAPE_I6, - }, - - [IN_ESCAPE] = { - ['d'] = JSON_ESCAPE, - ['i'] = JSON_ESCAPE, - ['p'] = JSON_ESCAPE, - ['s'] = JSON_ESCAPE, - ['f'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_L, - ['I'] = IN_ESCAPE_I, - }, - - /* top level rule */ - [IN_START] = { - ['"'] = IN_DQ_STRING, - ['\''] = IN_SQ_STRING, - ['0'] = IN_ZERO, - ['1' ... '9'] = IN_NONZERO_NUMBER, - ['-'] = IN_NEG_NONZERO_NUMBER, - ['{'] = JSON_OPERATOR, - ['}'] = JSON_OPERATOR, - ['['] = JSON_OPERATOR, - [']'] = JSON_OPERATOR, - [','] = JSON_OPERATOR, - [':'] = JSON_OPERATOR, - ['a' ... 'z'] = IN_KEYWORD, - ['%'] = IN_ESCAPE, - [' '] = IN_WHITESPACE, - ['\t'] = IN_WHITESPACE, - ['\r'] = IN_WHITESPACE, - ['\n'] = IN_WHITESPACE, - }, -}; - -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) -{ - lexer->emit = func; - lexer->state = IN_START; - lexer->token = qstring_new(); - lexer->x = lexer->y = 0; -} - -static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) -{ - int char_consumed, new_state; - - lexer->x++; - if (ch == '\n') { - lexer->x = 0; - lexer->y++; - } - - do { - new_state = json_lexer[lexer->state][(uint8_t)ch]; - char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state); - if (char_consumed) { - qstring_append_chr(lexer->token, ch); - } - - switch (new_state) { - case JSON_OPERATOR: - case JSON_ESCAPE: - case JSON_INTEGER: - case JSON_FLOAT: - case JSON_KEYWORD: - case JSON_STRING: - lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y); - /* fall through */ - case JSON_SKIP: - QDECREF(lexer->token); - lexer->token = qstring_new(); - new_state = IN_START; - break; - case IN_ERROR: - /* XXX: To avoid having previous bad input leaving the parser in an - * unresponsive state where we consume unpredictable amounts of - * subsequent "good" input, percolate this error state up to the - * tokenizer/parser by forcing a NULL object to be emitted, then - * reset state. - * - * Also note that this handling is required for reliable channel - * negotiation between QMP and the guest agent, since chr(0xFF) - * is placed at the beginning of certain events to ensure proper - * delivery when the channel is in an unknown state. chr(0xFF) is - * never a valid ASCII/UTF-8 sequence, so this should reliably - * induce an error/flush state. - */ - lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y); - QDECREF(lexer->token); - lexer->token = qstring_new(); - new_state = IN_START; - lexer->state = new_state; - return 0; - default: - break; - } - lexer->state = new_state; - } while (!char_consumed && !flush); - - /* Do not let a single token grow to an arbitrarily large size, - * this is a security consideration. - */ - if (lexer->token->length > MAX_TOKEN_SIZE) { - lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); - QDECREF(lexer->token); - lexer->token = qstring_new(); - lexer->state = IN_START; - } - - return 0; -} - -int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) -{ - size_t i; - - for (i = 0; i < size; i++) { - int err; - - err = json_lexer_feed_char(lexer, buffer[i], false); - if (err < 0) { - return err; - } - } - - return 0; -} - -int json_lexer_flush(JSONLexer *lexer) -{ - return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true); -} - -void json_lexer_destroy(JSONLexer *lexer) -{ - QDECREF(lexer->token); -} diff --git a/json-parser.c b/json-parser.c deleted file mode 100644 index 05279c1..0000000 --- a/json-parser.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - * JSON Parser - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include - -#include "qemu-common.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/qerror.h" - -typedef struct JSONParserContext -{ - Error *err; - struct { - QObject **buf; - size_t pos; - size_t count; - } tokens; -} JSONParserContext; - -#define BUG_ON(cond) assert(!(cond)) - -/** - * TODO - * - * 0) make errors meaningful again - * 1) add geometry information to tokens - * 3) should we return a parsed size? - * 4) deal with premature EOI - */ - -static QObject *parse_value(JSONParserContext *ctxt, va_list *ap); - -/** - * Token manipulators - * - * tokens are dictionaries that contain a type, a string value, and geometry information - * about a token identified by the lexer. These are routines that make working with - * these objects a bit easier. - */ -static const char *token_get_value(QObject *obj) -{ - return qdict_get_str(qobject_to_qdict(obj), "token"); -} - -static JSONTokenType token_get_type(QObject *obj) -{ - return qdict_get_int(qobject_to_qdict(obj), "type"); -} - -static int token_is_operator(QObject *obj, char op) -{ - const char *val; - - if (token_get_type(obj) != JSON_OPERATOR) { - return 0; - } - - val = token_get_value(obj); - - return (val[0] == op) && (val[1] == 0); -} - -static int token_is_keyword(QObject *obj, const char *value) -{ - if (token_get_type(obj) != JSON_KEYWORD) { - return 0; - } - - return strcmp(token_get_value(obj), value) == 0; -} - -static int token_is_escape(QObject *obj, const char *value) -{ - if (token_get_type(obj) != JSON_ESCAPE) { - return 0; - } - - return (strcmp(token_get_value(obj), value) == 0); -} - -/** - * Error handler - */ -static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, - QObject *token, const char *msg, ...) -{ - va_list ap; - char message[1024]; - va_start(ap, msg); - vsnprintf(message, sizeof(message), msg, ap); - va_end(ap); - if (ctxt->err) { - error_free(ctxt->err); - ctxt->err = NULL; - } - error_set(&ctxt->err, QERR_JSON_PARSE_ERROR, message); -} - -/** - * String helpers - * - * These helpers are used to unescape strings. - */ -static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length) -{ - if (wchar <= 0x007F) { - BUG_ON(buffer_length < 2); - - buffer[0] = wchar & 0x7F; - buffer[1] = 0; - } else if (wchar <= 0x07FF) { - BUG_ON(buffer_length < 3); - - buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F); - buffer[1] = 0x80 | (wchar & 0x3F); - buffer[2] = 0; - } else { - BUG_ON(buffer_length < 4); - - buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F); - buffer[1] = 0x80 | ((wchar >> 6) & 0x3F); - buffer[2] = 0x80 | (wchar & 0x3F); - buffer[3] = 0; - } -} - -static int hex2decimal(char ch) -{ - if (ch >= '0' && ch <= '9') { - return (ch - '0'); - } else if (ch >= 'a' && ch <= 'f') { - return 10 + (ch - 'a'); - } else if (ch >= 'A' && ch <= 'F') { - return 10 + (ch - 'A'); - } - - return -1; -} - -/** - * parse_string(): Parse a json string and return a QObject - * - * string - * "" - * " chars " - * chars - * char - * char chars - * char - * any-Unicode-character- - * except-"-or-\-or- - * control-character - * \" - * \\ - * \/ - * \b - * \f - * \n - * \r - * \t - * \u four-hex-digits - */ -static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token) -{ - const char *ptr = token_get_value(token); - QString *str; - int double_quote = 1; - - if (*ptr == '"') { - double_quote = 1; - } else { - double_quote = 0; - } - ptr++; - - str = qstring_new(); - while (*ptr && - ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) { - if (*ptr == '\\') { - ptr++; - - switch (*ptr) { - case '"': - qstring_append(str, "\""); - ptr++; - break; - case '\'': - qstring_append(str, "'"); - ptr++; - break; - case '\\': - qstring_append(str, "\\"); - ptr++; - break; - case '/': - qstring_append(str, "/"); - ptr++; - break; - case 'b': - qstring_append(str, "\b"); - ptr++; - break; - case 'f': - qstring_append(str, "\f"); - ptr++; - break; - case 'n': - qstring_append(str, "\n"); - ptr++; - break; - case 'r': - qstring_append(str, "\r"); - ptr++; - break; - case 't': - qstring_append(str, "\t"); - ptr++; - break; - case 'u': { - uint16_t unicode_char = 0; - char utf8_char[4]; - int i = 0; - - ptr++; - - for (i = 0; i < 4; i++) { - if (qemu_isxdigit(*ptr)) { - unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4); - } else { - parse_error(ctxt, token, - "invalid hex escape sequence in string"); - goto out; - } - ptr++; - } - - wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char)); - qstring_append(str, utf8_char); - } break; - default: - parse_error(ctxt, token, "invalid escape sequence in string"); - goto out; - } - } else { - char dummy[2]; - - dummy[0] = *ptr++; - dummy[1] = 0; - - qstring_append(str, dummy); - } - } - - return str; - -out: - QDECREF(str); - return NULL; -} - -static QObject *parser_context_pop_token(JSONParserContext *ctxt) -{ - QObject *token; - g_assert(ctxt->tokens.pos < ctxt->tokens.count); - token = ctxt->tokens.buf[ctxt->tokens.pos]; - ctxt->tokens.pos++; - return token; -} - -/* Note: parser_context_{peek|pop}_token do not increment the - * token object's refcount. In both cases the references will continue - * to be tracked and cleaned up in parser_context_free(), so do not - * attempt to free the token object. - */ -static QObject *parser_context_peek_token(JSONParserContext *ctxt) -{ - QObject *token; - g_assert(ctxt->tokens.pos < ctxt->tokens.count); - token = ctxt->tokens.buf[ctxt->tokens.pos]; - return token; -} - -static JSONParserContext parser_context_save(JSONParserContext *ctxt) -{ - JSONParserContext saved_ctxt = {0}; - saved_ctxt.tokens.pos = ctxt->tokens.pos; - saved_ctxt.tokens.count = ctxt->tokens.count; - saved_ctxt.tokens.buf = ctxt->tokens.buf; - return saved_ctxt; -} - -static void parser_context_restore(JSONParserContext *ctxt, - JSONParserContext saved_ctxt) -{ - ctxt->tokens.pos = saved_ctxt.tokens.pos; - ctxt->tokens.count = saved_ctxt.tokens.count; - ctxt->tokens.buf = saved_ctxt.tokens.buf; -} - -static void tokens_append_from_iter(QObject *obj, void *opaque) -{ - JSONParserContext *ctxt = opaque; - g_assert(ctxt->tokens.pos < ctxt->tokens.count); - ctxt->tokens.buf[ctxt->tokens.pos++] = obj; - qobject_incref(obj); -} - -static JSONParserContext *parser_context_new(QList *tokens) -{ - JSONParserContext *ctxt; - size_t count; - - if (!tokens) { - return NULL; - } - - count = qlist_size(tokens); - if (count == 0) { - return NULL; - } - - ctxt = g_malloc0(sizeof(JSONParserContext)); - ctxt->tokens.pos = 0; - ctxt->tokens.count = count; - ctxt->tokens.buf = g_malloc(count * sizeof(QObject *)); - qlist_iter(tokens, tokens_append_from_iter, ctxt); - ctxt->tokens.pos = 0; - - return ctxt; -} - -/* to support error propagation, ctxt->err must be freed separately */ -static void parser_context_free(JSONParserContext *ctxt) -{ - int i; - if (ctxt) { - for (i = 0; i < ctxt->tokens.count; i++) { - qobject_decref(ctxt->tokens.buf[i]); - } - g_free(ctxt->tokens.buf); - g_free(ctxt); - } -} - -/** - * Parsing rules - */ -static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) -{ - QObject *key = NULL, *token = NULL, *value, *peek; - JSONParserContext saved_ctxt = parser_context_save(ctxt); - - peek = parser_context_peek_token(ctxt); - if (peek == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - key = parse_value(ctxt, ap); - if (!key || qobject_type(key) != QTYPE_QSTRING) { - parse_error(ctxt, peek, "key is not a string in object"); - goto out; - } - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (!token_is_operator(token, ':')) { - parse_error(ctxt, token, "missing : in object pair"); - goto out; - } - - value = parse_value(ctxt, ap); - if (value == NULL) { - parse_error(ctxt, token, "Missing value in dict"); - goto out; - } - - qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value); - - qobject_decref(key); - - return 0; - -out: - parser_context_restore(ctxt, saved_ctxt); - qobject_decref(key); - - return -1; -} - -static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) -{ - QDict *dict = NULL; - QObject *token, *peek; - JSONParserContext saved_ctxt = parser_context_save(ctxt); - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (!token_is_operator(token, '{')) { - goto out; - } - token = NULL; - - dict = qdict_new(); - - peek = parser_context_peek_token(ctxt); - if (peek == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (!token_is_operator(peek, '}')) { - if (parse_pair(ctxt, dict, ap) == -1) { - goto out; - } - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - while (!token_is_operator(token, '}')) { - if (!token_is_operator(token, ',')) { - parse_error(ctxt, token, "expected separator in dict"); - goto out; - } - token = NULL; - - if (parse_pair(ctxt, dict, ap) == -1) { - goto out; - } - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - } - token = NULL; - } else { - token = parser_context_pop_token(ctxt); - token = NULL; - } - - return QOBJECT(dict); - -out: - parser_context_restore(ctxt, saved_ctxt); - QDECREF(dict); - return NULL; -} - -static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) -{ - QList *list = NULL; - QObject *token, *peek; - JSONParserContext saved_ctxt = parser_context_save(ctxt); - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (!token_is_operator(token, '[')) { - token = NULL; - goto out; - } - token = NULL; - - list = qlist_new(); - - peek = parser_context_peek_token(ctxt); - if (peek == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (!token_is_operator(peek, ']')) { - QObject *obj; - - obj = parse_value(ctxt, ap); - if (obj == NULL) { - parse_error(ctxt, token, "expecting value"); - goto out; - } - - qlist_append_obj(list, obj); - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - while (!token_is_operator(token, ']')) { - if (!token_is_operator(token, ',')) { - parse_error(ctxt, token, "expected separator in list"); - goto out; - } - - token = NULL; - - obj = parse_value(ctxt, ap); - if (obj == NULL) { - parse_error(ctxt, token, "expecting value"); - goto out; - } - - qlist_append_obj(list, obj); - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - } - - token = NULL; - } else { - token = parser_context_pop_token(ctxt); - token = NULL; - } - - return QOBJECT(list); - -out: - parser_context_restore(ctxt, saved_ctxt); - QDECREF(list); - return NULL; -} - -static QObject *parse_keyword(JSONParserContext *ctxt) -{ - QObject *token, *ret; - JSONParserContext saved_ctxt = parser_context_save(ctxt); - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (token_get_type(token) != JSON_KEYWORD) { - goto out; - } - - if (token_is_keyword(token, "true")) { - ret = QOBJECT(qbool_from_int(true)); - } else if (token_is_keyword(token, "false")) { - ret = QOBJECT(qbool_from_int(false)); - } else { - parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token)); - goto out; - } - - return ret; - -out: - parser_context_restore(ctxt, saved_ctxt); - - return NULL; -} - -static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) -{ - QObject *token = NULL, *obj; - JSONParserContext saved_ctxt = parser_context_save(ctxt); - - if (ap == NULL) { - goto out; - } - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - if (token_is_escape(token, "%p")) { - obj = va_arg(*ap, QObject *); - } else if (token_is_escape(token, "%i")) { - obj = QOBJECT(qbool_from_int(va_arg(*ap, int))); - } else if (token_is_escape(token, "%d")) { - obj = QOBJECT(qint_from_int(va_arg(*ap, int))); - } else if (token_is_escape(token, "%ld")) { - obj = QOBJECT(qint_from_int(va_arg(*ap, long))); - } else if (token_is_escape(token, "%lld") || - token_is_escape(token, "%I64d")) { - obj = QOBJECT(qint_from_int(va_arg(*ap, long long))); - } else if (token_is_escape(token, "%s")) { - obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *))); - } else if (token_is_escape(token, "%f")) { - obj = QOBJECT(qfloat_from_double(va_arg(*ap, double))); - } else { - goto out; - } - - return obj; - -out: - parser_context_restore(ctxt, saved_ctxt); - - return NULL; -} - -static QObject *parse_literal(JSONParserContext *ctxt) -{ - QObject *token, *obj; - JSONParserContext saved_ctxt = parser_context_save(ctxt); - - token = parser_context_pop_token(ctxt); - if (token == NULL) { - goto out; - } - - switch (token_get_type(token)) { - case JSON_STRING: - obj = QOBJECT(qstring_from_escaped_str(ctxt, token)); - break; - case JSON_INTEGER: - obj = QOBJECT(qint_from_int(strtoll(token_get_value(token), NULL, 10))); - break; - case JSON_FLOAT: - /* FIXME dependent on locale */ - obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL))); - break; - default: - goto out; - } - - return obj; - -out: - parser_context_restore(ctxt, saved_ctxt); - - return NULL; -} - -static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) -{ - QObject *obj; - - obj = parse_object(ctxt, ap); - if (obj == NULL) { - obj = parse_array(ctxt, ap); - } - if (obj == NULL) { - obj = parse_escape(ctxt, ap); - } - if (obj == NULL) { - obj = parse_keyword(ctxt); - } - if (obj == NULL) { - obj = parse_literal(ctxt); - } - - return obj; -} - -QObject *json_parser_parse(QList *tokens, va_list *ap) -{ - return json_parser_parse_err(tokens, ap, NULL); -} - -QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp) -{ - JSONParserContext *ctxt = parser_context_new(tokens); - QObject *result; - - if (!ctxt) { - return NULL; - } - - result = parse_value(ctxt, ap); - - error_propagate(errp, ctxt->err); - - parser_context_free(ctxt); - - return result; -} diff --git a/json-streamer.c b/json-streamer.c deleted file mode 100644 index 1b2f9b1..0000000 --- a/json-streamer.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * JSON streaming support - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qdict.h" -#include "qemu-common.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" - -#define MAX_TOKEN_SIZE (64ULL << 20) -#define MAX_NESTING (1ULL << 10) - -static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y) -{ - JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); - QDict *dict; - - if (type == JSON_OPERATOR) { - switch (qstring_get_str(token)[0]) { - case '{': - parser->brace_count++; - break; - case '}': - parser->brace_count--; - break; - case '[': - parser->bracket_count++; - break; - case ']': - parser->bracket_count--; - break; - default: - break; - } - } - - dict = qdict_new(); - qdict_put(dict, "type", qint_from_int(type)); - QINCREF(token); - qdict_put(dict, "token", token); - qdict_put(dict, "x", qint_from_int(x)); - qdict_put(dict, "y", qint_from_int(y)); - - parser->token_size += token->length; - - qlist_append(parser->tokens, dict); - - if (type == JSON_ERROR) { - goto out_emit_bad; - } else if (parser->brace_count < 0 || - parser->bracket_count < 0 || - (parser->brace_count == 0 && - parser->bracket_count == 0)) { - goto out_emit; - } else if (parser->token_size > MAX_TOKEN_SIZE || - parser->bracket_count > MAX_NESTING || - parser->brace_count > MAX_NESTING) { - /* Security consideration, we limit total memory allocated per object - * and the maximum recursion depth that a message can force. - */ - goto out_emit; - } - - return; - -out_emit_bad: - /* clear out token list and tell the parser to emit and error - * indication by passing it a NULL list - */ - QDECREF(parser->tokens); - parser->tokens = NULL; -out_emit: - /* send current list of tokens to parser and reset tokenizer */ - parser->brace_count = 0; - parser->bracket_count = 0; - parser->emit(parser, parser->tokens); - if (parser->tokens) { - QDECREF(parser->tokens); - } - parser->tokens = qlist_new(); - parser->token_size = 0; -} - -void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, QList *)) -{ - parser->emit = func; - parser->brace_count = 0; - parser->bracket_count = 0; - parser->tokens = qlist_new(); - parser->token_size = 0; - - json_lexer_init(&parser->lexer, json_message_process_token); -} - -int json_message_parser_feed(JSONMessageParser *parser, - const char *buffer, size_t size) -{ - return json_lexer_feed(&parser->lexer, buffer, size); -} - -int json_message_parser_flush(JSONMessageParser *parser) -{ - return json_lexer_flush(&parser->lexer); -} - -void json_message_parser_destroy(JSONMessageParser *parser) -{ - json_lexer_destroy(&parser->lexer); - QDECREF(parser->tokens); -} diff --git a/qbool.c b/qbool.c deleted file mode 100644 index a3d2afa..0000000 --- a/qbool.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * QBool Module - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qobject.h" -#include "qemu-common.h" - -static void qbool_destroy_obj(QObject *obj); - -static const QType qbool_type = { - .code = QTYPE_QBOOL, - .destroy = qbool_destroy_obj, -}; - -/** - * qbool_from_int(): Create a new QBool from an int - * - * Return strong reference. - */ -QBool *qbool_from_int(int value) -{ - QBool *qb; - - qb = g_malloc(sizeof(*qb)); - qb->value = value; - QOBJECT_INIT(qb, &qbool_type); - - return qb; -} - -/** - * qbool_get_int(): Get the stored int - */ -int qbool_get_int(const QBool *qb) -{ - return qb->value; -} - -/** - * qobject_to_qbool(): Convert a QObject into a QBool - */ -QBool *qobject_to_qbool(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QBOOL) - return NULL; - - return container_of(obj, QBool, base); -} - -/** - * qbool_destroy_obj(): Free all memory allocated by a - * QBool object - */ -static void qbool_destroy_obj(QObject *obj) -{ - assert(obj != NULL); - g_free(qobject_to_qbool(obj)); -} diff --git a/qdict.c b/qdict.c deleted file mode 100644 index 7543ccc..0000000 --- a/qdict.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * QDict Module - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qobject.h" -#include "qemu/queue.h" -#include "qemu-common.h" - -static void qdict_destroy_obj(QObject *obj); - -static const QType qdict_type = { - .code = QTYPE_QDICT, - .destroy = qdict_destroy_obj, -}; - -/** - * qdict_new(): Create a new QDict - * - * Return strong reference. - */ -QDict *qdict_new(void) -{ - QDict *qdict; - - qdict = g_malloc0(sizeof(*qdict)); - QOBJECT_INIT(qdict, &qdict_type); - - return qdict; -} - -/** - * qobject_to_qdict(): Convert a QObject into a QDict - */ -QDict *qobject_to_qdict(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QDICT) - return NULL; - - return container_of(obj, QDict, base); -} - -/** - * tdb_hash(): based on the hash agorithm from gdbm, via tdb - * (from module-init-tools) - */ -static unsigned int tdb_hash(const char *name) -{ - unsigned value; /* Used to compute the hash value. */ - unsigned i; /* Used to cycle through random values. */ - - /* Set the initial value from the key size. */ - for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++) - value = (value + (((const unsigned char *)name)[i] << (i*5 % 24))); - - return (1103515243 * value + 12345); -} - -/** - * alloc_entry(): allocate a new QDictEntry - */ -static QDictEntry *alloc_entry(const char *key, QObject *value) -{ - QDictEntry *entry; - - entry = g_malloc0(sizeof(*entry)); - entry->key = g_strdup(key); - entry->value = value; - - return entry; -} - -/** - * qdict_entry_value(): Return qdict entry value - * - * Return weak reference. - */ -QObject *qdict_entry_value(const QDictEntry *entry) -{ - return entry->value; -} - -/** - * qdict_entry_key(): Return qdict entry key - * - * Return a *pointer* to the string, it has to be duplicated before being - * stored. - */ -const char *qdict_entry_key(const QDictEntry *entry) -{ - return entry->key; -} - -/** - * qdict_find(): List lookup function - */ -static QDictEntry *qdict_find(const QDict *qdict, - const char *key, unsigned int bucket) -{ - QDictEntry *entry; - - QLIST_FOREACH(entry, &qdict->table[bucket], next) - if (!strcmp(entry->key, key)) - return entry; - - return NULL; -} - -/** - * qdict_put_obj(): Put a new QObject into the dictionary - * - * Insert the pair 'key:value' into 'qdict', if 'key' already exists - * its 'value' will be replaced. - * - * This is done by freeing the reference to the stored QObject and - * storing the new one in the same entry. - * - * NOTE: ownership of 'value' is transferred to the QDict - */ -void qdict_put_obj(QDict *qdict, const char *key, QObject *value) -{ - unsigned int bucket; - QDictEntry *entry; - - bucket = tdb_hash(key) % QDICT_BUCKET_MAX; - entry = qdict_find(qdict, key, bucket); - if (entry) { - /* replace key's value */ - qobject_decref(entry->value); - entry->value = value; - } else { - /* allocate a new entry */ - entry = alloc_entry(key, value); - QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next); - qdict->size++; - } -} - -/** - * qdict_get(): Lookup for a given 'key' - * - * Return a weak reference to the QObject associated with 'key' if - * 'key' is present in the dictionary, NULL otherwise. - */ -QObject *qdict_get(const QDict *qdict, const char *key) -{ - QDictEntry *entry; - - entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); - return (entry == NULL ? NULL : entry->value); -} - -/** - * qdict_haskey(): Check if 'key' exists - * - * Return 1 if 'key' exists in the dict, 0 otherwise - */ -int qdict_haskey(const QDict *qdict, const char *key) -{ - unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX; - return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1); -} - -/** - * qdict_size(): Return the size of the dictionary - */ -size_t qdict_size(const QDict *qdict) -{ - return qdict->size; -} - -/** - * qdict_get_obj(): Get a QObject of a specific type - */ -static QObject *qdict_get_obj(const QDict *qdict, const char *key, - qtype_code type) -{ - QObject *obj; - - obj = qdict_get(qdict, key); - assert(obj != NULL); - assert(qobject_type(obj) == type); - - return obj; -} - -/** - * qdict_get_double(): Get an number mapped by 'key' - * - * This function assumes that 'key' exists and it stores a - * QFloat or QInt object. - * - * Return number mapped by 'key'. - */ -double qdict_get_double(const QDict *qdict, const char *key) -{ - QObject *obj = qdict_get(qdict, key); - - assert(obj); - switch (qobject_type(obj)) { - case QTYPE_QFLOAT: - return qfloat_get_double(qobject_to_qfloat(obj)); - case QTYPE_QINT: - return qint_get_int(qobject_to_qint(obj)); - default: - abort(); - } -} - -/** - * qdict_get_int(): Get an integer mapped by 'key' - * - * This function assumes that 'key' exists and it stores a - * QInt object. - * - * Return integer mapped by 'key'. - */ -int64_t qdict_get_int(const QDict *qdict, const char *key) -{ - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QINT); - return qint_get_int(qobject_to_qint(obj)); -} - -/** - * qdict_get_bool(): Get a bool mapped by 'key' - * - * This function assumes that 'key' exists and it stores a - * QBool object. - * - * Return bool mapped by 'key'. - */ -int qdict_get_bool(const QDict *qdict, const char *key) -{ - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QBOOL); - return qbool_get_int(qobject_to_qbool(obj)); -} - -/** - * qdict_get_qlist(): Get the QList mapped by 'key' - * - * This function assumes that 'key' exists and it stores a - * QList object. - * - * Return QList mapped by 'key'. - */ -QList *qdict_get_qlist(const QDict *qdict, const char *key) -{ - return qobject_to_qlist(qdict_get_obj(qdict, key, QTYPE_QLIST)); -} - -/** - * qdict_get_qdict(): Get the QDict mapped by 'key' - * - * This function assumes that 'key' exists and it stores a - * QDict object. - * - * Return QDict mapped by 'key'. - */ -QDict *qdict_get_qdict(const QDict *qdict, const char *key) -{ - return qobject_to_qdict(qdict_get_obj(qdict, key, QTYPE_QDICT)); -} - -/** - * qdict_get_str(): Get a pointer to the stored string mapped - * by 'key' - * - * This function assumes that 'key' exists and it stores a - * QString object. - * - * Return pointer to the string mapped by 'key'. - */ -const char *qdict_get_str(const QDict *qdict, const char *key) -{ - QObject *obj = qdict_get_obj(qdict, key, QTYPE_QSTRING); - return qstring_get_str(qobject_to_qstring(obj)); -} - -/** - * qdict_get_try_int(): Try to get integer mapped by 'key' - * - * Return integer mapped by 'key', if it is not present in - * the dictionary or if the stored object is not of QInt type - * 'def_value' will be returned. - */ -int64_t qdict_get_try_int(const QDict *qdict, const char *key, - int64_t def_value) -{ - QObject *obj; - - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QINT) - return def_value; - - return qint_get_int(qobject_to_qint(obj)); -} - -/** - * qdict_get_try_bool(): Try to get a bool mapped by 'key' - * - * Return bool mapped by 'key', if it is not present in the - * dictionary or if the stored object is not of QBool type - * 'def_value' will be returned. - */ -int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value) -{ - QObject *obj; - - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QBOOL) - return def_value; - - return qbool_get_int(qobject_to_qbool(obj)); -} - -/** - * qdict_get_try_str(): Try to get a pointer to the stored string - * mapped by 'key' - * - * Return a pointer to the string mapped by 'key', if it is not present - * in the dictionary or if the stored object is not of QString type - * NULL will be returned. - */ -const char *qdict_get_try_str(const QDict *qdict, const char *key) -{ - QObject *obj; - - obj = qdict_get(qdict, key); - if (!obj || qobject_type(obj) != QTYPE_QSTRING) - return NULL; - - return qstring_get_str(qobject_to_qstring(obj)); -} - -/** - * qdict_iter(): Iterate over all the dictionary's stored values. - * - * This function allows the user to provide an iterator, which will be - * called for each stored value in the dictionary. - */ -void qdict_iter(const QDict *qdict, - void (*iter)(const char *key, QObject *obj, void *opaque), - void *opaque) -{ - int i; - QDictEntry *entry; - - for (i = 0; i < QDICT_BUCKET_MAX; i++) { - QLIST_FOREACH(entry, &qdict->table[i], next) - iter(entry->key, entry->value, opaque); - } -} - -static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket) -{ - int i; - - for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) { - if (!QLIST_EMPTY(&qdict->table[i])) { - return QLIST_FIRST(&qdict->table[i]); - } - } - - return NULL; -} - -/** - * qdict_first(): Return first qdict entry for iteration. - */ -const QDictEntry *qdict_first(const QDict *qdict) -{ - return qdict_next_entry(qdict, 0); -} - -/** - * qdict_next(): Return next qdict entry in an iteration. - */ -const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry) -{ - QDictEntry *ret; - - ret = QLIST_NEXT(entry, next); - if (!ret) { - unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX; - ret = qdict_next_entry(qdict, bucket + 1); - } - - return ret; -} - -/** - * qentry_destroy(): Free all the memory allocated by a QDictEntry - */ -static void qentry_destroy(QDictEntry *e) -{ - assert(e != NULL); - assert(e->key != NULL); - assert(e->value != NULL); - - qobject_decref(e->value); - g_free(e->key); - g_free(e); -} - -/** - * qdict_del(): Delete a 'key:value' pair from the dictionary - * - * This will destroy all data allocated by this entry. - */ -void qdict_del(QDict *qdict, const char *key) -{ - QDictEntry *entry; - - entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); - if (entry) { - QLIST_REMOVE(entry, next); - qentry_destroy(entry); - qdict->size--; - } -} - -/** - * qdict_destroy_obj(): Free all the memory allocated by a QDict - */ -static void qdict_destroy_obj(QObject *obj) -{ - int i; - QDict *qdict; - - assert(obj != NULL); - qdict = qobject_to_qdict(obj); - - for (i = 0; i < QDICT_BUCKET_MAX; i++) { - QDictEntry *entry = QLIST_FIRST(&qdict->table[i]); - while (entry) { - QDictEntry *tmp = QLIST_NEXT(entry, next); - QLIST_REMOVE(entry, next); - qentry_destroy(entry); - entry = tmp; - } - } - - g_free(qdict); -} diff --git a/qerror.c b/qerror.c deleted file mode 100644 index 3aee1cf..0000000 --- a/qerror.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * QError Module - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "monitor/monitor.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qerror.h" -#include "qemu-common.h" - -static void qerror_destroy_obj(QObject *obj); - -static const QType qerror_type = { - .code = QTYPE_QERROR, - .destroy = qerror_destroy_obj, -}; - -/** - * qerror_new(): Create a new QError - * - * Return strong reference. - */ -static QError *qerror_new(void) -{ - QError *qerr; - - qerr = g_malloc0(sizeof(*qerr)); - QOBJECT_INIT(qerr, &qerror_type); - - return qerr; -} - -/** - * qerror_from_info(): Create a new QError from error information - * - * Return strong reference. - */ -static QError *qerror_from_info(ErrorClass err_class, const char *fmt, - va_list *va) -{ - QError *qerr; - - qerr = qerror_new(); - loc_save(&qerr->loc); - - qerr->err_msg = g_strdup_vprintf(fmt, *va); - qerr->err_class = err_class; - - return qerr; -} - -/** - * qerror_human(): Format QError data into human-readable string. - */ -QString *qerror_human(const QError *qerror) -{ - return qstring_from_str(qerror->err_msg); -} - -/** - * qerror_print(): Print QError data - * - * This function will print the member 'desc' of the specified QError object, - * it uses error_report() for this, so that the output is routed to the right - * place (ie. stderr or Monitor's device). - */ -static void qerror_print(QError *qerror) -{ - QString *qstring = qerror_human(qerror); - loc_push_restore(&qerror->loc); - error_report("%s", qstring_get_str(qstring)); - loc_pop(&qerror->loc); - QDECREF(qstring); -} - -void qerror_report(ErrorClass eclass, const char *fmt, ...) -{ - va_list va; - QError *qerror; - - va_start(va, fmt); - qerror = qerror_from_info(eclass, fmt, &va); - va_end(va); - - if (monitor_cur_is_qmp()) { - monitor_set_error(cur_mon, qerror); - } else { - qerror_print(qerror); - QDECREF(qerror); - } -} - -/* Evil... */ -struct Error -{ - char *msg; - ErrorClass err_class; -}; - -void qerror_report_err(Error *err) -{ - QError *qerr; - - qerr = qerror_new(); - loc_save(&qerr->loc); - qerr->err_msg = g_strdup(err->msg); - qerr->err_class = err->err_class; - - if (monitor_cur_is_qmp()) { - monitor_set_error(cur_mon, qerr); - } else { - qerror_print(qerr); - QDECREF(qerr); - } -} - -void assert_no_error(Error *err) -{ - if (err) { - qerror_report_err(err); - abort(); - } -} - -/** - * qobject_to_qerror(): Convert a QObject into a QError - */ -static QError *qobject_to_qerror(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QERROR) { - return NULL; - } - - return container_of(obj, QError, base); -} - -/** - * qerror_destroy_obj(): Free all memory allocated by a QError - */ -static void qerror_destroy_obj(QObject *obj) -{ - QError *qerr; - - assert(obj != NULL); - qerr = qobject_to_qerror(obj); - - g_free(qerr->err_msg); - g_free(qerr); -} diff --git a/qfloat.c b/qfloat.c deleted file mode 100644 index 7de0992..0000000 --- a/qfloat.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * QFloat Module - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qobject.h" -#include "qemu-common.h" - -static void qfloat_destroy_obj(QObject *obj); - -static const QType qfloat_type = { - .code = QTYPE_QFLOAT, - .destroy = qfloat_destroy_obj, -}; - -/** - * qfloat_from_int(): Create a new QFloat from a float - * - * Return strong reference. - */ -QFloat *qfloat_from_double(double value) -{ - QFloat *qf; - - qf = g_malloc(sizeof(*qf)); - qf->value = value; - QOBJECT_INIT(qf, &qfloat_type); - - return qf; -} - -/** - * qfloat_get_double(): Get the stored float - */ -double qfloat_get_double(const QFloat *qf) -{ - return qf->value; -} - -/** - * qobject_to_qfloat(): Convert a QObject into a QFloat - */ -QFloat *qobject_to_qfloat(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QFLOAT) - return NULL; - - return container_of(obj, QFloat, base); -} - -/** - * qfloat_destroy_obj(): Free all memory allocated by a - * QFloat object - */ -static void qfloat_destroy_obj(QObject *obj) -{ - assert(obj != NULL); - g_free(qobject_to_qfloat(obj)); -} diff --git a/qint.c b/qint.c deleted file mode 100644 index 86b9b04..0000000 --- a/qint.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * QInt Module - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qobject.h" -#include "qemu-common.h" - -static void qint_destroy_obj(QObject *obj); - -static const QType qint_type = { - .code = QTYPE_QINT, - .destroy = qint_destroy_obj, -}; - -/** - * qint_from_int(): Create a new QInt from an int64_t - * - * Return strong reference. - */ -QInt *qint_from_int(int64_t value) -{ - QInt *qi; - - qi = g_malloc(sizeof(*qi)); - qi->value = value; - QOBJECT_INIT(qi, &qint_type); - - return qi; -} - -/** - * qint_get_int(): Get the stored integer - */ -int64_t qint_get_int(const QInt *qi) -{ - return qi->value; -} - -/** - * qobject_to_qint(): Convert a QObject into a QInt - */ -QInt *qobject_to_qint(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QINT) - return NULL; - - return container_of(obj, QInt, base); -} - -/** - * qint_destroy_obj(): Free all memory allocated by a - * QInt object - */ -static void qint_destroy_obj(QObject *obj) -{ - assert(obj != NULL); - g_free(qobject_to_qint(obj)); -} diff --git a/qjson.c b/qjson.c deleted file mode 100644 index 83a6b4f..0000000 --- a/qjson.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * QObject JSON integration - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-streamer.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qdict.h" - -typedef struct JSONParsingState -{ - JSONMessageParser parser; - va_list *ap; - QObject *result; -} JSONParsingState; - -static void parse_json(JSONMessageParser *parser, QList *tokens) -{ - JSONParsingState *s = container_of(parser, JSONParsingState, parser); - s->result = json_parser_parse(tokens, s->ap); -} - -QObject *qobject_from_jsonv(const char *string, va_list *ap) -{ - JSONParsingState state = {}; - - state.ap = ap; - - json_message_parser_init(&state.parser, parse_json); - json_message_parser_feed(&state.parser, string, strlen(string)); - json_message_parser_flush(&state.parser); - json_message_parser_destroy(&state.parser); - - return state.result; -} - -QObject *qobject_from_json(const char *string) -{ - return qobject_from_jsonv(string, NULL); -} - -/* - * IMPORTANT: This function aborts on error, thus it must not - * be used with untrusted arguments. - */ -QObject *qobject_from_jsonf(const char *string, ...) -{ - QObject *obj; - va_list ap; - - va_start(ap, string); - obj = qobject_from_jsonv(string, &ap); - va_end(ap); - - assert(obj != NULL); - return obj; -} - -typedef struct ToJsonIterState -{ - int indent; - int pretty; - int count; - QString *str; -} ToJsonIterState; - -static void to_json(const QObject *obj, QString *str, int pretty, int indent); - -static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) -{ - ToJsonIterState *s = opaque; - QString *qkey; - int j; - - if (s->count) - qstring_append(s->str, ", "); - - if (s->pretty) { - qstring_append(s->str, "\n"); - for (j = 0 ; j < s->indent ; j++) - qstring_append(s->str, " "); - } - - qkey = qstring_from_str(key); - to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); - QDECREF(qkey); - - qstring_append(s->str, ": "); - to_json(obj, s->str, s->pretty, s->indent); - s->count++; -} - -static void to_json_list_iter(QObject *obj, void *opaque) -{ - ToJsonIterState *s = opaque; - int j; - - if (s->count) - qstring_append(s->str, ", "); - - if (s->pretty) { - qstring_append(s->str, "\n"); - for (j = 0 ; j < s->indent ; j++) - qstring_append(s->str, " "); - } - - to_json(obj, s->str, s->pretty, s->indent); - s->count++; -} - -static void to_json(const QObject *obj, QString *str, int pretty, int indent) -{ - switch (qobject_type(obj)) { - case QTYPE_QINT: { - QInt *val = qobject_to_qint(obj); - char buffer[1024]; - - snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val)); - qstring_append(str, buffer); - break; - } - case QTYPE_QSTRING: { - QString *val = qobject_to_qstring(obj); - const char *ptr; - - ptr = qstring_get_str(val); - qstring_append(str, "\""); - while (*ptr) { - if ((ptr[0] & 0xE0) == 0xE0 && - (ptr[1] & 0x80) && (ptr[2] & 0x80)) { - uint16_t wchar; - char escape[7]; - - wchar = (ptr[0] & 0x0F) << 12; - wchar |= (ptr[1] & 0x3F) << 6; - wchar |= (ptr[2] & 0x3F); - ptr += 2; - - snprintf(escape, sizeof(escape), "\\u%04X", wchar); - qstring_append(str, escape); - } else if ((ptr[0] & 0xE0) == 0xC0 && (ptr[1] & 0x80)) { - uint16_t wchar; - char escape[7]; - - wchar = (ptr[0] & 0x1F) << 6; - wchar |= (ptr[1] & 0x3F); - ptr++; - - snprintf(escape, sizeof(escape), "\\u%04X", wchar); - qstring_append(str, escape); - } else switch (ptr[0]) { - case '\"': - qstring_append(str, "\\\""); - break; - case '\\': - qstring_append(str, "\\\\"); - break; - case '\b': - qstring_append(str, "\\b"); - break; - case '\f': - qstring_append(str, "\\f"); - break; - case '\n': - qstring_append(str, "\\n"); - break; - case '\r': - qstring_append(str, "\\r"); - break; - case '\t': - qstring_append(str, "\\t"); - break; - default: { - if (ptr[0] <= 0x1F) { - char escape[7]; - snprintf(escape, sizeof(escape), "\\u%04X", ptr[0]); - qstring_append(str, escape); - } else { - char buf[2] = { ptr[0], 0 }; - qstring_append(str, buf); - } - break; - } - } - ptr++; - } - qstring_append(str, "\""); - break; - } - case QTYPE_QDICT: { - ToJsonIterState s; - QDict *val = qobject_to_qdict(obj); - - s.count = 0; - s.str = str; - s.indent = indent + 1; - s.pretty = pretty; - qstring_append(str, "{"); - qdict_iter(val, to_json_dict_iter, &s); - if (pretty) { - int j; - qstring_append(str, "\n"); - for (j = 0 ; j < indent ; j++) - qstring_append(str, " "); - } - qstring_append(str, "}"); - break; - } - case QTYPE_QLIST: { - ToJsonIterState s; - QList *val = qobject_to_qlist(obj); - - s.count = 0; - s.str = str; - s.indent = indent + 1; - s.pretty = pretty; - qstring_append(str, "["); - qlist_iter(val, (void *)to_json_list_iter, &s); - if (pretty) { - int j; - qstring_append(str, "\n"); - for (j = 0 ; j < indent ; j++) - qstring_append(str, " "); - } - qstring_append(str, "]"); - break; - } - case QTYPE_QFLOAT: { - QFloat *val = qobject_to_qfloat(obj); - char buffer[1024]; - int len; - - len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val)); - while (len > 0 && buffer[len - 1] == '0') { - len--; - } - - if (len && buffer[len - 1] == '.') { - buffer[len - 1] = 0; - } else { - buffer[len] = 0; - } - - qstring_append(str, buffer); - break; - } - case QTYPE_QBOOL: { - QBool *val = qobject_to_qbool(obj); - - if (qbool_get_int(val)) { - qstring_append(str, "true"); - } else { - qstring_append(str, "false"); - } - break; - } - case QTYPE_QERROR: - /* XXX: should QError be emitted? */ - case QTYPE_NONE: - break; - } -} - -QString *qobject_to_json(const QObject *obj) -{ - QString *str = qstring_new(); - - to_json(obj, str, 0, 0); - - return str; -} - -QString *qobject_to_json_pretty(const QObject *obj) -{ - QString *str = qstring_new(); - - to_json(obj, str, 1, 0); - - return str; -} diff --git a/qlist.c b/qlist.c deleted file mode 100644 index 1ced0de..0000000 --- a/qlist.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * QList Module - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qobject.h" -#include "qemu/queue.h" -#include "qemu-common.h" - -static void qlist_destroy_obj(QObject *obj); - -static const QType qlist_type = { - .code = QTYPE_QLIST, - .destroy = qlist_destroy_obj, -}; - -/** - * qlist_new(): Create a new QList - * - * Return strong reference. - */ -QList *qlist_new(void) -{ - QList *qlist; - - qlist = g_malloc(sizeof(*qlist)); - QTAILQ_INIT(&qlist->head); - QOBJECT_INIT(qlist, &qlist_type); - - return qlist; -} - -static void qlist_copy_elem(QObject *obj, void *opaque) -{ - QList *dst = opaque; - - qobject_incref(obj); - qlist_append_obj(dst, obj); -} - -QList *qlist_copy(QList *src) -{ - QList *dst = qlist_new(); - - qlist_iter(src, qlist_copy_elem, dst); - - return dst; -} - -/** - * qlist_append_obj(): Append an QObject into QList - * - * NOTE: ownership of 'value' is transferred to the QList - */ -void qlist_append_obj(QList *qlist, QObject *value) -{ - QListEntry *entry; - - entry = g_malloc(sizeof(*entry)); - entry->value = value; - - QTAILQ_INSERT_TAIL(&qlist->head, entry, next); -} - -/** - * qlist_iter(): Iterate over all the list's stored values. - * - * This function allows the user to provide an iterator, which will be - * called for each stored value in the list. - */ -void qlist_iter(const QList *qlist, - void (*iter)(QObject *obj, void *opaque), void *opaque) -{ - QListEntry *entry; - - QTAILQ_FOREACH(entry, &qlist->head, next) - iter(entry->value, opaque); -} - -QObject *qlist_pop(QList *qlist) -{ - QListEntry *entry; - QObject *ret; - - if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { - return NULL; - } - - entry = QTAILQ_FIRST(&qlist->head); - QTAILQ_REMOVE(&qlist->head, entry, next); - - ret = entry->value; - g_free(entry); - - return ret; -} - -QObject *qlist_peek(QList *qlist) -{ - QListEntry *entry; - QObject *ret; - - if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { - return NULL; - } - - entry = QTAILQ_FIRST(&qlist->head); - - ret = entry->value; - - return ret; -} - -int qlist_empty(const QList *qlist) -{ - return QTAILQ_EMPTY(&qlist->head); -} - -static void qlist_size_iter(QObject *obj, void *opaque) -{ - size_t *count = opaque; - (*count)++; -} - -size_t qlist_size(const QList *qlist) -{ - size_t count = 0; - qlist_iter(qlist, qlist_size_iter, &count); - return count; -} - -/** - * qobject_to_qlist(): Convert a QObject into a QList - */ -QList *qobject_to_qlist(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QLIST) { - return NULL; - } - - return container_of(obj, QList, base); -} - -/** - * qlist_destroy_obj(): Free all the memory allocated by a QList - */ -static void qlist_destroy_obj(QObject *obj) -{ - QList *qlist; - QListEntry *entry, *next_entry; - - assert(obj != NULL); - qlist = qobject_to_qlist(obj); - - QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { - QTAILQ_REMOVE(&qlist->head, entry, next); - qobject_decref(entry->value); - g_free(entry); - } - - g_free(qlist); -} diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs new file mode 100644 index 0000000..c9ff59c --- /dev/null +++ b/qobject/Makefile.objs @@ -0,0 +1,3 @@ +util-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o +util-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o +util-obj-y += qerror.o diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c new file mode 100644 index 0000000..440df60 --- /dev/null +++ b/qobject/json-lexer.c @@ -0,0 +1,373 @@ +/* + * JSON lexer + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qint.h" +#include "qemu-common.h" +#include "qapi/qmp/json-lexer.h" + +#define MAX_TOKEN_SIZE (64ULL << 20) + +/* + * \"([^\\\"]|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*\" + * '([^\\']|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*' + * 0|([1-9][0-9]*(.[0-9]+)?([eE]([-+])?[0-9]+)) + * [{}\[\],:] + * [a-z]+ + * + */ + +enum json_lexer_state { + IN_ERROR = 0, + IN_DQ_UCODE3, + IN_DQ_UCODE2, + IN_DQ_UCODE1, + IN_DQ_UCODE0, + IN_DQ_STRING_ESCAPE, + IN_DQ_STRING, + IN_SQ_UCODE3, + IN_SQ_UCODE2, + IN_SQ_UCODE1, + IN_SQ_UCODE0, + IN_SQ_STRING_ESCAPE, + IN_SQ_STRING, + IN_ZERO, + IN_DIGITS, + IN_DIGIT, + IN_EXP_E, + IN_MANTISSA, + IN_MANTISSA_DIGITS, + IN_NONZERO_NUMBER, + IN_NEG_NONZERO_NUMBER, + IN_KEYWORD, + IN_ESCAPE, + IN_ESCAPE_L, + IN_ESCAPE_LL, + IN_ESCAPE_I, + IN_ESCAPE_I6, + IN_ESCAPE_I64, + IN_WHITESPACE, + IN_START, +}; + +#define TERMINAL(state) [0 ... 0x7F] = (state) + +/* Return whether TERMINAL is a terminal state and the transition to it + from OLD_STATE required lookahead. This happens whenever the table + below uses the TERMINAL macro. */ +#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \ + (json_lexer[(old_state)][0] == (terminal)) + +static const uint8_t json_lexer[][256] = { + /* double quote string */ + [IN_DQ_UCODE3] = { + ['0' ... '9'] = IN_DQ_STRING, + ['a' ... 'f'] = IN_DQ_STRING, + ['A' ... 'F'] = IN_DQ_STRING, + }, + [IN_DQ_UCODE2] = { + ['0' ... '9'] = IN_DQ_UCODE3, + ['a' ... 'f'] = IN_DQ_UCODE3, + ['A' ... 'F'] = IN_DQ_UCODE3, + }, + [IN_DQ_UCODE1] = { + ['0' ... '9'] = IN_DQ_UCODE2, + ['a' ... 'f'] = IN_DQ_UCODE2, + ['A' ... 'F'] = IN_DQ_UCODE2, + }, + [IN_DQ_UCODE0] = { + ['0' ... '9'] = IN_DQ_UCODE1, + ['a' ... 'f'] = IN_DQ_UCODE1, + ['A' ... 'F'] = IN_DQ_UCODE1, + }, + [IN_DQ_STRING_ESCAPE] = { + ['b'] = IN_DQ_STRING, + ['f'] = IN_DQ_STRING, + ['n'] = IN_DQ_STRING, + ['r'] = IN_DQ_STRING, + ['t'] = IN_DQ_STRING, + ['/'] = IN_DQ_STRING, + ['\\'] = IN_DQ_STRING, + ['\''] = IN_DQ_STRING, + ['\"'] = IN_DQ_STRING, + ['u'] = IN_DQ_UCODE0, + }, + [IN_DQ_STRING] = { + [1 ... 0xBF] = IN_DQ_STRING, + [0xC2 ... 0xF4] = IN_DQ_STRING, + ['\\'] = IN_DQ_STRING_ESCAPE, + ['"'] = JSON_STRING, + }, + + /* single quote string */ + [IN_SQ_UCODE3] = { + ['0' ... '9'] = IN_SQ_STRING, + ['a' ... 'f'] = IN_SQ_STRING, + ['A' ... 'F'] = IN_SQ_STRING, + }, + [IN_SQ_UCODE2] = { + ['0' ... '9'] = IN_SQ_UCODE3, + ['a' ... 'f'] = IN_SQ_UCODE3, + ['A' ... 'F'] = IN_SQ_UCODE3, + }, + [IN_SQ_UCODE1] = { + ['0' ... '9'] = IN_SQ_UCODE2, + ['a' ... 'f'] = IN_SQ_UCODE2, + ['A' ... 'F'] = IN_SQ_UCODE2, + }, + [IN_SQ_UCODE0] = { + ['0' ... '9'] = IN_SQ_UCODE1, + ['a' ... 'f'] = IN_SQ_UCODE1, + ['A' ... 'F'] = IN_SQ_UCODE1, + }, + [IN_SQ_STRING_ESCAPE] = { + ['b'] = IN_SQ_STRING, + ['f'] = IN_SQ_STRING, + ['n'] = IN_SQ_STRING, + ['r'] = IN_SQ_STRING, + ['t'] = IN_SQ_STRING, + ['/'] = IN_DQ_STRING, + ['\\'] = IN_DQ_STRING, + ['\''] = IN_SQ_STRING, + ['\"'] = IN_SQ_STRING, + ['u'] = IN_SQ_UCODE0, + }, + [IN_SQ_STRING] = { + [1 ... 0xBF] = IN_SQ_STRING, + [0xC2 ... 0xF4] = IN_SQ_STRING, + ['\\'] = IN_SQ_STRING_ESCAPE, + ['\''] = JSON_STRING, + }, + + /* Zero */ + [IN_ZERO] = { + TERMINAL(JSON_INTEGER), + ['0' ... '9'] = IN_ERROR, + ['.'] = IN_MANTISSA, + }, + + /* Float */ + [IN_DIGITS] = { + TERMINAL(JSON_FLOAT), + ['0' ... '9'] = IN_DIGITS, + }, + + [IN_DIGIT] = { + ['0' ... '9'] = IN_DIGITS, + }, + + [IN_EXP_E] = { + ['-'] = IN_DIGIT, + ['+'] = IN_DIGIT, + ['0' ... '9'] = IN_DIGITS, + }, + + [IN_MANTISSA_DIGITS] = { + TERMINAL(JSON_FLOAT), + ['0' ... '9'] = IN_MANTISSA_DIGITS, + ['e'] = IN_EXP_E, + ['E'] = IN_EXP_E, + }, + + [IN_MANTISSA] = { + ['0' ... '9'] = IN_MANTISSA_DIGITS, + }, + + /* Number */ + [IN_NONZERO_NUMBER] = { + TERMINAL(JSON_INTEGER), + ['0' ... '9'] = IN_NONZERO_NUMBER, + ['e'] = IN_EXP_E, + ['E'] = IN_EXP_E, + ['.'] = IN_MANTISSA, + }, + + [IN_NEG_NONZERO_NUMBER] = { + ['0'] = IN_ZERO, + ['1' ... '9'] = IN_NONZERO_NUMBER, + }, + + /* keywords */ + [IN_KEYWORD] = { + TERMINAL(JSON_KEYWORD), + ['a' ... 'z'] = IN_KEYWORD, + }, + + /* whitespace */ + [IN_WHITESPACE] = { + TERMINAL(JSON_SKIP), + [' '] = IN_WHITESPACE, + ['\t'] = IN_WHITESPACE, + ['\r'] = IN_WHITESPACE, + ['\n'] = IN_WHITESPACE, + }, + + /* escape */ + [IN_ESCAPE_LL] = { + ['d'] = JSON_ESCAPE, + }, + + [IN_ESCAPE_L] = { + ['d'] = JSON_ESCAPE, + ['l'] = IN_ESCAPE_LL, + }, + + [IN_ESCAPE_I64] = { + ['d'] = JSON_ESCAPE, + }, + + [IN_ESCAPE_I6] = { + ['4'] = IN_ESCAPE_I64, + }, + + [IN_ESCAPE_I] = { + ['6'] = IN_ESCAPE_I6, + }, + + [IN_ESCAPE] = { + ['d'] = JSON_ESCAPE, + ['i'] = JSON_ESCAPE, + ['p'] = JSON_ESCAPE, + ['s'] = JSON_ESCAPE, + ['f'] = JSON_ESCAPE, + ['l'] = IN_ESCAPE_L, + ['I'] = IN_ESCAPE_I, + }, + + /* top level rule */ + [IN_START] = { + ['"'] = IN_DQ_STRING, + ['\''] = IN_SQ_STRING, + ['0'] = IN_ZERO, + ['1' ... '9'] = IN_NONZERO_NUMBER, + ['-'] = IN_NEG_NONZERO_NUMBER, + ['{'] = JSON_OPERATOR, + ['}'] = JSON_OPERATOR, + ['['] = JSON_OPERATOR, + [']'] = JSON_OPERATOR, + [','] = JSON_OPERATOR, + [':'] = JSON_OPERATOR, + ['a' ... 'z'] = IN_KEYWORD, + ['%'] = IN_ESCAPE, + [' '] = IN_WHITESPACE, + ['\t'] = IN_WHITESPACE, + ['\r'] = IN_WHITESPACE, + ['\n'] = IN_WHITESPACE, + }, +}; + +void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) +{ + lexer->emit = func; + lexer->state = IN_START; + lexer->token = qstring_new(); + lexer->x = lexer->y = 0; +} + +static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) +{ + int char_consumed, new_state; + + lexer->x++; + if (ch == '\n') { + lexer->x = 0; + lexer->y++; + } + + do { + new_state = json_lexer[lexer->state][(uint8_t)ch]; + char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state); + if (char_consumed) { + qstring_append_chr(lexer->token, ch); + } + + switch (new_state) { + case JSON_OPERATOR: + case JSON_ESCAPE: + case JSON_INTEGER: + case JSON_FLOAT: + case JSON_KEYWORD: + case JSON_STRING: + lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y); + /* fall through */ + case JSON_SKIP: + QDECREF(lexer->token); + lexer->token = qstring_new(); + new_state = IN_START; + break; + case IN_ERROR: + /* XXX: To avoid having previous bad input leaving the parser in an + * unresponsive state where we consume unpredictable amounts of + * subsequent "good" input, percolate this error state up to the + * tokenizer/parser by forcing a NULL object to be emitted, then + * reset state. + * + * Also note that this handling is required for reliable channel + * negotiation between QMP and the guest agent, since chr(0xFF) + * is placed at the beginning of certain events to ensure proper + * delivery when the channel is in an unknown state. chr(0xFF) is + * never a valid ASCII/UTF-8 sequence, so this should reliably + * induce an error/flush state. + */ + lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y); + QDECREF(lexer->token); + lexer->token = qstring_new(); + new_state = IN_START; + lexer->state = new_state; + return 0; + default: + break; + } + lexer->state = new_state; + } while (!char_consumed && !flush); + + /* Do not let a single token grow to an arbitrarily large size, + * this is a security consideration. + */ + if (lexer->token->length > MAX_TOKEN_SIZE) { + lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); + QDECREF(lexer->token); + lexer->token = qstring_new(); + lexer->state = IN_START; + } + + return 0; +} + +int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) { + int err; + + err = json_lexer_feed_char(lexer, buffer[i], false); + if (err < 0) { + return err; + } + } + + return 0; +} + +int json_lexer_flush(JSONLexer *lexer) +{ + return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true); +} + +void json_lexer_destroy(JSONLexer *lexer) +{ + QDECREF(lexer->token); +} diff --git a/qobject/json-parser.c b/qobject/json-parser.c new file mode 100644 index 0000000..05279c1 --- /dev/null +++ b/qobject/json-parser.c @@ -0,0 +1,704 @@ +/* + * JSON Parser + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include + +#include "qemu-common.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qfloat.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/json-parser.h" +#include "qapi/qmp/json-lexer.h" +#include "qapi/qmp/qerror.h" + +typedef struct JSONParserContext +{ + Error *err; + struct { + QObject **buf; + size_t pos; + size_t count; + } tokens; +} JSONParserContext; + +#define BUG_ON(cond) assert(!(cond)) + +/** + * TODO + * + * 0) make errors meaningful again + * 1) add geometry information to tokens + * 3) should we return a parsed size? + * 4) deal with premature EOI + */ + +static QObject *parse_value(JSONParserContext *ctxt, va_list *ap); + +/** + * Token manipulators + * + * tokens are dictionaries that contain a type, a string value, and geometry information + * about a token identified by the lexer. These are routines that make working with + * these objects a bit easier. + */ +static const char *token_get_value(QObject *obj) +{ + return qdict_get_str(qobject_to_qdict(obj), "token"); +} + +static JSONTokenType token_get_type(QObject *obj) +{ + return qdict_get_int(qobject_to_qdict(obj), "type"); +} + +static int token_is_operator(QObject *obj, char op) +{ + const char *val; + + if (token_get_type(obj) != JSON_OPERATOR) { + return 0; + } + + val = token_get_value(obj); + + return (val[0] == op) && (val[1] == 0); +} + +static int token_is_keyword(QObject *obj, const char *value) +{ + if (token_get_type(obj) != JSON_KEYWORD) { + return 0; + } + + return strcmp(token_get_value(obj), value) == 0; +} + +static int token_is_escape(QObject *obj, const char *value) +{ + if (token_get_type(obj) != JSON_ESCAPE) { + return 0; + } + + return (strcmp(token_get_value(obj), value) == 0); +} + +/** + * Error handler + */ +static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, + QObject *token, const char *msg, ...) +{ + va_list ap; + char message[1024]; + va_start(ap, msg); + vsnprintf(message, sizeof(message), msg, ap); + va_end(ap); + if (ctxt->err) { + error_free(ctxt->err); + ctxt->err = NULL; + } + error_set(&ctxt->err, QERR_JSON_PARSE_ERROR, message); +} + +/** + * String helpers + * + * These helpers are used to unescape strings. + */ +static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length) +{ + if (wchar <= 0x007F) { + BUG_ON(buffer_length < 2); + + buffer[0] = wchar & 0x7F; + buffer[1] = 0; + } else if (wchar <= 0x07FF) { + BUG_ON(buffer_length < 3); + + buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F); + buffer[1] = 0x80 | (wchar & 0x3F); + buffer[2] = 0; + } else { + BUG_ON(buffer_length < 4); + + buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F); + buffer[1] = 0x80 | ((wchar >> 6) & 0x3F); + buffer[2] = 0x80 | (wchar & 0x3F); + buffer[3] = 0; + } +} + +static int hex2decimal(char ch) +{ + if (ch >= '0' && ch <= '9') { + return (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + return 10 + (ch - 'a'); + } else if (ch >= 'A' && ch <= 'F') { + return 10 + (ch - 'A'); + } + + return -1; +} + +/** + * parse_string(): Parse a json string and return a QObject + * + * string + * "" + * " chars " + * chars + * char + * char chars + * char + * any-Unicode-character- + * except-"-or-\-or- + * control-character + * \" + * \\ + * \/ + * \b + * \f + * \n + * \r + * \t + * \u four-hex-digits + */ +static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token) +{ + const char *ptr = token_get_value(token); + QString *str; + int double_quote = 1; + + if (*ptr == '"') { + double_quote = 1; + } else { + double_quote = 0; + } + ptr++; + + str = qstring_new(); + while (*ptr && + ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) { + if (*ptr == '\\') { + ptr++; + + switch (*ptr) { + case '"': + qstring_append(str, "\""); + ptr++; + break; + case '\'': + qstring_append(str, "'"); + ptr++; + break; + case '\\': + qstring_append(str, "\\"); + ptr++; + break; + case '/': + qstring_append(str, "/"); + ptr++; + break; + case 'b': + qstring_append(str, "\b"); + ptr++; + break; + case 'f': + qstring_append(str, "\f"); + ptr++; + break; + case 'n': + qstring_append(str, "\n"); + ptr++; + break; + case 'r': + qstring_append(str, "\r"); + ptr++; + break; + case 't': + qstring_append(str, "\t"); + ptr++; + break; + case 'u': { + uint16_t unicode_char = 0; + char utf8_char[4]; + int i = 0; + + ptr++; + + for (i = 0; i < 4; i++) { + if (qemu_isxdigit(*ptr)) { + unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4); + } else { + parse_error(ctxt, token, + "invalid hex escape sequence in string"); + goto out; + } + ptr++; + } + + wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char)); + qstring_append(str, utf8_char); + } break; + default: + parse_error(ctxt, token, "invalid escape sequence in string"); + goto out; + } + } else { + char dummy[2]; + + dummy[0] = *ptr++; + dummy[1] = 0; + + qstring_append(str, dummy); + } + } + + return str; + +out: + QDECREF(str); + return NULL; +} + +static QObject *parser_context_pop_token(JSONParserContext *ctxt) +{ + QObject *token; + g_assert(ctxt->tokens.pos < ctxt->tokens.count); + token = ctxt->tokens.buf[ctxt->tokens.pos]; + ctxt->tokens.pos++; + return token; +} + +/* Note: parser_context_{peek|pop}_token do not increment the + * token object's refcount. In both cases the references will continue + * to be tracked and cleaned up in parser_context_free(), so do not + * attempt to free the token object. + */ +static QObject *parser_context_peek_token(JSONParserContext *ctxt) +{ + QObject *token; + g_assert(ctxt->tokens.pos < ctxt->tokens.count); + token = ctxt->tokens.buf[ctxt->tokens.pos]; + return token; +} + +static JSONParserContext parser_context_save(JSONParserContext *ctxt) +{ + JSONParserContext saved_ctxt = {0}; + saved_ctxt.tokens.pos = ctxt->tokens.pos; + saved_ctxt.tokens.count = ctxt->tokens.count; + saved_ctxt.tokens.buf = ctxt->tokens.buf; + return saved_ctxt; +} + +static void parser_context_restore(JSONParserContext *ctxt, + JSONParserContext saved_ctxt) +{ + ctxt->tokens.pos = saved_ctxt.tokens.pos; + ctxt->tokens.count = saved_ctxt.tokens.count; + ctxt->tokens.buf = saved_ctxt.tokens.buf; +} + +static void tokens_append_from_iter(QObject *obj, void *opaque) +{ + JSONParserContext *ctxt = opaque; + g_assert(ctxt->tokens.pos < ctxt->tokens.count); + ctxt->tokens.buf[ctxt->tokens.pos++] = obj; + qobject_incref(obj); +} + +static JSONParserContext *parser_context_new(QList *tokens) +{ + JSONParserContext *ctxt; + size_t count; + + if (!tokens) { + return NULL; + } + + count = qlist_size(tokens); + if (count == 0) { + return NULL; + } + + ctxt = g_malloc0(sizeof(JSONParserContext)); + ctxt->tokens.pos = 0; + ctxt->tokens.count = count; + ctxt->tokens.buf = g_malloc(count * sizeof(QObject *)); + qlist_iter(tokens, tokens_append_from_iter, ctxt); + ctxt->tokens.pos = 0; + + return ctxt; +} + +/* to support error propagation, ctxt->err must be freed separately */ +static void parser_context_free(JSONParserContext *ctxt) +{ + int i; + if (ctxt) { + for (i = 0; i < ctxt->tokens.count; i++) { + qobject_decref(ctxt->tokens.buf[i]); + } + g_free(ctxt->tokens.buf); + g_free(ctxt); + } +} + +/** + * Parsing rules + */ +static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) +{ + QObject *key = NULL, *token = NULL, *value, *peek; + JSONParserContext saved_ctxt = parser_context_save(ctxt); + + peek = parser_context_peek_token(ctxt); + if (peek == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + key = parse_value(ctxt, ap); + if (!key || qobject_type(key) != QTYPE_QSTRING) { + parse_error(ctxt, peek, "key is not a string in object"); + goto out; + } + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + if (!token_is_operator(token, ':')) { + parse_error(ctxt, token, "missing : in object pair"); + goto out; + } + + value = parse_value(ctxt, ap); + if (value == NULL) { + parse_error(ctxt, token, "Missing value in dict"); + goto out; + } + + qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value); + + qobject_decref(key); + + return 0; + +out: + parser_context_restore(ctxt, saved_ctxt); + qobject_decref(key); + + return -1; +} + +static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) +{ + QDict *dict = NULL; + QObject *token, *peek; + JSONParserContext saved_ctxt = parser_context_save(ctxt); + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + goto out; + } + + if (!token_is_operator(token, '{')) { + goto out; + } + token = NULL; + + dict = qdict_new(); + + peek = parser_context_peek_token(ctxt); + if (peek == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + if (!token_is_operator(peek, '}')) { + if (parse_pair(ctxt, dict, ap) == -1) { + goto out; + } + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + while (!token_is_operator(token, '}')) { + if (!token_is_operator(token, ',')) { + parse_error(ctxt, token, "expected separator in dict"); + goto out; + } + token = NULL; + + if (parse_pair(ctxt, dict, ap) == -1) { + goto out; + } + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + } + token = NULL; + } else { + token = parser_context_pop_token(ctxt); + token = NULL; + } + + return QOBJECT(dict); + +out: + parser_context_restore(ctxt, saved_ctxt); + QDECREF(dict); + return NULL; +} + +static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) +{ + QList *list = NULL; + QObject *token, *peek; + JSONParserContext saved_ctxt = parser_context_save(ctxt); + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + goto out; + } + + if (!token_is_operator(token, '[')) { + token = NULL; + goto out; + } + token = NULL; + + list = qlist_new(); + + peek = parser_context_peek_token(ctxt); + if (peek == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + if (!token_is_operator(peek, ']')) { + QObject *obj; + + obj = parse_value(ctxt, ap); + if (obj == NULL) { + parse_error(ctxt, token, "expecting value"); + goto out; + } + + qlist_append_obj(list, obj); + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + while (!token_is_operator(token, ']')) { + if (!token_is_operator(token, ',')) { + parse_error(ctxt, token, "expected separator in list"); + goto out; + } + + token = NULL; + + obj = parse_value(ctxt, ap); + if (obj == NULL) { + parse_error(ctxt, token, "expecting value"); + goto out; + } + + qlist_append_obj(list, obj); + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + } + + token = NULL; + } else { + token = parser_context_pop_token(ctxt); + token = NULL; + } + + return QOBJECT(list); + +out: + parser_context_restore(ctxt, saved_ctxt); + QDECREF(list); + return NULL; +} + +static QObject *parse_keyword(JSONParserContext *ctxt) +{ + QObject *token, *ret; + JSONParserContext saved_ctxt = parser_context_save(ctxt); + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + goto out; + } + + if (token_get_type(token) != JSON_KEYWORD) { + goto out; + } + + if (token_is_keyword(token, "true")) { + ret = QOBJECT(qbool_from_int(true)); + } else if (token_is_keyword(token, "false")) { + ret = QOBJECT(qbool_from_int(false)); + } else { + parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token)); + goto out; + } + + return ret; + +out: + parser_context_restore(ctxt, saved_ctxt); + + return NULL; +} + +static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) +{ + QObject *token = NULL, *obj; + JSONParserContext saved_ctxt = parser_context_save(ctxt); + + if (ap == NULL) { + goto out; + } + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + goto out; + } + + if (token_is_escape(token, "%p")) { + obj = va_arg(*ap, QObject *); + } else if (token_is_escape(token, "%i")) { + obj = QOBJECT(qbool_from_int(va_arg(*ap, int))); + } else if (token_is_escape(token, "%d")) { + obj = QOBJECT(qint_from_int(va_arg(*ap, int))); + } else if (token_is_escape(token, "%ld")) { + obj = QOBJECT(qint_from_int(va_arg(*ap, long))); + } else if (token_is_escape(token, "%lld") || + token_is_escape(token, "%I64d")) { + obj = QOBJECT(qint_from_int(va_arg(*ap, long long))); + } else if (token_is_escape(token, "%s")) { + obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *))); + } else if (token_is_escape(token, "%f")) { + obj = QOBJECT(qfloat_from_double(va_arg(*ap, double))); + } else { + goto out; + } + + return obj; + +out: + parser_context_restore(ctxt, saved_ctxt); + + return NULL; +} + +static QObject *parse_literal(JSONParserContext *ctxt) +{ + QObject *token, *obj; + JSONParserContext saved_ctxt = parser_context_save(ctxt); + + token = parser_context_pop_token(ctxt); + if (token == NULL) { + goto out; + } + + switch (token_get_type(token)) { + case JSON_STRING: + obj = QOBJECT(qstring_from_escaped_str(ctxt, token)); + break; + case JSON_INTEGER: + obj = QOBJECT(qint_from_int(strtoll(token_get_value(token), NULL, 10))); + break; + case JSON_FLOAT: + /* FIXME dependent on locale */ + obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL))); + break; + default: + goto out; + } + + return obj; + +out: + parser_context_restore(ctxt, saved_ctxt); + + return NULL; +} + +static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) +{ + QObject *obj; + + obj = parse_object(ctxt, ap); + if (obj == NULL) { + obj = parse_array(ctxt, ap); + } + if (obj == NULL) { + obj = parse_escape(ctxt, ap); + } + if (obj == NULL) { + obj = parse_keyword(ctxt); + } + if (obj == NULL) { + obj = parse_literal(ctxt); + } + + return obj; +} + +QObject *json_parser_parse(QList *tokens, va_list *ap) +{ + return json_parser_parse_err(tokens, ap, NULL); +} + +QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp) +{ + JSONParserContext *ctxt = parser_context_new(tokens); + QObject *result; + + if (!ctxt) { + return NULL; + } + + result = parse_value(ctxt, ap); + + error_propagate(errp, ctxt->err); + + parser_context_free(ctxt); + + return result; +} diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c new file mode 100644 index 0000000..1b2f9b1 --- /dev/null +++ b/qobject/json-streamer.c @@ -0,0 +1,122 @@ +/* + * JSON streaming support + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qdict.h" +#include "qemu-common.h" +#include "qapi/qmp/json-lexer.h" +#include "qapi/qmp/json-streamer.h" + +#define MAX_TOKEN_SIZE (64ULL << 20) +#define MAX_NESTING (1ULL << 10) + +static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y) +{ + JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); + QDict *dict; + + if (type == JSON_OPERATOR) { + switch (qstring_get_str(token)[0]) { + case '{': + parser->brace_count++; + break; + case '}': + parser->brace_count--; + break; + case '[': + parser->bracket_count++; + break; + case ']': + parser->bracket_count--; + break; + default: + break; + } + } + + dict = qdict_new(); + qdict_put(dict, "type", qint_from_int(type)); + QINCREF(token); + qdict_put(dict, "token", token); + qdict_put(dict, "x", qint_from_int(x)); + qdict_put(dict, "y", qint_from_int(y)); + + parser->token_size += token->length; + + qlist_append(parser->tokens, dict); + + if (type == JSON_ERROR) { + goto out_emit_bad; + } else if (parser->brace_count < 0 || + parser->bracket_count < 0 || + (parser->brace_count == 0 && + parser->bracket_count == 0)) { + goto out_emit; + } else if (parser->token_size > MAX_TOKEN_SIZE || + parser->bracket_count > MAX_NESTING || + parser->brace_count > MAX_NESTING) { + /* Security consideration, we limit total memory allocated per object + * and the maximum recursion depth that a message can force. + */ + goto out_emit; + } + + return; + +out_emit_bad: + /* clear out token list and tell the parser to emit and error + * indication by passing it a NULL list + */ + QDECREF(parser->tokens); + parser->tokens = NULL; +out_emit: + /* send current list of tokens to parser and reset tokenizer */ + parser->brace_count = 0; + parser->bracket_count = 0; + parser->emit(parser, parser->tokens); + if (parser->tokens) { + QDECREF(parser->tokens); + } + parser->tokens = qlist_new(); + parser->token_size = 0; +} + +void json_message_parser_init(JSONMessageParser *parser, + void (*func)(JSONMessageParser *, QList *)) +{ + parser->emit = func; + parser->brace_count = 0; + parser->bracket_count = 0; + parser->tokens = qlist_new(); + parser->token_size = 0; + + json_lexer_init(&parser->lexer, json_message_process_token); +} + +int json_message_parser_feed(JSONMessageParser *parser, + const char *buffer, size_t size) +{ + return json_lexer_feed(&parser->lexer, buffer, size); +} + +int json_message_parser_flush(JSONMessageParser *parser) +{ + return json_lexer_flush(&parser->lexer); +} + +void json_message_parser_destroy(JSONMessageParser *parser) +{ + json_lexer_destroy(&parser->lexer); + QDECREF(parser->tokens); +} diff --git a/qobject/qbool.c b/qobject/qbool.c new file mode 100644 index 0000000..a3d2afa --- /dev/null +++ b/qobject/qbool.c @@ -0,0 +1,68 @@ +/* + * QBool Module + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qobject.h" +#include "qemu-common.h" + +static void qbool_destroy_obj(QObject *obj); + +static const QType qbool_type = { + .code = QTYPE_QBOOL, + .destroy = qbool_destroy_obj, +}; + +/** + * qbool_from_int(): Create a new QBool from an int + * + * Return strong reference. + */ +QBool *qbool_from_int(int value) +{ + QBool *qb; + + qb = g_malloc(sizeof(*qb)); + qb->value = value; + QOBJECT_INIT(qb, &qbool_type); + + return qb; +} + +/** + * qbool_get_int(): Get the stored int + */ +int qbool_get_int(const QBool *qb) +{ + return qb->value; +} + +/** + * qobject_to_qbool(): Convert a QObject into a QBool + */ +QBool *qobject_to_qbool(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QBOOL) + return NULL; + + return container_of(obj, QBool, base); +} + +/** + * qbool_destroy_obj(): Free all memory allocated by a + * QBool object + */ +static void qbool_destroy_obj(QObject *obj) +{ + assert(obj != NULL); + g_free(qobject_to_qbool(obj)); +} diff --git a/qobject/qdict.c b/qobject/qdict.c new file mode 100644 index 0000000..7543ccc --- /dev/null +++ b/qobject/qdict.c @@ -0,0 +1,456 @@ +/* + * QDict Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qfloat.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qobject.h" +#include "qemu/queue.h" +#include "qemu-common.h" + +static void qdict_destroy_obj(QObject *obj); + +static const QType qdict_type = { + .code = QTYPE_QDICT, + .destroy = qdict_destroy_obj, +}; + +/** + * qdict_new(): Create a new QDict + * + * Return strong reference. + */ +QDict *qdict_new(void) +{ + QDict *qdict; + + qdict = g_malloc0(sizeof(*qdict)); + QOBJECT_INIT(qdict, &qdict_type); + + return qdict; +} + +/** + * qobject_to_qdict(): Convert a QObject into a QDict + */ +QDict *qobject_to_qdict(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QDICT) + return NULL; + + return container_of(obj, QDict, base); +} + +/** + * tdb_hash(): based on the hash agorithm from gdbm, via tdb + * (from module-init-tools) + */ +static unsigned int tdb_hash(const char *name) +{ + unsigned value; /* Used to compute the hash value. */ + unsigned i; /* Used to cycle through random values. */ + + /* Set the initial value from the key size. */ + for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++) + value = (value + (((const unsigned char *)name)[i] << (i*5 % 24))); + + return (1103515243 * value + 12345); +} + +/** + * alloc_entry(): allocate a new QDictEntry + */ +static QDictEntry *alloc_entry(const char *key, QObject *value) +{ + QDictEntry *entry; + + entry = g_malloc0(sizeof(*entry)); + entry->key = g_strdup(key); + entry->value = value; + + return entry; +} + +/** + * qdict_entry_value(): Return qdict entry value + * + * Return weak reference. + */ +QObject *qdict_entry_value(const QDictEntry *entry) +{ + return entry->value; +} + +/** + * qdict_entry_key(): Return qdict entry key + * + * Return a *pointer* to the string, it has to be duplicated before being + * stored. + */ +const char *qdict_entry_key(const QDictEntry *entry) +{ + return entry->key; +} + +/** + * qdict_find(): List lookup function + */ +static QDictEntry *qdict_find(const QDict *qdict, + const char *key, unsigned int bucket) +{ + QDictEntry *entry; + + QLIST_FOREACH(entry, &qdict->table[bucket], next) + if (!strcmp(entry->key, key)) + return entry; + + return NULL; +} + +/** + * qdict_put_obj(): Put a new QObject into the dictionary + * + * Insert the pair 'key:value' into 'qdict', if 'key' already exists + * its 'value' will be replaced. + * + * This is done by freeing the reference to the stored QObject and + * storing the new one in the same entry. + * + * NOTE: ownership of 'value' is transferred to the QDict + */ +void qdict_put_obj(QDict *qdict, const char *key, QObject *value) +{ + unsigned int bucket; + QDictEntry *entry; + + bucket = tdb_hash(key) % QDICT_BUCKET_MAX; + entry = qdict_find(qdict, key, bucket); + if (entry) { + /* replace key's value */ + qobject_decref(entry->value); + entry->value = value; + } else { + /* allocate a new entry */ + entry = alloc_entry(key, value); + QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next); + qdict->size++; + } +} + +/** + * qdict_get(): Lookup for a given 'key' + * + * Return a weak reference to the QObject associated with 'key' if + * 'key' is present in the dictionary, NULL otherwise. + */ +QObject *qdict_get(const QDict *qdict, const char *key) +{ + QDictEntry *entry; + + entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); + return (entry == NULL ? NULL : entry->value); +} + +/** + * qdict_haskey(): Check if 'key' exists + * + * Return 1 if 'key' exists in the dict, 0 otherwise + */ +int qdict_haskey(const QDict *qdict, const char *key) +{ + unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX; + return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1); +} + +/** + * qdict_size(): Return the size of the dictionary + */ +size_t qdict_size(const QDict *qdict) +{ + return qdict->size; +} + +/** + * qdict_get_obj(): Get a QObject of a specific type + */ +static QObject *qdict_get_obj(const QDict *qdict, const char *key, + qtype_code type) +{ + QObject *obj; + + obj = qdict_get(qdict, key); + assert(obj != NULL); + assert(qobject_type(obj) == type); + + return obj; +} + +/** + * qdict_get_double(): Get an number mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QFloat or QInt object. + * + * Return number mapped by 'key'. + */ +double qdict_get_double(const QDict *qdict, const char *key) +{ + QObject *obj = qdict_get(qdict, key); + + assert(obj); + switch (qobject_type(obj)) { + case QTYPE_QFLOAT: + return qfloat_get_double(qobject_to_qfloat(obj)); + case QTYPE_QINT: + return qint_get_int(qobject_to_qint(obj)); + default: + abort(); + } +} + +/** + * qdict_get_int(): Get an integer mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QInt object. + * + * Return integer mapped by 'key'. + */ +int64_t qdict_get_int(const QDict *qdict, const char *key) +{ + QObject *obj = qdict_get_obj(qdict, key, QTYPE_QINT); + return qint_get_int(qobject_to_qint(obj)); +} + +/** + * qdict_get_bool(): Get a bool mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QBool object. + * + * Return bool mapped by 'key'. + */ +int qdict_get_bool(const QDict *qdict, const char *key) +{ + QObject *obj = qdict_get_obj(qdict, key, QTYPE_QBOOL); + return qbool_get_int(qobject_to_qbool(obj)); +} + +/** + * qdict_get_qlist(): Get the QList mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QList object. + * + * Return QList mapped by 'key'. + */ +QList *qdict_get_qlist(const QDict *qdict, const char *key) +{ + return qobject_to_qlist(qdict_get_obj(qdict, key, QTYPE_QLIST)); +} + +/** + * qdict_get_qdict(): Get the QDict mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QDict object. + * + * Return QDict mapped by 'key'. + */ +QDict *qdict_get_qdict(const QDict *qdict, const char *key) +{ + return qobject_to_qdict(qdict_get_obj(qdict, key, QTYPE_QDICT)); +} + +/** + * qdict_get_str(): Get a pointer to the stored string mapped + * by 'key' + * + * This function assumes that 'key' exists and it stores a + * QString object. + * + * Return pointer to the string mapped by 'key'. + */ +const char *qdict_get_str(const QDict *qdict, const char *key) +{ + QObject *obj = qdict_get_obj(qdict, key, QTYPE_QSTRING); + return qstring_get_str(qobject_to_qstring(obj)); +} + +/** + * qdict_get_try_int(): Try to get integer mapped by 'key' + * + * Return integer mapped by 'key', if it is not present in + * the dictionary or if the stored object is not of QInt type + * 'def_value' will be returned. + */ +int64_t qdict_get_try_int(const QDict *qdict, const char *key, + int64_t def_value) +{ + QObject *obj; + + obj = qdict_get(qdict, key); + if (!obj || qobject_type(obj) != QTYPE_QINT) + return def_value; + + return qint_get_int(qobject_to_qint(obj)); +} + +/** + * qdict_get_try_bool(): Try to get a bool mapped by 'key' + * + * Return bool mapped by 'key', if it is not present in the + * dictionary or if the stored object is not of QBool type + * 'def_value' will be returned. + */ +int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value) +{ + QObject *obj; + + obj = qdict_get(qdict, key); + if (!obj || qobject_type(obj) != QTYPE_QBOOL) + return def_value; + + return qbool_get_int(qobject_to_qbool(obj)); +} + +/** + * qdict_get_try_str(): Try to get a pointer to the stored string + * mapped by 'key' + * + * Return a pointer to the string mapped by 'key', if it is not present + * in the dictionary or if the stored object is not of QString type + * NULL will be returned. + */ +const char *qdict_get_try_str(const QDict *qdict, const char *key) +{ + QObject *obj; + + obj = qdict_get(qdict, key); + if (!obj || qobject_type(obj) != QTYPE_QSTRING) + return NULL; + + return qstring_get_str(qobject_to_qstring(obj)); +} + +/** + * qdict_iter(): Iterate over all the dictionary's stored values. + * + * This function allows the user to provide an iterator, which will be + * called for each stored value in the dictionary. + */ +void qdict_iter(const QDict *qdict, + void (*iter)(const char *key, QObject *obj, void *opaque), + void *opaque) +{ + int i; + QDictEntry *entry; + + for (i = 0; i < QDICT_BUCKET_MAX; i++) { + QLIST_FOREACH(entry, &qdict->table[i], next) + iter(entry->key, entry->value, opaque); + } +} + +static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket) +{ + int i; + + for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) { + if (!QLIST_EMPTY(&qdict->table[i])) { + return QLIST_FIRST(&qdict->table[i]); + } + } + + return NULL; +} + +/** + * qdict_first(): Return first qdict entry for iteration. + */ +const QDictEntry *qdict_first(const QDict *qdict) +{ + return qdict_next_entry(qdict, 0); +} + +/** + * qdict_next(): Return next qdict entry in an iteration. + */ +const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry) +{ + QDictEntry *ret; + + ret = QLIST_NEXT(entry, next); + if (!ret) { + unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX; + ret = qdict_next_entry(qdict, bucket + 1); + } + + return ret; +} + +/** + * qentry_destroy(): Free all the memory allocated by a QDictEntry + */ +static void qentry_destroy(QDictEntry *e) +{ + assert(e != NULL); + assert(e->key != NULL); + assert(e->value != NULL); + + qobject_decref(e->value); + g_free(e->key); + g_free(e); +} + +/** + * qdict_del(): Delete a 'key:value' pair from the dictionary + * + * This will destroy all data allocated by this entry. + */ +void qdict_del(QDict *qdict, const char *key) +{ + QDictEntry *entry; + + entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); + if (entry) { + QLIST_REMOVE(entry, next); + qentry_destroy(entry); + qdict->size--; + } +} + +/** + * qdict_destroy_obj(): Free all the memory allocated by a QDict + */ +static void qdict_destroy_obj(QObject *obj) +{ + int i; + QDict *qdict; + + assert(obj != NULL); + qdict = qobject_to_qdict(obj); + + for (i = 0; i < QDICT_BUCKET_MAX; i++) { + QDictEntry *entry = QLIST_FIRST(&qdict->table[i]); + while (entry) { + QDictEntry *tmp = QLIST_NEXT(entry, next); + QLIST_REMOVE(entry, next); + qentry_destroy(entry); + entry = tmp; + } + } + + g_free(qdict); +} diff --git a/qobject/qerror.c b/qobject/qerror.c new file mode 100644 index 0000000..3aee1cf --- /dev/null +++ b/qobject/qerror.c @@ -0,0 +1,156 @@ +/* + * QError Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "monitor/monitor.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qerror.h" +#include "qemu-common.h" + +static void qerror_destroy_obj(QObject *obj); + +static const QType qerror_type = { + .code = QTYPE_QERROR, + .destroy = qerror_destroy_obj, +}; + +/** + * qerror_new(): Create a new QError + * + * Return strong reference. + */ +static QError *qerror_new(void) +{ + QError *qerr; + + qerr = g_malloc0(sizeof(*qerr)); + QOBJECT_INIT(qerr, &qerror_type); + + return qerr; +} + +/** + * qerror_from_info(): Create a new QError from error information + * + * Return strong reference. + */ +static QError *qerror_from_info(ErrorClass err_class, const char *fmt, + va_list *va) +{ + QError *qerr; + + qerr = qerror_new(); + loc_save(&qerr->loc); + + qerr->err_msg = g_strdup_vprintf(fmt, *va); + qerr->err_class = err_class; + + return qerr; +} + +/** + * qerror_human(): Format QError data into human-readable string. + */ +QString *qerror_human(const QError *qerror) +{ + return qstring_from_str(qerror->err_msg); +} + +/** + * qerror_print(): Print QError data + * + * This function will print the member 'desc' of the specified QError object, + * it uses error_report() for this, so that the output is routed to the right + * place (ie. stderr or Monitor's device). + */ +static void qerror_print(QError *qerror) +{ + QString *qstring = qerror_human(qerror); + loc_push_restore(&qerror->loc); + error_report("%s", qstring_get_str(qstring)); + loc_pop(&qerror->loc); + QDECREF(qstring); +} + +void qerror_report(ErrorClass eclass, const char *fmt, ...) +{ + va_list va; + QError *qerror; + + va_start(va, fmt); + qerror = qerror_from_info(eclass, fmt, &va); + va_end(va); + + if (monitor_cur_is_qmp()) { + monitor_set_error(cur_mon, qerror); + } else { + qerror_print(qerror); + QDECREF(qerror); + } +} + +/* Evil... */ +struct Error +{ + char *msg; + ErrorClass err_class; +}; + +void qerror_report_err(Error *err) +{ + QError *qerr; + + qerr = qerror_new(); + loc_save(&qerr->loc); + qerr->err_msg = g_strdup(err->msg); + qerr->err_class = err->err_class; + + if (monitor_cur_is_qmp()) { + monitor_set_error(cur_mon, qerr); + } else { + qerror_print(qerr); + QDECREF(qerr); + } +} + +void assert_no_error(Error *err) +{ + if (err) { + qerror_report_err(err); + abort(); + } +} + +/** + * qobject_to_qerror(): Convert a QObject into a QError + */ +static QError *qobject_to_qerror(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QERROR) { + return NULL; + } + + return container_of(obj, QError, base); +} + +/** + * qerror_destroy_obj(): Free all memory allocated by a QError + */ +static void qerror_destroy_obj(QObject *obj) +{ + QError *qerr; + + assert(obj != NULL); + qerr = qobject_to_qerror(obj); + + g_free(qerr->err_msg); + g_free(qerr); +} diff --git a/qobject/qfloat.c b/qobject/qfloat.c new file mode 100644 index 0000000..7de0992 --- /dev/null +++ b/qobject/qfloat.c @@ -0,0 +1,68 @@ +/* + * QFloat Module + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qmp/qfloat.h" +#include "qapi/qmp/qobject.h" +#include "qemu-common.h" + +static void qfloat_destroy_obj(QObject *obj); + +static const QType qfloat_type = { + .code = QTYPE_QFLOAT, + .destroy = qfloat_destroy_obj, +}; + +/** + * qfloat_from_int(): Create a new QFloat from a float + * + * Return strong reference. + */ +QFloat *qfloat_from_double(double value) +{ + QFloat *qf; + + qf = g_malloc(sizeof(*qf)); + qf->value = value; + QOBJECT_INIT(qf, &qfloat_type); + + return qf; +} + +/** + * qfloat_get_double(): Get the stored float + */ +double qfloat_get_double(const QFloat *qf) +{ + return qf->value; +} + +/** + * qobject_to_qfloat(): Convert a QObject into a QFloat + */ +QFloat *qobject_to_qfloat(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QFLOAT) + return NULL; + + return container_of(obj, QFloat, base); +} + +/** + * qfloat_destroy_obj(): Free all memory allocated by a + * QFloat object + */ +static void qfloat_destroy_obj(QObject *obj) +{ + assert(obj != NULL); + g_free(qobject_to_qfloat(obj)); +} diff --git a/qobject/qint.c b/qobject/qint.c new file mode 100644 index 0000000..86b9b04 --- /dev/null +++ b/qobject/qint.c @@ -0,0 +1,67 @@ +/* + * QInt Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qobject.h" +#include "qemu-common.h" + +static void qint_destroy_obj(QObject *obj); + +static const QType qint_type = { + .code = QTYPE_QINT, + .destroy = qint_destroy_obj, +}; + +/** + * qint_from_int(): Create a new QInt from an int64_t + * + * Return strong reference. + */ +QInt *qint_from_int(int64_t value) +{ + QInt *qi; + + qi = g_malloc(sizeof(*qi)); + qi->value = value; + QOBJECT_INIT(qi, &qint_type); + + return qi; +} + +/** + * qint_get_int(): Get the stored integer + */ +int64_t qint_get_int(const QInt *qi) +{ + return qi->value; +} + +/** + * qobject_to_qint(): Convert a QObject into a QInt + */ +QInt *qobject_to_qint(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QINT) + return NULL; + + return container_of(obj, QInt, base); +} + +/** + * qint_destroy_obj(): Free all memory allocated by a + * QInt object + */ +static void qint_destroy_obj(QObject *obj) +{ + assert(obj != NULL); + g_free(qobject_to_qint(obj)); +} diff --git a/qobject/qjson.c b/qobject/qjson.c new file mode 100644 index 0000000..83a6b4f --- /dev/null +++ b/qobject/qjson.c @@ -0,0 +1,294 @@ +/* + * QObject JSON integration + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qmp/json-lexer.h" +#include "qapi/qmp/json-parser.h" +#include "qapi/qmp/json-streamer.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qfloat.h" +#include "qapi/qmp/qdict.h" + +typedef struct JSONParsingState +{ + JSONMessageParser parser; + va_list *ap; + QObject *result; +} JSONParsingState; + +static void parse_json(JSONMessageParser *parser, QList *tokens) +{ + JSONParsingState *s = container_of(parser, JSONParsingState, parser); + s->result = json_parser_parse(tokens, s->ap); +} + +QObject *qobject_from_jsonv(const char *string, va_list *ap) +{ + JSONParsingState state = {}; + + state.ap = ap; + + json_message_parser_init(&state.parser, parse_json); + json_message_parser_feed(&state.parser, string, strlen(string)); + json_message_parser_flush(&state.parser); + json_message_parser_destroy(&state.parser); + + return state.result; +} + +QObject *qobject_from_json(const char *string) +{ + return qobject_from_jsonv(string, NULL); +} + +/* + * IMPORTANT: This function aborts on error, thus it must not + * be used with untrusted arguments. + */ +QObject *qobject_from_jsonf(const char *string, ...) +{ + QObject *obj; + va_list ap; + + va_start(ap, string); + obj = qobject_from_jsonv(string, &ap); + va_end(ap); + + assert(obj != NULL); + return obj; +} + +typedef struct ToJsonIterState +{ + int indent; + int pretty; + int count; + QString *str; +} ToJsonIterState; + +static void to_json(const QObject *obj, QString *str, int pretty, int indent); + +static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) +{ + ToJsonIterState *s = opaque; + QString *qkey; + int j; + + if (s->count) + qstring_append(s->str, ", "); + + if (s->pretty) { + qstring_append(s->str, "\n"); + for (j = 0 ; j < s->indent ; j++) + qstring_append(s->str, " "); + } + + qkey = qstring_from_str(key); + to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); + QDECREF(qkey); + + qstring_append(s->str, ": "); + to_json(obj, s->str, s->pretty, s->indent); + s->count++; +} + +static void to_json_list_iter(QObject *obj, void *opaque) +{ + ToJsonIterState *s = opaque; + int j; + + if (s->count) + qstring_append(s->str, ", "); + + if (s->pretty) { + qstring_append(s->str, "\n"); + for (j = 0 ; j < s->indent ; j++) + qstring_append(s->str, " "); + } + + to_json(obj, s->str, s->pretty, s->indent); + s->count++; +} + +static void to_json(const QObject *obj, QString *str, int pretty, int indent) +{ + switch (qobject_type(obj)) { + case QTYPE_QINT: { + QInt *val = qobject_to_qint(obj); + char buffer[1024]; + + snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val)); + qstring_append(str, buffer); + break; + } + case QTYPE_QSTRING: { + QString *val = qobject_to_qstring(obj); + const char *ptr; + + ptr = qstring_get_str(val); + qstring_append(str, "\""); + while (*ptr) { + if ((ptr[0] & 0xE0) == 0xE0 && + (ptr[1] & 0x80) && (ptr[2] & 0x80)) { + uint16_t wchar; + char escape[7]; + + wchar = (ptr[0] & 0x0F) << 12; + wchar |= (ptr[1] & 0x3F) << 6; + wchar |= (ptr[2] & 0x3F); + ptr += 2; + + snprintf(escape, sizeof(escape), "\\u%04X", wchar); + qstring_append(str, escape); + } else if ((ptr[0] & 0xE0) == 0xC0 && (ptr[1] & 0x80)) { + uint16_t wchar; + char escape[7]; + + wchar = (ptr[0] & 0x1F) << 6; + wchar |= (ptr[1] & 0x3F); + ptr++; + + snprintf(escape, sizeof(escape), "\\u%04X", wchar); + qstring_append(str, escape); + } else switch (ptr[0]) { + case '\"': + qstring_append(str, "\\\""); + break; + case '\\': + qstring_append(str, "\\\\"); + break; + case '\b': + qstring_append(str, "\\b"); + break; + case '\f': + qstring_append(str, "\\f"); + break; + case '\n': + qstring_append(str, "\\n"); + break; + case '\r': + qstring_append(str, "\\r"); + break; + case '\t': + qstring_append(str, "\\t"); + break; + default: { + if (ptr[0] <= 0x1F) { + char escape[7]; + snprintf(escape, sizeof(escape), "\\u%04X", ptr[0]); + qstring_append(str, escape); + } else { + char buf[2] = { ptr[0], 0 }; + qstring_append(str, buf); + } + break; + } + } + ptr++; + } + qstring_append(str, "\""); + break; + } + case QTYPE_QDICT: { + ToJsonIterState s; + QDict *val = qobject_to_qdict(obj); + + s.count = 0; + s.str = str; + s.indent = indent + 1; + s.pretty = pretty; + qstring_append(str, "{"); + qdict_iter(val, to_json_dict_iter, &s); + if (pretty) { + int j; + qstring_append(str, "\n"); + for (j = 0 ; j < indent ; j++) + qstring_append(str, " "); + } + qstring_append(str, "}"); + break; + } + case QTYPE_QLIST: { + ToJsonIterState s; + QList *val = qobject_to_qlist(obj); + + s.count = 0; + s.str = str; + s.indent = indent + 1; + s.pretty = pretty; + qstring_append(str, "["); + qlist_iter(val, (void *)to_json_list_iter, &s); + if (pretty) { + int j; + qstring_append(str, "\n"); + for (j = 0 ; j < indent ; j++) + qstring_append(str, " "); + } + qstring_append(str, "]"); + break; + } + case QTYPE_QFLOAT: { + QFloat *val = qobject_to_qfloat(obj); + char buffer[1024]; + int len; + + len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val)); + while (len > 0 && buffer[len - 1] == '0') { + len--; + } + + if (len && buffer[len - 1] == '.') { + buffer[len - 1] = 0; + } else { + buffer[len] = 0; + } + + qstring_append(str, buffer); + break; + } + case QTYPE_QBOOL: { + QBool *val = qobject_to_qbool(obj); + + if (qbool_get_int(val)) { + qstring_append(str, "true"); + } else { + qstring_append(str, "false"); + } + break; + } + case QTYPE_QERROR: + /* XXX: should QError be emitted? */ + case QTYPE_NONE: + break; + } +} + +QString *qobject_to_json(const QObject *obj) +{ + QString *str = qstring_new(); + + to_json(obj, str, 0, 0); + + return str; +} + +QString *qobject_to_json_pretty(const QObject *obj) +{ + QString *str = qstring_new(); + + to_json(obj, str, 1, 0); + + return str; +} diff --git a/qobject/qlist.c b/qobject/qlist.c new file mode 100644 index 0000000..1ced0de --- /dev/null +++ b/qobject/qlist.c @@ -0,0 +1,170 @@ +/* + * QList Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qobject.h" +#include "qemu/queue.h" +#include "qemu-common.h" + +static void qlist_destroy_obj(QObject *obj); + +static const QType qlist_type = { + .code = QTYPE_QLIST, + .destroy = qlist_destroy_obj, +}; + +/** + * qlist_new(): Create a new QList + * + * Return strong reference. + */ +QList *qlist_new(void) +{ + QList *qlist; + + qlist = g_malloc(sizeof(*qlist)); + QTAILQ_INIT(&qlist->head); + QOBJECT_INIT(qlist, &qlist_type); + + return qlist; +} + +static void qlist_copy_elem(QObject *obj, void *opaque) +{ + QList *dst = opaque; + + qobject_incref(obj); + qlist_append_obj(dst, obj); +} + +QList *qlist_copy(QList *src) +{ + QList *dst = qlist_new(); + + qlist_iter(src, qlist_copy_elem, dst); + + return dst; +} + +/** + * qlist_append_obj(): Append an QObject into QList + * + * NOTE: ownership of 'value' is transferred to the QList + */ +void qlist_append_obj(QList *qlist, QObject *value) +{ + QListEntry *entry; + + entry = g_malloc(sizeof(*entry)); + entry->value = value; + + QTAILQ_INSERT_TAIL(&qlist->head, entry, next); +} + +/** + * qlist_iter(): Iterate over all the list's stored values. + * + * This function allows the user to provide an iterator, which will be + * called for each stored value in the list. + */ +void qlist_iter(const QList *qlist, + void (*iter)(QObject *obj, void *opaque), void *opaque) +{ + QListEntry *entry; + + QTAILQ_FOREACH(entry, &qlist->head, next) + iter(entry->value, opaque); +} + +QObject *qlist_pop(QList *qlist) +{ + QListEntry *entry; + QObject *ret; + + if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { + return NULL; + } + + entry = QTAILQ_FIRST(&qlist->head); + QTAILQ_REMOVE(&qlist->head, entry, next); + + ret = entry->value; + g_free(entry); + + return ret; +} + +QObject *qlist_peek(QList *qlist) +{ + QListEntry *entry; + QObject *ret; + + if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { + return NULL; + } + + entry = QTAILQ_FIRST(&qlist->head); + + ret = entry->value; + + return ret; +} + +int qlist_empty(const QList *qlist) +{ + return QTAILQ_EMPTY(&qlist->head); +} + +static void qlist_size_iter(QObject *obj, void *opaque) +{ + size_t *count = opaque; + (*count)++; +} + +size_t qlist_size(const QList *qlist) +{ + size_t count = 0; + qlist_iter(qlist, qlist_size_iter, &count); + return count; +} + +/** + * qobject_to_qlist(): Convert a QObject into a QList + */ +QList *qobject_to_qlist(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QLIST) { + return NULL; + } + + return container_of(obj, QList, base); +} + +/** + * qlist_destroy_obj(): Free all the memory allocated by a QList + */ +static void qlist_destroy_obj(QObject *obj) +{ + QList *qlist; + QListEntry *entry, *next_entry; + + assert(obj != NULL); + qlist = qobject_to_qlist(obj); + + QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { + QTAILQ_REMOVE(&qlist->head, entry, next); + qobject_decref(entry->value); + g_free(entry); + } + + g_free(qlist); +} diff --git a/qobject/qstring.c b/qobject/qstring.c new file mode 100644 index 0000000..5f7376c --- /dev/null +++ b/qobject/qstring.c @@ -0,0 +1,141 @@ +/* + * QString Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qstring.h" +#include "qemu-common.h" + +static void qstring_destroy_obj(QObject *obj); + +static const QType qstring_type = { + .code = QTYPE_QSTRING, + .destroy = qstring_destroy_obj, +}; + +/** + * qstring_new(): Create a new empty QString + * + * Return strong reference. + */ +QString *qstring_new(void) +{ + return qstring_from_str(""); +} + +/** + * qstring_from_substr(): Create a new QString from a C string substring + * + * Return string reference + */ +QString *qstring_from_substr(const char *str, int start, int end) +{ + QString *qstring; + + qstring = g_malloc(sizeof(*qstring)); + + qstring->length = end - start + 1; + qstring->capacity = qstring->length; + + qstring->string = g_malloc(qstring->capacity + 1); + memcpy(qstring->string, str + start, qstring->length); + qstring->string[qstring->length] = 0; + + QOBJECT_INIT(qstring, &qstring_type); + + return qstring; +} + +/** + * qstring_from_str(): Create a new QString from a regular C string + * + * Return strong reference. + */ +QString *qstring_from_str(const char *str) +{ + return qstring_from_substr(str, 0, strlen(str) - 1); +} + +static void capacity_increase(QString *qstring, size_t len) +{ + if (qstring->capacity < (qstring->length + len)) { + qstring->capacity += len; + qstring->capacity *= 2; /* use exponential growth */ + + qstring->string = g_realloc(qstring->string, qstring->capacity + 1); + } +} + +/* qstring_append(): Append a C string to a QString + */ +void qstring_append(QString *qstring, const char *str) +{ + size_t len = strlen(str); + + capacity_increase(qstring, len); + memcpy(qstring->string + qstring->length, str, len); + qstring->length += len; + qstring->string[qstring->length] = 0; +} + +void qstring_append_int(QString *qstring, int64_t value) +{ + char num[32]; + + snprintf(num, sizeof(num), "%" PRId64, value); + qstring_append(qstring, num); +} + +/** + * qstring_append_chr(): Append a C char to a QString + */ +void qstring_append_chr(QString *qstring, int c) +{ + capacity_increase(qstring, 1); + qstring->string[qstring->length++] = c; + qstring->string[qstring->length] = 0; +} + +/** + * qobject_to_qstring(): Convert a QObject to a QString + */ +QString *qobject_to_qstring(const QObject *obj) +{ + if (qobject_type(obj) != QTYPE_QSTRING) + return NULL; + + return container_of(obj, QString, base); +} + +/** + * qstring_get_str(): Return a pointer to the stored string + * + * NOTE: Should be used with caution, if the object is deallocated + * this pointer becomes invalid. + */ +const char *qstring_get_str(const QString *qstring) +{ + return qstring->string; +} + +/** + * qstring_destroy_obj(): Free all memory allocated by a QString + * object + */ +static void qstring_destroy_obj(QObject *obj) +{ + QString *qs; + + assert(obj != NULL); + qs = qobject_to_qstring(obj); + g_free(qs->string); + g_free(qs); +} diff --git a/qstring.c b/qstring.c deleted file mode 100644 index 5f7376c..0000000 --- a/qstring.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * QString Module - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qstring.h" -#include "qemu-common.h" - -static void qstring_destroy_obj(QObject *obj); - -static const QType qstring_type = { - .code = QTYPE_QSTRING, - .destroy = qstring_destroy_obj, -}; - -/** - * qstring_new(): Create a new empty QString - * - * Return strong reference. - */ -QString *qstring_new(void) -{ - return qstring_from_str(""); -} - -/** - * qstring_from_substr(): Create a new QString from a C string substring - * - * Return string reference - */ -QString *qstring_from_substr(const char *str, int start, int end) -{ - QString *qstring; - - qstring = g_malloc(sizeof(*qstring)); - - qstring->length = end - start + 1; - qstring->capacity = qstring->length; - - qstring->string = g_malloc(qstring->capacity + 1); - memcpy(qstring->string, str + start, qstring->length); - qstring->string[qstring->length] = 0; - - QOBJECT_INIT(qstring, &qstring_type); - - return qstring; -} - -/** - * qstring_from_str(): Create a new QString from a regular C string - * - * Return strong reference. - */ -QString *qstring_from_str(const char *str) -{ - return qstring_from_substr(str, 0, strlen(str) - 1); -} - -static void capacity_increase(QString *qstring, size_t len) -{ - if (qstring->capacity < (qstring->length + len)) { - qstring->capacity += len; - qstring->capacity *= 2; /* use exponential growth */ - - qstring->string = g_realloc(qstring->string, qstring->capacity + 1); - } -} - -/* qstring_append(): Append a C string to a QString - */ -void qstring_append(QString *qstring, const char *str) -{ - size_t len = strlen(str); - - capacity_increase(qstring, len); - memcpy(qstring->string + qstring->length, str, len); - qstring->length += len; - qstring->string[qstring->length] = 0; -} - -void qstring_append_int(QString *qstring, int64_t value) -{ - char num[32]; - - snprintf(num, sizeof(num), "%" PRId64, value); - qstring_append(qstring, num); -} - -/** - * qstring_append_chr(): Append a C char to a QString - */ -void qstring_append_chr(QString *qstring, int c) -{ - capacity_increase(qstring, 1); - qstring->string[qstring->length++] = c; - qstring->string[qstring->length] = 0; -} - -/** - * qobject_to_qstring(): Convert a QObject to a QString - */ -QString *qobject_to_qstring(const QObject *obj) -{ - if (qobject_type(obj) != QTYPE_QSTRING) - return NULL; - - return container_of(obj, QString, base); -} - -/** - * qstring_get_str(): Return a pointer to the stored string - * - * NOTE: Should be used with caution, if the object is deallocated - * this pointer becomes invalid. - */ -const char *qstring_get_str(const QString *qstring) -{ - return qstring->string; -} - -/** - * qstring_destroy_obj(): Free all memory allocated by a QString - * object - */ -static void qstring_destroy_obj(QObject *obj) -{ - QString *qs; - - assert(obj != NULL); - qs = qobject_to_qstring(obj); - g_free(qs->string); - g_free(qs); -} diff --git a/tests/Makefile b/tests/Makefile index a398b4a..837f769 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -70,17 +70,17 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ tests/test-qmp-commands.o tests/test-visitor-serialization.o -test-qapi-obj-y = $(qobject-obj-y) $(qapi-obj-y) +test-qapi-obj-y = $(qapi-obj-y) test-qapi-obj-y += tests/test-qapi-visit.o tests/test-qapi-types.o $(test-obj-y): QEMU_INCLUDES += -Itests -tests/check-qint$(EXESUF): tests/check-qint.o qint.o -tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o -tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o -tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o -tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o -tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) libqemuutil.a libqemustub.a +tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a +tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a +tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a +tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a +tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a +tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a -- cgit v1.1