Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
a3b6ddc5
Commit
a3b6ddc5
authored
Nov 03, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/neverlord/json-api'
parents
7168be7a
c5bb6a65
Changes
28
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
3232 additions
and
84 deletions
+3232
-84
CHANGELOG.md
CHANGELOG.md
+7
-0
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+8
-0
libcaf_core/caf/detail/json.hpp
libcaf_core/caf/detail/json.hpp
+766
-10
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
+5
-5
libcaf_core/caf/fwd.hpp
libcaf_core/caf/fwd.hpp
+5
-0
libcaf_core/caf/json.hpp
libcaf_core/caf/json.hpp
+12
-0
libcaf_core/caf/json_array.hpp
libcaf_core/caf/json_array.hpp
+174
-0
libcaf_core/caf/json_builder.hpp
libcaf_core/caf/json_builder.hpp
+244
-0
libcaf_core/caf/json_object.hpp
libcaf_core/caf/json_object.hpp
+178
-0
libcaf_core/caf/json_reader.hpp
libcaf_core/caf/json_reader.hpp
+11
-0
libcaf_core/caf/json_value.hpp
libcaf_core/caf/json_value.hpp
+185
-0
libcaf_core/caf/json_writer.hpp
libcaf_core/caf/json_writer.hpp
+9
-1
libcaf_core/caf/parser_state.hpp
libcaf_core/caf/parser_state.hpp
+2
-0
libcaf_core/caf/type_id.hpp
libcaf_core/caf/type_id.hpp
+4
-1
libcaf_core/src/detail/json.cpp
libcaf_core/src/detail/json.cpp
+287
-39
libcaf_core/src/init_global_meta_objects.cpp
libcaf_core/src/init_global_meta_objects.cpp
+3
-0
libcaf_core/src/json_array.cpp
libcaf_core/src/json_array.cpp
+15
-0
libcaf_core/src/json_builder.cpp
libcaf_core/src/json_builder.cpp
+504
-0
libcaf_core/src/json_object.cpp
libcaf_core/src/json_object.cpp
+26
-0
libcaf_core/src/json_reader.cpp
libcaf_core/src/json_reader.cpp
+31
-7
libcaf_core/src/json_value.cpp
libcaf_core/src/json_value.cpp
+146
-0
libcaf_core/src/json_writer.cpp
libcaf_core/src/json_writer.cpp
+19
-21
libcaf_core/test/core-test.hpp
libcaf_core/test/core-test.hpp
+29
-0
libcaf_core/test/detail/json.cpp
libcaf_core/test/detail/json.cpp
+6
-0
libcaf_core/test/json_array.cpp
libcaf_core/test/json_array.cpp
+67
-0
libcaf_core/test/json_builder.cpp
libcaf_core/test/json_builder.cpp
+152
-0
libcaf_core/test/json_object.cpp
libcaf_core/test/json_object.cpp
+74
-0
libcaf_core/test/json_value.cpp
libcaf_core/test/json_value.cpp
+263
-0
No files found.
CHANGELOG.md
View file @
a3b6ddc5
...
@@ -3,6 +3,13 @@
...
@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file. The format
All notable changes to this project will be documented in this file. The format
is based on
[
Keep a Changelog
](
https://keepachangelog.com
)
.
is based on
[
Keep a Changelog
](
https://keepachangelog.com
)
.
## [Unreleased]
### Added
-
The new classes
`json_value`
,
`json_array`
and
`json_object`
allow working
with JSON inputs directly. Actors can also pass around JSON values safely.
## [0.19.0-rc.1] - 2020-10-31
## [0.19.0-rc.1] - 2020-10-31
### Added
### Added
...
...
libcaf_core/CMakeLists.txt
View file @
a3b6ddc5
...
@@ -161,7 +161,11 @@ caf_add_component(
...
@@ -161,7 +161,11 @@ caf_add_component(
src/ipv6_address.cpp
src/ipv6_address.cpp
src/ipv6_endpoint.cpp
src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp
src/ipv6_subnet.cpp
src/json_array.cpp
src/json_builder.cpp
src/json_object.cpp
src/json_reader.cpp
src/json_reader.cpp
src/json_value.cpp
src/json_writer.cpp
src/json_writer.cpp
src/load_inspector.cpp
src/load_inspector.cpp
src/local_actor.cpp
src/local_actor.cpp
...
@@ -313,7 +317,11 @@ caf_add_component(
...
@@ -313,7 +317,11 @@ caf_add_component(
ipv6_address
ipv6_address
ipv6_endpoint
ipv6_endpoint
ipv6_subnet
ipv6_subnet
json_array
json_builder
json_object
json_reader
json_reader
json_value
json_writer
json_writer
load_inspector
load_inspector
logger
logger
...
...
libcaf_core/caf/detail/json.hpp
View file @
a3b6ddc5
...
@@ -4,13 +4,20 @@
...
@@ -4,13 +4,20 @@
#pragma once
#pragma once
#include "caf/detail/monotonic_buffer_resource.hpp"
#include "caf/detail/print.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/parser_state.hpp"
#include "caf/ref_counted.hpp"
#include "caf/span.hpp"
#include "caf/type_id.hpp"
#include <cstdint>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <new>
#include <string_view>
#include <string_view>
#include <variant>
#include <variant>
#include <vector>
#include "caf/detail/monotonic_buffer_resource.hpp"
#include "caf/parser_state.hpp"
// This JSON abstraction is designed to allocate its entire state in a monotonic
// This JSON abstraction is designed to allocate its entire state in a monotonic
// buffer resource. This minimizes memory allocations and also enables us to
// buffer resource. This minimizes memory allocations and also enables us to
...
@@ -20,25 +27,313 @@
...
@@ -20,25 +27,313 @@
namespace
caf
::
detail
::
json
{
namespace
caf
::
detail
::
json
{
// -- utility classes ----------------------------------------------------------
// Wraps a buffer resource with a reference count.
struct
CAF_CORE_EXPORT
storage
:
public
ref_counted
{
/// Provides the memory for all of our parsed JSON entities.
detail
::
monotonic_buffer_resource
buf
;
};
// -- helper for modeling the JSON type system ---------------------------------
using
storage_ptr
=
intrusive_ptr
<
storage
>
;
struct
null_t
{};
struct
null_t
{};
class
value
{
constexpr
bool
operator
==
(
null_t
,
null_t
)
{
return
true
;
}
constexpr
bool
operator
!=
(
null_t
,
null_t
)
{
return
false
;
}
struct
undefined_t
{};
constexpr
bool
operator
==
(
undefined_t
,
undefined_t
)
{
return
true
;
}
constexpr
bool
operator
!=
(
undefined_t
,
undefined_t
)
{
return
false
;
}
template
<
class
T
>
struct
linked_list_node
{
T
value
;
linked_list_node
*
next
;
};
template
<
class
T
>
class
linked_list_iterator
{
public:
using
difference_type
=
ptrdiff_t
;
using
value_type
=
T
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
using
iterator_category
=
std
::
forward_iterator_tag
;
using
node_pointer
=
std
::
conditional_t
<
std
::
is_const_v
<
T
>
,
const
linked_list_node
<
std
::
remove_const_t
<
T
>>*
,
linked_list_node
<
value_type
>*>
;
constexpr
linked_list_iterator
()
noexcept
=
default
;
constexpr
explicit
linked_list_iterator
(
node_pointer
ptr
)
noexcept
:
ptr_
(
ptr
)
{
// nop
}
constexpr
linked_list_iterator
(
const
linked_list_iterator
&
)
noexcept
=
default
;
constexpr
linked_list_iterator
&
operator
=
(
const
linked_list_iterator
&
)
noexcept
=
default
;
constexpr
node_pointer
get
()
const
noexcept
{
return
ptr_
;
}
linked_list_iterator
&
operator
++
()
noexcept
{
ptr_
=
ptr_
->
next
;
return
*
this
;
}
linked_list_iterator
operator
++
(
int
)
noexcept
{
return
linked_list_iterator
{
ptr_
->
next
};
}
T
&
operator
*
()
const
noexcept
{
return
ptr_
->
value
;
}
T
*
operator
->
()
const
noexcept
{
return
std
::
addressof
(
ptr_
->
value
);
}
private:
node_pointer
ptr_
=
nullptr
;
};
template
<
class
T
>
constexpr
bool
operator
==
(
linked_list_iterator
<
T
>
lhs
,
linked_list_iterator
<
T
>
rhs
)
{
return
lhs
.
get
()
==
rhs
.
get
();
}
template
<
class
T
>
constexpr
bool
operator
!=
(
linked_list_iterator
<
T
>
lhs
,
linked_list_iterator
<
T
>
rhs
)
{
return
!
(
lhs
==
rhs
);
}
// A minimal version of a linked list that has constexpr constructor and an
// iterator type where the default-constructed iterator is the past-the-end
// iterator. Properties that std::list unfortunately lacks.
//
// The default-constructed list object is an empty list that does not allow
// push_back.
template
<
class
T
>
class
linked_list
{
public:
using
value_type
=
T
;
using
node_type
=
linked_list_node
<
value_type
>
;
using
allocator_type
=
monotonic_buffer_resource
::
allocator
<
node_type
>
;
using
reference
=
value_type
&
;
using
const_reference
=
const
value_type
&
;
using
pointer
=
value_type
*
;
using
const_pointer
=
const
value_type
*
;
using
node_pointer
=
node_type
*
;
using
iterator
=
linked_list_iterator
<
value_type
>
;
using
const_iterator
=
linked_list_iterator
<
const
value_type
>
;
linked_list
()
noexcept
{
// nop
}
~
linked_list
()
{
auto
*
ptr
=
head_
;
while
(
ptr
!=
nullptr
)
{
auto
*
next
=
ptr
->
next
;
ptr
->~
node_type
();
allocator_
.
deallocate
(
ptr
,
1
);
ptr
=
next
;
}
}
explicit
linked_list
(
allocator_type
allocator
)
noexcept
:
allocator_
(
allocator
)
{
// nop
}
linked_list
(
const
linked_list
&
)
=
delete
;
linked_list
(
linked_list
&&
other
)
:
size_
(
other
.
size_
),
head_
(
other
.
head_
),
tail_
(
other
.
tail_
),
allocator_
(
other
.
allocator_
)
{
other
.
size_
=
0
;
other
.
head_
=
nullptr
;
other
.
tail_
=
nullptr
;
}
linked_list
&
operator
=
(
const
linked_list
&
)
=
delete
;
linked_list
&
operator
=
(
linked_list
&&
other
)
{
using
std
::
swap
;
swap
(
size_
,
other
.
size_
);
swap
(
head_
,
other
.
head_
);
swap
(
tail_
,
other
.
tail_
);
swap
(
allocator_
,
other
.
allocator_
);
return
*
this
;
}
[[
nodiscard
]]
bool
empty
()
const
noexcept
{
return
size_
==
0
;
}
[[
nodiscard
]]
size_t
size
()
const
noexcept
{
return
size_
;
}
[[
nodiscard
]]
iterator
begin
()
noexcept
{
return
iterator
{
head_
};
}
[[
nodiscard
]]
const_iterator
begin
()
const
noexcept
{
return
const_iterator
{
head_
};
}
[[
nodiscard
]]
const_iterator
cbegin
()
const
noexcept
{
return
begin
();
}
[[
nodiscard
]]
iterator
end
()
noexcept
{
return
{};
}
[[
nodiscard
]]
const_iterator
end
()
const
noexcept
{
return
{};
}
[[
nodiscard
]]
const_iterator
cend
()
const
noexcept
{
return
{};
}
[[
nodiscard
]]
reference
front
()
noexcept
{
return
head_
->
value
;
}
[[
nodiscard
]]
const_reference
front
()
const
noexcept
{
return
head_
->
value
;
}
[[
nodiscard
]]
reference
back
()
noexcept
{
return
tail_
->
value
;
}
[[
nodiscard
]]
const_reference
back
()
const
noexcept
{
return
tail_
->
value
;
}
[[
nodiscard
]]
allocator_type
get_allocator
()
const
noexcept
{
return
allocator_
;
}
void
push_back
(
T
value
)
{
++
size_
;
auto
new_node
=
allocator_
->
allocate
(
1
);
new
(
new_node
)
node_type
{
std
::
move
(
value
),
nullptr
};
if
(
head_
==
nullptr
)
{
head_
=
tail_
=
new_node
;
}
else
{
tail_
->
next
=
new_node
;
tail_
=
new_node
;
}
}
template
<
class
...
Ts
>
reference
emplace_back
(
Ts
&&
...
args
)
{
++
size_
;
auto
new_node
=
allocator_
.
allocate
(
1
);
new
(
new_node
)
node_type
{
T
{
std
::
forward
<
Ts
>
(
args
)...},
nullptr
};
if
(
head_
==
nullptr
)
{
head_
=
tail_
=
new_node
;
}
else
{
tail_
->
next
=
new_node
;
tail_
=
new_node
;
}
return
new_node
->
value
;
}
private:
size_t
size_
=
0
;
node_pointer
head_
=
nullptr
;
node_pointer
tail_
=
nullptr
;
allocator_type
allocator_
;
};
template
<
class
T
>
bool
operator
==
(
const
linked_list
<
T
>&
lhs
,
const
linked_list
<
T
>&
rhs
)
{
return
std
::
equal
(
lhs
.
begin
(),
lhs
.
end
(),
rhs
.
begin
(),
rhs
.
end
());
}
template
<
class
T
>
bool
operator
!=
(
const
linked_list
<
T
>&
lhs
,
const
linked_list
<
T
>&
rhs
)
{
return
!
(
lhs
==
rhs
);
}
/// Re-allocates the given string at the buffer resource.
CAF_CORE_EXPORT
std
::
string_view
realloc
(
std
::
string_view
str
,
monotonic_buffer_resource
*
res
);
inline
std
::
string_view
realloc
(
std
::
string_view
str
,
const
storage_ptr
&
ptr
)
{
return
realloc
(
str
,
&
ptr
->
buf
);
}
/// Concatenates all strings and allocates a single new string for the result.
CAF_CORE_EXPORT
std
::
string_view
concat
(
std
::
initializer_list
<
std
::
string_view
>
xs
,
monotonic_buffer_resource
*
res
);
inline
std
::
string_view
concat
(
std
::
initializer_list
<
std
::
string_view
>
xs
,
const
storage_ptr
&
ptr
)
{
return
concat
(
xs
,
&
ptr
->
buf
);
}
class
CAF_CORE_EXPORT
value
{
public:
public:
using
array
_allocator
=
monotonic_buffer_resource
::
allocator
<
value
>
;
using
array
=
linked_list
<
value
>
;
using
array
=
std
::
vector
<
value
,
array_allocator
>
;
using
array
_allocator
=
array
::
allocator_type
;
struct
member
{
struct
member
{
std
::
string_view
key
;
std
::
string_view
key
;
value
*
val
=
nullptr
;
value
*
val
=
nullptr
;
};
};
using
member_allocator
=
monotonic_buffer_resource
::
allocator
<
member
>
;
using
object
=
linked_list
<
member
>
;
using
object
=
std
::
vector
<
member
,
member_allocator
>
;
using
object
_allocator
=
object
::
allocator_type
;
using
data_type
=
std
::
variant
<
null_t
,
int64_t
,
double
,
bool
,
using
data_type
=
std
::
variant
<
null_t
,
int64_t
,
double
,
bool
,
std
::
string_view
,
array
,
object
>
;
std
::
string_view
,
array
,
object
,
undefined_t
>
;
static
constexpr
size_t
null_index
=
0
;
static
constexpr
size_t
null_index
=
0
;
...
@@ -54,21 +349,482 @@ public:
...
@@ -54,21 +349,482 @@ public:
static
constexpr
size_t
object_index
=
6
;
static
constexpr
size_t
object_index
=
6
;
static
constexpr
size_t
undefined_index
=
7
;
data_type
data
;
data_type
data
;
bool
is_null
()
const
noexcept
{
return
data
.
index
()
==
null_index
;
}
bool
is_integer
()
const
noexcept
{
return
data
.
index
()
==
integer_index
;
}
bool
is_double
()
const
noexcept
{
return
data
.
index
()
==
double_index
;
}
bool
is_bool
()
const
noexcept
{
return
data
.
index
()
==
bool_index
;
}
bool
is_string
()
const
noexcept
{
return
data
.
index
()
==
string_index
;
}
bool
is_array
()
const
noexcept
{
return
data
.
index
()
==
array_index
;
}
bool
is_object
()
const
noexcept
{
return
data
.
index
()
==
object_index
;
}
bool
is_undefined
()
const
noexcept
{
return
data
.
index
()
==
undefined_index
;
}
void
assign_string
(
std
::
string_view
str
,
monotonic_buffer_resource
*
res
)
{
data
=
realloc
(
str
,
res
);
}
void
assign_string
(
std
::
string_view
str
,
const
storage_ptr
&
ptr
)
{
data
=
realloc
(
str
,
&
ptr
->
buf
);
}
void
assign_object
(
monotonic_buffer_resource
*
res
)
{
data
=
object
{
object_allocator
{
res
}};
}
void
assign_object
(
const
storage_ptr
&
ptr
)
{
assign_object
(
&
ptr
->
buf
);
}
void
assign_array
(
monotonic_buffer_resource
*
res
)
{
data
=
array
{
array_allocator
{
res
}};
}
void
assign_array
(
const
storage_ptr
&
ptr
)
{
assign_array
(
&
ptr
->
buf
);
}
};
};
inline
bool
operator
==
(
const
value
&
lhs
,
const
value
&
rhs
)
{
return
lhs
.
data
==
rhs
.
data
;
}
inline
bool
operator
!=
(
const
value
&
lhs
,
const
value
&
rhs
)
{
return
!
(
lhs
==
rhs
);
}
inline
bool
operator
==
(
const
value
::
member
&
lhs
,
const
value
::
member
&
rhs
)
{
if
(
lhs
.
key
==
rhs
.
key
&&
lhs
.
val
!=
nullptr
&&
rhs
.
val
!=
nullptr
)
{
return
*
lhs
.
val
==
*
rhs
.
val
;
}
return
false
;
}
inline
bool
operator
!=
(
const
value
::
member
&
lhs
,
const
value
::
member
&
rhs
)
{
return
!
(
lhs
==
rhs
);
}
using
array
=
value
::
array
;
using
array
=
value
::
array
;
using
member
=
value
::
member
;
using
member
=
value
::
member
;
using
object
=
value
::
object
;
using
object
=
value
::
object
;
// -- factory functions --------------------------------------------------------
value
*
make_value
(
monotonic_buffer_resource
*
storage
);
value
*
make_value
(
monotonic_buffer_resource
*
storage
);
inline
value
*
make_value
(
const
storage_ptr
&
ptr
)
{
return
make_value
(
&
ptr
->
buf
);
}
array
*
make_array
(
monotonic_buffer_resource
*
storage
);
array
*
make_array
(
monotonic_buffer_resource
*
storage
);
inline
array
*
make_array
(
const
storage_ptr
&
ptr
)
{
return
make_array
(
&
ptr
->
buf
);
}
object
*
make_object
(
monotonic_buffer_resource
*
storage
);
object
*
make_object
(
monotonic_buffer_resource
*
storage
);
inline
object
*
make_object
(
const
storage_ptr
&
ptr
)
{
return
make_object
(
&
ptr
->
buf
);
}
// -- saving -------------------------------------------------------------------
template
<
class
Serializer
>
bool
save
(
Serializer
&
sink
,
const
object
&
obj
);
template
<
class
Serializer
>
bool
save
(
Serializer
&
sink
,
const
array
&
arr
);
template
<
class
Serializer
>
bool
save
(
Serializer
&
sink
,
const
value
&
val
)
{
// On the "wire", we only use the public types.
if
(
!
sink
.
begin_object
(
type_id_v
<
json_value
>
,
type_name_v
<
json_value
>
))
return
false
;
// Maps our type indexes to their public API counterpart.
type_id_t
mapping
[]
=
{
type_id_v
<
unit_t
>
,
type_id_v
<
int64_t
>
,
type_id_v
<
double
>
,
type_id_v
<
bool
>
,
type_id_v
<
std
::
string
>
,
type_id_v
<
json_array
>
,
type_id_v
<
json_object
>
,
type_id_v
<
none_t
>
};
// Act as-if this type is a variant of the mapped types.
auto
type_index
=
val
.
data
.
index
();
if
(
!
sink
.
begin_field
(
"value"
,
make_span
(
mapping
),
type_index
))
return
false
;
// Dispatch on the run-time type of this value.
switch
(
type_index
)
{
case
value
:
:
integer_index
:
if
(
!
sink
.
apply
(
std
::
get
<
int64_t
>
(
val
.
data
)))
return
false
;
break
;
case
value
:
:
double_index
:
if
(
!
sink
.
apply
(
std
::
get
<
double
>
(
val
.
data
)))
return
false
;
break
;
case
value
:
:
bool_index
:
if
(
!
sink
.
apply
(
std
::
get
<
bool
>
(
val
.
data
)))
return
false
;
break
;
case
value
:
:
string_index
:
if
(
!
sink
.
apply
(
std
::
get
<
std
::
string_view
>
(
val
.
data
)))
return
false
;
break
;
case
value
:
:
array_index
:
if
(
!
save
(
sink
,
std
::
get
<
array
>
(
val
.
data
)))
return
false
;
break
;
case
value
:
:
object_index
:
if
(
!
save
(
sink
,
std
::
get
<
object
>
(
val
.
data
)))
return
false
;
break
;
default:
// null and undefined both have no data
break
;
}
// Wrap up.
return
sink
.
end_field
()
&&
sink
.
end_object
();
}
template
<
class
Serializer
>
bool
save
(
Serializer
&
sink
,
const
object
&
obj
)
{
if
(
!
sink
.
begin_associative_array
(
obj
.
size
()))
return
false
;
for
(
const
auto
&
kvp
:
obj
)
{
if
(
kvp
.
val
!=
nullptr
)
{
if
(
!
sink
.
begin_key_value_pair
()
// <key-value-pair>
||
!
sink
.
value
(
kvp
.
key
)
// <key ...>
||
!
save
(
sink
,
*
kvp
.
val
)
// <value ...>
||
!
sink
.
end_key_value_pair
())
// </key-value-pair>
return
false
;
}
}
return
sink
.
end_associative_array
();
}
template
<
class
Serializer
>
bool
save
(
Serializer
&
sink
,
const
array
&
arr
)
{
if
(
!
sink
.
begin_sequence
(
arr
.
size
()))
return
false
;
for
(
const
auto
&
val
:
arr
)
if
(
!
save
(
sink
,
val
))
return
false
;
return
sink
.
end_sequence
();
}
// -- loading ------------------------------------------------------------------
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
object
&
obj
,
monotonic_buffer_resource
*
res
);
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
array
&
arr
,
monotonic_buffer_resource
*
res
);
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
value
&
val
,
monotonic_buffer_resource
*
res
)
{
// On the "wire", we only use the public types.
if
(
!
source
.
begin_object
(
type_id_v
<
json_value
>
,
type_name_v
<
json_value
>
))
return
false
;
// Maps our type indexes to their public API counterpart.
type_id_t
mapping
[]
=
{
type_id_v
<
unit_t
>
,
type_id_v
<
int64_t
>
,
type_id_v
<
double
>
,
type_id_v
<
bool
>
,
type_id_v
<
std
::
string
>
,
type_id_v
<
json_array
>
,
type_id_v
<
json_object
>
,
type_id_v
<
none_t
>
};
// Act as-if this type is a variant of the mapped types.
auto
type_index
=
size_t
{
0
};
if
(
!
source
.
begin_field
(
"value"
,
make_span
(
mapping
),
type_index
))
return
false
;
// Dispatch on the run-time type of this value.
switch
(
type_index
)
{
case
value
:
:
null_index
:
val
.
data
=
null_t
{};
break
;
case
value
:
:
integer_index
:
{
auto
tmp
=
int64_t
{
0
};
if
(
!
source
.
apply
(
tmp
))
return
false
;
val
.
data
=
tmp
;
break
;
}
case
value
:
:
double_index
:
{
auto
tmp
=
double
{
0
};
if
(
!
source
.
apply
(
tmp
))
return
false
;
val
.
data
=
tmp
;
break
;
}
case
value
:
:
bool_index
:
{
auto
tmp
=
false
;
if
(
!
source
.
apply
(
tmp
))
return
false
;
val
.
data
=
tmp
;
break
;
}
case
value
:
:
string_index
:
{
auto
tmp
=
std
::
string
{};
if
(
!
source
.
apply
(
tmp
))
return
false
;
if
(
tmp
.
empty
())
{
val
.
data
=
std
::
string_view
{};
}
else
{
val
.
assign_string
(
tmp
,
res
);
}
break
;
}
case
value
:
:
array_index
:
val
.
data
=
array
{
array
::
allocator_type
{
res
}};
if
(
!
load
(
source
,
std
::
get
<
array
>
(
val
.
data
),
res
))
return
false
;
break
;
case
value
:
:
object_index
:
val
.
data
=
object
{
object
::
allocator_type
{
res
}};
if
(
!
load
(
source
,
std
::
get
<
object
>
(
val
.
data
),
res
))
return
false
;
break
;
default:
// undefined
val
.
data
=
undefined_t
{};
break
;
}
// Wrap up.
return
source
.
end_field
()
&&
source
.
end_object
();
}
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
object
&
obj
,
monotonic_buffer_resource
*
res
)
{
auto
size
=
size_t
{
0
};
if
(
!
source
.
begin_associative_array
(
size
))
return
false
;
for
(
size_t
i
=
0
;
i
<
size
;
++
i
)
{
if
(
!
source
.
begin_key_value_pair
())
return
false
;
auto
&
kvp
=
obj
.
emplace_back
();
// Deserialize the key.
auto
key
=
std
::
string
{};
if
(
!
source
.
apply
(
key
))
return
false
;
if
(
key
.
empty
())
{
kvp
.
key
=
std
::
string_view
{};
}
else
{
kvp
.
key
=
realloc
(
key
,
res
);
}
// Deserialize the value.
kvp
.
val
=
make_value
(
res
);
if
(
!
load
(
source
,
*
kvp
.
val
,
res
))
return
false
;
if
(
!
source
.
end_key_value_pair
())
return
false
;
}
return
source
.
end_associative_array
();
}
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
array
&
arr
,
monotonic_buffer_resource
*
res
)
{
auto
size
=
size_t
{
0
};
if
(
!
source
.
begin_sequence
(
size
))
return
false
;
for
(
size_t
i
=
0
;
i
<
size
;
++
i
)
{
auto
&
val
=
arr
.
emplace_back
();
if
(
!
load
(
source
,
val
,
res
))
return
false
;
}
return
source
.
end_sequence
();
}
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
object
&
obj
,
const
storage_ptr
&
ptr
)
{
return
load
(
source
,
obj
,
std
::
addressof
(
ptr
->
buf
));
}
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
array
&
arr
,
const
storage_ptr
&
ptr
)
{
return
load
(
source
,
arr
,
std
::
addressof
(
ptr
->
buf
));
}
template
<
class
Deserializer
>
bool
load
(
Deserializer
&
source
,
value
&
val
,
const
storage_ptr
&
ptr
)
{
return
load
(
source
,
val
,
std
::
addressof
(
ptr
->
buf
));
}
// -- singletons ---------------------------------------------------------------
const
value
*
null_value
()
noexcept
;
const
value
*
undefined_value
()
noexcept
;
const
object
*
empty_object
()
noexcept
;
const
array
*
empty_array
()
noexcept
;
// -- parsing ------------------------------------------------------------------
// Specialization for parsers operating on mutable character sequences.
using
mutable_string_parser_state
=
parser_state
<
char
*>
;
// Specialization for parsers operating on files.
using
file_parser_state
=
parser_state
<
std
::
istreambuf_iterator
<
char
>>
;
// Parses the input string and makes a deep copy of all strings.
value
*
parse
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
);
value
*
parse
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
);
// Parses the input string and makes a deep copy of all strings.
value
*
parse
(
file_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
);
// Parses the input file and makes a deep copy of all strings.
value
*
parse
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
);
// Parses the input and makes a shallow copy of strings whenever possible.
// Strings that do not have escaped characters are not copied, other strings
// will be copied.
value
*
parse_shallow
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
);
// Parses the input and makes a shallow copy of all strings. Strings with
// escaped characters are decoded in place.
value
*
parse_in_situ
(
mutable_string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
);
// -- printing -----------------------------------------------------------------
template
<
class
Buffer
>
static
void
print_to
(
Buffer
&
buf
,
std
::
string_view
str
)
{
buf
.
insert
(
buf
.
end
(),
str
.
begin
(),
str
.
end
());
}
template
<
class
Buffer
>
static
void
print_nl_to
(
Buffer
&
buf
,
size_t
indentation
)
{
buf
.
push_back
(
'\n'
);
buf
.
insert
(
buf
.
end
(),
indentation
,
' '
);
}
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
const
array
&
arr
,
size_t
indentation_factor
,
size_t
offset
=
0
);
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
const
object
&
obj
,
size_t
indentation_factor
,
size_t
offset
=
0
);
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
const
value
&
val
,
size_t
indentation_factor
,
size_t
offset
=
0
)
{
using
namespace
std
::
literals
;
switch
(
val
.
data
.
index
())
{
case
value
:
:
integer_index
:
print
(
buf
,
std
::
get
<
int64_t
>
(
val
.
data
));
break
;
case
value
:
:
double_index
:
print
(
buf
,
std
::
get
<
double
>
(
val
.
data
));
break
;
case
value
:
:
bool_index
:
print
(
buf
,
std
::
get
<
bool
>
(
val
.
data
));
break
;
case
value
:
:
string_index
:
print_escaped
(
buf
,
std
::
get
<
std
::
string_view
>
(
val
.
data
));
break
;
case
value
:
:
array_index
:
print_to
(
buf
,
std
::
get
<
array
>
(
val
.
data
),
indentation_factor
,
offset
);
break
;
case
value
:
:
object_index
:
print_to
(
buf
,
std
::
get
<
object
>
(
val
.
data
),
indentation_factor
,
offset
);
break
;
default:
print_to
(
buf
,
"null"
sv
);
}
}
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
const
array
&
arr
,
size_t
indentation_factor
,
size_t
offset
)
{
using
namespace
std
::
literals
;
if
(
arr
.
empty
())
{
print_to
(
buf
,
"[]"
sv
);
}
else
if
(
indentation_factor
==
0
)
{
buf
.
push_back
(
'['
);
auto
i
=
arr
.
begin
();
print_to
(
buf
,
*
i
,
0
);
auto
e
=
arr
.
end
();
while
(
++
i
!=
e
)
{
print_to
(
buf
,
", "
sv
);
print_to
(
buf
,
*
i
,
0
);
}
buf
.
push_back
(
']'
);
}
else
{
buf
.
push_back
(
'['
);
auto
new_offset
=
indentation_factor
+
offset
;
print_nl_to
(
buf
,
new_offset
);
auto
i
=
arr
.
begin
();
print_to
(
buf
,
*
i
,
indentation_factor
,
new_offset
);
auto
e
=
arr
.
end
();
while
(
++
i
!=
e
)
{
buf
.
push_back
(
','
);
print_nl_to
(
buf
,
new_offset
);
print_to
(
buf
,
*
i
,
indentation_factor
,
new_offset
);
}
print_nl_to
(
buf
,
offset
);
buf
.
push_back
(
']'
);
}
}
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
const
object
&
obj
,
size_t
indentation_factor
,
size_t
offset
)
{
using
namespace
std
::
literals
;
auto
print_member
=
[
&
](
const
value
::
member
&
kvp
,
size_t
new_offset
)
{
print_escaped
(
buf
,
kvp
.
key
);
print_to
(
buf
,
": "
sv
);
print_to
(
buf
,
*
kvp
.
val
,
indentation_factor
,
new_offset
);
};
if
(
obj
.
empty
())
{
print_to
(
buf
,
"{}"
sv
);
}
else
if
(
indentation_factor
==
0
)
{
buf
.
push_back
(
'{'
);
auto
i
=
obj
.
begin
();
print_member
(
*
i
,
offset
);
auto
e
=
obj
.
end
();
while
(
++
i
!=
e
)
{
print_to
(
buf
,
", "
sv
);
print_member
(
*
i
,
offset
);
}
buf
.
push_back
(
'}'
);
}
else
{
buf
.
push_back
(
'{'
);
auto
new_offset
=
indentation_factor
+
offset
;
print_nl_to
(
buf
,
new_offset
);
auto
i
=
obj
.
begin
();
print_member
(
*
i
,
new_offset
);
auto
e
=
obj
.
end
();
while
(
++
i
!=
e
)
{
buf
.
push_back
(
','
);
print_nl_to
(
buf
,
new_offset
);
print_member
(
*
i
,
new_offset
);
}
print_nl_to
(
buf
,
offset
);
buf
.
push_back
(
'}'
);
}
}
}
// namespace caf::detail::json
}
// namespace caf::detail::json
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
View file @
a3b6ddc5
...
@@ -55,20 +55,20 @@ public:
...
@@ -55,20 +55,20 @@ public:
using
other
=
allocator
<
U
>
;
using
other
=
allocator
<
U
>
;
};
};
explicit
allocator
(
monotonic_buffer_resource
*
mbr
)
:
mbr_
(
mbr
)
{
constexpr
explicit
allocator
(
monotonic_buffer_resource
*
mbr
)
:
mbr_
(
mbr
)
{
// nop
// nop
}
}
allocator
()
:
mbr_
(
nullptr
)
{
constexpr
allocator
()
:
mbr_
(
nullptr
)
{
// nop
// nop
}
}
allocator
(
const
allocator
&
)
=
default
;
constexpr
allocator
(
const
allocator
&
)
=
default
;
allocator
&
operator
=
(
const
allocator
&
)
=
default
;
constexpr
allocator
&
operator
=
(
const
allocator
&
)
=
default
;
template
<
class
U
>
template
<
class
U
>
allocator
(
const
allocator
<
U
>&
other
)
:
mbr_
(
other
.
resource
())
{
constexpr
allocator
(
const
allocator
<
U
>&
other
)
:
mbr_
(
other
.
resource
())
{
// nop
// nop
}
}
...
...
libcaf_core/caf/fwd.hpp
View file @
a3b6ddc5
...
@@ -110,6 +110,11 @@ class ipv4_subnet;
...
@@ -110,6 +110,11 @@ class ipv4_subnet;
class
ipv6_address
;
class
ipv6_address
;
class
ipv6_endpoint
;
class
ipv6_endpoint
;
class
ipv6_subnet
;
class
ipv6_subnet
;
class
json_array
;
class
json_object
;
class
json_reader
;
class
json_value
;
class
json_writer
;
class
local_actor
;
class
local_actor
;
class
mailbox_element
;
class
mailbox_element
;
class
message
;
class
message
;
...
...
libcaf_core/caf/json.hpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/json_array.hpp"
#include "caf/json_builder.hpp"
#include "caf/json_object.hpp"
#include "caf/json_reader.hpp"
#include "caf/json_value.hpp"
#include "caf/json_writer.hpp"
libcaf_core/caf/json_array.hpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/fwd.hpp"
#include "caf/json_value.hpp"
#include <iterator>
#include <memory>
namespace
caf
{
/// Represents a JSON array.
class
CAF_CORE_EXPORT
json_array
{
public:
// -- friends ----------------------------------------------------------------
friend
class
json_value
;
// -- member types -----------------------------------------------------------
class
const_iterator
{
public:
using
difference_type
=
ptrdiff_t
;
using
value_type
=
json_value
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
using
iterator_category
=
std
::
forward_iterator_tag
;
const_iterator
(
detail
::
json
::
value
::
array
::
const_iterator
iter
,
detail
::
json
::
storage
*
storage
)
:
iter_
(
iter
),
storage_
(
storage
)
{
// nop
}
const_iterator
()
noexcept
:
storage_
(
nullptr
)
{
// nop
}
const_iterator
(
const
const_iterator
&
)
=
default
;
const_iterator
&
operator
=
(
const
const_iterator
&
)
=
default
;
json_value
value
()
const
noexcept
{
return
json_value
{
std
::
addressof
(
*
iter_
),
storage_
};
}
json_value
operator
*
()
const
noexcept
{
return
value
();
}
const_iterator
&
operator
++
()
noexcept
{
++
iter_
;
return
*
this
;
}
const_iterator
operator
++
(
int
)
noexcept
{
return
{
iter_
++
,
storage_
};
}
bool
equal_to
(
const
const_iterator
&
other
)
const
noexcept
{
return
iter_
==
other
.
iter_
;
}
private:
detail
::
json
::
value
::
array
::
const_iterator
iter_
;
detail
::
json
::
storage
*
storage_
;
};
// -- constructors, destructors, and assignment operators --------------------
json_array
()
noexcept
:
arr_
(
detail
::
json
::
empty_array
())
{
// nop
}
json_array
(
json_array
&&
)
noexcept
=
default
;
json_array
(
const
json_array
&
)
noexcept
=
default
;
json_array
&
operator
=
(
json_array
&&
)
noexcept
=
default
;
json_array
&
operator
=
(
const
json_array
&
)
noexcept
=
default
;
// -- properties -------------------------------------------------------------
/// Checks whether the array has no members.
bool
empty
()
const
noexcept
{
return
arr_
->
empty
();
}
/// Alias for @c empty.
bool
is_empty
()
const
noexcept
{
return
empty
();
}
/// Returns the number of key-value pairs in this array.
size_t
size
()
const
noexcept
{
return
arr_
->
size
();
}
const_iterator
begin
()
const
noexcept
{
return
{
arr_
->
begin
(),
storage_
.
get
()};
}
const_iterator
end
()
const
noexcept
{
return
{
arr_
->
end
(),
storage_
.
get
()};
}
// -- printing ---------------------------------------------------------------
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
size_t
indentation_factor
=
0
)
const
{
detail
::
json
::
print_to
(
buf
,
*
arr_
,
indentation_factor
);
}
// -- serialization ----------------------------------------------------------
template
<
class
Inspector
>
friend
bool
inspect
(
Inspector
&
inspector
,
json_array
&
arr
)
{
if
constexpr
(
Inspector
::
is_loading
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
auto
*
internal_arr
=
detail
::
json
::
make_array
(
storage
);
if
(
!
detail
::
json
::
load
(
inspector
,
*
internal_arr
,
storage
))
return
false
;
arr
=
json_array
{
internal_arr
,
std
::
move
(
storage
)};
return
true
;
}
else
{
return
detail
::
json
::
save
(
inspector
,
*
arr
.
arr_
);
}
}
private:
json_array
(
const
detail
::
json
::
value
::
array
*
obj
,
detail
::
json
::
storage_ptr
sptr
)
noexcept
:
arr_
(
obj
),
storage_
(
sptr
)
{
// nop
}
const
detail
::
json
::
value
::
array
*
arr_
=
nullptr
;
detail
::
json
::
storage_ptr
storage_
;
};
// -- free functions -----------------------------------------------------------
inline
bool
operator
==
(
const
json_array
::
const_iterator
&
lhs
,
const
json_array
::
const_iterator
&
rhs
)
noexcept
{
return
lhs
.
equal_to
(
rhs
);
}
inline
bool
operator
!=
(
const
json_array
::
const_iterator
&
lhs
,
const
json_array
::
const_iterator
&
rhs
)
noexcept
{
return
!
lhs
.
equal_to
(
rhs
);
}
inline
bool
operator
==
(
const
json_array
&
lhs
,
const
json_array
&
rhs
)
noexcept
{
return
std
::
equal
(
lhs
.
begin
(),
lhs
.
end
(),
rhs
.
begin
(),
rhs
.
end
());
}
inline
bool
operator
!=
(
const
json_array
&
lhs
,
const
json_array
&
rhs
)
noexcept
{
return
!
(
lhs
==
rhs
);
}
/// @relates json_array
CAF_CORE_EXPORT
std
::
string
to_string
(
const
json_array
&
arr
);
}
// namespace caf
libcaf_core/caf/json_builder.hpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/json_value.hpp"
#include "caf/json_writer.hpp"
#include "caf/serializer.hpp"
namespace
caf
{
/// Serializes an inspectable object to a @ref json_value.
class
json_builder
:
public
serializer
{
public:
// -- member types -----------------------------------------------------------
using
super
=
serializer
;
using
type
=
json_writer
::
type
;
// -- constructors, destructors, and assignment operators --------------------
json_builder
();
explicit
json_builder
(
actor_system
&
sys
);
explicit
json_builder
(
execution_unit
*
ctx
);
json_builder
(
const
json_builder
&
)
=
delete
;
json_builder
&
operator
=
(
const
json_builder
&
)
=
delete
;
~
json_builder
()
override
;
// -- properties -------------------------------------------------------------
/// Returns whether the writer omits empty fields entirely (true) or renders
/// empty fields as `$field: null` (false).
[[
nodiscard
]]
bool
skip_empty_fields
()
const
noexcept
{
return
skip_empty_fields_
;
}
/// Configures whether the writer omits empty fields.
void
skip_empty_fields
(
bool
value
)
noexcept
{
skip_empty_fields_
=
value
;
}
/// Returns whether the writer omits '@type' annotations for JSON objects.
[[
nodiscard
]]
bool
skip_object_type_annotation
()
const
noexcept
{
return
skip_object_type_annotation_
;
}
/// Configures whether the writer omits '@type' annotations for JSON objects.
void
skip_object_type_annotation
(
bool
value
)
noexcept
{
skip_object_type_annotation_
=
value
;
}
/// Returns the suffix for generating type annotation fields for variant
/// fields. For example, CAF inserts field called "@foo${field_type_suffix}"
/// for a variant field called "foo".
[[
nodiscard
]]
std
::
string_view
field_type_suffix
()
const
noexcept
{
return
field_type_suffix_
;
}
/// Configures whether the writer omits empty fields.
void
field_type_suffix
(
std
::
string_view
suffix
)
noexcept
{
field_type_suffix_
=
suffix
;
}
// -- modifiers --------------------------------------------------------------
/// Restores the writer to its initial state.
void
reset
();
/// Seals the JSON value, i.e., rendering it immutable, and returns it. After
/// calling this member function, the @ref json_builder is in a moved-from
/// state and users may only call @c reset to start a new building process or
/// destroy this instance.
json_value
seal
();
// -- overrides --------------------------------------------------------------
bool
begin_object
(
type_id_t
type
,
std
::
string_view
name
)
override
;
bool
end_object
()
override
;
bool
begin_field
(
std
::
string_view
)
override
;
bool
begin_field
(
std
::
string_view
name
,
bool
is_present
)
override
;
bool
begin_field
(
std
::
string_view
name
,
span
<
const
type_id_t
>
types
,
size_t
index
)
override
;
bool
begin_field
(
std
::
string_view
name
,
bool
is_present
,
span
<
const
type_id_t
>
types
,
size_t
index
)
override
;
bool
end_field
()
override
;
bool
begin_tuple
(
size_t
size
)
override
;
bool
end_tuple
()
override
;
bool
begin_key_value_pair
()
override
;
bool
end_key_value_pair
()
override
;
bool
begin_sequence
(
size_t
size
)
override
;
bool
end_sequence
()
override
;
bool
begin_associative_array
(
size_t
size
)
override
;
bool
end_associative_array
()
override
;
bool
value
(
std
::
byte
x
)
override
;
bool
value
(
bool
x
)
override
;
bool
value
(
int8_t
x
)
override
;
bool
value
(
uint8_t
x
)
override
;
bool
value
(
int16_t
x
)
override
;
bool
value
(
uint16_t
x
)
override
;
bool
value
(
int32_t
x
)
override
;
bool
value
(
uint32_t
x
)
override
;
bool
value
(
int64_t
x
)
override
;
bool
value
(
uint64_t
x
)
override
;
bool
value
(
float
x
)
override
;
bool
value
(
double
x
)
override
;
bool
value
(
long
double
x
)
override
;
bool
value
(
std
::
string_view
x
)
override
;
bool
value
(
const
std
::
u16string
&
x
)
override
;
bool
value
(
const
std
::
u32string
&
x
)
override
;
bool
value
(
span
<
const
std
::
byte
>
x
)
override
;
private:
// -- implementation details -------------------------------------------------
template
<
class
T
>
bool
number
(
T
);
using
key_type
=
std
::
string_view
;
// -- state management -------------------------------------------------------
void
init
();
// Returns the current top of the stack or `null` if empty.
type
top
();
// Returns the current top of the stack or `null` if empty.
template
<
class
T
=
detail
::
json
::
value
>
T
*
top_ptr
();
// Returns the current top-level object.
detail
::
json
::
object
*
top_obj
();
// Enters a new level of nesting.
void
push
(
detail
::
json
::
value
*
,
type
);
// Enters a new level of nesting with type member.
void
push
(
detail
::
json
::
value
::
member
*
);
// Enters a new level of nesting with type key.
void
push
(
key_type
*
);
// Backs up one level of nesting.
bool
pop
();
// Backs up one level of nesting but checks that current top is `t` before.
bool
pop_if
(
type
t
);
// Sets an error reason that the inspector failed to write a t.
void
fail
(
type
t
);
// Checks whether any element in the stack has the type `object`.
bool
inside_object
()
const
noexcept
;
// -- member variables -------------------------------------------------------
// Our output.
detail
::
json
::
value
*
val_
;
// Storage for the assembled output.
detail
::
json
::
storage_ptr
storage_
;
struct
entry
{
union
{
detail
::
json
::
value
*
val_ptr
;
detail
::
json
::
member
*
mem_ptr
;
key_type
*
key_ptr
;
};
type
t
;
entry
(
detail
::
json
::
value
*
ptr
,
type
ptr_type
)
noexcept
{
val_ptr
=
ptr
;
t
=
ptr_type
;
}
explicit
entry
(
detail
::
json
::
member
*
ptr
)
noexcept
{
mem_ptr
=
ptr
;
t
=
type
::
member
;
}
explicit
entry
(
key_type
*
ptr
)
noexcept
{
key_ptr
=
ptr
;
t
=
type
::
key
;
}
entry
(
const
entry
&
)
noexcept
=
default
;
entry
&
operator
=
(
const
entry
&
)
noexcept
=
default
;
};
// Bookkeeping for where we are in the current object.
std
::
vector
<
entry
>
stack_
;
// Configures whether we omit empty fields entirely (true) or render empty
// fields as `$field: null` (false).
bool
skip_empty_fields_
=
json_writer
::
skip_empty_fields_default
;
// Configures whether we omit the top-level '@type' annotation.
bool
skip_object_type_annotation_
=
false
;
std
::
string_view
field_type_suffix_
=
json_writer
::
field_type_suffix_default
;
};
}
// namespace caf
libcaf_core/caf/json_object.hpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/fwd.hpp"
#include "caf/json_value.hpp"
#include <iterator>
namespace
caf
{
/// Represents a JSON object.
class
CAF_CORE_EXPORT
json_object
{
public:
// -- friends ----------------------------------------------------------------
friend
class
json_value
;
// -- member types -----------------------------------------------------------
class
const_iterator
{
public:
using
difference_type
=
ptrdiff_t
;
using
value_type
=
std
::
pair
<
std
::
string_view
,
json_value
>
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
using
iterator_category
=
std
::
forward_iterator_tag
;
const_iterator
(
detail
::
json
::
value
::
object
::
const_iterator
iter
,
detail
::
json
::
storage
*
storage
)
:
iter_
(
iter
),
storage_
(
storage
)
{
// nop
}
const_iterator
()
noexcept
=
default
;
const_iterator
(
const
const_iterator
&
)
=
default
;
const_iterator
&
operator
=
(
const
const_iterator
&
)
=
default
;
std
::
string_view
key
()
const
noexcept
{
return
iter_
->
key
;
}
json_value
value
()
const
noexcept
{
return
json_value
{
iter_
->
val
,
storage_
};
}
value_type
operator
*
()
const
noexcept
{
return
{
key
(),
value
()};
}
const_iterator
&
operator
++
()
noexcept
{
++
iter_
;
return
*
this
;
}
const_iterator
operator
++
(
int
)
noexcept
{
return
{
iter_
++
,
storage_
};
}
bool
equal_to
(
const
const_iterator
&
other
)
const
noexcept
{
return
iter_
==
other
.
iter_
;
}
private:
detail
::
json
::
value
::
object
::
const_iterator
iter_
;
detail
::
json
::
storage
*
storage_
=
nullptr
;
};
// -- constructors, destructors, and assignment operators --------------------
json_object
()
noexcept
:
obj_
(
detail
::
json
::
empty_object
())
{
// nop
}
json_object
(
json_object
&&
)
noexcept
=
default
;
json_object
(
const
json_object
&
)
noexcept
=
default
;
json_object
&
operator
=
(
json_object
&&
)
noexcept
=
default
;
json_object
&
operator
=
(
const
json_object
&
)
noexcept
=
default
;
// -- properties -------------------------------------------------------------
/// Checks whether the object has no members.
bool
empty
()
const
noexcept
{
return
!
obj_
||
obj_
->
empty
();
}
/// Alias for @c empty.
bool
is_empty
()
const
noexcept
{
return
empty
();
}
/// Returns the number of key-value pairs in this object.
size_t
size
()
const
noexcept
{
return
obj_
?
obj_
->
size
()
:
0u
;
}
/// Returns the value for @p key or an @c undefined value if the object does
/// not contain a value for @p key.
json_value
value
(
std
::
string_view
key
)
const
;
const_iterator
begin
()
const
noexcept
{
return
{
obj_
->
begin
(),
storage_
.
get
()};
}
const_iterator
end
()
const
noexcept
{
return
{
obj_
->
end
(),
storage_
.
get
()};
}
// -- printing ---------------------------------------------------------------
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
size_t
indentation_factor
=
0
)
const
{
detail
::
json
::
print_to
(
buf
,
*
obj_
,
indentation_factor
);
}
// -- serialization ----------------------------------------------------------
template
<
class
Inspector
>
friend
bool
inspect
(
Inspector
&
inspector
,
json_object
&
obj
)
{
if
constexpr
(
Inspector
::
is_loading
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
auto
*
internal_obj
=
detail
::
json
::
make_object
(
storage
);
if
(
!
detail
::
json
::
load
(
inspector
,
*
internal_obj
,
storage
))
return
false
;
obj
=
json_object
{
internal_obj
,
std
::
move
(
storage
)};
return
true
;
}
else
{
return
detail
::
json
::
save
(
inspector
,
*
obj
.
obj_
);
}
}
private:
json_object
(
const
detail
::
json
::
value
::
object
*
obj
,
detail
::
json
::
storage_ptr
sptr
)
noexcept
:
obj_
(
obj
),
storage_
(
sptr
)
{
// nop
}
const
detail
::
json
::
value
::
object
*
obj_
=
nullptr
;
detail
::
json
::
storage_ptr
storage_
;
};
inline
bool
operator
==
(
const
json_object
::
const_iterator
&
lhs
,
const
json_object
::
const_iterator
&
rhs
)
noexcept
{
return
lhs
.
equal_to
(
rhs
);
}
inline
bool
operator
!=
(
const
json_object
::
const_iterator
&
lhs
,
const
json_object
::
const_iterator
&
rhs
)
noexcept
{
return
!
lhs
.
equal_to
(
rhs
);
}
inline
bool
operator
==
(
const
json_object
&
lhs
,
const
json_object
&
rhs
)
noexcept
{
return
std
::
equal
(
lhs
.
begin
(),
lhs
.
end
(),
rhs
.
begin
(),
rhs
.
end
());
}
inline
bool
operator
!=
(
const
json_object
&
lhs
,
const
json_object
&
rhs
)
noexcept
{
return
!
(
lhs
==
rhs
);
}
/// @relates json_object
CAF_CORE_EXPORT
std
::
string
to_string
(
const
json_object
&
val
);
}
// namespace caf
libcaf_core/caf/json_reader.hpp
View file @
a3b6ddc5
...
@@ -133,6 +133,17 @@ public:
...
@@ -133,6 +133,17 @@ public:
/// @note Implicitly calls `reset`.
/// @note Implicitly calls `reset`.
bool
load
(
std
::
string_view
json_text
);
bool
load
(
std
::
string_view
json_text
);
/// Parses the content of the file under the given @p path. After loading the
/// content of the JSON file, the reader is ready for attempting to
/// deserialize inspectable objects.
/// @note Implicitly calls `reset`.
bool
load_file
(
const
char
*
path
);
/// @copydoc load_file
bool
load_file
(
const
std
::
string
&
path
)
{
return
load_file
(
path
.
c_str
());
}
/// Reverts the state of the reader back to where it was after calling `load`.
/// Reverts the state of the reader back to where it was after calling `load`.
/// @post The reader is ready for attempting to deserialize another
/// @post The reader is ready for attempting to deserialize another
/// inspectable object.
/// inspectable object.
...
...
libcaf_core/caf/json_value.hpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/detail/json.hpp"
#include "caf/fwd.hpp"
#include "caf/make_counted.hpp"
#include <string>
#include <string_view>
namespace
caf
{
/// Represents an immutable JSON value.
class
CAF_CORE_EXPORT
json_value
{
public:
// -- constructors, destructors, and assignment operators --------------------
json_value
()
noexcept
:
val_
(
detail
::
json
::
null_value
())
{
// nop
}
json_value
(
const
detail
::
json
::
value
*
val
,
detail
::
json
::
storage_ptr
sptr
)
noexcept
:
val_
(
val
),
storage_
(
sptr
)
{
// nop
}
json_value
(
json_value
&&
)
noexcept
=
default
;
json_value
(
const
json_value
&
)
noexcept
=
default
;
json_value
&
operator
=
(
json_value
&&
)
noexcept
=
default
;
json_value
&
operator
=
(
const
json_value
&
)
noexcept
=
default
;
// -- factories --------------------------------------------------------------
static
json_value
undefined
()
noexcept
{
return
json_value
{
detail
::
json
::
undefined_value
(),
nullptr
};
}
// -- properties -------------------------------------------------------------
/// Checks whether the value is @c null.
bool
is_null
()
const
noexcept
{
return
val_
->
is_null
();
}
/// Checks whether the value is undefined. This special state indicates that a
/// previous key lookup failed.
bool
is_undefined
()
const
noexcept
{
return
val_
->
is_undefined
();
}
/// Checks whether the value is an @c int64_t.
bool
is_integer
()
const
noexcept
{
return
val_
->
is_integer
();
}
/// Checks whether the value is a @c double.
bool
is_double
()
const
noexcept
{
return
val_
->
is_double
();
}
/// Checks whether the value is a number, i.e., an @c int64_t or a @c double.
bool
is_number
()
const
noexcept
{
return
is_integer
()
||
is_double
();
}
/// Checks whether the value is a @c bool.
bool
is_bool
()
const
noexcept
{
return
val_
->
is_bool
();
}
/// Checks whether the value is a JSON string (@c std::string_view).
bool
is_string
()
const
noexcept
{
return
val_
->
is_string
();
}
/// Checks whether the value is an JSON array.
bool
is_array
()
const
noexcept
{
return
val_
->
is_array
();
}
/// Checks whether the value is a JSON object.
bool
is_object
()
const
noexcept
{
return
val_
->
is_object
();
}
// -- conversion -------------------------------------------------------------
int64_t
to_integer
(
int64_t
fallback
=
0
)
const
;
double
to_double
(
double
fallback
=
0.0
)
const
;
bool
to_bool
(
bool
fallback
=
false
)
const
;
std
::
string_view
to_string
()
const
;
std
::
string_view
to_string
(
std
::
string_view
fallback
)
const
;
json_array
to_array
()
const
;
json_array
to_array
(
json_array
fallback
)
const
;
json_object
to_object
()
const
;
json_object
to_object
(
json_object
fallback
)
const
;
// -- comparison -------------------------------------------------------------
bool
equal_to
(
const
json_value
&
other
)
const
noexcept
;
// -- parsing ----------------------------------------------------------------
/// Attempts to parse @p str as JSON input into a self-contained value.
static
expected
<
json_value
>
parse
(
std
::
string_view
str
);
/// Attempts to parse @p str as JSON input into a value that avoids copies
/// where possible by pointing into @p str.
/// @warning The returned @ref json_value may hold pointers into @p str. Thus,
/// the input *must* outlive the @ref json_value and any other JSON
/// objects created from that value.
static
expected
<
json_value
>
parse_shallow
(
std
::
string_view
str
);
/// Attempts to parse @p str as JSON input. Decodes JSON in place and points
/// back into the @p str for all strings in the JSON input.
/// @warning The returned @ref json_value may hold pointers into @p str. Thus,
/// the input *must* outlive the @ref json_value and any other JSON
/// objects created from that value.
static
expected
<
json_value
>
parse_in_situ
(
std
::
string
&
str
);
/// Attempts to parse the content of the file at @p path as JSON input into a
/// self-contained value.
static
expected
<
json_value
>
parse_file
(
const
char
*
path
);
/// @copydoc parse_file
static
expected
<
json_value
>
parse_file
(
const
std
::
string
&
path
);
// -- printing ---------------------------------------------------------------
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
size_t
indentation_factor
=
0
)
const
{
detail
::
json
::
print_to
(
buf
,
*
val_
,
indentation_factor
);
}
// -- serialization ----------------------------------------------------------
template
<
class
Inspector
>
friend
bool
inspect
(
Inspector
&
inspector
,
json_value
&
val
)
{
if
constexpr
(
Inspector
::
is_loading
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
auto
*
internal_val
=
detail
::
json
::
make_value
(
storage
);
if
(
!
detail
::
json
::
load
(
inspector
,
*
internal_val
,
storage
))
return
false
;
val
=
json_value
{
internal_val
,
std
::
move
(
storage
)};
return
true
;
}
else
{
return
detail
::
json
::
save
(
inspector
,
*
val
.
val_
);
}
}
private:
const
detail
::
json
::
value
*
val_
;
detail
::
json
::
storage_ptr
storage_
;
};
// -- free functions -----------------------------------------------------------
inline
bool
operator
==
(
const
json_value
&
lhs
,
const
json_value
&
rhs
)
{
return
lhs
.
equal_to
(
rhs
);
}
inline
bool
operator
!=
(
const
json_value
&
lhs
,
const
json_value
&
rhs
)
{
return
!
(
lhs
==
rhs
);
}
/// @relates json_value
CAF_CORE_EXPORT
std
::
string
to_string
(
const
json_value
&
val
);
}
// namespace caf
libcaf_core/caf/json_writer.hpp
View file @
a3b6ddc5
...
@@ -210,7 +210,7 @@ private:
...
@@ -210,7 +210,7 @@ private:
void
init
();
void
init
();
// Returns the current top of the stack or `null
_literal
` if empty.
// Returns the current top of the stack or `null` if empty.
type
top
();
type
top
();
// Enters a new level of nesting.
// Enters a new level of nesting.
...
@@ -300,4 +300,12 @@ private:
...
@@ -300,4 +300,12 @@ private:
const
type_id_mapper
*
mapper_
=
&
default_mapper_
;
const
type_id_mapper
*
mapper_
=
&
default_mapper_
;
};
};
/// @relates json_writer::type
CAF_CORE_EXPORT
std
::
string_view
as_json_type_name
(
json_writer
::
type
t
);
/// @relates json_writer::type
constexpr
bool
can_morph
(
json_writer
::
type
from
,
json_writer
::
type
to
)
{
return
from
==
json_writer
::
type
::
element
&&
to
!=
json_writer
::
type
::
member
;
}
}
// namespace caf
}
// namespace caf
libcaf_core/caf/parser_state.hpp
View file @
a3b6ddc5
...
@@ -16,6 +16,8 @@ namespace caf {
...
@@ -16,6 +16,8 @@ namespace caf {
/// Stores all information necessary for implementing an FSM-based parser.
/// Stores all information necessary for implementing an FSM-based parser.
template
<
class
Iterator
,
class
Sentinel
>
template
<
class
Iterator
,
class
Sentinel
>
struct
parser_state
{
struct
parser_state
{
using
iterator_type
=
Iterator
;
/// Current position of the parser.
/// Current position of the parser.
Iterator
i
;
Iterator
i
;
...
...
libcaf_core/caf/type_id.hpp
View file @
a3b6ddc5
...
@@ -405,7 +405,6 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
...
@@ -405,7 +405,6 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
cow_string
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
cow_string
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
cow_u16string
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
cow_u16string
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
cow_u32string
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
cow_u32string
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
dictionary
<
caf
::
config_value
>
)
)
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
down_msg
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
down_msg
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
error
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
error
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
exit_msg
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
exit_msg
))
...
@@ -419,6 +418,9 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
...
@@ -419,6 +418,9 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
ipv6_address
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
ipv6_address
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
ipv6_endpoint
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
ipv6_endpoint
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
ipv6_subnet
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
ipv6_subnet
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
json_array
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
json_object
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
json_value
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
message
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
message
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
message_id
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
message_id
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
node_down_msg
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
node_down_msg
))
...
@@ -426,6 +428,7 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
...
@@ -426,6 +428,7 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_module, 0)
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
none_t
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
none_t
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
pec
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
pec
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
sec
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
sec
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
settings
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
shared_action_ptr
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
shared_action_ptr
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
stream
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
stream
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
stream_abort_msg
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
stream_abort_msg
))
...
...
libcaf_core/src/detail/json.cpp
View file @
a3b6ddc5
...
@@ -4,8 +4,11 @@
...
@@ -4,8 +4,11 @@
#include "caf/detail/json.hpp"
#include "caf/detail/json.hpp"
#include <cstring>
#include <iterator>
#include <iterator>
#include <memory>
#include <memory>
#include <numeric>
#include <streambuf>
#include "caf/config.hpp"
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/chars.hpp"
...
@@ -14,6 +17,7 @@
...
@@ -14,6 +17,7 @@
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
#include "caf/pec.hpp"
#include "caf/span.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
CAF_PUSH_UNUSED_LABEL_WARNING
...
@@ -23,6 +27,115 @@ namespace {
...
@@ -23,6 +27,115 @@ namespace {
constexpr
size_t
max_nesting_level
=
128
;
constexpr
size_t
max_nesting_level
=
128
;
size_t
do_unescape
(
const
char
*
i
,
const
char
*
e
,
char
*
out
)
{
size_t
new_size
=
0
;
while
(
i
!=
e
)
{
switch
(
*
i
)
{
default:
if
(
i
!=
out
)
{
*
out
++
=
*
i
++
;
}
else
{
++
out
;
++
i
;
}
++
new_size
;
break
;
case
'\\'
:
if
(
++
i
!=
e
)
{
switch
(
*
i
)
{
case
'"'
:
*
out
++
=
'"'
;
break
;
case
'\\'
:
*
out
++
=
'\\'
;
break
;
case
'b'
:
*
out
++
=
'\b'
;
break
;
case
'f'
:
*
out
++
=
'\f'
;
break
;
case
'n'
:
*
out
++
=
'\n'
;
break
;
case
'r'
:
*
out
++
=
'\r'
;
break
;
case
't'
:
*
out
++
=
'\t'
;
break
;
case
'v'
:
*
out
++
=
'\v'
;
break
;
default:
// TODO: support control characters in \uXXXX notation.
*
out
++
=
'?'
;
}
++
i
;
++
new_size
;
}
}
}
return
new_size
;
}
std
::
string_view
as_str_view
(
const
char
*
first
,
const
char
*
last
)
{
return
{
first
,
static_cast
<
size_t
>
(
last
-
first
)};
}
struct
regular_unescaper
{
std
::
string_view
operator
()(
caf
::
detail
::
monotonic_buffer_resource
*
storage
,
const
char
*
first
,
const
char
*
last
,
bool
is_escaped
)
const
{
auto
len
=
static_cast
<
size_t
>
(
last
-
first
);
caf
::
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
alloc
{
storage
};
auto
*
str_buf
=
alloc
.
allocate
(
len
);
if
(
!
is_escaped
)
{
strncpy
(
str_buf
,
first
,
len
);
return
std
::
string_view
{
str_buf
,
len
};
}
auto
unescaped_size
=
do_unescape
(
first
,
last
,
str_buf
);
return
std
::
string_view
{
str_buf
,
unescaped_size
};
}
};
struct
shallow_unescaper
{
std
::
string_view
operator
()(
caf
::
detail
::
monotonic_buffer_resource
*
storage
,
const
char
*
first
,
const
char
*
last
,
bool
is_escaped
)
const
{
if
(
!
is_escaped
)
return
as_str_view
(
first
,
last
);
caf
::
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
alloc
{
storage
};
auto
*
str_buf
=
alloc
.
allocate
(
static_cast
<
size_t
>
(
last
-
first
));
auto
unescaped_size
=
do_unescape
(
first
,
last
,
str_buf
);
return
std
::
string_view
{
str_buf
,
unescaped_size
};
}
};
struct
in_situ_unescaper
{
std
::
string_view
operator
()(
caf
::
detail
::
monotonic_buffer_resource
*
,
char
*
first
,
char
*
last
,
bool
is_escaped
)
const
{
if
(
!
is_escaped
)
return
as_str_view
(
first
,
last
);
auto
unescaped_size
=
do_unescape
(
first
,
last
,
first
);
return
std
::
string_view
{
first
,
unescaped_size
};
}
};
template
<
class
Escaper
,
class
Consumer
,
class
Iterator
>
void
assign_value
(
Escaper
escaper
,
Consumer
&
consumer
,
Iterator
first
,
Iterator
last
,
bool
is_escaped
)
{
auto
iter2ptr
=
[](
Iterator
iter
)
{
if
constexpr
(
std
::
is_pointer_v
<
Iterator
>
)
return
iter
;
else
return
std
::
addressof
(
*
iter
);
};
auto
str
=
escaper
(
consumer
.
storage
,
iter2ptr
(
first
),
iter2ptr
(
last
),
is_escaped
);
consumer
.
value
(
str
);
}
}
// namespace
}
// namespace
namespace
caf
::
detail
::
parser
{
namespace
caf
::
detail
::
parser
{
...
@@ -46,6 +159,7 @@ struct val_consumer {
...
@@ -46,6 +159,7 @@ struct val_consumer {
};
};
struct
key_consumer
{
struct
key_consumer
{
monotonic_buffer_resource
*
storage
;
std
::
string_view
*
ptr
;
std
::
string_view
*
ptr
;
void
value
(
std
::
string_view
str
)
{
void
value
(
std
::
string_view
str
)
{
...
@@ -58,7 +172,7 @@ struct member_consumer {
...
@@ -58,7 +172,7 @@ struct member_consumer {
json
::
member
*
ptr
;
json
::
member
*
ptr
;
key_consumer
begin_key
()
{
key_consumer
begin_key
()
{
return
{
std
::
addressof
(
ptr
->
key
)};
return
{
st
orage
,
st
d
::
addressof
(
ptr
->
key
)};
}
}
val_consumer
begin_val
()
{
val_consumer
begin_val
()
{
...
@@ -71,8 +185,8 @@ struct obj_consumer {
...
@@ -71,8 +185,8 @@ struct obj_consumer {
json
::
object
*
ptr
;
json
::
object
*
ptr
;
member_consumer
begin_member
()
{
member_consumer
begin_member
()
{
ptr
->
emplace_back
();
auto
&
new_member
=
ptr
->
emplace_back
();
return
{
ptr
->
get_allocator
().
resource
(),
std
::
addressof
(
ptr
->
back
())
};
return
{
ptr
->
get_allocator
().
resource
(),
&
new_member
};
}
}
};
};
...
@@ -80,30 +194,25 @@ struct arr_consumer {
...
@@ -80,30 +194,25 @@ struct arr_consumer {
json
::
array
*
ptr
;
json
::
array
*
ptr
;
val_consumer
begin_value
()
{
val_consumer
begin_value
()
{
ptr
->
emplace_back
();
auto
&
new_element
=
ptr
->
emplace_back
();
return
{
ptr
->
get_allocator
().
resource
(),
std
::
addressof
(
ptr
->
back
())
};
return
{
ptr
->
get_allocator
().
resource
(),
&
new_element
};
}
}
};
};
arr_consumer
val_consumer
::
begin_array
()
{
arr_consumer
val_consumer
::
begin_array
()
{
ptr
->
data
=
json
::
array
(
json
::
value
::
array_allocator
{
storage
});
ptr
->
data
=
json
::
array
(
json
::
value
::
array_allocator
{
storage
});
auto
&
arr
=
std
::
get
<
json
::
array
>
(
ptr
->
data
);
auto
&
arr
=
std
::
get
<
json
::
array
>
(
ptr
->
data
);
arr
.
reserve
(
16
);
return
{
&
arr
};
return
{
&
arr
};
}
}
obj_consumer
val_consumer
::
begin_object
()
{
obj_consumer
val_consumer
::
begin_object
()
{
ptr
->
data
=
json
::
object
(
json
::
value
::
member
_allocator
{
storage
});
ptr
->
data
=
json
::
object
(
json
::
value
::
object
_allocator
{
storage
});
auto
&
obj
=
std
::
get
<
json
::
object
>
(
ptr
->
data
);
auto
&
obj
=
std
::
get
<
json
::
object
>
(
ptr
->
data
);
obj
.
reserve
(
16
);
return
{
&
obj
};
return
{
&
obj
};
}
}
void
read_value
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
Consumer
>
val_consumer
consumer
);
void
read_json_null_or_nan
(
ParserState
&
ps
,
Consumer
consumer
)
{
template
<
class
Consumer
>
void
read_json_null_or_nan
(
string_parser_state
&
ps
,
Consumer
consumer
)
{
enum
{
nil
,
is_null
,
is_nan
};
enum
{
nil
,
is_null
,
is_nan
};
auto
res_type
=
nil
;
auto
res_type
=
nil
;
auto
g
=
make_scope_guard
([
&
]
{
auto
g
=
make_scope_guard
([
&
]
{
...
@@ -141,9 +250,16 @@ void read_json_null_or_nan(string_parser_state& ps, Consumer consumer) {
...
@@ -141,9 +250,16 @@ void read_json_null_or_nan(string_parser_state& ps, Consumer consumer) {
// clang-format on
// clang-format on
}
}
template
<
class
Consumer
>
// If we have an iterator into a contiguous memory block, we simply store the
void
read_json_string
(
string_parser_state
&
ps
,
Consumer
consumer
)
{
// iterator position and use the escaper to decide whether we make regular,
std
::
string_view
::
iterator
first
;
// shallow or in-situ copies. Otherwise, we use the scratch-space and decode the
// string while parsing.
template
<
class
ParserState
,
class
Unescaper
,
class
Consumer
>
void
read_json_string
(
ParserState
&
ps
,
unit_t
,
Unescaper
escaper
,
Consumer
consumer
)
{
using
iterator_t
=
typename
ParserState
::
iterator_type
;
iterator_t
first
;
// clang-format off
// clang-format off
start
();
start
();
state
(
init
)
{
state
(
init
)
{
...
@@ -152,14 +268,17 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
...
@@ -152,14 +268,17 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
}
}
state
(
read_chars
)
{
state
(
read_chars
)
{
transition
(
escape
,
'\\'
)
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
,
transition
(
done
,
'"'
,
assign_value
(
escaper
,
consumer
,
first
,
ps
.
i
,
false
))
consumer
.
value
(
std
::
string_view
{
std
::
addressof
(
*
first
),
static_cast
<
size_t
>
(
ps
.
i
-
first
)}))
transition
(
read_chars
,
any_char
)
transition
(
read_chars
,
any_char
)
}
}
state
(
read_chars_after_escape
)
{
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
,
assign_value
(
escaper
,
consumer
,
first
,
ps
.
i
,
true
))
transition
(
read_chars_after_escape
,
any_char
)
}
state
(
escape
)
{
state
(
escape
)
{
// TODO: Add support for JSON's \uXXXX escaping.
// TODO: Add support for JSON's \uXXXX escaping.
transition
(
read_chars
,
"
\"\\
/bfnrt
"
)
transition
(
read_chars
_after_escape
,
"
\"\\
/bfnrtv
"
)
}
}
term_state
(
done
)
{
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
transition
(
done
,
"
\t\n
"
)
...
@@ -168,17 +287,57 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
...
@@ -168,17 +287,57 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
// clang-format on
// clang-format on
}
}
void
read_member
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
Unescaper
,
class
Consumer
>
void
read_json_string
(
ParserState
&
ps
,
std
::
vector
<
char
>&
scratch_space
,
Unescaper
escaper
,
Consumer
consumer
)
{
scratch_space
.
clear
();
// clang-format off
start
();
state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
transition
(
read_chars
,
'"'
)
}
state
(
read_chars
)
{
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
,
assign_value
(
escaper
,
consumer
,
scratch_space
.
begin
(),
scratch_space
.
end
(),
false
))
transition
(
read_chars
,
any_char
,
scratch_space
.
push_back
(
ch
))
}
state
(
escape
)
{
// TODO: Add support for JSON's \uXXXX escaping.
transition
(
read_chars
,
'"'
,
scratch_space
.
push_back
(
'"'
))
transition
(
read_chars
,
'\\'
,
scratch_space
.
push_back
(
'\\'
))
transition
(
read_chars
,
'b'
,
scratch_space
.
push_back
(
'\b'
))
transition
(
read_chars
,
'f'
,
scratch_space
.
push_back
(
'\f'
))
transition
(
read_chars
,
'n'
,
scratch_space
.
push_back
(
'\n'
))
transition
(
read_chars
,
'r'
,
scratch_space
.
push_back
(
'\r'
))
transition
(
read_chars
,
't'
,
scratch_space
.
push_back
(
'\t'
))
transition
(
read_chars
,
'v'
,
scratch_space
.
push_back
(
'\v'
))
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
}
fin
();
// clang-format on
}
template
<
class
ParserState
,
class
ScratchSpace
,
class
Unescaper
>
void
read_member
(
ParserState
&
ps
,
ScratchSpace
&
scratch_space
,
Unescaper
unescaper
,
size_t
nesting_level
,
member_consumer
consumer
)
{
member_consumer
consumer
)
{
// clang-format off
// clang-format off
start
();
start
();
state
(
init
)
{
state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
transition
(
init
,
"
\t\n
"
)
fsm_epsilon
(
read_json_string
(
ps
,
consumer
.
begin_key
()),
after_key
,
'"'
)
fsm_epsilon
(
read_json_string
(
ps
,
scratch_space
,
unescaper
,
consumer
.
begin_key
()),
after_key
,
'"'
)
}
}
state
(
after_key
)
{
state
(
after_key
)
{
transition
(
after_key
,
"
\t\n
"
)
transition
(
after_key
,
"
\t\n
"
)
fsm_transition
(
read_value
(
ps
,
nesting_level
,
consumer
.
begin_val
()),
fsm_transition
(
read_value
(
ps
,
scratch_space
,
unescaper
,
nesting_level
,
consumer
.
begin_val
()),
done
,
':'
)
done
,
':'
)
}
}
term_state
(
done
)
{
term_state
(
done
)
{
...
@@ -188,7 +347,9 @@ void read_member(string_parser_state& ps, size_t nesting_level,
...
@@ -188,7 +347,9 @@ void read_member(string_parser_state& ps, size_t nesting_level,
// clang-format on
// clang-format on
}
}
void
read_json_object
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
ScratchSpace
,
class
Unescaper
>
void
read_json_object
(
ParserState
&
ps
,
ScratchSpace
&
scratch_space
,
Unescaper
unescaper
,
size_t
nesting_level
,
obj_consumer
consumer
)
{
obj_consumer
consumer
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
ps
.
code
=
pec
::
nested_too_deeply
;
ps
.
code
=
pec
::
nested_too_deeply
;
...
@@ -202,7 +363,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
...
@@ -202,7 +363,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
}
}
state
(
has_open_brace
)
{
state
(
has_open_brace
)
{
transition
(
has_open_brace
,
"
\t\n
"
)
transition
(
has_open_brace
,
"
\t\n
"
)
fsm_epsilon
(
read_member
(
ps
,
nesting_level
+
1
,
consumer
.
begin_member
()),
fsm_epsilon
(
read_member
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_member
()),
after_member
,
'"'
)
after_member
,
'"'
)
transition
(
done
,
'}'
)
transition
(
done
,
'}'
)
}
}
...
@@ -213,7 +375,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
...
@@ -213,7 +375,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
}
}
state
(
after_comma
)
{
state
(
after_comma
)
{
transition
(
after_comma
,
"
\t\n
"
)
transition
(
after_comma
,
"
\t\n
"
)
fsm_epsilon
(
read_member
(
ps
,
nesting_level
+
1
,
consumer
.
begin_member
()),
fsm_epsilon
(
read_member
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_member
()),
after_member
,
'"'
)
after_member
,
'"'
)
}
}
term_state
(
done
)
{
term_state
(
done
)
{
...
@@ -223,7 +386,9 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
...
@@ -223,7 +386,9 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
// clang-format on
// clang-format on
}
}
void
read_json_array
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
ScratchSpace
,
class
Unescaper
>
void
read_json_array
(
ParserState
&
ps
,
ScratchSpace
&
scratch_space
,
Unescaper
unescaper
,
size_t
nesting_level
,
arr_consumer
consumer
)
{
arr_consumer
consumer
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
ps
.
code
=
pec
::
nested_too_deeply
;
ps
.
code
=
pec
::
nested_too_deeply
;
...
@@ -238,7 +403,8 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
...
@@ -238,7 +403,8 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
state
(
has_open_brace
)
{
state
(
has_open_brace
)
{
transition
(
has_open_brace
,
"
\t\n
"
)
transition
(
has_open_brace
,
"
\t\n
"
)
transition
(
done
,
']'
)
transition
(
done
,
']'
)
fsm_epsilon
(
read_value
(
ps
,
nesting_level
+
1
,
consumer
.
begin_value
()),
fsm_epsilon
(
read_value
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_value
()),
after_value
)
after_value
)
}
}
state
(
after_value
)
{
state
(
after_value
)
{
...
@@ -248,7 +414,8 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
...
@@ -248,7 +414,8 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
}
}
state
(
after_comma
)
{
state
(
after_comma
)
{
transition
(
after_comma
,
"
\t\n
"
)
transition
(
after_comma
,
"
\t\n
"
)
fsm_epsilon
(
read_value
(
ps
,
nesting_level
+
1
,
consumer
.
begin_value
()),
fsm_epsilon
(
read_value
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_value
()),
after_value
)
after_value
)
}
}
term_state
(
done
)
{
term_state
(
done
)
{
...
@@ -258,19 +425,24 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
...
@@ -258,19 +425,24 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
// clang-format on
// clang-format on
}
}
void
read_value
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
ScratchSpace
,
class
Unescaper
>
void
read_value
(
ParserState
&
ps
,
ScratchSpace
&
scratch_space
,
Unescaper
unescaper
,
size_t
nesting_level
,
val_consumer
consumer
)
{
val_consumer
consumer
)
{
// clang-format off
// clang-format off
start
();
start
();
state
(
init
)
{
state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
transition
(
init
,
"
\t\n
"
)
fsm_epsilon
(
read_json_string
(
ps
,
consumer
),
done
,
'"'
)
fsm_epsilon
(
read_json_string
(
ps
,
scratch_space
,
unescaper
,
consumer
),
done
,
'"'
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_json_null_or_nan
(
ps
,
consumer
),
done
,
"n"
)
fsm_epsilon
(
read_json_null_or_nan
(
ps
,
consumer
),
done
,
"n"
)
fsm_epsilon
(
read_number
(
ps
,
consumer
),
done
,
"+-.0123456789"
)
fsm_epsilon
(
read_number
(
ps
,
consumer
),
done
,
"+-.0123456789"
)
fsm_epsilon
(
read_json_object
(
ps
,
nesting_level
,
consumer
.
begin_object
()),
fsm_epsilon
(
read_json_object
(
ps
,
scratch_space
,
unescaper
,
nesting_level
,
consumer
.
begin_object
()),
done
,
'{'
)
done
,
'{'
)
fsm_epsilon
(
read_json_array
(
ps
,
nesting_level
,
consumer
.
begin_array
()),
fsm_epsilon
(
read_json_array
(
ps
,
scratch_space
,
unescaper
,
nesting_level
,
consumer
.
begin_array
()),
done
,
'['
)
done
,
'['
)
}
}
term_state
(
done
)
{
term_state
(
done
)
{
...
@@ -288,9 +460,10 @@ namespace caf::detail::json {
...
@@ -288,9 +460,10 @@ namespace caf::detail::json {
namespace
{
namespace
{
template
<
class
T
,
class
Allocator
>
template
<
class
T
>
void
init
(
std
::
vector
<
T
,
Allocator
>*
ptr
,
monotonic_buffer_resource
*
storage
)
{
void
init
(
linked_list
<
T
>*
ptr
,
monotonic_buffer_resource
*
storage
)
{
new
(
ptr
)
std
::
vector
<
T
,
Allocator
>
(
Allocator
{
storage
});
using
allocator_type
=
typename
linked_list
<
T
>::
allocator_type
;
new
(
ptr
)
linked_list
<
T
>
(
allocator_type
{
storage
});
}
}
void
init
(
value
*
ptr
,
monotonic_buffer_resource
*
)
{
void
init
(
value
*
ptr
,
monotonic_buffer_resource
*
)
{
...
@@ -305,28 +478,103 @@ T* make_impl(monotonic_buffer_resource* storage) {
...
@@ -305,28 +478,103 @@ T* make_impl(monotonic_buffer_resource* storage) {
return
result
;
return
result
;
}
}
const
value
null_value_instance
;
const
value
undefined_value_instance
=
value
{
undefined_t
{}};
const
object
empty_object_instance
;
const
array
empty_array_instance
;
}
// namespace
}
// namespace
std
::
string_view
realloc
(
std
::
string_view
str
,
monotonic_buffer_resource
*
res
)
{
using
alloc_t
=
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
;
auto
buf
=
alloc_t
{
res
}.
allocate
(
str
.
size
());
strncpy
(
buf
,
str
.
data
(),
str
.
size
());
return
std
::
string_view
{
buf
,
str
.
size
()};
}
std
::
string_view
concat
(
std
::
initializer_list
<
std
::
string_view
>
xs
,
monotonic_buffer_resource
*
res
)
{
auto
get_size
=
[](
size_t
x
,
std
::
string_view
str
)
{
return
x
+
str
.
size
();
};
auto
total_size
=
std
::
accumulate
(
xs
.
begin
(),
xs
.
end
(),
size_t
{
0
},
get_size
);
using
alloc_t
=
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
;
auto
*
buf
=
alloc_t
{
res
}.
allocate
(
total_size
);
auto
*
pos
=
buf
;
for
(
auto
str
:
xs
)
{
strncpy
(
pos
,
str
.
data
(),
str
.
size
());
pos
+=
str
.
size
();
}
return
std
::
string_view
{
buf
,
total_size
};
}
value
*
make_value
(
monotonic_buffer_resource
*
storage
)
{
value
*
make_value
(
monotonic_buffer_resource
*
storage
)
{
return
make_impl
<
value
>
(
storage
);
return
make_impl
<
value
>
(
storage
);
}
}
array
*
make_array
(
monotonic_buffer_resource
*
storage
)
{
array
*
make_array
(
monotonic_buffer_resource
*
storage
)
{
auto
result
=
make_impl
<
array
>
(
storage
);
auto
result
=
make_impl
<
array
>
(
storage
);
result
->
reserve
(
16
);
return
result
;
return
result
;
}
}
object
*
make_object
(
monotonic_buffer_resource
*
storage
)
{
object
*
make_object
(
monotonic_buffer_resource
*
storage
)
{
auto
result
=
make_impl
<
object
>
(
storage
);
auto
result
=
make_impl
<
object
>
(
storage
);
result
->
reserve
(
16
);
return
result
;
return
result
;
}
}
const
value
*
null_value
()
noexcept
{
return
&
null_value_instance
;
}
const
value
*
undefined_value
()
noexcept
{
return
&
undefined_value_instance
;
}
const
object
*
empty_object
()
noexcept
{
return
&
empty_object_instance
;
}
const
array
*
empty_array
()
noexcept
{
return
&
empty_array_instance
;
}
value
*
parse
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
value
*
parse
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
unit_t
scratch_space
;
regular_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
scratch_space
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
value
*
parse
(
file_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
std
::
vector
<
char
>
scratch_space
;
scratch_space
.
reserve
(
64
);
regular_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
scratch_space
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
value
*
parse_shallow
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
unit_t
scratch_space
;
shallow_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
scratch_space
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
value
*
parse_in_situ
(
mutable_string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
unit_t
scratch_space
;
in_situ_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
0
,
{
storage
,
result
});
parser
::
read_value
(
ps
,
scratch_space
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
return
result
;
}
}
...
...
libcaf_core/src/init_global_meta_objects.cpp
View file @
a3b6ddc5
...
@@ -21,6 +21,9 @@
...
@@ -21,6 +21,9 @@
#include "caf/ipv6_address.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/ipv6_endpoint.hpp"
#include "caf/ipv6_endpoint.hpp"
#include "caf/ipv6_subnet.hpp"
#include "caf/ipv6_subnet.hpp"
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
#include "caf/json_value.hpp"
#include "caf/message.hpp"
#include "caf/message.hpp"
#include "caf/message_id.hpp"
#include "caf/message_id.hpp"
#include "caf/node_id.hpp"
#include "caf/node_id.hpp"
...
...
libcaf_core/src/json_array.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_array.hpp"
namespace
caf
{
std
::
string
to_string
(
const
json_array
&
arr
)
{
std
::
string
result
;
arr
.
print_to
(
result
);
return
result
;
}
}
// namespace caf
libcaf_core/src/json_builder.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_builder.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/detail/print.hpp"
#include "caf/json_value.hpp"
#include <type_traits>
using
namespace
std
::
literals
;
namespace
caf
{
namespace
{
static
constexpr
const
char
class_name
[]
=
"caf::json_builder"
;
}
// namespace
// -- implementation details ---------------------------------------------------
template
<
class
T
>
bool
json_builder
::
number
(
T
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
{
if
constexpr
(
std
::
is_floating_point_v
<
T
>
)
{
top_ptr
()
->
data
=
static_cast
<
double
>
(
x
);
}
else
{
top_ptr
()
->
data
=
static_cast
<
int64_t
>
(
x
);
}
pop
();
return
true
;
}
case
type
:
:
key
:
{
std
::
string
str
;
detail
::
print
(
str
,
x
);
*
top_ptr
<
key_type
>
()
=
detail
::
json
::
realloc
(
str
,
&
storage_
->
buf
);
return
true
;
}
case
type
:
:
array
:
{
auto
&
new_entry
=
top_ptr
<
detail
::
json
::
array
>
()
->
emplace_back
();
if
constexpr
(
std
::
is_floating_point_v
<
T
>
)
{
new_entry
.
data
=
static_cast
<
double
>
(
x
);
}
else
{
new_entry
.
data
=
static_cast
<
int64_t
>
(
x
);
}
return
true
;
}
default:
fail
(
type
::
number
);
return
false
;
}
}
// -- constructors, destructors, and assignment operators ----------------------
json_builder
::
json_builder
()
:
json_builder
(
nullptr
)
{
init
();
}
json_builder
::
json_builder
(
actor_system
&
sys
)
:
super
(
sys
)
{
init
();
}
json_builder
::
json_builder
(
execution_unit
*
ctx
)
:
super
(
ctx
)
{
init
();
}
json_builder
::~
json_builder
()
{
// nop
}
// -- modifiers ----------------------------------------------------------------
void
json_builder
::
reset
()
{
stack_
.
clear
();
if
(
!
storage_
)
{
storage_
=
make_counted
<
detail
::
json
::
storage
>
();
}
else
{
storage_
->
buf
.
reclaim
();
}
val_
=
detail
::
json
::
make_value
(
storage_
);
push
(
val_
,
type
::
element
);
}
json_value
json_builder
::
seal
()
{
return
json_value
{
val_
,
std
::
move
(
storage_
)};
}
// -- overrides ----------------------------------------------------------------
bool
json_builder
::
begin_object
(
type_id_t
id
,
std
::
string_view
name
)
{
auto
add_type_annotation
=
[
this
,
id
,
name
]
{
CAF_ASSERT
(
top
()
==
type
::
key
);
*
top_ptr
<
key_type
>
()
=
"@type"
sv
;
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
if
(
auto
tname
=
query_type_name
(
id
);
!
tname
.
empty
())
{
top_ptr
()
->
data
=
tname
;
}
else
{
top_ptr
()
->
data
=
name
;
}
pop
();
return
true
;
};
if
(
skip_object_type_annotation_
||
inside_object
())
return
begin_associative_array
(
0
);
else
return
begin_associative_array
(
0
)
// Put opening paren, ...
&&
begin_key_value_pair
()
// ... add implicit @type member, ..
&&
add_type_annotation
()
// ... write content ...
&&
end_key_value_pair
();
// ... and wait for next field.
}
bool
json_builder
::
end_object
()
{
return
end_associative_array
();
}
bool
json_builder
::
begin_field
(
std
::
string_view
name
)
{
if
(
begin_key_value_pair
())
{
CAF_ASSERT
(
top
()
==
type
::
key
);
*
top_ptr
<
key_type
>
()
=
name
;
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
return
true
;
}
else
{
return
false
;
}
}
bool
json_builder
::
begin_field
(
std
::
string_view
name
,
bool
is_present
)
{
if
(
skip_empty_fields_
&&
!
is_present
)
{
auto
t
=
top
();
switch
(
t
)
{
case
type
:
:
object
:
push
(
static_cast
<
detail
::
json
::
member
*>
(
nullptr
));
return
true
;
default:
{
std
::
string
str
=
"expected object, found "
;
str
+=
as_json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
return
false
;
}
}
}
else
if
(
begin_key_value_pair
())
{
CAF_ASSERT
(
top
()
==
type
::
key
);
*
top_ptr
<
key_type
>
()
=
name
;
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
if
(
!
is_present
)
{
// We don't need to assign nullptr explicitly since it's the default.
pop
();
}
return
true
;
}
else
{
return
false
;
}
}
bool
json_builder
::
begin_field
(
std
::
string_view
name
,
span
<
const
type_id_t
>
types
,
size_t
index
)
{
if
(
index
>=
types
.
size
())
{
emplace_error
(
sec
::
runtime_error
,
"index >= types.size()"
);
return
false
;
}
if
(
begin_key_value_pair
())
{
if
(
auto
tname
=
query_type_name
(
types
[
index
]);
!
tname
.
empty
())
{
auto
&
annotation
=
top_obj
()
->
emplace_back
();
annotation
.
key
=
detail
::
json
::
concat
({
"@"
sv
,
name
,
field_type_suffix_
},
&
storage_
->
buf
);
annotation
.
val
=
detail
::
json
::
make_value
(
storage_
);
annotation
.
val
->
data
=
tname
;
}
else
{
emplace_error
(
sec
::
runtime_error
,
"query_type_name failed"
);
return
false
;
}
CAF_ASSERT
(
top
()
==
type
::
key
);
*
top_ptr
<
key_type
>
()
=
name
;
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
return
true
;
}
else
{
return
false
;
}
}
bool
json_builder
::
begin_field
(
std
::
string_view
name
,
bool
is_present
,
span
<
const
type_id_t
>
types
,
size_t
index
)
{
if
(
is_present
)
return
begin_field
(
name
,
types
,
index
);
else
return
begin_field
(
name
,
is_present
);
}
bool
json_builder
::
end_field
()
{
return
end_key_value_pair
();
}
bool
json_builder
::
begin_tuple
(
size_t
size
)
{
return
begin_sequence
(
size
);
}
bool
json_builder
::
end_tuple
()
{
return
end_sequence
();
}
bool
json_builder
::
begin_key_value_pair
()
{
auto
t
=
top
();
switch
(
t
)
{
case
type
:
:
object
:
{
auto
*
obj
=
top_ptr
<
detail
::
json
::
object
>
();
auto
&
new_member
=
obj
->
emplace_back
();
new_member
.
val
=
detail
::
json
::
make_value
(
storage_
);
push
(
&
new_member
);
push
(
new_member
.
val
,
type
::
element
);
push
(
&
new_member
.
key
);
return
true
;
}
default:
{
std
::
string
str
=
"expected object, found "
;
str
+=
as_json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
return
false
;
}
}
}
bool
json_builder
::
end_key_value_pair
()
{
return
pop_if
(
type
::
member
);
}
bool
json_builder
::
begin_sequence
(
size_t
)
{
switch
(
top
())
{
default:
emplace_error
(
sec
::
runtime_error
,
"unexpected begin_sequence"
);
return
false
;
case
type
:
:
element
:
{
auto
*
val
=
top_ptr
();
val
->
assign_array
(
storage_
);
push
(
val
,
type
::
array
);
return
true
;
}
case
type
:
:
array
:
{
auto
*
arr
=
top_ptr
<
detail
::
json
::
array
>
();
auto
&
new_val
=
arr
->
emplace_back
();
new_val
.
assign_array
(
storage_
);
push
(
&
new_val
,
type
::
array
);
return
true
;
}
}
}
bool
json_builder
::
end_sequence
()
{
return
pop_if
(
type
::
array
);
}
bool
json_builder
::
begin_associative_array
(
size_t
)
{
switch
(
top
())
{
default:
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
"unexpected begin_object or begin_associative_array"
);
return
false
;
case
type
:
:
element
:
top_ptr
()
->
assign_object
(
storage_
);
stack_
.
back
().
t
=
type
::
object
;
return
true
;
case
type
:
:
array
:
{
auto
&
new_val
=
top_ptr
<
detail
::
json
::
array
>
()
->
emplace_back
();
new_val
.
assign_object
(
storage_
);
push
(
&
new_val
,
type
::
object
);
return
true
;
}
}
}
bool
json_builder
::
end_associative_array
()
{
return
pop_if
(
type
::
object
);
}
bool
json_builder
::
value
(
std
::
byte
x
)
{
return
number
(
std
::
to_integer
<
uint8_t
>
(
x
));
}
bool
json_builder
::
value
(
bool
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
top_ptr
()
->
data
=
x
;
pop
();
return
true
;
case
type
:
:
key
:
*
top_ptr
<
key_type
>
()
=
x
?
"true"
sv
:
"false"
sv
;
return
true
;
case
type
:
:
array
:
{
auto
*
arr
=
top_ptr
<
detail
::
json
::
array
>
();
arr
->
emplace_back
().
data
=
x
;
return
true
;
}
default:
fail
(
type
::
boolean
);
return
false
;
}
}
bool
json_builder
::
value
(
int8_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
uint8_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
int16_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
uint16_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
int32_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
uint32_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
int64_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
uint64_t
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
float
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
double
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
long
double
x
)
{
return
number
(
x
);
}
bool
json_builder
::
value
(
std
::
string_view
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
top_ptr
()
->
assign_string
(
x
,
storage_
);
pop
();
return
true
;
case
type
:
:
key
:
*
top_ptr
<
key_type
>
()
=
detail
::
json
::
realloc
(
x
,
storage_
);
pop
();
return
true
;
case
type
:
:
array
:
{
auto
&
new_val
=
top_ptr
<
detail
::
json
::
array
>
()
->
emplace_back
();
new_val
.
assign_string
(
x
,
storage_
);
return
true
;
}
default:
fail
(
type
::
string
);
return
false
;
}
}
bool
json_builder
::
value
(
const
std
::
u16string
&
)
{
emplace_error
(
sec
::
unsupported_operation
,
"u16string not supported yet by caf::json_builder"
);
return
false
;
}
bool
json_builder
::
value
(
const
std
::
u32string
&
)
{
emplace_error
(
sec
::
unsupported_operation
,
"u32string not supported yet by caf::json_builder"
);
return
false
;
}
bool
json_builder
::
value
(
span
<
const
std
::
byte
>
x
)
{
std
::
vector
<
char
>
buf
;
buf
.
reserve
(
x
.
size
()
*
2
);
detail
::
append_hex
(
buf
,
reinterpret_cast
<
const
void
*>
(
x
.
data
()),
x
.
size
());
auto
hex_str
=
std
::
string_view
{
buf
.
data
(),
buf
.
size
()};
switch
(
top
())
{
case
type
:
:
element
:
top_ptr
()
->
assign_string
(
hex_str
,
storage_
);
pop
();
return
true
;
case
type
:
:
key
:
*
top_ptr
<
key_type
>
()
=
detail
::
json
::
realloc
(
hex_str
,
storage_
);
pop
();
return
true
;
case
type
:
:
array
:
{
auto
&
new_val
=
top_ptr
<
detail
::
json
::
array
>
()
->
emplace_back
();
new_val
.
assign_string
(
hex_str
,
storage_
);
return
true
;
}
default:
fail
(
type
::
string
);
return
false
;
}
}
// -- state management ---------------------------------------------------------
void
json_builder
::
init
()
{
has_human_readable_format_
=
true
;
storage_
=
make_counted
<
detail
::
json
::
storage
>
();
val_
=
detail
::
json
::
make_value
(
storage_
);
stack_
.
reserve
(
32
);
push
(
val_
,
type
::
element
);
}
json_builder
::
type
json_builder
::
top
()
{
if
(
!
stack_
.
empty
())
return
stack_
.
back
().
t
;
else
return
type
::
null
;
}
template
<
class
T
>
T
*
json_builder
::
top_ptr
()
{
if
constexpr
(
std
::
is_same_v
<
T
,
key_type
>
)
{
return
stack_
.
back
().
key_ptr
;
}
else
if
constexpr
(
std
::
is_same_v
<
T
,
detail
::
json
::
member
>
)
{
return
stack_
.
back
().
mem_ptr
;
}
else
if
constexpr
(
std
::
is_same_v
<
T
,
detail
::
json
::
object
>
)
{
return
&
std
::
get
<
detail
::
json
::
object
>
(
stack_
.
back
().
val_ptr
->
data
);
}
else
if
constexpr
(
std
::
is_same_v
<
T
,
detail
::
json
::
array
>
)
{
return
&
std
::
get
<
detail
::
json
::
array
>
(
stack_
.
back
().
val_ptr
->
data
);
}
else
{
static_assert
(
std
::
is_same_v
<
T
,
detail
::
json
::
value
>
);
return
stack_
.
back
().
val_ptr
;
}
}
detail
::
json
::
object
*
json_builder
::
top_obj
()
{
auto
is_obj
=
[](
const
entry
&
x
)
{
return
x
.
t
==
type
::
object
;
};
auto
i
=
std
::
find_if
(
stack_
.
rbegin
(),
stack_
.
rend
(),
is_obj
);
if
(
i
!=
stack_
.
rend
())
return
&
std
::
get
<
detail
::
json
::
object
>
(
i
->
val_ptr
->
data
);
CAF_RAISE_ERROR
(
"json_builder::top_obj was unable to find an object"
);
}
void
json_builder
::
push
(
detail
::
json
::
value
*
ptr
,
type
t
)
{
stack_
.
emplace_back
(
ptr
,
t
);
}
void
json_builder
::
push
(
detail
::
json
::
value
::
member
*
ptr
)
{
stack_
.
emplace_back
(
ptr
);
}
void
json_builder
::
push
(
key_type
*
ptr
)
{
stack_
.
emplace_back
(
ptr
);
}
// Backs up one level of nesting.
bool
json_builder
::
pop
()
{
if
(
!
stack_
.
empty
())
{
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop() called with an empty stack: begin/end mismatch"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
bool
json_builder
::
pop_if
(
type
t
)
{
if
(
!
stack_
.
empty
()
&&
stack_
.
back
().
t
==
t
)
{
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop_if failed: expected "
;
str
+=
as_json_type_name
(
t
);
if
(
stack_
.
empty
())
{
str
+=
", found an empty stack"
;
}
else
{
str
+=
", found "
;
str
+=
as_json_type_name
(
stack_
.
back
().
t
);
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
void
json_builder
::
fail
(
type
t
)
{
std
::
string
str
=
"failed to write a "
;
str
+=
as_json_type_name
(
t
);
str
+=
": invalid position (begin/end mismatch?)"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
}
bool
json_builder
::
inside_object
()
const
noexcept
{
auto
is_object
=
[](
const
entry
&
x
)
{
return
x
.
t
==
type
::
object
;
};
return
std
::
any_of
(
stack_
.
begin
(),
stack_
.
end
(),
is_object
);
}
}
// namespace caf
libcaf_core/src/json_object.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_object.hpp"
namespace
caf
{
// -- properties ---------------------------------------------------------------
json_value
json_object
::
value
(
std
::
string_view
key
)
const
{
auto
pred
=
[
key
](
const
auto
&
member
)
{
return
member
.
key
==
key
;
};
auto
i
=
std
::
find_if
(
obj_
->
begin
(),
obj_
->
end
(),
pred
);
if
(
i
!=
obj_
->
end
())
{
return
{
i
->
val
,
storage_
};
}
return
json_value
::
undefined
();
}
std
::
string
to_string
(
const
json_object
&
obj
)
{
std
::
string
result
;
obj
.
print_to
(
result
);
return
result
;
}
}
// namespace caf
libcaf_core/src/json_reader.cpp
View file @
a3b6ddc5
...
@@ -8,6 +8,8 @@
...
@@ -8,6 +8,8 @@
#include "caf/detail/print.hpp"
#include "caf/detail/print.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/string_algorithms.hpp"
#include <fstream>
namespace
{
namespace
{
static
constexpr
const
char
class_name
[]
=
"caf::json_reader"
;
static
constexpr
const
char
class_name
[]
=
"caf::json_reader"
;
...
@@ -145,19 +147,41 @@ json_reader::~json_reader() {
...
@@ -145,19 +147,41 @@ json_reader::~json_reader() {
bool
json_reader
::
load
(
std
::
string_view
json_text
)
{
bool
json_reader
::
load
(
std
::
string_view
json_text
)
{
reset
();
reset
();
string_parser_state
ps
{
json_text
.
begin
(),
json_text
.
end
()};
string_parser_state
ps
{
json_text
.
begin
(),
json_text
.
end
()};
root_
=
detail
::
json
::
parse_shallow
(
ps
,
&
buf_
);
if
(
ps
.
code
!=
pec
::
success
)
{
set_error
(
make_error
(
ps
));
st_
=
nullptr
;
return
false
;
}
err_
.
reset
();
detail
::
monotonic_buffer_resource
::
allocator
<
stack_type
>
alloc
{
&
buf_
};
st_
=
new
(
alloc
.
allocate
(
1
))
stack_type
(
stack_allocator
{
&
buf_
});
st_
->
reserve
(
16
);
st_
->
emplace_back
(
root_
);
return
true
;
}
bool
json_reader
::
load_file
(
const
char
*
path
)
{
using
iterator_t
=
std
::
istreambuf_iterator
<
char
>
;
reset
();
std
::
ifstream
input
{
path
};
if
(
!
input
.
is_open
())
{
emplace_error
(
sec
::
cannot_open_file
);
return
false
;
}
detail
::
json
::
file_parser_state
ps
{
iterator_t
{
input
},
iterator_t
{}};
root_
=
detail
::
json
::
parse
(
ps
,
&
buf_
);
root_
=
detail
::
json
::
parse
(
ps
,
&
buf_
);
if
(
ps
.
code
!=
pec
::
success
)
{
if
(
ps
.
code
!=
pec
::
success
)
{
set_error
(
make_error
(
ps
));
set_error
(
make_error
(
ps
));
st_
=
nullptr
;
st_
=
nullptr
;
return
false
;
return
false
;
}
else
{
err_
.
reset
();
detail
::
monotonic_buffer_resource
::
allocator
<
stack_type
>
alloc
{
&
buf_
};
st_
=
new
(
alloc
.
allocate
(
1
))
stack_type
(
stack_allocator
{
&
buf_
});
st_
->
reserve
(
16
);
st_
->
emplace_back
(
root_
);
return
true
;
}
}
err_
.
reset
();
detail
::
monotonic_buffer_resource
::
allocator
<
stack_type
>
alloc
{
&
buf_
};
st_
=
new
(
alloc
.
allocate
(
1
))
stack_type
(
stack_allocator
{
&
buf_
});
st_
->
reserve
(
16
);
st_
->
emplace_back
(
root_
);
return
true
;
}
}
void
json_reader
::
revert
()
{
void
json_reader
::
revert
()
{
...
...
libcaf_core/src/json_value.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_value.hpp"
#include "caf/expected.hpp"
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
#include "caf/make_counted.hpp"
#include "caf/parser_state.hpp"
#include <fstream>
namespace
caf
{
// -- conversion ---------------------------------------------------------------
int64_t
json_value
::
to_integer
(
int64_t
fallback
)
const
{
if
(
is_integer
())
{
return
std
::
get
<
int64_t
>
(
val_
->
data
);
}
if
(
is_double
())
{
return
static_cast
<
int64_t
>
(
std
::
get
<
double
>
(
val_
->
data
));
}
return
fallback
;
}
double
json_value
::
to_double
(
double
fallback
)
const
{
if
(
is_double
())
{
return
std
::
get
<
double
>
(
val_
->
data
);
}
if
(
is_integer
())
{
return
static_cast
<
double
>
(
std
::
get
<
int64_t
>
(
val_
->
data
));
}
return
fallback
;
}
bool
json_value
::
to_bool
(
bool
fallback
)
const
{
if
(
is_bool
())
{
return
std
::
get
<
bool
>
(
val_
->
data
);
}
return
fallback
;
}
std
::
string_view
json_value
::
to_string
()
const
{
return
to_string
(
std
::
string_view
{});
}
std
::
string_view
json_value
::
to_string
(
std
::
string_view
fallback
)
const
{
if
(
is_string
())
{
return
std
::
get
<
std
::
string_view
>
(
val_
->
data
);
}
return
fallback
;
}
json_object
json_value
::
to_object
()
const
{
return
to_object
(
json_object
{});
}
json_object
json_value
::
to_object
(
json_object
fallback
)
const
{
if
(
is_object
())
{
return
json_object
{
&
std
::
get
<
detail
::
json
::
object
>
(
val_
->
data
),
storage_
};
}
return
fallback
;
}
json_array
json_value
::
to_array
()
const
{
return
to_array
(
json_array
{});
}
json_array
json_value
::
to_array
(
json_array
fallback
)
const
{
if
(
is_array
())
{
return
json_array
{
&
std
::
get
<
detail
::
json
::
array
>
(
val_
->
data
),
storage_
};
}
return
fallback
;
}
// -- comparison ---------------------------------------------------------------
bool
json_value
::
equal_to
(
const
json_value
&
other
)
const
noexcept
{
if
(
val_
==
other
.
val_
)
{
return
true
;
}
if
(
val_
!=
nullptr
&&
other
.
val_
!=
nullptr
)
{
return
*
val_
==
*
other
.
val_
;
}
return
false
;
}
// -- parsing ------------------------------------------------------------------
expected
<
json_value
>
json_value
::
parse
(
std
::
string_view
str
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
string_parser_state
ps
{
str
.
begin
(),
str
.
end
()};
auto
root
=
detail
::
json
::
parse
(
ps
,
&
storage
->
buf
);
if
(
ps
.
code
==
pec
::
success
)
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
return
{
make_error
(
ps
)};
}
expected
<
json_value
>
json_value
::
parse_shallow
(
std
::
string_view
str
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
string_parser_state
ps
{
str
.
begin
(),
str
.
end
()};
auto
root
=
detail
::
json
::
parse_shallow
(
ps
,
&
storage
->
buf
);
if
(
ps
.
code
==
pec
::
success
)
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
return
{
make_error
(
ps
)};
}
expected
<
json_value
>
json_value
::
parse_in_situ
(
std
::
string
&
str
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
detail
::
json
::
mutable_string_parser_state
ps
{
str
.
data
(),
str
.
data
()
+
str
.
size
()};
auto
root
=
detail
::
json
::
parse_in_situ
(
ps
,
&
storage
->
buf
);
if
(
ps
.
code
==
pec
::
success
)
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
return
{
make_error
(
ps
)};
}
expected
<
json_value
>
json_value
::
parse_file
(
const
char
*
path
)
{
using
iterator_t
=
std
::
istreambuf_iterator
<
char
>
;
std
::
ifstream
input
{
path
};
if
(
!
input
.
is_open
())
return
make_error
(
sec
::
cannot_open_file
);
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
detail
::
json
::
file_parser_state
ps
{
iterator_t
{
input
},
iterator_t
{}};
auto
root
=
detail
::
json
::
parse
(
ps
,
&
storage
->
buf
);
if
(
ps
.
code
==
pec
::
success
)
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
return
{
make_error
(
ps
)};
}
expected
<
json_value
>
json_value
::
parse_file
(
const
std
::
string
&
path
)
{
return
parse_file
(
path
.
c_str
());
}
// -- free functions -----------------------------------------------------------
std
::
string
to_string
(
const
json_value
&
val
)
{
std
::
string
result
;
val
.
print_to
(
result
);
return
result
;
}
}
// namespace caf
libcaf_core/src/json_writer.cpp
View file @
a3b6ddc5
...
@@ -13,17 +13,9 @@ namespace {
...
@@ -13,17 +13,9 @@ namespace {
static
constexpr
const
char
class_name
[]
=
"caf::json_writer"
;
static
constexpr
const
char
class_name
[]
=
"caf::json_writer"
;
constexpr
bool
can_morph
(
json_writer
::
type
from
,
json_writer
::
type
to
)
{
constexpr
std
::
string_view
json_type_names
[]
=
{
"element"
,
"object"
,
"member"
,
return
from
==
json_writer
::
type
::
element
&&
to
!=
json_writer
::
type
::
member
;
"array"
,
"string"
,
"number"
,
}
"bool"
,
"null"
};
constexpr
const
char
*
json_type_names
[]
=
{
"element"
,
"object"
,
"member"
,
"array"
,
"string"
,
"number"
,
"bool"
,
"null"
};
constexpr
const
char
*
json_type_name
(
json_writer
::
type
t
)
{
return
json_type_names
[
static_cast
<
uint8_t
>
(
t
)];
}
char
last_non_ws_char
(
const
std
::
vector
<
char
>&
buf
)
{
char
last_non_ws_char
(
const
std
::
vector
<
char
>&
buf
)
{
auto
not_ws
=
[](
char
c
)
{
return
!
std
::
isspace
(
c
);
};
auto
not_ws
=
[](
char
c
)
{
return
!
std
::
isspace
(
c
);
};
...
@@ -104,7 +96,7 @@ bool json_writer::begin_object(type_id_t id, std::string_view name) {
...
@@ -104,7 +96,7 @@ bool json_writer::begin_object(type_id_t id, std::string_view name) {
pop
();
pop
();
return
true
;
return
true
;
};
};
if
(
inside_object
()
||
skip_object_type_annotation_
)
if
(
skip_object_type_annotation_
||
inside_object
()
)
return
begin_associative_array
(
0
);
return
begin_associative_array
(
0
);
else
else
return
begin_associative_array
(
0
)
// Put opening paren, ...
return
begin_associative_array
(
0
)
// Put opening paren, ...
...
@@ -140,7 +132,7 @@ bool json_writer::begin_field(std::string_view name, bool is_present) {
...
@@ -140,7 +132,7 @@ bool json_writer::begin_field(std::string_view name, bool is_present) {
return
true
;
return
true
;
default:
{
default:
{
std
::
string
str
=
"expected object, found "
;
std
::
string
str
=
"expected object, found "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
return
false
;
return
false
;
}
}
...
@@ -222,7 +214,7 @@ bool json_writer::begin_key_value_pair() {
...
@@ -222,7 +214,7 @@ bool json_writer::begin_key_value_pair() {
return
true
;
return
true
;
default:
{
default:
{
std
::
string
str
=
"expected object, found "
;
std
::
string
str
=
"expected object, found "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
return
false
;
return
false
;
}
}
...
@@ -489,12 +481,12 @@ bool json_writer::pop_if(type t) {
...
@@ -489,12 +481,12 @@ bool json_writer::pop_if(type t) {
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
str
=
"pop_if failed: expected "
;
std
::
string
str
=
"pop_if failed: expected "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
if
(
stack_
.
empty
())
{
if
(
stack_
.
empty
())
{
str
+=
", found an empty stack"
;
str
+=
", found an empty stack"
;
}
else
{
}
else
{
str
+=
", found "
;
str
+=
", found "
;
str
+=
json_type_name
(
stack_
.
back
().
t
);
str
+=
as_
json_type_name
(
stack_
.
back
().
t
);
}
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
return
false
;
...
@@ -509,13 +501,13 @@ bool json_writer::pop_if_next(type t) {
...
@@ -509,13 +501,13 @@ bool json_writer::pop_if_next(type t) {
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
str
=
"pop_if_next failed: expected "
;
std
::
string
str
=
"pop_if_next failed: expected "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
if
(
stack_
.
size
()
<
2
)
{
if
(
stack_
.
size
()
<
2
)
{
str
+=
", found a stack of size "
;
str
+=
", found a stack of size "
;
detail
::
print
(
str
,
stack_
.
size
());
detail
::
print
(
str
,
stack_
.
size
());
}
else
{
}
else
{
str
+=
", found "
;
str
+=
", found "
;
str
+=
json_type_name
(
stack_
[
stack_
.
size
()
-
2
].
t
);
str
+=
as_
json_type_name
(
stack_
[
stack_
.
size
()
-
2
].
t
);
}
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
return
false
;
...
@@ -536,9 +528,9 @@ bool json_writer::morph(type t, type& prev) {
...
@@ -536,9 +528,9 @@ bool json_writer::morph(type t, type& prev) {
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
str
=
"cannot convert "
;
std
::
string
str
=
"cannot convert "
;
str
+=
json_type_name
(
stack_
.
back
().
t
);
str
+=
as_
json_type_name
(
stack_
.
back
().
t
);
str
+=
" to "
;
str
+=
" to "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
return
false
;
}
}
...
@@ -555,7 +547,7 @@ void json_writer::unsafe_morph(type t) {
...
@@ -555,7 +547,7 @@ void json_writer::unsafe_morph(type t) {
void
json_writer
::
fail
(
type
t
)
{
void
json_writer
::
fail
(
type
t
)
{
std
::
string
str
=
"failed to write a "
;
std
::
string
str
=
"failed to write a "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
str
+=
": invalid position (begin/end mismatch?)"
;
str
+=
": invalid position (begin/end mismatch?)"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
}
}
...
@@ -589,4 +581,10 @@ void json_writer::sep() {
...
@@ -589,4 +581,10 @@ void json_writer::sep() {
}
}
}
}
// -- free functions -----------------------------------------------------------
std
::
string_view
as_json_type_name
(
json_writer
::
type
t
)
{
return
json_type_names
[
static_cast
<
uint8_t
>
(
t
)];
}
}
// namespace caf
}
// namespace caf
libcaf_core/test/core-test.hpp
View file @
a3b6ddc5
#pragma once
#pragma once
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/cow_vector.hpp"
#include "caf/cow_vector.hpp"
#include "caf/fwd.hpp"
#include "caf/fwd.hpp"
#include "caf/result.hpp"
#include "caf/result.hpp"
...
@@ -13,6 +16,32 @@
...
@@ -13,6 +16,32 @@
#include <string>
#include <string>
#include <utility>
#include <utility>
// -- utility for testing serialization round-trips ----------------------------
template
<
class
T
>
T
deep_copy
(
const
T
&
val
)
{
using
namespace
std
::
literals
;
caf
::
byte_buffer
buf
;
{
caf
::
binary_serializer
sink
{
nullptr
,
buf
};
if
(
!
sink
.
apply
(
val
))
{
auto
msg
=
"serialization failed in deep_copy: "
s
;
msg
+=
to_string
(
sink
.
get_error
());
CAF_RAISE_ERROR
(
msg
.
c_str
());
}
}
auto
result
=
T
{};
{
caf
::
binary_deserializer
sink
{
nullptr
,
buf
};
if
(
!
sink
.
apply
(
result
))
{
auto
msg
=
"deserialization failed in deep_copy: "
s
;
msg
+=
to_string
(
sink
.
get_error
());
CAF_RAISE_ERROR
(
msg
.
c_str
());
}
}
return
result
;
}
// -- forward declarations for all unit test suites ----------------------------
// -- forward declarations for all unit test suites ----------------------------
using
float_actor
=
caf
::
typed_actor
<
caf
::
result
<
void
>
(
float
)
>
;
using
float_actor
=
caf
::
typed_actor
<
caf
::
result
<
void
>
(
float
)
>
;
...
...
libcaf_core/test/detail/json.cpp
View file @
a3b6ddc5
...
@@ -106,6 +106,12 @@ void stringify(std::string& str, size_t, detail::json::null_t) {
...
@@ -106,6 +106,12 @@ void stringify(std::string& str, size_t, detail::json::null_t) {
str
+=
"null"
;
str
+=
"null"
;
}
}
void
stringify
(
std
::
string
&
str
,
size_t
,
detail
::
json
::
undefined_t
)
{
// The parser never emits undefined objects, but we still need to provide an
// overload for this type.
str
+=
"null"
;
}
void
stringify
(
std
::
string
&
str
,
size_t
indent
,
const
detail
::
json
::
array
&
xs
)
{
void
stringify
(
std
::
string
&
str
,
size_t
indent
,
const
detail
::
json
::
array
&
xs
)
{
if
(
xs
.
empty
())
{
if
(
xs
.
empty
())
{
str
+=
"[]"
;
str
+=
"[]"
;
...
...
libcaf_core/test/json_array.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_array
#include "caf/json_array.hpp"
#include "core-test.hpp"
#include "caf/json_array.hpp"
#include "caf/json_value.hpp"
using
namespace
caf
;
using
namespace
std
::
literals
;
namespace
{
std
::
string
printed
(
const
json_array
&
arr
)
{
std
::
string
result
;
arr
.
print_to
(
result
,
2
);
return
result
;
}
}
// namespace
TEST_CASE
(
"default-constructed"
)
{
auto
arr
=
json_array
{};
CHECK
(
arr
.
empty
());
CHECK
(
arr
.
is_empty
());
CHECK
(
arr
.
begin
()
==
arr
.
end
());
CHECK_EQ
(
arr
.
size
(),
0u
);
CHECK_EQ
(
to_string
(
arr
),
"[]"
);
CHECK_EQ
(
printed
(
arr
),
"[]"
);
CHECK_EQ
(
deep_copy
(
arr
),
arr
);
}
TEST_CASE
(
"from empty array"
)
{
auto
arr
=
json_value
::
parse
(
"[]"
)
->
to_array
();
CHECK
(
arr
.
empty
());
CHECK
(
arr
.
is_empty
());
CHECK
(
arr
.
begin
()
==
arr
.
end
());
CHECK_EQ
(
arr
.
size
(),
0u
);
CHECK_EQ
(
to_string
(
arr
),
"[]"
);
CHECK_EQ
(
printed
(
arr
),
"[]"
);
CHECK_EQ
(
deep_copy
(
arr
),
arr
);
}
TEST_CASE
(
"from non-empty array"
)
{
auto
arr
=
json_value
::
parse
(
R"_([1, "two", 3.0])_"
)
->
to_array
();
CHECK
(
!
arr
.
empty
());
CHECK
(
!
arr
.
is_empty
());
CHECK
(
arr
.
begin
()
!=
arr
.
end
());
REQUIRE_EQ
(
arr
.
size
(),
3u
);
CHECK_EQ
((
*
arr
.
begin
()).
to_integer
(),
1
);
std
::
vector
<
json_value
>
vals
;
for
(
const
auto
&
val
:
arr
)
{
vals
.
emplace_back
(
val
);
}
REQUIRE_EQ
(
vals
.
size
(),
3u
);
CHECK_EQ
(
vals
[
0
].
to_integer
(),
1
);
CHECK_EQ
(
vals
[
1
].
to_string
(),
"two"
);
CHECK_EQ
(
vals
[
2
].
to_double
(),
3.0
);
CHECK_EQ
(
to_string
(
arr
),
R"_([1, "two", 3])_"
);
CHECK_EQ
(
printed
(
arr
),
"[
\n
1,
\n
\"
two
\"
,
\n
3
\n
]"
);
CHECK_EQ
(
deep_copy
(
arr
),
arr
);
}
libcaf_core/test/json_builder.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_builder
#include "caf/json_builder.hpp"
#include "core-test.hpp"
#include "caf/json_value.hpp"
#include <string_view>
using
namespace
caf
;
using
namespace
std
::
literals
;
namespace
{
struct
fixture
{
fixture
()
{
builder
.
skip_object_type_annotation
(
true
);
}
std
::
string
printed
(
const
json_value
&
val
,
size_t
indentation_factor
=
0
)
{
std
::
string
result
;
val
.
print_to
(
result
,
indentation_factor
);
return
result
;
}
json_builder
builder
;
};
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
TEST_CASE
(
"empty JSON value"
)
{
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_null
());
}
TEST_CASE
(
"integer"
)
{
CHECK
(
builder
.
value
(
int32_t
{
42
}));
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_integer
());
CHECK_EQ
(
val
.
to_integer
(),
42
);
}
TEST_CASE
(
"floating point"
)
{
CHECK
(
builder
.
value
(
4.2
));
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_double
());
CHECK_EQ
(
val
.
to_double
(),
4.2
);
}
TEST_CASE
(
"boolean"
)
{
CHECK
(
builder
.
value
(
true
));
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_bool
());
CHECK_EQ
(
val
.
to_bool
(),
true
);
}
TEST_CASE
(
"string"
)
{
CHECK
(
builder
.
value
(
"Hello, world!"
sv
));
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_string
());
CHECK_EQ
(
val
.
to_string
(),
"Hello, world!"
sv
);
}
TEST_CASE
(
"array"
)
{
auto
xs
=
std
::
vector
{
1
,
2
,
3
};
CHECK
(
builder
.
apply
(
xs
));
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_array
());
CHECK_EQ
(
printed
(
val
),
"[1, 2, 3]"
sv
);
}
TEST_CASE
(
"flat object"
)
{
auto
req
=
my_request
{
10
,
20
};
if
(
!
CHECK
(
builder
.
apply
(
req
)))
{
MESSAGE
(
"builder: "
<<
builder
.
get_error
());
}
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_object
());
CHECK_EQ
(
printed
(
val
),
R"_({"a": 10, "b": 20})_"
);
}
TEST_CASE
(
"flat object with type annotation"
)
{
builder
.
skip_object_type_annotation
(
false
);
auto
req
=
my_request
{
10
,
20
};
if
(
!
CHECK
(
builder
.
apply
(
req
)))
{
MESSAGE
(
"builder: "
<<
builder
.
get_error
());
}
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_object
());
CHECK_EQ
(
printed
(
val
),
R"_({"@type": "my_request", "a": 10, "b": 20})_"
);
}
namespace
{
constexpr
std
::
string_view
rect_str
=
R"_({
"top-left": {
"x": 10,
"y": 10
},
"bottom-right": {
"x": 20,
"y": 20
}
})_"
;
}
// namespace
TEST_CASE
(
"nested object"
)
{
auto
rect
=
rectangle
{{
10
,
10
},
{
20
,
20
}};
if
(
!
CHECK
(
builder
.
apply
(
rect
)))
{
MESSAGE
(
"builder: "
<<
builder
.
get_error
());
}
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_object
());
CHECK_EQ
(
printed
(
val
,
2
),
rect_str
);
}
namespace
{
constexpr
std
::
string_view
annotated_rect_str
=
R"_({
"@type": "rectangle",
"top-left": {
"x": 10,
"y": 10
},
"bottom-right": {
"x": 20,
"y": 20
}
})_"
;
}
// namespace
TEST_CASE
(
"nested object with type annotation"
)
{
builder
.
skip_object_type_annotation
(
false
);
auto
rect
=
rectangle
{{
10
,
10
},
{
20
,
20
}};
if
(
!
CHECK
(
builder
.
apply
(
rect
)))
{
MESSAGE
(
"builder: "
<<
builder
.
get_error
());
}
auto
val
=
builder
.
seal
();
CHECK
(
val
.
is_object
());
CHECK_EQ
(
printed
(
val
,
2
),
annotated_rect_str
);
}
END_FIXTURE_SCOPE
()
libcaf_core/test/json_object.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_object
#include "caf/json_object.hpp"
#include "core-test.hpp"
#include "caf/json_array.hpp"
#include "caf/json_value.hpp"
using
namespace
caf
;
using
namespace
std
::
literals
;
namespace
{
std
::
string
printed
(
const
json_object
&
obj
)
{
std
::
string
result
;
obj
.
print_to
(
result
,
2
);
return
result
;
}
}
// namespace
TEST_CASE
(
"default-constructed"
)
{
auto
obj
=
json_object
{};
CHECK
(
obj
.
empty
());
CHECK
(
obj
.
is_empty
());
CHECK
(
obj
.
begin
()
==
obj
.
end
());
CHECK_EQ
(
obj
.
size
(),
0u
);
CHECK
(
obj
.
value
(
"foo"
).
is_undefined
());
CHECK_EQ
(
to_string
(
obj
),
"{}"
);
CHECK_EQ
(
printed
(
obj
),
"{}"
);
CHECK_EQ
(
deep_copy
(
obj
),
obj
);
}
TEST_CASE
(
"from empty object"
)
{
auto
obj
=
json_value
::
parse
(
"{}"
)
->
to_object
();
CHECK
(
obj
.
empty
());
CHECK
(
obj
.
is_empty
());
CHECK
(
obj
.
begin
()
==
obj
.
end
());
CHECK_EQ
(
obj
.
size
(),
0u
);
CHECK
(
obj
.
value
(
"foo"
).
is_undefined
());
CHECK_EQ
(
to_string
(
obj
),
"{}"
);
CHECK_EQ
(
printed
(
obj
),
"{}"
);
CHECK_EQ
(
deep_copy
(
obj
),
obj
);
}
TEST_CASE
(
"from non-empty object"
)
{
auto
obj
=
json_value
::
parse
(
R"_({"a": "one", "b": 2})_"
)
->
to_object
();
CHECK
(
!
obj
.
empty
());
CHECK
(
!
obj
.
is_empty
());
CHECK
(
obj
.
begin
()
!=
obj
.
end
());
REQUIRE_EQ
(
obj
.
size
(),
2u
);
CHECK_EQ
(
obj
.
begin
().
key
(),
"a"
);
CHECK_EQ
(
obj
.
begin
().
value
().
to_string
(),
"one"
);
CHECK_EQ
(
obj
.
value
(
"a"
).
to_string
(),
"one"
);
CHECK_EQ
(
obj
.
value
(
"b"
).
to_integer
(),
2
);
CHECK
(
obj
.
value
(
"c"
).
is_undefined
());
std
::
vector
<
std
::
pair
<
std
::
string_view
,
json_value
>>
vals
;
for
(
const
auto
&
val
:
obj
)
{
vals
.
emplace_back
(
val
);
}
REQUIRE_EQ
(
vals
.
size
(),
2u
);
CHECK_EQ
(
vals
[
0
].
first
,
"a"
);
CHECK_EQ
(
vals
[
0
].
second
.
to_string
(),
"one"
);
CHECK_EQ
(
vals
[
1
].
first
,
"b"
);
CHECK_EQ
(
vals
[
1
].
second
.
to_integer
(),
2
);
CHECK_EQ
(
to_string
(
obj
),
R"_({"a": "one", "b": 2})_"
);
CHECK_EQ
(
printed
(
obj
),
"{
\n
\"
a
\"
:
\"
one
\"
,
\n
\"
b
\"
: 2
\n
}"
);
CHECK_EQ
(
deep_copy
(
obj
),
obj
);
}
libcaf_core/test/json_value.cpp
0 → 100644
View file @
a3b6ddc5
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_value
#include "caf/json_value.hpp"
#include "core-test.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/json_array.hpp"
#include "caf/json_object.hpp"
using
namespace
caf
;
using
namespace
std
::
literals
;
namespace
{
std
::
string
printed
(
const
json_value
&
val
)
{
std
::
string
result
;
val
.
print_to
(
result
,
2
);
return
result
;
}
}
// namespace
TEST_CASE
(
"default-constructed"
)
{
auto
val
=
json_value
{};
CHECK
(
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"null"
);
CHECK_EQ
(
printed
(
val
),
"null"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from undefined"
)
{
auto
val
=
json_value
::
undefined
();
CHECK
(
!
val
.
is_null
());
CHECK
(
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"null"
);
CHECK_EQ
(
printed
(
val
),
"null"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from integer"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"42"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
42
);
CHECK_EQ
(
val
.
to_double
(),
42.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"42"
);
CHECK_EQ
(
printed
(
val
),
"42"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from double"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"42.0"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
val
.
is_double
());
CHECK
(
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
42
);
CHECK_EQ
(
val
.
to_double
(),
42.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"42"
);
CHECK_EQ
(
printed
(
val
),
"42"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from bool"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"true"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
true
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"true"
);
CHECK_EQ
(
printed
(
val
),
"true"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from string"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
R"_("Hello, world!")_"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
"Hello, world!"
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
R"_("Hello, world!")_"
);
CHECK_EQ
(
printed
(
val
),
R"_("Hello, world!")_"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from empty array"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"[]"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_array
().
size
(),
0u
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"[]"
);
CHECK_EQ
(
printed
(
val
),
"[]"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from array of size 1"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"[1]"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_array
().
size
(),
1u
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"[1]"
);
CHECK_EQ
(
printed
(
val
),
"[
\n
1
\n
]"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from array of size 3"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"[1, 2, 3]"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
val
.
is_array
());
CHECK
(
!
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_array
().
size
(),
3u
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"[1, 2, 3]"
);
CHECK_EQ
(
printed
(
val
),
"[
\n
1,
\n
2,
\n
3
\n
]"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from empty object"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"{}"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"{}"
);
CHECK_EQ
(
printed
(
val
),
"{}"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from non-empty object"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
R"_({"foo": "bar"})_"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
!
val
.
is_double
());
CHECK
(
!
val
.
is_number
());
CHECK
(
!
val
.
is_bool
());
CHECK
(
!
val
.
is_string
());
CHECK
(
!
val
.
is_array
());
CHECK
(
val
.
is_object
());
CHECK_EQ
(
val
.
to_integer
(),
0
);
CHECK_EQ
(
val
.
to_double
(),
0.0
);
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
1u
);
CHECK_EQ
(
to_string
(
val
),
R"_({"foo": "bar"})_"
);
CHECK_EQ
(
printed
(
val
),
"{
\n
\"
foo
\"
:
\"
bar
\"\n
}"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
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