mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-03-17 20:58:34 +02:00
Add basic C JSON parser.
This commit is contained in:
parent
31167d8f98
commit
7993f1a966
@ -86,6 +86,9 @@ path-close: 97
|
||||
# Unable to get info for a file
|
||||
file-info: 98
|
||||
|
||||
# Invalid JSON format. Eventually this should be a child of format error and share the same code
|
||||
json-format: 99
|
||||
|
||||
# This error should not be thrown directly -- it serves as a parent for the C errors
|
||||
runtime: 122
|
||||
|
||||
|
@ -53,6 +53,10 @@
|
||||
<p>Add <code>uint64</code> variant type and supporting conversion functions.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Add basic C JSON parser.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Allow <code>Buffer</code> object <quote>used size</quote> to be different than <quote>allocated size</quote>. Add functions to manage used size and remaining size and update automatically when possible.</p>
|
||||
</release-item>
|
||||
|
@ -165,6 +165,8 @@ use constant ERROR_PATH_CLOSE => 97;
|
||||
push @EXPORT, qw(ERROR_PATH_CLOSE);
|
||||
use constant ERROR_FILE_INFO => 98;
|
||||
push @EXPORT, qw(ERROR_FILE_INFO);
|
||||
use constant ERROR_JSON_FORMAT => 99;
|
||||
push @EXPORT, qw(ERROR_JSON_FORMAT);
|
||||
use constant ERROR_RUNTIME => 122;
|
||||
push @EXPORT, qw(ERROR_RUNTIME);
|
||||
use constant ERROR_INVALID => 123;
|
||||
|
@ -214,6 +214,9 @@ common/type/buffer.o: common/type/buffer.c common/assert.h common/debug.h common
|
||||
common/type/convert.o: common/type/convert.c common/assert.h common/debug.h common/error.auto.h common/error.h common/logLevel.h common/stackTrace.h common/type/convert.h
|
||||
$(CC) $(CFLAGS) -c common/type/convert.c -o common/type/convert.o
|
||||
|
||||
common/type/json.o: common/type/json.c common/debug.h common/log.h common/logLevel.h common/stackTrace.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/variantList.h
|
||||
$(CC) $(CFLAGS) -c common/type/json.c -o common/type/json.o
|
||||
|
||||
common/type/keyValue.o: common/type/keyValue.c common/debug.h common/error.auto.h common/error.h common/logLevel.h common/memContext.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h
|
||||
$(CC) $(CFLAGS) -c common/type/keyValue.c -o common/type/keyValue.o
|
||||
|
||||
|
@ -80,6 +80,7 @@ ERROR_DEFINE( 95, CipherError, RuntimeError);
|
||||
ERROR_DEFINE( 96, ParamInvalidError, RuntimeError);
|
||||
ERROR_DEFINE( 97, PathCloseError, RuntimeError);
|
||||
ERROR_DEFINE( 98, FileInfoError, RuntimeError);
|
||||
ERROR_DEFINE( 99, JsonFormatError, RuntimeError);
|
||||
ERROR_DEFINE(122, RuntimeError, RuntimeError);
|
||||
ERROR_DEFINE(123, InvalidError, RuntimeError);
|
||||
ERROR_DEFINE(124, UnhandledError, RuntimeError);
|
||||
@ -163,6 +164,7 @@ static const ErrorType *errorTypeList[] =
|
||||
&ParamInvalidError,
|
||||
&PathCloseError,
|
||||
&FileInfoError,
|
||||
&JsonFormatError,
|
||||
&RuntimeError,
|
||||
&InvalidError,
|
||||
&UnhandledError,
|
||||
|
@ -82,6 +82,7 @@ ERROR_DECLARE(CipherError);
|
||||
ERROR_DECLARE(ParamInvalidError);
|
||||
ERROR_DECLARE(PathCloseError);
|
||||
ERROR_DECLARE(FileInfoError);
|
||||
ERROR_DECLARE(JsonFormatError);
|
||||
ERROR_DECLARE(RuntimeError);
|
||||
ERROR_DECLARE(InvalidError);
|
||||
ERROR_DECLARE(UnhandledError);
|
||||
|
111
src/common/type/json.c
Normal file
111
src/common/type/json.c
Normal file
@ -0,0 +1,111 @@
|
||||
/***********************************************************************************************************************************
|
||||
Convert JSON to/from KeyValue
|
||||
***********************************************************************************************************************************/
|
||||
#include <ctype.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/log.h"
|
||||
#include "common/type/json.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Convert JSON to KeyValue object
|
||||
|
||||
Currently this function is only intended to convert the limited types that are included in info files. More types will be added as
|
||||
needed. Since this function is only intended to read internally-generated JSON it is assumed to be well-formed with no extraneous
|
||||
whitespace.
|
||||
***********************************************************************************************************************************/
|
||||
KeyValue *jsonToKv(const String *json)
|
||||
{
|
||||
FUNCTION_DEBUG_BEGIN(logLevelTrace);
|
||||
FUNCTION_DEBUG_PARAM(STRING, json);
|
||||
|
||||
FUNCTION_TEST_ASSERT(json != NULL);
|
||||
FUNCTION_DEBUG_END();
|
||||
|
||||
KeyValue *result = kvNew();
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// We'll examine the string byte by byte
|
||||
const char *jsonC = strPtr(json);
|
||||
unsigned int jsonPos = 0;
|
||||
|
||||
// Consume the initial delimiter
|
||||
if (jsonC[jsonPos] == '[')
|
||||
THROW(JsonFormatError, "arrays not supported");
|
||||
else if (jsonC[jsonPos] != '{')
|
||||
THROW_FMT(JsonFormatError, "expected '{' but found '%c'", jsonC[jsonPos]);
|
||||
|
||||
// Start parsing key/value pairs
|
||||
do
|
||||
{
|
||||
Variant *value = NULL;
|
||||
|
||||
jsonPos++;
|
||||
|
||||
// Parse the key which should always be quoted
|
||||
if (jsonC[jsonPos] != '"')
|
||||
THROW_FMT(JsonFormatError, "expected '\"' but found '%c'", jsonC[jsonPos]);
|
||||
|
||||
unsigned int keyBeginPos = ++jsonPos;
|
||||
|
||||
while (jsonC[jsonPos] != '"' && jsonPos < strSize(json) - 1)
|
||||
jsonPos++;
|
||||
|
||||
if (jsonC[jsonPos] != '"')
|
||||
THROW_FMT(JsonFormatError, "expected '\"' but found '%c'", jsonC[jsonPos]);
|
||||
|
||||
String *key = strNewN(jsonC + keyBeginPos, jsonPos - keyBeginPos);
|
||||
|
||||
if (strSize(key) == 0)
|
||||
THROW(JsonFormatError, "zero-length key not allowed");
|
||||
|
||||
if (jsonC[++jsonPos] != ':')
|
||||
THROW_FMT(JsonFormatError, "expected ':' but found '%c'", jsonC[jsonPos]);
|
||||
|
||||
unsigned int valueBeginPos = ++jsonPos;
|
||||
|
||||
// The value appears to be a string
|
||||
if (jsonC[jsonPos] == '"')
|
||||
{
|
||||
valueBeginPos++;
|
||||
jsonPos++;
|
||||
|
||||
while (jsonC[jsonPos] != '"' && jsonPos < strSize(json) - 1)
|
||||
jsonPos++;
|
||||
|
||||
if (jsonC[jsonPos] != '"')
|
||||
THROW_FMT(JsonFormatError, "expected '\"' but found '%c'", jsonC[jsonPos]);
|
||||
|
||||
value = varNewStr(strNewN(jsonC + valueBeginPos, jsonPos - valueBeginPos));
|
||||
|
||||
jsonPos++;
|
||||
}
|
||||
|
||||
// The value appears to be a number
|
||||
else if (isdigit(jsonC[jsonPos]))
|
||||
{
|
||||
while (isdigit(jsonC[jsonPos]) && jsonPos < strSize(json) - 1)
|
||||
jsonPos++;
|
||||
|
||||
String *valueStr = strNewN(jsonC + valueBeginPos, jsonPos - valueBeginPos);
|
||||
|
||||
value = varNewUInt64(cvtZToUInt64(strPtr(valueStr)));
|
||||
}
|
||||
|
||||
// Else not sure what it is. Currently booleans and nulls will error.
|
||||
else
|
||||
THROW(JsonFormatError, "unknown value type");
|
||||
|
||||
kvPut(result, varNewStr(key), value);
|
||||
}
|
||||
while (jsonC[jsonPos] == ',');
|
||||
|
||||
// Look for end delimiter
|
||||
if (jsonC[jsonPos] != '}')
|
||||
THROW_FMT(JsonFormatError, "expected '}' but found '%c'", jsonC[jsonPos]);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_DEBUG_RESULT(KEY_VALUE, result);
|
||||
}
|
14
src/common/type/json.h
Normal file
14
src/common/type/json.h
Normal file
@ -0,0 +1,14 @@
|
||||
/***********************************************************************************************************************************
|
||||
Convert JSON to/from KeyValue
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMON_TYPE_JSON_H
|
||||
#define COMMON_TYPE_JSON_H
|
||||
|
||||
#include "common/type/keyValue.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
KeyValue *jsonToKv(const String *json);
|
||||
|
||||
#endif
|
@ -5046,6 +5046,8 @@ static const EmbeddedModule embeddedModule[] =
|
||||
"push @EXPORT, qw(ERROR_PATH_CLOSE);\n"
|
||||
"use constant ERROR_FILE_INFO => 98;\n"
|
||||
"push @EXPORT, qw(ERROR_FILE_INFO);\n"
|
||||
"use constant ERROR_JSON_FORMAT => 99;\n"
|
||||
"push @EXPORT, qw(ERROR_JSON_FORMAT);\n"
|
||||
"use constant ERROR_RUNTIME => 122;\n"
|
||||
"push @EXPORT, qw(ERROR_RUNTIME);\n"
|
||||
"use constant ERROR_INVALID => 123;\n"
|
||||
|
@ -185,6 +185,13 @@ unit:
|
||||
coverage:
|
||||
common/type/variantList: full
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: type-json
|
||||
total: 1
|
||||
|
||||
coverage:
|
||||
common/type/json: full
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: type-key-value
|
||||
total: 2
|
||||
|
50
test/src/module/common/typeJsonTest.c
Normal file
50
test/src/module/common/typeJsonTest.c
Normal file
@ -0,0 +1,50 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Convert JSON to/from KeyValue
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
testRun(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("jsonToKv()"))
|
||||
{
|
||||
TEST_ERROR(jsonToKv(strNew("[")), JsonFormatError, "arrays not supported");
|
||||
TEST_ERROR(jsonToKv(strNew("<")), JsonFormatError, "expected '{' but found '<'");
|
||||
TEST_ERROR(jsonToKv(strNew("{>")), JsonFormatError, "expected '\"' but found '>'");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"\"")), JsonFormatError, "zero-length key not allowed");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"key1\"")), JsonFormatError, "expected ':' but found '");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"key1'")), JsonFormatError, "expected '\"' but found '''");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"key1\":t")), JsonFormatError, "unknown value type");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"key1\":123")), JsonFormatError, "expected '}' but found '3'");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"key1\":123>")), JsonFormatError, "expected '}' but found '>'");
|
||||
TEST_ERROR(jsonToKv(strNew("{\"key1\":\"123'}")), JsonFormatError, "expected '\"' but found '}'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
KeyValue *kv = NULL;
|
||||
|
||||
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":123}")), "single integer value");
|
||||
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("key1")))), 123, " check integer");
|
||||
|
||||
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":\"value1\"}")), "single string value");
|
||||
TEST_RESULT_STR(strPtr(varStr(kvGet(kv, varNewStr(strNew("key1"))))), "value1", " check string");
|
||||
|
||||
TEST_ASSIGN(
|
||||
kv,
|
||||
jsonToKv(
|
||||
strNew(
|
||||
"{\"db-catalog-version\":201409291,\"db-control-version\":942,\"db-system-id\":6116111691796124355,"
|
||||
"\"db-version\":\"9.4\"}")),
|
||||
"multiple values");
|
||||
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("db-catalog-version")))), 201409291, " check integer");
|
||||
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("db-control-version")))), 942, " check integer");
|
||||
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("db-system-id")))), 6116111691796124355, " check integer");
|
||||
TEST_RESULT_STR(strPtr(varStr(kvGet(kv, varNewStr(strNew("db-version"))))), "9.4", " check string");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user