mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 01:33:56 -06:00
build: move qobject files to qobject/ and libqemuutil.a
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
baacf04799
commit
a372823a14
15 changed files with 14 additions and 22 deletions
3
qobject/Makefile.objs
Normal file
3
qobject/Makefile.objs
Normal file
|
@ -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
|
373
qobject/json-lexer.c
Normal file
373
qobject/json-lexer.c
Normal file
|
@ -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_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);
|
||||
}
|
704
qobject/json-parser.c
Normal file
704
qobject/json-parser.c
Normal file
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* 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"
|
||||
#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;
|
||||
}
|
122
qobject/json-streamer.c
Normal file
122
qobject/json-streamer.c
Normal file
|
@ -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);
|
||||
}
|
68
qobject/qbool.c
Normal file
68
qobject/qbool.c
Normal file
|
@ -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_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));
|
||||
}
|
456
qobject/qdict.c
Normal file
456
qobject/qdict.c
Normal file
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* 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'.
|
||||
*/
|
||||
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);
|
||||
}
|
156
qobject/qerror.c
Normal file
156
qobject/qerror.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* QError 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 "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);
|
||||
}
|
68
qobject/qfloat.c
Normal file
68
qobject/qfloat.c
Normal file
|
@ -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));
|
||||
}
|
67
qobject/qint.c
Normal file
67
qobject/qint.c
Normal file
|
@ -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));
|
||||
}
|
294
qobject/qjson.c
Normal file
294
qobject/qjson.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* 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, ", ");
|
||||
|
||||
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;
|
||||
}
|
170
qobject/qlist.c
Normal file
170
qobject/qlist.c
Normal file
|
@ -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);
|
||||
}
|
141
qobject/qstring.c
Normal file
141
qobject/qstring.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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_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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue