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
50984474
Commit
50984474
authored
Aug 26, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new API for interacting with JSON values
parent
7168be7a
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1839 additions
and
53 deletions
+1839
-53
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+5
-0
libcaf_core/caf/detail/json.hpp
libcaf_core/caf/detail/json.hpp
+596
-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
+11
-0
libcaf_core/caf/json_array.hpp
libcaf_core/caf/json_array.hpp
+162
-0
libcaf_core/caf/json_object.hpp
libcaf_core/caf/json_object.hpp
+168
-0
libcaf_core/caf/json_value.hpp
libcaf_core/caf/json_value.hpp
+166
-0
libcaf_core/caf/parser_state.hpp
libcaf_core/caf/parser_state.hpp
+5
-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
+208
-36
libcaf_core/src/init_global_meta_objects.cpp
libcaf_core/src/init_global_meta_objects.cpp
+3
-0
libcaf_core/src/json_object.cpp
libcaf_core/src/json_object.cpp
+20
-0
libcaf_core/src/json_reader.cpp
libcaf_core/src/json_reader.cpp
+1
-1
libcaf_core/src/json_value.cpp
libcaf_core/src/json_value.cpp
+124
-0
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
+51
-0
libcaf_core/test/json_object.cpp
libcaf_core/test/json_object.cpp
+58
-0
libcaf_core/test/json_value.cpp
libcaf_core/test/json_value.cpp
+212
-0
No files found.
libcaf_core/CMakeLists.txt
View file @
50984474
...
...
@@ -161,7 +161,9 @@ caf_add_component(
src/ipv6_address.cpp
src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp
src/json_object.cpp
src/json_reader.cpp
src/json_value.cpp
src/json_writer.cpp
src/load_inspector.cpp
src/local_actor.cpp
...
...
@@ -313,7 +315,10 @@ caf_add_component(
ipv6_address
ipv6_endpoint
ipv6_subnet
json_array
json_object
json_reader
json_value
json_writer
load_inspector
logger
...
...
libcaf_core/caf/detail/json.hpp
View file @
50984474
...
...
@@ -4,13 +4,19 @@
#pragma once
#include "caf/detail/monotonic_buffer_resource.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 <cstring>
#include <iterator>
#include <new>
#include <string_view>
#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
// buffer resource. This minimizes memory allocations and also enables us to
...
...
@@ -22,23 +28,281 @@ namespace caf::detail::json {
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
);
}
class
CAF_CORE_EXPORT
value
{
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
{
std
::
string_view
key
;
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
,
std
::
string_view
,
array
,
object
>
;
std
::
string_view
,
array
,
object
,
undefined_t
>
;
static
constexpr
size_t
null_index
=
0
;
...
...
@@ -54,21 +318,343 @@ public:
static
constexpr
size_t
object_index
=
6
;
static
constexpr
size_t
undefined_index
=
7
;
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
;
}
};
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
member
=
value
::
member
;
using
object
=
value
::
object
;
// -- 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
;
};
using
storage_ptr
=
intrusive_ptr
<
storage
>
;
// -- factory functions --------------------------------------------------------
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
);
inline
array
*
make_array
(
const
storage_ptr
&
ptr
)
{
return
make_array
(
&
ptr
->
buf
);
}
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
{
using
alloc_t
=
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
;
auto
buf
=
alloc_t
{
res
}.
allocate
(
tmp
.
size
());
strncpy
(
buf
,
tmp
.
data
(),
tmp
.
size
());
val
.
data
=
std
::
string_view
{
buf
,
tmp
.
size
()};
}
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
{
using
alloc_t
=
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
;
auto
buf
=
alloc_t
{
res
}.
allocate
(
key
.
size
());
strncpy
(
buf
,
key
.
data
(),
key
.
size
());
kvp
.
key
=
std
::
string_view
{
buf
,
key
.
size
()};
}
// 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 ------------------------------------------------------------------
// Parses the input 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
);
}
// namespace caf::detail::json
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
View file @
50984474
...
...
@@ -55,20 +55,20 @@ public:
using
other
=
allocator
<
U
>
;
};
explicit
allocator
(
monotonic_buffer_resource
*
mbr
)
:
mbr_
(
mbr
)
{
constexpr
explicit
allocator
(
monotonic_buffer_resource
*
mbr
)
:
mbr_
(
mbr
)
{
// nop
}
allocator
()
:
mbr_
(
nullptr
)
{
constexpr
allocator
()
:
mbr_
(
nullptr
)
{
// nop
}
allocator
(
const
allocator
&
)
=
default
;
constexpr
allocator
(
const
allocator
&
)
=
default
;
allocator
&
operator
=
(
const
allocator
&
)
=
default
;
constexpr
allocator
&
operator
=
(
const
allocator
&
)
=
default
;
template
<
class
U
>
allocator
(
const
allocator
<
U
>&
other
)
:
mbr_
(
other
.
resource
())
{
constexpr
allocator
(
const
allocator
<
U
>&
other
)
:
mbr_
(
other
.
resource
())
{
// nop
}
...
...
libcaf_core/caf/fwd.hpp
View file @
50984474
...
...
@@ -110,6 +110,11 @@ class ipv4_subnet;
class
ipv6_address
;
class
ipv6_endpoint
;
class
ipv6_subnet
;
class
json_array
;
class
json_object
;
class
json_reader
;
class
json_value
;
class
json_writer
;
class
local_actor
;
class
mailbox_element
;
class
message
;
...
...
libcaf_core/caf/json.hpp
0 → 100644
View file @
50984474
// 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_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 @
50984474
// 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
()};
}
// -- 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_
;
};
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
);
}
}
// namespace caf
libcaf_core/caf/json_object.hpp
0 → 100644
View file @
50984474
// 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
()};
}
// -- 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
);
}
}
// namespace caf
libcaf_core/caf/json_value.hpp
0 → 100644
View file @
50984474
// 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
);
// -- 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_
;
};
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
);
}
}
// namespace caf
libcaf_core/caf/parser_state.hpp
View file @
50984474
...
...
@@ -16,6 +16,8 @@ namespace caf {
/// Stores all information necessary for implementing an FSM-based parser.
template
<
class
Iterator
,
class
Sentinel
>
struct
parser_state
{
using
iterator_type
=
Iterator
;
/// Current position of the parser.
Iterator
i
;
...
...
@@ -139,4 +141,7 @@ auto make_error(const parser_state<Iterator, Sentinel>& ps, Ts&&... xs)
/// Specialization for parsers operating on string views.
using
string_parser_state
=
parser_state
<
std
::
string_view
::
iterator
>
;
/// Specialization for parsers operating on mutable character sequences.
using
mutable_string_parser_state
=
parser_state
<
char
*>
;
}
// namespace caf
libcaf_core/caf/type_id.hpp
View file @
50984474
...
...
@@ -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_u16string
))
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
::
error
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
exit_msg
))
...
...
@@ -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_endpoint
))
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_id
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
node_down_msg
))
...
...
@@ -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
::
pec
))
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
::
stream
))
CAF_ADD_TYPE_ID
(
core_module
,
(
caf
::
stream_abort_msg
))
...
...
libcaf_core/src/detail/json.cpp
View file @
50984474
...
...
@@ -4,6 +4,7 @@
#include "caf/detail/json.hpp"
#include <cstring>
#include <iterator>
#include <memory>
...
...
@@ -14,6 +15,7 @@
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
#include "caf/span.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
...
...
@@ -23,6 +25,116 @@ namespace {
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
;
}
struct
regular_unescaper
{
std
::
string_view
unescape
(
caf
::
detail
::
monotonic_buffer_resource
*
storage
,
std
::
string_view
str
,
bool
is_escaped
)
const
{
caf
::
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
alloc
{
storage
};
auto
*
str_buf
=
alloc
.
allocate
(
str
.
size
());
if
(
!
is_escaped
)
{
strncpy
(
str_buf
,
str
.
data
(),
str
.
size
());
return
std
::
string_view
{
str_buf
,
str
.
size
()};
}
auto
unescaped_size
=
do_unescape
(
str
.
data
(),
str
.
data
()
+
str
.
size
(),
str_buf
);
return
std
::
string_view
{
str_buf
,
unescaped_size
};
}
template
<
class
Consumer
>
void
assign
(
Consumer
&
f
,
const
char
*
str
,
size_t
len
,
bool
is_escaped
)
const
{
auto
val
=
std
::
string_view
{
str
,
len
};
f
.
value
(
unescape
(
f
.
storage
,
val
,
is_escaped
));
}
};
struct
shallow_unescaper
{
std
::
string_view
unescape
(
caf
::
detail
::
monotonic_buffer_resource
*
storage
,
std
::
string_view
str
,
bool
is_escaped
)
const
{
if
(
!
is_escaped
)
{
return
str
;
}
caf
::
detail
::
monotonic_buffer_resource
::
allocator
<
char
>
alloc
{
storage
};
auto
*
str_buf
=
alloc
.
allocate
(
str
.
size
());
auto
unescaped_size
=
do_unescape
(
str
.
data
(),
str
.
data
()
+
str
.
size
(),
str_buf
);
return
std
::
string_view
{
str_buf
,
unescaped_size
};
}
template
<
class
Consumer
>
void
assign
(
Consumer
&
f
,
const
char
*
str
,
size_t
len
,
bool
is_escaped
)
const
{
auto
val
=
std
::
string_view
{
str
,
len
};
f
.
value
(
unescape
(
f
.
storage
,
val
,
is_escaped
));
}
};
struct
in_situ_unescaper
{
std
::
string_view
unescape
(
caf
::
span
<
char
>
str
,
bool
is_escaped
)
const
{
if
(
!
is_escaped
)
{
return
std
::
string_view
{
str
.
data
(),
str
.
size
()};
}
auto
unescaped_size
=
do_unescape
(
str
.
data
(),
str
.
data
()
+
str
.
size
(),
str
.
data
());
return
std
::
string_view
{
str
.
data
(),
unescaped_size
};
}
template
<
class
Consumer
>
void
assign
(
Consumer
&
f
,
char
*
str
,
size_t
len
,
bool
is_escaped
)
const
{
auto
val
=
caf
::
span
<
char
>
{
str
,
len
};
f
.
value
(
unescape
(
val
,
is_escaped
));
}
};
}
// namespace
namespace
caf
::
detail
::
parser
{
...
...
@@ -46,6 +158,7 @@ struct val_consumer {
};
struct
key_consumer
{
monotonic_buffer_resource
*
storage
;
std
::
string_view
*
ptr
;
void
value
(
std
::
string_view
str
)
{
...
...
@@ -58,7 +171,7 @@ struct member_consumer {
json
::
member
*
ptr
;
key_consumer
begin_key
()
{
return
{
std
::
addressof
(
ptr
->
key
)};
return
{
st
orage
,
st
d
::
addressof
(
ptr
->
key
)};
}
val_consumer
begin_val
()
{
...
...
@@ -71,8 +184,8 @@ struct obj_consumer {
json
::
object
*
ptr
;
member_consumer
begin_member
()
{
ptr
->
emplace_back
();
return
{
ptr
->
get_allocator
().
resource
(),
std
::
addressof
(
ptr
->
back
())
};
auto
&
new_member
=
ptr
->
emplace_back
();
return
{
ptr
->
get_allocator
().
resource
(),
&
new_member
};
}
};
...
...
@@ -80,30 +193,28 @@ struct arr_consumer {
json
::
array
*
ptr
;
val_consumer
begin_value
()
{
ptr
->
emplace_back
();
return
{
ptr
->
get_allocator
().
resource
(),
std
::
addressof
(
ptr
->
back
())
};
auto
&
new_element
=
ptr
->
emplace_back
();
return
{
ptr
->
get_allocator
().
resource
(),
&
new_element
};
}
};
arr_consumer
val_consumer
::
begin_array
()
{
ptr
->
data
=
json
::
array
(
json
::
value
::
array_allocator
{
storage
});
auto
&
arr
=
std
::
get
<
json
::
array
>
(
ptr
->
data
);
arr
.
reserve
(
16
);
return
{
&
arr
};
}
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
);
obj
.
reserve
(
16
);
return
{
&
obj
};
}
void
read_value
(
string_parser_state
&
ps
,
size_t
nesting_level
,
val_consumer
consumer
);
template
<
class
Consumer
>
void
read_json_null_or_nan
(
string_parser_s
tate
&
ps
,
Consumer
consumer
)
{
template
<
class
ParserState
,
class
Consumer
>
void
read_json_null_or_nan
(
ParserS
tate
&
ps
,
Consumer
consumer
)
{
enum
{
nil
,
is_null
,
is_nan
};
auto
res_type
=
nil
;
auto
g
=
make_scope_guard
([
&
]
{
...
...
@@ -141,9 +252,9 @@ void read_json_null_or_nan(string_parser_state& ps, Consumer consumer) {
// clang-format on
}
template
<
class
Consumer
>
void
read_json_string
(
string_parser_state
&
ps
,
Consumer
consumer
)
{
std
::
string_view
::
iterator
first
;
template
<
class
ParserState
,
class
Unescaper
,
class
Consumer
>
void
read_json_string
(
ParserState
&
ps
,
Unescaper
escaper
,
Consumer
consumer
)
{
typename
ParserState
::
iterator_type
first
;
// clang-format off
start
();
state
(
init
)
{
...
...
@@ -153,13 +264,20 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
state
(
read_chars
)
{
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
,
consumer
.
value
(
std
::
string_view
{
std
::
addressof
(
*
first
),
static_cast
<
size_t
>
(
ps
.
i
-
first
)}
))
escaper
.
assign
(
consumer
,
std
::
addressof
(
*
first
),
static_cast
<
size_t
>
(
ps
.
i
-
first
),
false
))
transition
(
read_chars
,
any_char
)
}
state
(
read_chars_after_escape
)
{
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
,
escaper
.
assign
(
consumer
,
std
::
addressof
(
*
first
),
static_cast
<
size_t
>
(
ps
.
i
-
first
),
true
))
transition
(
read_chars_after_escape
,
any_char
)
}
state
(
escape
)
{
// TODO: Add support for JSON's \uXXXX escaping.
transition
(
read_chars
,
"
\"\\
/bfnrt"
)
transition
(
read_chars
_after_escape
,
"
\"\\
/bfnrt"
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
...
...
@@ -168,17 +286,20 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
// clang-format on
}
void
read_member
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
Unescaper
>
void
read_member
(
ParserState
&
ps
,
Unescaper
unescaper
,
size_t
nesting_level
,
member_consumer
consumer
)
{
// clang-format off
start
();
state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
fsm_epsilon
(
read_json_string
(
ps
,
consumer
.
begin_key
()),
after_key
,
'"'
)
fsm_epsilon
(
read_json_string
(
ps
,
unescaper
,
consumer
.
begin_key
()),
after_key
,
'"'
)
}
state
(
after_key
)
{
transition
(
after_key
,
"
\t\n
"
)
fsm_transition
(
read_value
(
ps
,
nesting_level
,
consumer
.
begin_val
()),
fsm_transition
(
read_value
(
ps
,
unescaper
,
nesting_level
,
consumer
.
begin_val
()),
done
,
':'
)
}
term_state
(
done
)
{
...
...
@@ -188,8 +309,9 @@ void read_member(string_parser_state& ps, size_t nesting_level,
// clang-format on
}
void
read_json_object
(
string_parser_state
&
ps
,
size_t
nesting_level
,
obj_consumer
consumer
)
{
template
<
class
ParserState
,
class
Unescaper
>
void
read_json_object
(
ParserState
&
ps
,
Unescaper
unescaper
,
size_t
nesting_level
,
obj_consumer
consumer
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
ps
.
code
=
pec
::
nested_too_deeply
;
return
;
...
...
@@ -202,7 +324,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
}
state
(
has_open_brace
)
{
transition
(
has_open_brace
,
"
\t\n
"
)
fsm_epsilon
(
read_member
(
ps
,
nesting_level
+
1
,
consumer
.
begin_member
()),
fsm_epsilon
(
read_member
(
ps
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_member
()),
after_member
,
'"'
)
transition
(
done
,
'}'
)
}
...
...
@@ -213,7 +336,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
}
state
(
after_comma
)
{
transition
(
after_comma
,
"
\t\n
"
)
fsm_epsilon
(
read_member
(
ps
,
nesting_level
+
1
,
consumer
.
begin_member
()),
fsm_epsilon
(
read_member
(
ps
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_member
()),
after_member
,
'"'
)
}
term_state
(
done
)
{
...
...
@@ -223,7 +347,8 @@ void read_json_object(string_parser_state& ps, size_t nesting_level,
// clang-format on
}
void
read_json_array
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
Unescaper
>
void
read_json_array
(
ParserState
&
ps
,
Unescaper
unescaper
,
size_t
nesting_level
,
arr_consumer
consumer
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
ps
.
code
=
pec
::
nested_too_deeply
;
...
...
@@ -238,7 +363,8 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
state
(
has_open_brace
)
{
transition
(
has_open_brace
,
"
\t\n
"
)
transition
(
done
,
']'
)
fsm_epsilon
(
read_value
(
ps
,
nesting_level
+
1
,
consumer
.
begin_value
()),
fsm_epsilon
(
read_value
(
ps
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_value
()),
after_value
)
}
state
(
after_value
)
{
...
...
@@ -248,7 +374,8 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
}
state
(
after_comma
)
{
transition
(
after_comma
,
"
\t\n
"
)
fsm_epsilon
(
read_value
(
ps
,
nesting_level
+
1
,
consumer
.
begin_value
()),
fsm_epsilon
(
read_value
(
ps
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_value
()),
after_value
)
}
term_state
(
done
)
{
...
...
@@ -258,19 +385,22 @@ void read_json_array(string_parser_state& ps, size_t nesting_level,
// clang-format on
}
void
read_value
(
string_parser_state
&
ps
,
size_t
nesting_level
,
template
<
class
ParserState
,
class
Unescaper
>
void
read_value
(
ParserState
&
ps
,
Unescaper
unescaper
,
size_t
nesting_level
,
val_consumer
consumer
)
{
// clang-format off
start
();
state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
fsm_epsilon
(
read_json_string
(
ps
,
consumer
),
done
,
'"'
)
fsm_epsilon
(
read_json_string
(
ps
,
unescaper
,
consumer
),
done
,
'"'
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_json_null_or_nan
(
ps
,
consumer
),
done
,
"n"
)
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
,
unescaper
,
nesting_level
,
consumer
.
begin_object
()),
done
,
'{'
)
fsm_epsilon
(
read_json_array
(
ps
,
nesting_level
,
consumer
.
begin_array
()),
fsm_epsilon
(
read_json_array
(
ps
,
unescaper
,
nesting_level
,
consumer
.
begin_array
()),
done
,
'['
)
}
term_state
(
done
)
{
...
...
@@ -288,9 +418,10 @@ namespace caf::detail::json {
namespace
{
template
<
class
T
,
class
Allocator
>
void
init
(
std
::
vector
<
T
,
Allocator
>*
ptr
,
monotonic_buffer_resource
*
storage
)
{
new
(
ptr
)
std
::
vector
<
T
,
Allocator
>
(
Allocator
{
storage
});
template
<
class
T
>
void
init
(
linked_list
<
T
>*
ptr
,
monotonic_buffer_resource
*
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
*
)
{
...
...
@@ -305,6 +436,14 @@ T* make_impl(monotonic_buffer_resource* storage) {
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
value
*
make_value
(
monotonic_buffer_resource
*
storage
)
{
...
...
@@ -313,20 +452,53 @@ value* make_value(monotonic_buffer_resource* storage) {
array
*
make_array
(
monotonic_buffer_resource
*
storage
)
{
auto
result
=
make_impl
<
array
>
(
storage
);
result
->
reserve
(
16
);
return
result
;
}
object
*
make_object
(
monotonic_buffer_resource
*
storage
)
{
auto
result
=
make_impl
<
object
>
(
storage
);
result
->
reserve
(
16
);
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
)
{
regular_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
value
*
parse_shallow
(
string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
shallow_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
value
*
parse_in_situ
(
mutable_string_parser_state
&
ps
,
monotonic_buffer_resource
*
storage
)
{
in_situ_unescaper
unescaper
;
monotonic_buffer_resource
::
allocator
<
value
>
alloc
{
storage
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
0
,
{
storage
,
result
});
parser
::
read_value
(
ps
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
...
...
libcaf_core/src/init_global_meta_objects.cpp
View file @
50984474
...
...
@@ -21,6 +21,9 @@
#include "caf/ipv6_address.hpp"
#include "caf/ipv6_endpoint.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_id.hpp"
#include "caf/node_id.hpp"
...
...
libcaf_core/src/json_object.cpp
0 → 100644
View file @
50984474
// 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
();
}
}
// namespace caf
libcaf_core/src/json_reader.cpp
View file @
50984474
...
...
@@ -145,7 +145,7 @@ json_reader::~json_reader() {
bool
json_reader
::
load
(
std
::
string_view
json_text
)
{
reset
();
string_parser_state
ps
{
json_text
.
begin
(),
json_text
.
end
()};
root_
=
detail
::
json
::
parse
(
ps
,
&
buf_
);
root_
=
detail
::
json
::
parse
_shallow
(
ps
,
&
buf_
);
if
(
ps
.
code
!=
pec
::
success
)
{
set_error
(
make_error
(
ps
));
st_
=
nullptr
;
...
...
libcaf_core/src/json_value.cpp
0 → 100644
View file @
50984474
// 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"
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
{
make_error
(
ps
)};
}
else
{
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
}
}
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
{
make_error
(
ps
)};
}
else
{
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
}
}
expected
<
json_value
>
json_value
::
parse_in_situ
(
std
::
string
&
str
)
{
auto
storage
=
make_counted
<
detail
::
json
::
storage
>
();
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
{
make_error
(
ps
)};
}
else
{
return
{
json_value
{
root
,
std
::
move
(
storage
)}};
}
}
}
// namespace caf
libcaf_core/test/core-test.hpp
View file @
50984474
#pragma once
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/cow_vector.hpp"
#include "caf/fwd.hpp"
#include "caf/result.hpp"
...
...
@@ -13,6 +16,32 @@
#include <string>
#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 ----------------------------
using
float_actor
=
caf
::
typed_actor
<
caf
::
result
<
void
>
(
float
)
>
;
...
...
libcaf_core/test/detail/json.cpp
View file @
50984474
...
...
@@ -106,6 +106,12 @@ void stringify(std::string& str, size_t, detail::json::null_t) {
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
)
{
if
(
xs
.
empty
())
{
str
+=
"[]"
;
...
...
libcaf_core/test/json_array.cpp
0 → 100644
View file @
50984474
// 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
;
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
(
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
(
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
(
deep_copy
(
arr
),
arr
);
}
libcaf_core/test/json_object.cpp
0 → 100644
View file @
50984474
// 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
;
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
(
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
(
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
(
deep_copy
(
obj
),
obj
);
}
libcaf_core/test/json_value.cpp
0 → 100644
View file @
50984474
// 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
;
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
(
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
(
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
(
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
(
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
);
MESSAGE
(
deep_to_string
(
val
));
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
(
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
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from non-empty array"
)
{
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
(
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
(
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
(
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