diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /qobject | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'qobject')
| -rw-r--r-- | qobject/Makefile.objs | 2 | ||||
| -rw-r--r-- | qobject/json-lexer.c | 373 | ||||
| -rw-r--r-- | qobject/json-parser.c | 714 | ||||
| -rw-r--r-- | qobject/json-streamer.c | 122 | ||||
| -rw-r--r-- | qobject/qbool.c | 68 | ||||
| -rw-r--r-- | qobject/qdict.c | 802 | ||||
| -rw-r--r-- | qobject/qfloat.c | 68 | ||||
| -rw-r--r-- | qobject/qint.c | 67 | ||||
| -rw-r--r-- | qobject/qjson.c | 285 | ||||
| -rw-r--r-- | qobject/qlist.c | 170 | ||||
| -rw-r--r-- | qobject/qnull.c | 29 | ||||
| -rw-r--r-- | qobject/qstring.c | 149 | 
12 files changed, 2849 insertions, 0 deletions
diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs new file mode 100644 index 00000000..0031e8b6 --- /dev/null +++ b/qobject/Makefile.objs @@ -0,0 +1,2 @@ +util-obj-y = qnull.o 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 diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c new file mode 100644 index 00000000..b19623e2 --- /dev/null +++ b/qobject/json-lexer.c @@ -0,0 +1,373 @@ +/* + * JSON lexer + * + * Copyright IBM, Corp. 2009 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * 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_SQ_STRING, +        ['\\'] = IN_SQ_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 00000000..ac991ba3 --- /dev/null +++ b/qobject/json-parser.c @@ -0,0 +1,714 @@ +/* + * JSON Parser  + * + * Copyright IBM, Corp. 2009 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * 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 <stdarg.h> + +#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" + +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_setg(&ctxt->err, "JSON parse error, %s", 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; +    } + +    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; +            } + +            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; +            } +        } +    } else { +        (void)parser_context_pop_token(ctxt); +    } + +    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, '[')) { +        goto out; +    } + +    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; +            } + +            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; +            } +        } +    } else { +        (void)parser_context_pop_token(ctxt); +    } + +    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_bool(true)); +    } else if (token_is_keyword(token, "false")) { +        ret = QOBJECT(qbool_from_bool(false)); +    } else if (token_is_keyword(token, "null")) { +        ret = qnull(); +    } 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_bool(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: { +        /* A possibility exists that this is a whole-valued float where the +         * fractional part was left out due to being 0 (.0). It's not a big +         * deal to treat these as ints in the parser, so long as users of the +         * resulting QObject know to expect a QInt in place of a QFloat in +         * cases like these. +         * +         * However, in some cases these values will overflow/underflow a +         * QInt/int64 container, thus we should assume these are to be handled +         * as QFloats/doubles rather than silently changing their values. +         * +         * strtoll() indicates these instances by setting errno to ERANGE +         */ +        int64_t value; + +        errno = 0; /* strtoll doesn't set errno on success */ +        value = strtoll(token_get_value(token), NULL, 10); +        if (errno != ERANGE) { +            obj = QOBJECT(qint_from_int(value)); +            break; +        } +        /* fall through to JSON_FLOAT */ +    } +    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 00000000..1b2f9b1d --- /dev/null +++ b/qobject/json-streamer.c @@ -0,0 +1,122 @@ +/* + * JSON streaming support + * + * Copyright IBM, Corp. 2009 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * 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 00000000..5ff69f0b --- /dev/null +++ b/qobject/qbool.c @@ -0,0 +1,68 @@ +/* + * QBool Module + * + * Copyright IBM, Corp. 2009 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * 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_bool(): Create a new QBool from a bool + * + * Return strong reference. + */ +QBool *qbool_from_bool(bool value) +{ +    QBool *qb; + +    qb = g_malloc(sizeof(*qb)); +    qb->value = value; +    QOBJECT_INIT(qb, &qbool_type); + +    return qb; +} + +/** + * qbool_get_bool(): Get the stored bool + */ +bool qbool_get_bool(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 00000000..67b1a58a --- /dev/null +++ b/qobject/qdict.c @@ -0,0 +1,802 @@ +/* + * QDict Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + *  Luiz Capitulino <lcapitulino@redhat.com> + * + * 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'. + */ +bool qdict_get_bool(const QDict *qdict, const char *key) +{ +    QObject *obj = qdict_get_obj(qdict, key, QTYPE_QBOOL); +    return qbool_get_bool(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. + */ +bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value) +{ +    QObject *obj; + +    obj = qdict_get(qdict, key); +    if (!obj || qobject_type(obj) != QTYPE_QBOOL) +        return def_value; + +    return qbool_get_bool(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; +} + +/** + * qdict_clone_shallow(): Clones a given QDict. Its entries are not copied, but + * another reference is added. + */ +QDict *qdict_clone_shallow(const QDict *src) +{ +    QDict *dest; +    QDictEntry *entry; +    int i; + +    dest = qdict_new(); + +    for (i = 0; i < QDICT_BUCKET_MAX; i++) { +        QLIST_FOREACH(entry, &src->table[i], next) { +            qobject_incref(entry->value); +            qdict_put_obj(dest, entry->key, entry->value); +        } +    } + +    return dest; +} + +/** + * 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); +} + +/** + * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the + * value of 'key' in 'src' is copied there (and the refcount increased + * accordingly). + */ +void qdict_copy_default(QDict *dst, QDict *src, const char *key) +{ +    QObject *val; + +    if (qdict_haskey(dst, key)) { +        return; +    } + +    val = qdict_get(src, key); +    if (val) { +        qobject_incref(val); +        qdict_put_obj(dst, key, val); +    } +} + +/** + * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a + * new QString initialised by 'val' is put there. + */ +void qdict_set_default_str(QDict *dst, const char *key, const char *val) +{ +    if (qdict_haskey(dst, key)) { +        return; +    } + +    qdict_put(dst, key, qstring_from_str(val)); +} + +static void qdict_flatten_qdict(QDict *qdict, QDict *target, +                                const char *prefix); + +static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) +{ +    QObject *value; +    const QListEntry *entry; +    char *new_key; +    int i; + +    /* This function is never called with prefix == NULL, i.e., it is always +     * called from within qdict_flatten_q(list|dict)(). Therefore, it does not +     * need to remove list entries during the iteration (the whole list will be +     * deleted eventually anyway from qdict_flatten_qdict()). */ +    assert(prefix); + +    entry = qlist_first(qlist); + +    for (i = 0; entry; entry = qlist_next(entry), i++) { +        value = qlist_entry_obj(entry); +        new_key = g_strdup_printf("%s.%i", prefix, i); + +        if (qobject_type(value) == QTYPE_QDICT) { +            qdict_flatten_qdict(qobject_to_qdict(value), target, new_key); +        } else if (qobject_type(value) == QTYPE_QLIST) { +            qdict_flatten_qlist(qobject_to_qlist(value), target, new_key); +        } else { +            /* All other types are moved to the target unchanged. */ +            qobject_incref(value); +            qdict_put_obj(target, new_key, value); +        } + +        g_free(new_key); +    } +} + +static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) +{ +    QObject *value; +    const QDictEntry *entry, *next; +    char *new_key; +    bool delete; + +    entry = qdict_first(qdict); + +    while (entry != NULL) { + +        next = qdict_next(qdict, entry); +        value = qdict_entry_value(entry); +        new_key = NULL; +        delete = false; + +        if (prefix) { +            new_key = g_strdup_printf("%s.%s", prefix, entry->key); +        } + +        if (qobject_type(value) == QTYPE_QDICT) { +            /* Entries of QDicts are processed recursively, the QDict object +             * itself disappears. */ +            qdict_flatten_qdict(qobject_to_qdict(value), target, +                                new_key ? new_key : entry->key); +            delete = true; +        } else if (qobject_type(value) == QTYPE_QLIST) { +            qdict_flatten_qlist(qobject_to_qlist(value), target, +                                new_key ? new_key : entry->key); +            delete = true; +        } else if (prefix) { +            /* All other objects are moved to the target unchanged. */ +            qobject_incref(value); +            qdict_put_obj(target, new_key, value); +            delete = true; +        } + +        g_free(new_key); + +        if (delete) { +            qdict_del(qdict, entry->key); + +            /* Restart loop after modifying the iterated QDict */ +            entry = qdict_first(qdict); +            continue; +        } + +        entry = next; +    } +} + +/** + * qdict_flatten(): For each nested QDict with key x, all fields with key y + * are moved to this QDict and their key is renamed to "x.y". For each nested + * QList with key x, the field at index y is moved to this QDict with the key + * "x.y" (i.e., the reverse of what qdict_array_split() does). + * This operation is applied recursively for nested QDicts and QLists. + */ +void qdict_flatten(QDict *qdict) +{ +    qdict_flatten_qdict(qdict, qdict, NULL); +} + +/* extract all the src QDict entries starting by start into dst */ +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) + +{ +    const QDictEntry *entry, *next; +    const char *p; + +    *dst = qdict_new(); +    entry = qdict_first(src); + +    while (entry != NULL) { +        next = qdict_next(src, entry); +        if (strstart(entry->key, start, &p)) { +            qobject_incref(entry->value); +            qdict_put_obj(*dst, p, entry->value); +            qdict_del(src, entry->key); +        } +        entry = next; +    } +} + +static int qdict_count_prefixed_entries(const QDict *src, const char *start) +{ +    const QDictEntry *entry; +    int count = 0; + +    for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { +        if (strstart(entry->key, start, NULL)) { +            if (count == INT_MAX) { +                return -ERANGE; +            } +            count++; +        } +    } + +    return count; +} + +/** + * qdict_array_split(): This function moves array-like elements of a QDict into + * a new QList. Every entry in the original QDict with a key "%u" or one + * prefixed "%u.", where %u designates an unsigned integer starting at 0 and + * incrementally counting up, will be moved to a new QDict at index %u in the + * output QList with the key prefix removed, if that prefix is "%u.". If the + * whole key is just "%u", the whole QObject will be moved unchanged without + * creating a new QDict. The function terminates when there is no entry in the + * QDict with a prefix directly (incrementally) following the last one; it also + * returns if there are both entries with "%u" and "%u." for the same index %u. + * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} + *      (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) + *       => [{"a": 42, "b": 23}, {"x": 0}, 66] + *      and {"4.y": 1, "o.o": 7} (remainder of the old QDict) + */ +void qdict_array_split(QDict *src, QList **dst) +{ +    unsigned i; + +    *dst = qlist_new(); + +    for (i = 0; i < UINT_MAX; i++) { +        QObject *subqobj; +        bool is_subqdict; +        QDict *subqdict; +        char indexstr[32], prefix[32]; +        size_t snprintf_ret; + +        snprintf_ret = snprintf(indexstr, 32, "%u", i); +        assert(snprintf_ret < 32); + +        subqobj = qdict_get(src, indexstr); + +        snprintf_ret = snprintf(prefix, 32, "%u.", i); +        assert(snprintf_ret < 32); + +        /* Overflow is the same as positive non-zero results */ +        is_subqdict = qdict_count_prefixed_entries(src, prefix); + +        // There may be either a single subordinate object (named "%u") or +        // multiple objects (each with a key prefixed "%u."), but not both. +        if (!subqobj == !is_subqdict) { +            break; +        } + +        if (is_subqdict) { +            qdict_extract_subqdict(src, &subqdict, prefix); +            assert(qdict_size(subqdict) > 0); +        } else { +            qobject_incref(subqobj); +            qdict_del(src, indexstr); +        } + +        qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); +    } +} + +/** + * qdict_array_entries(): Returns the number of direct array entries if the + * sub-QDict of src specified by the prefix in subqdict (or src itself for + * prefix == "") is valid as an array, i.e. the length of the created list if + * the sub-QDict would become empty after calling qdict_array_split() on it. If + * the array is not valid, -EINVAL is returned. + */ +int qdict_array_entries(QDict *src, const char *subqdict) +{ +    const QDictEntry *entry; +    unsigned i; +    unsigned entries = 0; +    size_t subqdict_len = strlen(subqdict); + +    assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); + +    /* qdict_array_split() loops until UINT_MAX, but as we want to return +     * negative errors, we only have a signed return value here. Any additional +     * entries will lead to -EINVAL. */ +    for (i = 0; i < INT_MAX; i++) { +        QObject *subqobj; +        int subqdict_entries; +        size_t slen = 32 + subqdict_len; +        char indexstr[slen], prefix[slen]; +        size_t snprintf_ret; + +        snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i); +        assert(snprintf_ret < slen); + +        subqobj = qdict_get(src, indexstr); + +        snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i); +        assert(snprintf_ret < slen); + +        subqdict_entries = qdict_count_prefixed_entries(src, prefix); +        if (subqdict_entries < 0) { +            return subqdict_entries; +        } + +        /* There may be either a single subordinate object (named "%u") or +         * multiple objects (each with a key prefixed "%u."), but not both. */ +        if (subqobj && subqdict_entries) { +            return -EINVAL; +        } else if (!subqobj && !subqdict_entries) { +            break; +        } + +        entries += subqdict_entries ? subqdict_entries : 1; +    } + +    /* Consider everything handled that isn't part of the given sub-QDict */ +    for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { +        if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { +            entries++; +        } +    } + +    /* Anything left in the sub-QDict that wasn't handled? */ +    if (qdict_size(src) != entries) { +        return -EINVAL; +    } + +    return i; +} + +/** + * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all + * elements from src to dest. + * + * If an element from src has a key already present in dest, it will not be + * moved unless overwrite is true. + * + * If overwrite is true, the conflicting values in dest will be discarded and + * replaced by the corresponding values from src. + * + * Therefore, with overwrite being true, the src QDict will always be empty when + * this function returns. If overwrite is false, the src QDict will be empty + * iff there were no conflicts. + */ +void qdict_join(QDict *dest, QDict *src, bool overwrite) +{ +    const QDictEntry *entry, *next; + +    entry = qdict_first(src); +    while (entry) { +        next = qdict_next(src, entry); + +        if (overwrite || !qdict_haskey(dest, entry->key)) { +            qobject_incref(entry->value); +            qdict_put_obj(dest, entry->key, entry->value); +            qdict_del(src, entry->key); +        } + +        entry = next; +    } +} diff --git a/qobject/qfloat.c b/qobject/qfloat.c new file mode 100644 index 00000000..7de0992d --- /dev/null +++ b/qobject/qfloat.c @@ -0,0 +1,68 @@ +/* + * QFloat Module + * + * Copyright IBM, Corp. 2009 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * 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 00000000..86b9b04f --- /dev/null +++ b/qobject/qint.c @@ -0,0 +1,67 @@ +/* + * QInt Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + *  Luiz Capitulino <lcapitulino@redhat.com> + * + * 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 00000000..33f8ef53 --- /dev/null +++ b/qobject/qjson.c @@ -0,0 +1,285 @@ +/* + * QObject JSON integration + * + * Copyright IBM, Corp. 2009 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * 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, s->pretty ? "," : ", "); +    } + +    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, s->pretty ? "," : ", "); +    } + +    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_QNULL: +        qstring_append(str, "null"); +        break; +    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; +        int cp; +        char buf[16]; +        char *end; + +        ptr = qstring_get_str(val); +        qstring_append(str, "\""); + +        for (; *ptr; ptr = end) { +            cp = mod_utf8_codepoint(ptr, 6, &end); +            switch (cp) { +            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 (cp < 0) { +                    cp = 0xFFFD; /* replacement character */ +                } +                if (cp > 0xFFFF) { +                    /* beyond BMP; need a surrogate pair */ +                    snprintf(buf, sizeof(buf), "\\u%04X\\u%04X", +                             0xD800 + ((cp - 0x10000) >> 10), +                             0xDC00 + ((cp - 0x10000) & 0x3FF)); +                } else if (cp < 0x20 || cp >= 0x7F) { +                    snprintf(buf, sizeof(buf), "\\u%04X", cp); +                } else { +                    buf[0] = cp; +                    buf[1] = 0; +                } +                qstring_append(str, buf); +            } +        }; + +        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_bool(val)) { +            qstring_append(str, "true"); +        } else { +            qstring_append(str, "false"); +        } +        break; +    } +    default: +        abort(); +    } +} + +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 00000000..1ced0de5 --- /dev/null +++ b/qobject/qlist.c @@ -0,0 +1,170 @@ +/* + * QList Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + *  Luiz Capitulino <lcapitulino@redhat.com> + * + * 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/qnull.c b/qobject/qnull.c new file mode 100644 index 00000000..9873e266 --- /dev/null +++ b/qobject/qnull.c @@ -0,0 +1,29 @@ +/* + * QNull + * + * Copyright (C) 2015 Red Hat, Inc. + * + * Authors: + *  Markus Armbruster <armbru@redhat.com> + * + * 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 "qemu-common.h" +#include "qapi/qmp/qobject.h" + +static void qnull_destroy_obj(QObject *obj) +{ +    assert(0); +} + +static const QType qnull_type = { +    .code = QTYPE_QNULL, +    .destroy = qnull_destroy_obj, +}; + +QObject qnull_ = { +    .type = &qnull_type, +    .refcnt = 1, +}; diff --git a/qobject/qstring.c b/qobject/qstring.c new file mode 100644 index 00000000..607b7a14 --- /dev/null +++ b/qobject/qstring.c @@ -0,0 +1,149 @@ +/* + * QString Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + *  Luiz Capitulino <lcapitulino@redhat.com> + * + * 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_get_length(): Get the length of a QString + */ +size_t qstring_get_length(const QString *qstring) +{ +    return qstring->length; +} + +/** + * 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); +}  | 
