Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
607dbbf5
Commit
607dbbf5
authored
Nov 20, 2015
by
Kenton Varda
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #258 from kamalmarhubi/json-decodeRaw
Implement most of JsonCodec::decodeRaw
parents
e93c9aca
f60fe3c2
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
619 additions
and
4 deletions
+619
-4
CONTRIBUTORS
CONTRIBUTORS
+1
-0
json-test.c++
c++/src/capnp/compat/json-test.c++
+289
-0
json.c++
c++/src/capnp/compat/json.c++
+325
-4
json.h
c++/src/capnp/compat/json.h
+4
-0
No files found.
CONTRIBUTORS
View file @
607dbbf5
...
@@ -11,6 +11,7 @@ Bryan Borham <bjboreham@gmail.com>: Initial MSVC support
...
@@ -11,6 +11,7 @@ Bryan Borham <bjboreham@gmail.com>: Initial MSVC support
Philip Quinn <p@partylemon.com>: cmake build and other assorted bits
Philip Quinn <p@partylemon.com>: cmake build and other assorted bits
Brian Taylor <el.wubo@gmail.com>: emacs syntax highlighting
Brian Taylor <el.wubo@gmail.com>: emacs syntax highlighting
Ben Laurie <ben@links.org>: discovered and responsibly disclosed security bugs
Ben Laurie <ben@links.org>: discovered and responsibly disclosed security bugs
Kamal Marhubi <kamal@marhubi.com>: JSON parser
This file does not list people who maintain their own Cap'n Proto
This file does not list people who maintain their own Cap'n Proto
implementations as separate projects. Those people are awesome too! :)
implementations as separate projects. Those people are awesome too! :)
c++/src/capnp/compat/json-test.c++
View file @
607dbbf5
...
@@ -21,7 +21,9 @@
...
@@ -21,7 +21,9 @@
#include "json.h"
#include "json.h"
#include <capnp/test-util.h>
#include <capnp/test-util.h>
#include <capnp/compat/json.capnp.h>
#include <kj/debug.h>
#include <kj/debug.h>
#include <kj/string.h>
#include <kj/test.h>
#include <kj/test.h>
namespace
capnp
{
namespace
capnp
{
...
@@ -181,6 +183,293 @@ KJ_TEST("encode union") {
...
@@ -181,6 +183,293 @@ KJ_TEST("encode union") {
KJ_EXPECT
(
json
.
encode
(
root
)
==
"{
\"
before
\"
:
\"
a
\"
,
\"
middle
\"
:44,
\"
bar
\"
:321,
\"
after
\"
:
\"
c
\"
}"
);
KJ_EXPECT
(
json
.
encode
(
root
)
==
"{
\"
before
\"
:
\"
a
\"
,
\"
middle
\"
:44,
\"
bar
\"
:321,
\"
after
\"
:
\"
c
\"
}"
);
}
}
KJ_TEST
(
"basic json decoding"
)
{
// TODO(cleanup): this test is a mess!
JsonCodec
json
;
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"null"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
NULL_
);
KJ_EXPECT
(
root
.
getNull
()
==
VOID
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"false"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
BOOLEAN
);
KJ_EXPECT
(
root
.
getBoolean
()
==
false
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"true"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
BOOLEAN
);
KJ_EXPECT
(
root
.
getBoolean
()
==
true
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"
\"
foo
\"
"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
STRING
);
KJ_EXPECT
(
kj
::
str
(
"foo"
)
==
root
.
getString
());
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
R"("\"")"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
STRING
);
KJ_EXPECT
(
kj
::
str
(
"
\"
"
)
==
root
.
getString
());
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
R"("\\abc\"d\\e")"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
STRING
);
KJ_EXPECT
(
kj
::
str
(
"
\\
abc
\"
d
\\
e"
)
==
root
.
getString
());
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
R"("\"\\\/\b\f\n\r\t\u0003abc\u0064\u0065f")"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
STRING
);
KJ_EXPECT
(
kj
::
str
(
"
\"\\
/
\b\f\n\r\t\x03
""abcdef"
)
==
root
.
getString
(),
root
.
getString
());
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"[]"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
ARRAY
,
root
.
which
());
KJ_EXPECT
(
root
.
getArray
().
size
()
==
0
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"[true]"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
ARRAY
);
auto
array
=
root
.
getArray
();
KJ_EXPECT
(
array
.
size
()
==
1
,
array
.
size
());
KJ_EXPECT
(
root
.
getArray
()[
0
].
which
()
==
JsonValue
::
BOOLEAN
);
KJ_EXPECT
(
root
.
getArray
()[
0
].
getBoolean
()
==
true
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
" [ true , false
\t\n
, null]"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
ARRAY
);
auto
array
=
root
.
getArray
();
KJ_EXPECT
(
array
.
size
()
==
3
);
KJ_EXPECT
(
array
[
0
].
which
()
==
JsonValue
::
BOOLEAN
);
KJ_EXPECT
(
array
[
0
].
getBoolean
()
==
true
);
KJ_EXPECT
(
array
[
1
].
which
()
==
JsonValue
::
BOOLEAN
);
KJ_EXPECT
(
array
[
1
].
getBoolean
()
==
false
);
KJ_EXPECT
(
array
[
2
].
which
()
==
JsonValue
::
NULL_
);
KJ_EXPECT
(
array
[
2
].
getNull
()
==
VOID
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"{}"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
OBJECT
,
root
.
which
());
KJ_EXPECT
(
root
.
getObject
().
size
()
==
0
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
R"({"some": null})"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
OBJECT
,
root
.
which
());
auto
object
=
root
.
getObject
();
KJ_EXPECT
(
object
.
size
()
==
1
);
KJ_EXPECT
(
kj
::
str
(
"some"
)
==
object
[
0
].
getName
());
KJ_EXPECT
(
object
[
0
].
getValue
().
which
()
==
JsonValue
::
NULL_
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
R"({"foo\n\tbaz": "a val", "bar": ["a", -5.5e11, { "z": {}}]})"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
OBJECT
,
root
.
which
());
auto
object
=
root
.
getObject
();
KJ_EXPECT
(
object
.
size
()
==
2
);
KJ_EXPECT
(
kj
::
str
(
"foo
\n\t
baz"
)
==
object
[
0
].
getName
());
KJ_EXPECT
(
object
[
0
].
getValue
().
which
()
==
JsonValue
::
STRING
);
KJ_EXPECT
(
kj
::
str
(
"a val"
)
==
object
[
0
].
getValue
().
getString
());
KJ_EXPECT
(
kj
::
str
(
"bar"
)
==
object
[
1
].
getName
());
KJ_EXPECT
(
object
[
1
].
getValue
().
which
()
==
JsonValue
::
ARRAY
);
auto
array
=
object
[
1
].
getValue
().
getArray
();
KJ_EXPECT
(
array
.
size
()
==
3
,
array
.
size
());
KJ_EXPECT
(
array
[
0
].
which
()
==
JsonValue
::
STRING
);
KJ_EXPECT
(
kj
::
str
(
"a"
)
==
array
[
0
].
getString
());
KJ_EXPECT
(
array
[
1
].
which
()
==
JsonValue
::
NUMBER
);
KJ_EXPECT
(
array
[
1
].
getNumber
()
==
-
5.5e11
);
KJ_EXPECT
(
array
[
2
].
which
()
==
JsonValue
::
OBJECT
);
KJ_EXPECT
(
array
[
2
].
getObject
().
size
()
==
1
);
KJ_EXPECT
(
array
[
2
].
getObject
()[
0
].
getValue
().
which
()
==
JsonValue
::
OBJECT
);
KJ_EXPECT
(
array
[
2
].
getObject
()[
0
].
getValue
().
getObject
().
size
()
==
0
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"123"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
NUMBER
);
KJ_EXPECT
(
root
.
getNumber
()
==
123
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"input"
,
json
.
decodeRaw
(
"z"
,
root
));
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
// Leading + not allowed in numbers.
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected"
,
json
.
decodeRaw
(
"+123"
,
root
));
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"Overflow"
,
json
.
decodeRaw
(
"1e1024"
,
root
));
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"Underflow"
,
json
.
decodeRaw
(
"1e-1023"
,
root
));
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected"
,
json
.
decodeRaw
(
"[00]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected"
,
json
.
decodeRaw
(
"[01]"
,
root
));
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"ends prematurely"
,
json
.
decodeRaw
(
"-"
,
root
));
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"-5"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
NUMBER
);
KJ_EXPECT
(
root
.
getNumber
()
==
-
5
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
"-5.5"
,
root
);
KJ_EXPECT
(
root
.
which
()
==
JsonValue
::
NUMBER
);
KJ_EXPECT
(
root
.
getNumber
()
==
-
5.5
);
}
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"a"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"ends prematurely"
,
json
.
decodeRaw
(
"["
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"ends prematurely"
,
json
.
decodeRaw
(
"{"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[}"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"{]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[}]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[1, , ]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[,]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[true,]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[, 1]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[1
\"\"
]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"[1,,
\"\"
]"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"{
\"
a
\"
1: 0}"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
R"({"some": null,})"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Input remains"
,
json
.
decodeRaw
(
"11a"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Invalid escape"
,
json
.
decodeRaw
(
R"("\z")"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Invalid escape"
,
json
.
decodeRaw
(
R"("\z")"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"ends prematurely"
,
json
.
decodeRaw
(
R"(["\n\", 3])"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Invalid hex"
,
json
.
decodeRaw
(
R"("\u12zz")"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"ends prematurely"
,
json
.
decodeRaw
(
"-"
,
root
));
KJ_EXPECT_THROW_MESSAGE
(
"Unexpected input"
,
json
.
decodeRaw
(
"--"
,
root
));
}
}
KJ_TEST
(
"maximum nesting depth"
)
{
JsonCodec
json
;
auto
input
=
kj
::
str
(
R"({"foo": "a", "bar": ["b", { "baz": [-5.5e11] }, [ [ 1 ], { "z": 2 }]]})"
);
// `input` has a maximum nesting depth of 4, reached 3 times.
{
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
input
,
root
);
}
{
json
.
setMaxNestingDepth
(
0
);
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"nest"
,
json
.
decodeRaw
(
input
,
root
));
}
{
json
.
setMaxNestingDepth
(
3
);
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
KJ_EXPECT_THROW_MESSAGE
(
"nest"
,
json
.
decodeRaw
(
input
,
root
));
}
{
json
.
setMaxNestingDepth
(
4
);
MallocMessageBuilder
message
;
auto
root
=
message
.
initRoot
<
JsonValue
>
();
json
.
decodeRaw
(
input
,
root
);
}
}
class
TestHandler
:
public
JsonCodec
::
Handler
<
Text
>
{
class
TestHandler
:
public
JsonCodec
::
Handler
<
Text
>
{
public
:
public
:
void
encode
(
const
JsonCodec
&
codec
,
Text
::
Reader
input
,
void
encode
(
const
JsonCodec
&
codec
,
Text
::
Reader
input
,
...
...
c++/src/capnp/compat/json.c++
View file @
607dbbf5
...
@@ -20,8 +20,14 @@
...
@@ -20,8 +20,14 @@
// THE SOFTWARE.
// THE SOFTWARE.
#include "json.h"
#include "json.h"
#include <math.h> // for HUGEVAL to check for overflow in std::strtod
#include <stdlib.h> // std::strtod
#include <errno.h> // for std::strtod errors
#include <unordered_map>
#include <unordered_map>
#include <capnp/orphan.h>
#include <kj/debug.h>
#include <kj/debug.h>
#include <kj/function.h>
#include <kj/vector.h>
namespace
capnp
{
namespace
capnp
{
...
@@ -43,6 +49,7 @@ struct FieldHash {
...
@@ -43,6 +49,7 @@ struct FieldHash {
struct
JsonCodec
::
Impl
{
struct
JsonCodec
::
Impl
{
bool
prettyPrint
=
false
;
bool
prettyPrint
=
false
;
size_t
maxNestingDepth
=
64
;
std
::
unordered_map
<
Type
,
HandlerBase
*
,
TypeHash
>
typeHandlers
;
std
::
unordered_map
<
Type
,
HandlerBase
*
,
TypeHash
>
typeHandlers
;
std
::
unordered_map
<
StructSchema
::
Field
,
HandlerBase
*
,
FieldHash
>
fieldHandlers
;
std
::
unordered_map
<
StructSchema
::
Field
,
HandlerBase
*
,
FieldHash
>
fieldHandlers
;
...
@@ -184,6 +191,10 @@ JsonCodec::~JsonCodec() noexcept(false) {}
...
@@ -184,6 +191,10 @@ JsonCodec::~JsonCodec() noexcept(false) {}
void
JsonCodec
::
setPrettyPrint
(
bool
enabled
)
{
impl
->
prettyPrint
=
enabled
;
}
void
JsonCodec
::
setPrettyPrint
(
bool
enabled
)
{
impl
->
prettyPrint
=
enabled
;
}
void
JsonCodec
::
setMaxNestingDepth
(
size_t
maxNestingDepth
)
{
impl
->
maxNestingDepth
=
maxNestingDepth
;
}
kj
::
String
JsonCodec
::
encode
(
DynamicValue
::
Reader
value
,
Type
type
)
const
{
kj
::
String
JsonCodec
::
encode
(
DynamicValue
::
Reader
value
,
Type
type
)
const
{
MallocMessageBuilder
message
;
MallocMessageBuilder
message
;
auto
json
=
message
.
getRoot
<
JsonValue
>
();
auto
json
=
message
.
getRoot
<
JsonValue
>
();
...
@@ -211,10 +222,6 @@ kj::String JsonCodec::encodeRaw(JsonValue::Reader value) const {
...
@@ -211,10 +222,6 @@ kj::String JsonCodec::encodeRaw(JsonValue::Reader value) const {
return
impl
->
encodeRaw
(
value
,
0
,
multiline
,
false
).
flatten
();
return
impl
->
encodeRaw
(
value
,
0
,
multiline
,
false
).
flatten
();
}
}
void
JsonCodec
::
decodeRaw
(
kj
::
ArrayPtr
<
const
char
>
input
,
JsonValue
::
Builder
output
)
const
{
KJ_FAIL_ASSERT
(
"JSON decode not implement yet. :("
);
}
void
JsonCodec
::
encode
(
DynamicValue
::
Reader
input
,
Type
type
,
JsonValue
::
Builder
output
)
const
{
void
JsonCodec
::
encode
(
DynamicValue
::
Reader
input
,
Type
type
,
JsonValue
::
Builder
output
)
const
{
// TODO(soon): For interfaces, check for handlers on superclasses, per documentation...
// TODO(soon): For interfaces, check for handlers on superclasses, per documentation...
// TODO(soon): For branded types, should we check for handlers on the generic?
// TODO(soon): For branded types, should we check for handlers on the generic?
...
@@ -383,6 +390,320 @@ Orphan<DynamicValue> JsonCodec::decode(
...
@@ -383,6 +390,320 @@ Orphan<DynamicValue> JsonCodec::decode(
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
namespace
{
class
Parser
{
public
:
Parser
(
size_t
maxNestingDepth
,
kj
::
ArrayPtr
<
const
char
>
input
)
:
maxNestingDepth
(
maxNestingDepth
),
input
(
input
),
remaining
(
input
),
nestingDepth
(
0
)
{}
void
parseValue
(
JsonValue
::
Builder
&
output
)
{
consumeWhitespace
();
KJ_DEFER
(
consumeWhitespace
());
KJ_REQUIRE
(
!
inputExhausted
(),
"JSON message ends prematurely."
);
switch
(
nextChar
())
{
case
'n'
:
consume
(
kj
::
StringPtr
(
"null"
));
output
.
setNull
();
break
;
case
'f'
:
consume
(
kj
::
StringPtr
(
"false"
));
output
.
setBoolean
(
false
);
break
;
case
't'
:
consume
(
kj
::
StringPtr
(
"true"
));
output
.
setBoolean
(
true
);
break
;
case
'"'
:
parseString
(
output
);
break
;
case
'['
:
parseArray
(
output
);
break
;
case
'{'
:
parseObject
(
output
);
break
;
case
'-'
:
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
parseNumber
(
output
);
break
;
default
:
KJ_FAIL_REQUIRE
(
"Unexpected input in JSON message."
);
}
}
void
parseNumber
(
JsonValue
::
Builder
&
output
)
{
auto
numberStr
=
consumeNumber
();
char
*
endPtr
;
errno
=
0
;
double
value
=
std
::
strtod
(
numberStr
.
begin
(),
&
endPtr
);
KJ_ASSERT
(
endPtr
!=
numberStr
.
begin
(),
"strtod should not fail! Is consumeNumber wrong?"
);
KJ_REQUIRE
((
value
!=
HUGE_VAL
&&
value
!=
-
HUGE_VAL
)
||
errno
!=
ERANGE
,
"Overflow in JSON number."
);
KJ_REQUIRE
(
value
!=
0.0
||
errno
!=
ERANGE
,
"Underflow in JSON number."
);
output
.
setNumber
(
value
);
}
void
parseString
(
JsonValue
::
Builder
&
output
)
{
output
.
setString
(
consumeQuotedString
());
}
void
parseArray
(
JsonValue
::
Builder
&
output
)
{
// TODO(perf): Using orphans leaves holes in the message. It's expected
// that a JsonValue is used for interop, and won't be sent or written as a
// Cap'n Proto message. This also applies to parseObject below.
kj
::
Vector
<
Orphan
<
JsonValue
>>
values
;
auto
orphanage
=
Orphanage
::
getForMessageContaining
(
output
);
bool
expectComma
=
false
;
consume
(
'['
);
KJ_REQUIRE
(
++
nestingDepth
<=
maxNestingDepth
,
"JSON message nested too deeply."
);
KJ_DEFER
(
--
nestingDepth
);
while
(
consumeWhitespace
(),
nextChar
()
!=
']'
)
{
auto
orphan
=
orphanage
.
newOrphan
<
JsonValue
>
();
auto
builder
=
orphan
.
get
();
if
(
expectComma
)
{
consumeWhitespace
();
consume
(
','
);
consumeWhitespace
();
}
parseValue
(
builder
);
values
.
add
(
kj
::
mv
(
orphan
));
expectComma
=
true
;
}
output
.
initArray
(
values
.
size
());
auto
array
=
output
.
getArray
();
for
(
auto
i
:
kj
::
indices
(
values
))
{
array
.
adoptWithCaveats
(
i
,
kj
::
mv
(
values
[
i
]));
}
consume
(
']'
);
}
void
parseObject
(
JsonValue
::
Builder
&
output
)
{
kj
::
Vector
<
Orphan
<
JsonValue
::
Field
>>
fields
;
auto
orphanage
=
Orphanage
::
getForMessageContaining
(
output
);
bool
expectComma
=
false
;
consume
(
'{'
);
KJ_REQUIRE
(
++
nestingDepth
<=
maxNestingDepth
,
"JSON message nested too deeply."
);
KJ_DEFER
(
--
nestingDepth
);
while
(
consumeWhitespace
(),
nextChar
()
!=
'}'
)
{
auto
orphan
=
orphanage
.
newOrphan
<
JsonValue
::
Field
>
();
auto
builder
=
orphan
.
get
();
if
(
expectComma
)
{
consumeWhitespace
();
consume
(
','
);
consumeWhitespace
();
}
builder
.
setName
(
consumeQuotedString
());
consumeWhitespace
();
consume
(
':'
);
consumeWhitespace
();
auto
valueBuilder
=
builder
.
getValue
();
parseValue
(
valueBuilder
);
fields
.
add
(
kj
::
mv
(
orphan
));
expectComma
=
true
;
}
output
.
initObject
(
fields
.
size
());
auto
object
=
output
.
getObject
();
for
(
auto
i
:
kj
::
indices
(
fields
))
{
object
.
adoptWithCaveats
(
i
,
kj
::
mv
(
fields
[
i
]));
}
consume
(
'}'
);
}
bool
inputExhausted
()
{
return
remaining
.
size
()
==
0
||
remaining
.
front
()
==
'\0'
;
}
char
nextChar
()
{
KJ_REQUIRE
(
!
inputExhausted
(),
"JSON message ends prematurely."
);
return
remaining
.
front
();
}
void
advance
(
size_t
numBytes
=
1
)
{
KJ_REQUIRE
(
numBytes
<=
remaining
.
size
(),
"JSON message ends prematurely."
);
remaining
=
kj
::
arrayPtr
(
remaining
.
begin
()
+
numBytes
,
remaining
.
end
());
}
void
advanceTo
(
const
char
*
newPos
)
{
KJ_REQUIRE
(
remaining
.
begin
()
<=
newPos
&&
newPos
<
remaining
.
end
(),
"JSON message ends prematurely."
);
remaining
=
kj
::
arrayPtr
(
newPos
,
remaining
.
end
());
}
void
consume
(
char
expected
)
{
char
current
=
nextChar
();
KJ_REQUIRE
(
current
==
expected
,
"Unexpected input in JSON message."
);
advance
();
}
void
consume
(
kj
::
ArrayPtr
<
const
char
>
expected
)
{
KJ_REQUIRE
(
remaining
.
size
()
>=
expected
.
size
());
auto
prefix
=
remaining
.
slice
(
0
,
expected
.
size
());
KJ_REQUIRE
(
prefix
==
expected
,
"Unexpected input in JSON message."
);
advance
(
expected
.
size
());
}
bool
tryConsume
(
char
expected
)
{
bool
found
=
!
inputExhausted
()
&&
nextChar
()
==
expected
;
if
(
found
)
{
advance
();
}
return
found
;
}
template
<
typename
Predicate
>
void
consumeOne
(
Predicate
&&
predicate
)
{
char
current
=
nextChar
();
KJ_REQUIRE
(
predicate
(
current
),
"Unexpected input in JSON message."
);
advance
();
}
template
<
typename
Predicate
>
kj
::
ArrayPtr
<
const
char
>
consumeWhile
(
Predicate
&&
predicate
)
{
auto
originalPos
=
remaining
.
begin
();
while
(
!
inputExhausted
()
&&
predicate
(
nextChar
()))
{
advance
();
}
return
kj
::
arrayPtr
(
originalPos
,
remaining
.
begin
());
}
void
consumeWhitespace
()
{
consumeWhile
([](
char
chr
)
{
return
(
chr
==
' '
||
chr
==
'\f'
||
chr
==
'\n'
||
chr
==
'\r'
||
chr
==
'\t'
||
chr
==
'\v'
);
});
}
kj
::
String
consumeQuotedString
()
{
consume
(
'"'
);
// TODO(perf): Avoid copy / alloc if no escapes encoutered.
// TODO(perf): Get statistics on string size and preallocate?
kj
::
Vector
<
char
>
decoded
;
do
{
auto
stringValue
=
consumeWhile
([](
const
char
chr
)
{
return
chr
!=
'"'
&&
chr
!=
'\\'
;
});
decoded
.
addAll
(
stringValue
);
if
(
nextChar
()
==
'\\'
)
{
// handle escapes.
advance
();
switch
(
nextChar
())
{
case
'"'
:
decoded
.
add
(
'"'
);
advance
();
break
;
case
'\\'
:
decoded
.
add
(
'\\'
);
advance
();
break
;
case
'/'
:
decoded
.
add
(
'/'
);
advance
();
break
;
case
'b'
:
decoded
.
add
(
'\b'
);
advance
();
break
;
case
'f'
:
decoded
.
add
(
'\f'
);
advance
();
break
;
case
'n'
:
decoded
.
add
(
'\n'
);
advance
();
break
;
case
'r'
:
decoded
.
add
(
'\r'
);
advance
();
break
;
case
't'
:
decoded
.
add
(
'\t'
);
advance
();
break
;
case
'u'
:
advance
();
// consume 'u'
unescapeAndAppend
(
kj
::
arrayPtr
(
remaining
.
begin
(),
4
),
decoded
);
advance
(
4
);
break
;
default
:
KJ_FAIL_REQUIRE
(
"Invalid escape in JSON string."
);
break
;
}
}
}
while
(
nextChar
()
!=
'"'
);
consume
(
'"'
);
decoded
.
add
(
'\0'
);
// TODO(perf): This copy can be eliminated, but I can't find the kj::wayToDoIt();
return
kj
::
String
(
decoded
.
releaseAsArray
());
}
kj
::
String
consumeNumber
()
{
auto
originalPos
=
remaining
.
begin
();
tryConsume
(
'-'
);
if
(
!
tryConsume
(
'0'
))
{
consumeOne
([](
char
c
)
{
return
'1'
<=
c
&&
c
<=
'9'
;
});
consumeWhile
([](
char
c
)
{
return
'0'
<=
c
&&
c
<=
'9'
;
});
}
if
(
tryConsume
(
'.'
))
{
consumeWhile
([](
char
c
)
{
return
'0'
<=
c
&&
c
<=
'9'
;
});
}
if
(
tryConsume
(
'e'
)
||
tryConsume
(
'E'
))
{
tryConsume
(
'+'
)
||
tryConsume
(
'-'
);
consumeWhile
([](
char
c
)
{
return
'0'
<=
c
&&
c
<=
'9'
;
});
}
KJ_REQUIRE
(
remaining
.
begin
()
!=
originalPos
,
"Expected number in JSON input."
);
kj
::
Vector
<
char
>
number
;
number
.
addAll
(
originalPos
,
remaining
.
begin
());
number
.
add
(
'\0'
);
return
kj
::
String
(
number
.
releaseAsArray
());
}
// TODO(someday): This "interface" is ugly, and won't work if/when surrogates are handled.
void
unescapeAndAppend
(
kj
::
ArrayPtr
<
const
char
>
hex
,
kj
::
Vector
<
char
>&
target
)
{
KJ_REQUIRE
(
hex
.
size
()
==
4
);
int
codePoint
=
0
;
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
char
c
=
hex
[
i
];
codePoint
<<=
4
;
if
(
'0'
<=
c
&&
c
<=
'9'
)
{
codePoint
|=
c
-
'0'
;
}
else
if
(
'a'
<=
c
&&
c
<=
'f'
)
{
codePoint
|=
c
-
'a'
;
}
else
if
(
'A'
<=
c
&&
c
<=
'F'
)
{
codePoint
|=
c
-
'A'
;
}
else
{
KJ_FAIL_REQUIRE
(
"Invalid hex digit in unicode escape."
,
c
);
}
}
// TODO(soon): Support at least basic multi-lingual plane, ie ignore surrogates.
KJ_REQUIRE
(
codePoint
<
128
,
"non-ASCII unicode escapes are not supported (yet!)"
);
target
.
add
(
0x7f
&
static_cast
<
char
>
(
codePoint
));
}
private
:
const
size_t
maxNestingDepth
;
const
kj
::
ArrayPtr
<
const
char
>
input
;
kj
::
ArrayPtr
<
const
char
>
remaining
;
size_t
nestingDepth
;
};
// class Parser
}
// namespace
void
JsonCodec
::
decodeRaw
(
kj
::
ArrayPtr
<
const
char
>
input
,
JsonValue
::
Builder
output
)
const
{
Parser
parser
(
impl
->
maxNestingDepth
,
input
);
parser
.
parseValue
(
output
);
KJ_REQUIRE
(
parser
.
inputExhausted
(),
"Input remains after parsing JSON."
);
}
// -----------------------------------------------------------------------------
Orphan
<
DynamicValue
>
JsonCodec
::
HandlerBase
::
decodeBase
(
Orphan
<
DynamicValue
>
JsonCodec
::
HandlerBase
::
decodeBase
(
const
JsonCodec
&
codec
,
JsonValue
::
Reader
input
,
Orphanage
orphanage
)
const
{
const
JsonCodec
&
codec
,
JsonValue
::
Reader
input
,
Orphanage
orphanage
)
const
{
KJ_FAIL_ASSERT
(
"JSON decoder handler type / value type mismatch"
);
KJ_FAIL_ASSERT
(
"JSON decoder handler type / value type mismatch"
);
...
...
c++/src/capnp/compat/json.h
View file @
607dbbf5
...
@@ -71,6 +71,10 @@ public:
...
@@ -71,6 +71,10 @@ public:
// Enable to insert newlines, indentation, and other extra spacing into the output. The default
// Enable to insert newlines, indentation, and other extra spacing into the output. The default
// is to use minimal whitespace.
// is to use minimal whitespace.
void
setMaxNestingDepth
(
size_t
maxNestingDepth
);
// Set maximum nesting depth when decoding JSON to prevent highly nested input from overflowing
// the call stack. The default is 64.
template
<
typename
T
>
template
<
typename
T
>
kj
::
String
encode
(
T
&&
value
);
kj
::
String
encode
(
T
&&
value
);
// Encode any Cap'n Proto value to JSON, including primitives and
// Encode any Cap'n Proto value to JSON, including primitives and
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment