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
e0b41065
Commit
e0b41065
authored
May 01, 2023
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Backport support for uint64_t in JSON parsing
parent
e4b1bb30
Changes
36
Show whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
3873 additions
and
277 deletions
+3873
-277
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+8
-0
libcaf_core/caf/detail/bounds_checker.hpp
libcaf_core/caf/detail/bounds_checker.hpp
+21
-19
libcaf_core/caf/detail/config_consumer.hpp
libcaf_core/caf/detail/config_consumer.hpp
+12
-4
libcaf_core/caf/detail/consumer.hpp
libcaf_core/caf/detail/consumer.hpp
+107
-2
libcaf_core/caf/detail/json.hpp
libcaf_core/caf/detail/json.hpp
+792
-16
libcaf_core/caf/detail/parser/read_floating_point.hpp
libcaf_core/caf/detail/parser/read_floating_point.hpp
+7
-7
libcaf_core/caf/detail/parser/read_number.hpp
libcaf_core/caf/detail/parser/read_number.hpp
+212
-99
libcaf_core/caf/detail/parser/read_number_or_timespan.hpp
libcaf_core/caf/detail/parser/read_number_or_timespan.hpp
+16
-9
libcaf_core/caf/detail/parser/read_timespan.hpp
libcaf_core/caf/detail/parser/read_timespan.hpp
+2
-2
libcaf_core/caf/detail/type_traits.hpp
libcaf_core/caf/detail/type_traits.hpp
+4
-0
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
+213
-0
libcaf_core/caf/json_writer.hpp
libcaf_core/caf/json_writer.hpp
+9
-6
libcaf_core/caf/parser_state.hpp
libcaf_core/caf/parser_state.hpp
+3
-1
libcaf_core/caf/pec.hpp
libcaf_core/caf/pec.hpp
+2
-0
libcaf_core/caf/type_id.hpp
libcaf_core/caf/type_id.hpp
+3
-0
libcaf_core/src/detail/json.cpp
libcaf_core/src/detail/json.cpp
+315
-37
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
+79
-36
libcaf_core/src/json_value.cpp
libcaf_core/src/json_value.cpp
+198
-0
libcaf_core/src/json_writer.cpp
libcaf_core/src/json_writer.cpp
+40
-35
libcaf_core/test/detail/json.cpp
libcaf_core/test/detail/json.cpp
+10
-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_reader.cpp
libcaf_core/test/json_reader.cpp
+2
-2
libcaf_core/test/json_value.cpp
libcaf_core/test/json_value.cpp
+322
-0
libcaf_core/test/json_writer.cpp
libcaf_core/test/json_writer.cpp
+31
-2
No files found.
libcaf_core/CMakeLists.txt
View file @
e0b41065
...
...
@@ -162,7 +162,11 @@ caf_add_component(
src/ipv6_address.cpp
src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp
src/json_array.cpp
src/json_builder.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
...
...
@@ -304,7 +308,11 @@ caf_add_component(
ipv6_address
ipv6_endpoint
ipv6_subnet
json_array
json_builder
json_object
json_reader
json_value
json_writer
load_inspector
logger
...
...
libcaf_core/caf/detail/bounds_checker.hpp
View file @
e0b41065
...
...
@@ -10,26 +10,28 @@
namespace
caf
::
detail
{
template
<
class
To
,
bool
LargeUnsigned
=
sizeof
(
To
)
>
=
sizeof
(
int64_t
)
&&
std
::
is_unsigned
<
To
>::
value
>
template
<
class
To
>
struct
bounds_checker
{
static
constexpr
bool
check
(
int64_t
x
)
noexcept
{
return
x
>=
std
::
numeric_limits
<
To
>::
min
()
&&
x
<=
std
::
numeric_limits
<
To
>::
max
();
}
};
template
<
>
struct
bounds_checker
<
int64_t
,
false
>
{
static
constexpr
bool
check
(
int64_t
)
noexcept
{
return
true
;
template
<
class
From
>
static
constexpr
bool
check
(
From
x
)
noexcept
{
auto
converted
=
static_cast
<
To
>
(
x
);
if
constexpr
(
std
::
is_signed_v
<
From
>
==
std
::
is_signed_v
<
To
>
)
{
// If the source and target types have the same signedness, we can simply
// check whether the value is in the range of the target type.
return
static_cast
<
From
>
(
converted
)
==
x
;
}
else
if
constexpr
(
std
::
is_signed_v
<
From
>
)
{
// If the source type is signed and the target type is unsigned, we need
// to check that the value is not negative. Otherwise, the conversion
// could yield a positive value, which is out of range for the target
// type.
return
x
>=
0
&&
static_cast
<
From
>
(
converted
)
==
x
;
}
else
{
static_assert
(
std
::
is_signed_v
<
To
>
);
// If the source type is unsigned and the target type is signed, we need
// to check whether the conversion produced a non-negative value. If the
// value is negative, the conversion is out of range.
return
converted
>=
0
&&
static_cast
<
From
>
(
converted
)
==
x
;
}
};
template
<
class
To
>
struct
bounds_checker
<
To
,
true
>
{
static
constexpr
bool
check
(
int64_t
x
)
noexcept
{
return
x
>=
0
;
}
};
...
...
libcaf_core/caf/detail/config_consumer.hpp
View file @
e0b41065
...
...
@@ -59,7 +59,7 @@ private:
// -- member variables -------------------------------------------------------
const
config_option_set
*
options_
=
nullptr
;
variant
<
none_t
,
config_consumer
*
,
config_list_consumer
*
,
std
::
variant
<
none_t
,
config_consumer
*
,
config_list_consumer
*
,
config_value_consumer
*>
parent_
;
};
...
...
@@ -102,8 +102,16 @@ public:
template
<
class
T
>
pec
value
(
T
&&
x
)
{
using
val_t
=
std
::
decay_t
<
T
>
;
if
constexpr
(
std
::
is_same_v
<
val_t
,
uint64_t
>
)
{
if
(
x
<=
INT64_MAX
)
return
value_impl
(
config_value
{
static_cast
<
int64_t
>
(
x
)});
else
return
pec
::
integer_overflow
;
}
else
{
return
value_impl
(
config_value
{
std
::
forward
<
T
>
(
x
)});
}
}
const
std
::
string
&
current_key
()
{
return
current_key_
;
...
...
@@ -119,7 +127,7 @@ private:
// -- member variables -------------------------------------------------------
const
config_option_set
*
options_
=
nullptr
;
variant
<
none_t
,
config_consumer
*
,
config_list_consumer
*>
parent_
;
std
::
variant
<
none_t
,
config_consumer
*
,
config_list_consumer
*>
parent_
;
settings
*
cfg_
=
nullptr
;
std
::
string
current_key_
;
std
::
string
category_
;
...
...
libcaf_core/caf/detail/consumer.hpp
View file @
e0b41065
...
...
@@ -4,10 +4,15 @@
#pragma once
#include "caf/pec.hpp"
#include <optional>
#include <type_traits>
#include <utility>
namespace
caf
::
detail
{
/// Consumes a single value.
template
<
class
T
>
class
consumer
{
public:
...
...
@@ -17,17 +22,117 @@ public:
// nop
}
void
value
(
T
&&
y
)
{
x_
=
std
::
move
(
y
);
template
<
class
U
>
void
value
(
U
&&
y
)
{
x_
=
std
::
forward
<
U
>
(
y
);
}
private:
T
&
x_
;
};
/// Specializes `consumer` for `int64_t` with a safe conversion from `uint64_t`.
template
<
>
class
consumer
<
int64_t
>
{
public:
using
value_type
=
int64_t
;
explicit
consumer
(
int64_t
&
x
)
:
x_
(
x
)
{
// nop
}
void
value
(
int64_t
y
)
noexcept
{
x_
=
y
;
}
pec
value
(
uint64_t
y
)
noexcept
{
if
(
y
<
INT64_MAX
)
{
value
(
static_cast
<
int64_t
>
(
y
));
return
pec
::
success
;
}
return
pec
::
integer_overflow
;
}
private:
int64_t
&
x_
;
};
/// Specializes `consumer` for `uint64_t` with a safe conversion from `int64_t`.
template
<
>
class
consumer
<
uint64_t
>
{
public:
using
value_type
=
uint64_t
;
explicit
consumer
(
uint64_t
&
x
)
:
x_
(
x
)
{
// nop
}
void
value
(
uint64_t
y
)
noexcept
{
x_
=
y
;
}
pec
value
(
int64_t
y
)
noexcept
{
if
(
y
>=
0
)
{
value
(
static_cast
<
uint64_t
>
(
y
));
return
pec
::
success
;
}
return
pec
::
integer_underflow
;
}
private:
uint64_t
&
x_
;
};
template
<
class
...
Ts
>
class
consumer
<
std
::
variant
<
Ts
...
>>
{
public:
explicit
consumer
(
std
::
variant
<
Ts
...
>&
x
)
:
x_
(
x
)
{
// nop
}
template
<
class
T
>
void
value
(
T
&&
y
)
{
x_
=
std
::
forward
<
T
>
(
y
);
}
private:
std
::
variant
<
Ts
...
>&
x_
;
};
template
<
class
T
>
class
consumer
<
std
::
optional
<
T
>>
{
public:
using
value_type
=
T
;
explicit
consumer
(
std
::
optional
<
T
>&
x
)
:
x_
(
x
)
{
// nop
}
template
<
class
U
>
void
value
(
U
&&
y
)
{
x_
=
std
::
forward
<
U
>
(
y
);
}
private:
std
::
optional
<
T
>&
x_
;
};
template
<
class
T
>
consumer
<
T
>
make_consumer
(
T
&
x
)
{
return
consumer
<
T
>
{
x
};
}
/// Applies a consumer to a value and updates the error code if necessary.
template
<
class
Consumer
,
class
T
>
static
void
apply_consumer
(
Consumer
&&
consumer
,
T
&&
value
,
pec
&
code
)
{
using
res_t
=
decltype
(
consumer
.
value
(
std
::
forward
<
T
>
(
value
)));
if
constexpr
(
std
::
is_same_v
<
res_t
,
void
>
)
{
consumer
.
value
(
std
::
forward
<
T
>
(
value
));
}
else
{
auto
res
=
consumer
.
value
(
std
::
forward
<
T
>
(
value
));
if
(
res
!=
pec
::
success
)
code
=
res
;
}
}
}
// namespace caf::detail
libcaf_core/caf/detail/json.hpp
View file @
e0b41065
...
...
@@ -4,13 +4,20 @@
#pragma once
#include <cstdint>
#include <variant>
#include <vector>
#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/string_view.hpp"
#include "caf/type_id.hpp"
#include <cstdint>
#include <cstring>
#include <iterator>
#include <new>
#include <variant>
// This JSON abstraction is designed to allocate its entire state in a monotonic
// buffer resource. This minimizes memory allocations and also enables us to
...
...
@@ -20,55 +27,824 @@
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
{};
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
string_view
realloc
(
string_view
str
,
monotonic_buffer_resource
*
res
);
inline
string_view
realloc
(
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
string_view
concat
(
std
::
initializer_list
<
string_view
>
xs
,
monotonic_buffer_resource
*
res
);
inline
string_view
concat
(
std
::
initializer_list
<
string_view
>
xs
,
const
storage_ptr
&
ptr
)
{
return
concat
(
xs
,
&
ptr
->
buf
);
}
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
{
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
,
string_view
,
array
,
objec
t
>
;
using
data_type
=
std
::
variant
<
null_t
,
int64_t
,
uint64_t
,
double
,
bool
,
string_view
,
array
,
object
,
undefined_
t
>
;
static
constexpr
size_t
null_index
=
0
;
static
constexpr
size_t
integer_index
=
1
;
static
constexpr
size_t
double
_index
=
2
;
static
constexpr
size_t
unsigned
_index
=
2
;
static
constexpr
size_t
bool
_index
=
3
;
static
constexpr
size_t
double
_index
=
3
;
static
constexpr
size_t
string
_index
=
4
;
static
constexpr
size_t
bool
_index
=
4
;
static
constexpr
size_t
array
_index
=
5
;
static
constexpr
size_t
string
_index
=
5
;
static
constexpr
size_t
object_index
=
6
;
static
constexpr
size_t
array_index
=
6
;
static
constexpr
size_t
object_index
=
7
;
static
constexpr
size_t
undefined_index
=
8
;
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_unsigned
()
const
noexcept
{
return
data
.
index
()
==
unsigned_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
(
string_view
str
,
monotonic_buffer_resource
*
res
)
{
data
=
realloc
(
str
,
res
);
}
void
assign_string
(
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
member
=
value
::
member
;
using
object
=
value
::
object
;
// -- 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
<
uint64_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
:
:
unsigned_index
:
if
(
!
sink
.
apply
(
std
::
get
<
uint64_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
<
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
<
uint64_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
:
:
unsigned_index
:
{
auto
tmp
=
uint64_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
=
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
=
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
);
// 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
,
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
:
:
unsigned_index
:
print
(
buf
,
std
::
get
<
uint64_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
<
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
libcaf_core/caf/detail/parser/read_floating_point.hpp
View file @
e0b41065
...
...
@@ -5,6 +5,7 @@
#pragma once
#include <cstdint>
#include <optional>
#include <type_traits>
#include "caf/config.hpp"
...
...
@@ -14,7 +15,6 @@
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
...
...
@@ -30,7 +30,7 @@ namespace caf::detail::parser {
/// the pre-decimal value.
template
<
class
State
,
class
Consumer
,
class
ValueType
>
void
read_floating_point
(
State
&
ps
,
Consumer
&&
consumer
,
optional
<
ValueType
>
start_value
,
std
::
optional
<
ValueType
>
start_value
,
bool
negative
=
false
)
{
// Any exponent larger than 511 always overflows.
static
constexpr
int
max_double_exponent
=
511
;
...
...
@@ -38,7 +38,7 @@ void read_floating_point(State& ps, Consumer&& consumer,
enum
sign_t
{
plus
,
minus
};
sign_t
sign
;
ValueType
result
;
if
(
start_value
==
non
e
)
{
if
(
!
start_valu
e
)
{
sign
=
plus
;
result
=
0
;
}
else
if
(
*
start_value
<
0
)
{
...
...
@@ -72,8 +72,8 @@ void read_floating_point(State& ps, Consumer&& consumer,
}
// 3) Scale result.
// Pre-computed powers of 10 for the scaling loop.
static
double
powerTable
[]
=
{
1e1
,
1e2
,
1e4
,
1e8
,
1e16
,
1e32
,
1e64
,
1e128
,
1e256
};
static
double
powerTable
[]
=
{
1e1
,
1e2
,
1e4
,
1e8
,
1e16
,
1e32
,
1e64
,
1e128
,
1e256
};
auto
i
=
0
;
if
(
exp
<
0
)
{
for
(
auto
n
=
-
exp
;
n
!=
0
;
n
>>=
1
,
++
i
)
...
...
@@ -97,7 +97,7 @@ void read_floating_point(State& ps, Consumer&& consumer,
// Definition of our parser FSM.
start
();
unstable_state
(
init
)
{
epsilon_if
(
start_value
==
non
e
,
regular_init
)
epsilon_if
(
!
start_valu
e
,
regular_init
)
epsilon
(
after_dec
,
"eE."
)
epsilon
(
after_dot
,
any_char
)
}
...
...
@@ -173,7 +173,7 @@ template <class State, class Consumer>
void
read_floating_point
(
State
&
ps
,
Consumer
&&
consumer
)
{
using
consumer_type
=
typename
std
::
decay
<
Consumer
>::
type
;
using
value_type
=
typename
consumer_type
::
value_type
;
return
read_floating_point
(
ps
,
consumer
,
optional
<
value_type
>
{});
return
read_floating_point
(
ps
,
consumer
,
std
::
optional
<
value_type
>
{});
}
}
// namespace caf::detail::parser
...
...
libcaf_core/caf/detail/parser/read_number.hpp
View file @
e0b41065
...
...
@@ -4,9 +4,6 @@
#pragma once
#include <cstdint>
#include <type_traits>
#include "caf/config.hpp"
#include "caf/detail/consumer.hpp"
#include "caf/detail/parser/add_ascii.hpp"
...
...
@@ -16,8 +13,12 @@
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/pec.hpp"
#include <cstdint>
#include <type_traits>
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
...
...
@@ -32,14 +33,12 @@ namespace caf::detail::parser {
/// foo = [1..2]
/// ~~~^
/// ~~~
template
<
class
State
,
class
Consumer
>
void
read_number_range
(
State
&
ps
,
Consumer
&
consumer
,
int64_t
begin
);
template
<
class
State
,
class
Consumer
,
class
ValueType
>
void
read_number_range
(
State
&
ps
,
Consumer
&
consumer
,
ValueType
begin
);
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template
<
class
State
,
class
Consumer
,
class
EnableFloat
=
std
::
true_type
,
class
EnableRange
=
std
::
false_type
>
void
read_number
(
State
&
ps
,
Consumer
&
consumer
,
EnableFloat
=
{},
void
read_n
egative_n
umber
(
State
&
ps
,
Consumer
&
consumer
,
EnableFloat
=
{},
EnableRange
=
{})
{
static
constexpr
bool
enable_float
=
EnableFloat
::
value
;
static
constexpr
bool
enable_range
=
EnableRange
::
value
;
...
...
@@ -48,35 +47,97 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
// Computes the result on success.
auto
g
=
caf
::
detail
::
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
consumer
.
value
(
result
);
apply_consumer
(
consumer
,
result
,
ps
.
code
);
});
using
odbl
=
optional
<
double
>
;
using
odbl
=
std
::
optional
<
double
>
;
// clang-format off
// Definition of our parser FSM.
start
();
// Initial state.
state
(
init
)
{
transition
(
init
,
"
\t
"
)
transition
(
has_plus
,
'+'
)
transition
(
has_minus
,
'-'
)
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
}),
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
}
,
true
),
done
,
'.'
,
g
.
disable
())
epsilon
(
has_plus
)
transition
(
neg_zero
,
'0'
)
epsilon
(
neg_dec
)
}
// Disambiguate base.
term_state
(
neg_zero
)
{
transition
(
start_neg_bin
,
"bB"
)
transition
(
start_neg_hex
,
"xX"
)
transition_static_if
(
enable_float
||
enable_range
,
neg_dot
,
'.'
)
epsilon
(
neg_oct
)
}
// Binary integers.
state
(
start_neg_bin
)
{
epsilon
(
neg_bin
)
}
term_state
(
neg_bin
)
{
transition
(
neg_bin
,
"01"
,
sub_ascii
<
2
>
(
result
,
ch
),
pec
::
integer_underflow
)
}
// Octal integers.
state
(
start_neg_oct
)
{
epsilon
(
neg_oct
)
}
term_state
(
neg_oct
)
{
transition
(
neg_oct
,
octal_chars
,
sub_ascii
<
8
>
(
result
,
ch
),
pec
::
integer_underflow
)
}
// Hexal integers.
state
(
start_neg_hex
)
{
epsilon
(
neg_hex
)
}
term_state
(
neg_hex
)
{
transition
(
neg_hex
,
hexadecimal_chars
,
sub_ascii
<
16
>
(
result
,
ch
),
pec
::
integer_underflow
)
}
// "+" or "-" alone aren't numbers.
state
(
has_plus
)
{
// Reads the integer part of the mantissa or a negative decimal integer.
term_state
(
neg_dec
)
{
transition
(
neg_dec
,
decimal_chars
,
sub_ascii
<
10
>
(
result
,
ch
),
pec
::
integer_underflow
)
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
}),
read_floating_point
(
ps
,
consumer
,
odbl
{
result
},
true
),
done
,
"eE"
,
g
.
disable
())
transition_static_if
(
enable_float
||
enable_range
,
neg_dot
,
'.'
)
}
unstable_state
(
neg_dot
)
{
fsm_transition_static_if
(
enable_range
,
read_number_range
(
ps
,
consumer
,
result
),
done
,
'.'
,
g
.
disable
())
transition
(
pos_zero
,
'0'
)
epsilon
(
pos_dec
)
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
result
},
true
),
done
,
any_char
,
g
.
disable
())
epsilon
(
done
)
}
state
(
has_minus
)
{
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
,
class
EnableFloat
=
std
::
true_type
,
class
EnableRange
=
std
::
false_type
>
void
read_positive_number
(
State
&
ps
,
Consumer
&
consumer
,
EnableFloat
=
{},
EnableRange
=
{})
{
static
constexpr
bool
enable_float
=
EnableFloat
::
value
;
static
constexpr
bool
enable_range
=
EnableRange
::
value
;
// Our result when reading an integer number.
uint64_t
result
=
0
;
// Computes the result on success.
auto
g
=
caf
::
detail
::
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
apply_consumer
(
consumer
,
result
,
ps
.
code
);
});
using
odbl
=
std
::
optional
<
double
>
;
// clang-format off
start
();
// Initial state.
state
(
init
)
{
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
}
,
true
),
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
}),
done
,
'.'
,
g
.
disable
())
transition
(
neg
_zero
,
'0'
)
epsilon
(
neg
_dec
)
transition
(
pos
_zero
,
'0'
)
epsilon
(
pos
_dec
)
}
// Disambiguate base.
term_state
(
pos_zero
)
{
...
...
@@ -85,12 +146,6 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
transition_static_if
(
enable_float
||
enable_range
,
pos_dot
,
'.'
)
epsilon
(
pos_oct
)
}
term_state
(
neg_zero
)
{
transition
(
start_neg_bin
,
"bB"
)
transition
(
start_neg_hex
,
"xX"
)
transition_static_if
(
enable_float
||
enable_range
,
neg_dot
,
'.'
)
epsilon
(
neg_oct
)
}
// Binary integers.
state
(
start_pos_bin
)
{
epsilon
(
pos_bin
)
...
...
@@ -98,12 +153,6 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
term_state
(
pos_bin
)
{
transition
(
pos_bin
,
"01"
,
add_ascii
<
2
>
(
result
,
ch
),
pec
::
integer_overflow
)
}
state
(
start_neg_bin
)
{
epsilon
(
neg_bin
)
}
term_state
(
neg_bin
)
{
transition
(
neg_bin
,
"01"
,
sub_ascii
<
2
>
(
result
,
ch
),
pec
::
integer_underflow
)
}
// Octal integers.
state
(
start_pos_oct
)
{
epsilon
(
pos_oct
)
...
...
@@ -112,13 +161,6 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
transition
(
pos_oct
,
octal_chars
,
add_ascii
<
8
>
(
result
,
ch
),
pec
::
integer_overflow
)
}
state
(
start_neg_oct
)
{
epsilon
(
neg_oct
)
}
term_state
(
neg_oct
)
{
transition
(
neg_oct
,
octal_chars
,
sub_ascii
<
8
>
(
result
,
ch
),
pec
::
integer_underflow
)
}
// Hexal integers.
state
(
start_pos_hex
)
{
epsilon
(
pos_hex
)
...
...
@@ -127,13 +169,6 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
transition
(
pos_hex
,
hexadecimal_chars
,
add_ascii
<
16
>
(
result
,
ch
),
pec
::
integer_overflow
)
}
state
(
start_neg_hex
)
{
epsilon
(
neg_hex
)
}
term_state
(
neg_hex
)
{
transition
(
neg_hex
,
hexadecimal_chars
,
sub_ascii
<
16
>
(
result
,
ch
),
pec
::
integer_underflow
)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state
(
pos_dec
)
{
transition
(
pos_dec
,
decimal_chars
,
add_ascii
<
10
>
(
result
,
ch
),
...
...
@@ -144,14 +179,6 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
transition_static_if
(
enable_float
||
enable_range
,
pos_dot
,
'.'
)
}
// Reads the integer part of the mantissa or a negative decimal integer.
term_state
(
neg_dec
)
{
transition
(
neg_dec
,
decimal_chars
,
sub_ascii
<
10
>
(
result
,
ch
),
pec
::
integer_underflow
)
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
result
},
true
),
done
,
"eE"
,
g
.
disable
())
transition_static_if
(
enable_float
||
enable_range
,
neg_dot
,
'.'
)
}
unstable_state
(
pos_dot
)
{
fsm_transition_static_if
(
enable_range
,
read_number_range
(
ps
,
consumer
,
result
),
...
...
@@ -161,14 +188,34 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
done
,
any_char
,
g
.
disable
())
epsilon
(
done
)
}
unstable_state
(
neg_dot
)
{
fsm_transition_static_if
(
enable_range
,
read_number_range
(
ps
,
consumer
,
result
),
done
,
'.'
,
g
.
disable
())
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
/// Reads a number, i.e., on success produces an `int64_t`, an `uint64_t` or a
/// `double`.
template
<
class
State
,
class
Consumer
,
class
EnableFloat
=
std
::
true_type
,
class
EnableRange
=
std
::
false_type
>
void
read_number
(
State
&
ps
,
Consumer
&
consumer
,
EnableFloat
fl_token
=
{},
EnableRange
rng_token
=
{})
{
static
constexpr
bool
enable_float
=
EnableFloat
::
value
;
using
odbl
=
std
::
optional
<
double
>
;
// clang-format off
// Definition of our parser FSM.
start
();
state
(
init
)
{
transition
(
init
,
"
\t
"
)
fsm_transition
(
read_positive_number
(
ps
,
consumer
,
fl_token
,
rng_token
),
done
,
'+'
)
fsm_transition
(
read_negative_number
(
ps
,
consumer
,
fl_token
,
rng_token
),
done
,
'-'
)
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
result
},
true
),
done
,
any_char
,
g
.
disable
()
)
epsilon
(
done
)
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
}
),
done
,
'.'
)
fsm_epsilon
(
read_positive_number
(
ps
,
consumer
,
fl_token
,
rng_token
),
done
)
}
term_state
(
done
)
{
// nop
...
...
@@ -177,45 +224,111 @@ void read_number(State& ps, Consumer& consumer, EnableFloat = {},
// clang-format on
}
template
<
class
State
,
class
Consumer
>
void
read_number_range
(
State
&
ps
,
Consumer
&
consumer
,
int64_t
begin
)
{
optional
<
int64_t
>
end
;
optional
<
int64_t
>
step
;
auto
end_consumer
=
make_consumer
(
end
);
auto
step_consumer
=
make_consumer
(
step
);
auto
generate_2
=
[
&
](
int64_t
n
,
int64_t
m
)
{
if
(
n
<=
m
)
while
(
n
<=
m
)
consumer
.
value
(
n
++
);
else
while
(
n
>=
m
)
consumer
.
value
(
n
--
);
/// Generates a range of numbers and calls `consumer` for each value.
template
<
class
Consumer
,
class
T
>
void
generate_range_impl
(
pec
&
code
,
Consumer
&
consumer
,
T
min_val
,
T
max_val
,
std
::
optional
<
int64_t
>
step
)
{
auto
do_apply
=
[
&
](
T
x
)
{
using
consumer_result_type
=
decltype
(
consumer
.
value
(
x
));
if
constexpr
(
std
::
is_same_v
<
consumer_result_type
,
pec
>
)
{
auto
res
=
consumer
.
value
(
x
);
if
(
res
==
pec
::
success
)
return
true
;
code
=
res
;
return
false
;
}
else
{
static_assert
(
std
::
is_same_v
<
consumer_result_type
,
void
>
);
consumer
.
value
(
x
);
return
true
;
}
};
auto
generate_3
=
[
&
](
int64_t
n
,
int64_t
m
,
int64_t
s
)
{
if
(
n
==
m
)
{
consumer
.
value
(
n
);
if
(
min_val
==
max_val
)
{
do_apply
(
min_val
);
return
;
}
if
(
s
==
0
||
(
n
>
m
&&
s
>
0
)
||
(
n
<
m
&&
s
<
0
))
{
ps
.
code
=
pec
::
invalid_range_expression
;
if
(
min_val
<
max_val
)
{
auto
step_val
=
step
.
value_or
(
1
);
if
(
step_val
<=
0
)
{
code
=
pec
::
invalid_range_expression
;
return
;
}
if
(
n
<=
m
)
for
(
auto
i
=
n
;
i
<=
m
;
i
+=
s
)
consumer
.
value
(
i
);
auto
s
=
static_cast
<
T
>
(
step_val
);
auto
i
=
min_val
;
while
(
i
<
max_val
)
{
if
(
!
do_apply
(
i
))
return
;
if
(
max_val
-
i
<
s
)
// protect against overflows
return
;
i
+=
s
;
}
if
(
i
==
max_val
)
do_apply
(
i
);
return
;
}
auto
step_val
=
step
.
value_or
(
-
1
);
if
(
step_val
>=
0
)
{
code
=
pec
::
invalid_range_expression
;
return
;
}
auto
s
=
static_cast
<
T
>
(
-
step_val
);
auto
i
=
min_val
;
while
(
i
>
max_val
)
{
if
(
!
do_apply
(
i
))
return
;
if
(
i
-
max_val
<
s
)
// protect against underflows
return
;
i
-=
s
;
}
if
(
i
==
max_val
)
do_apply
(
i
);
}
/// Generates a range of numbers and calls `consumer` for each value.
template
<
class
Consumer
,
class
MinValueT
,
class
MaxValueT
>
void
generate_range
(
pec
&
code
,
Consumer
&
consumer
,
MinValueT
min_val
,
MaxValueT
max_val
,
std
::
optional
<
int64_t
>
step
)
{
static_assert
(
is_64bit_integer_v
<
MinValueT
>
);
static_assert
(
is_64bit_integer_v
<
MaxValueT
>
);
// Check whether any of the two types is signed. If so, we'll use signed
// integers for the range.
if
constexpr
(
std
::
is_signed_v
<
MinValueT
>
==
std
::
is_signed_v
<
MaxValueT
>
)
{
generate_range_impl
(
code
,
consumer
,
min_val
,
max_val
,
step
);
}
else
if
constexpr
(
std
::
is_signed_v
<
MinValueT
>
)
{
if
(
max_val
>
INT64_MAX
)
code
=
pec
::
integer_overflow
;
else
for
(
auto
i
=
n
;
i
>=
m
;
i
+=
s
)
consumer
.
value
(
i
);
};
generate_range_impl
(
code
,
consumer
,
min_val
,
static_cast
<
int64_t
>
(
max_val
),
step
);
}
else
{
if
(
min_val
>
INT64_MAX
)
code
=
pec
::
integer_overflow
;
else
generate_range_impl
(
code
,
consumer
,
static_cast
<
int64_t
>
(
min_val
),
max_val
,
step
);
}
}
template
<
class
State
,
class
Consumer
,
class
ValueType
>
void
read_number_range
(
State
&
ps
,
Consumer
&
consumer
,
ValueType
begin
)
{
// Our final value (inclusive). We don't know yet whether we're dealing with
// a signed or unsigned range.
std
::
variant
<
none_t
,
int64_t
,
uint64_t
>
end
;
// Note: The step value is always signed, even if the range is unsigned. For
// example, [10..2..-2] is a valid range description for the unsigned values
// [10, 8, 6, 4, 2].
std
::
optional
<
int64_t
>
step
;
auto
end_consumer
=
make_consumer
(
end
);
auto
step_consumer
=
make_consumer
(
step
);
auto
g
=
caf
::
detail
::
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
{
if
(
!
end
)
{
auto
fn
=
[
&
](
auto
end_val
)
{
if
constexpr
(
std
::
is_same_v
<
decltype
(
end_val
),
none_t
>
)
{
ps
.
code
=
pec
::
invalid_range_expression
;
}
else
if
(
!
step
)
{
generate_2
(
begin
,
*
end
);
}
else
{
generate_3
(
begin
,
*
end
,
*
step
);
generate_range
(
ps
.
code
,
consumer
,
begin
,
end_val
,
step
);
}
};
std
::
visit
(
fn
,
end
);
}
});
static
constexpr
std
::
false_type
no_float
=
std
::
false_type
{};
...
...
libcaf_core/caf/detail/parser/read_number_or_timespan.hpp
View file @
e0b41065
...
...
@@ -15,10 +15,8 @@
#include "caf/detail/parser/read_timespan.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
...
...
@@ -35,34 +33,43 @@ void read_number_or_timespan(State& ps, Consumer& consumer,
struct
interim_consumer
{
size_t
invocations
=
0
;
Consumer
*
outer
=
nullptr
;
variant
<
none_t
,
int64_t
,
double
>
interim
;
std
::
variant
<
none_t
,
int64_t
,
double
>
interim
;
void
value
(
int64_t
x
)
{
// If we see a second integer, we have a range of integers and forward all
// calls to the outer consumer.
switch
(
++
invocations
)
{
case
1
:
interim
=
x
;
break
;
case
2
:
CAF_ASSERT
(
holds_alternative
<
int64_t
>
(
interim
));
outer
->
value
(
get
<
int64_t
>
(
interim
));
CAF_ASSERT
(
std
::
holds_alternative
<
int64_t
>
(
interim
));
outer
->
value
(
std
::
get
<
int64_t
>
(
interim
));
interim
=
none
;
[[
fallthrough
]];
default:
outer
->
value
(
x
);
}
}
pec
value
(
uint64_t
x
)
{
if
(
x
<=
INT64_MAX
)
{
value
(
static_cast
<
int64_t
>
(
x
));
return
pec
::
success
;
}
return
pec
::
integer_overflow
;
}
void
value
(
double
x
)
{
interim
=
x
;
}
};
interim_consumer
ic
;
ic
.
outer
=
&
consumer
;
auto
has_int
=
[
&
]
{
return
holds_alternative
<
int64_t
>
(
ic
.
interim
);
};
auto
has_dbl
=
[
&
]
{
return
holds_alternative
<
double
>
(
ic
.
interim
);
};
auto
get_int
=
[
&
]
{
return
get
<
int64_t
>
(
ic
.
interim
);
};
auto
has_int
=
[
&
]
{
return
std
::
holds_alternative
<
int64_t
>
(
ic
.
interim
);
};
auto
has_dbl
=
[
&
]
{
return
std
::
holds_alternative
<
double
>
(
ic
.
interim
);
};
auto
get_int
=
[
&
]
{
return
std
::
get
<
int64_t
>
(
ic
.
interim
);
};
auto
g
=
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
{
if
(
has_dbl
())
consumer
.
value
(
get
<
double
>
(
ic
.
interim
));
consumer
.
value
(
std
::
get
<
double
>
(
ic
.
interim
));
else
if
(
has_int
())
consumer
.
value
(
get_int
());
}
...
...
libcaf_core/caf/detail/parser/read_timespan.hpp
View file @
e0b41065
...
...
@@ -6,12 +6,12 @@
#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include "caf/config.hpp"
#include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
#include "caf/timestamp.hpp"
...
...
@@ -24,7 +24,7 @@ namespace caf::detail::parser {
/// Reads a timespan.
template
<
class
State
,
class
Consumer
>
void
read_timespan
(
State
&
ps
,
Consumer
&&
consumer
,
optional
<
int64_t
>
num
=
none
)
{
std
::
optional
<
int64_t
>
num
=
std
::
nullopt
)
{
using
namespace
std
::
chrono
;
struct
interim_consumer
{
using
value_type
=
int64_t
;
...
...
libcaf_core/caf/detail/type_traits.hpp
View file @
e0b41065
...
...
@@ -1050,6 +1050,10 @@ struct unboxed_oracle<std::optional<T>> {
template
<
class
T
>
using
unboxed_t
=
typename
unboxed_oracle
<
T
>::
type
;
template
<
class
T
>
constexpr
bool
is_64bit_integer_v
=
std
::
is_same_v
<
T
,
int64_t
>
||
std
::
is_same_v
<
T
,
uint64_t
>
;
}
// namespace caf::detail
#undef CAF_HAS_MEMBER_TRAIT
...
...
libcaf_core/caf/fwd.hpp
View file @
e0b41065
...
...
@@ -115,6 +115,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 @
e0b41065
// 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 @
e0b41065
// 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 @
e0b41065
// 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
]]
string_view
field_type_suffix
()
const
noexcept
{
return
field_type_suffix_
;
}
/// Configures whether the writer omits empty fields.
void
field_type_suffix
(
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
,
string_view
name
)
override
;
bool
end_object
()
override
;
bool
begin_field
(
string_view
)
override
;
bool
begin_field
(
string_view
name
,
bool
is_present
)
override
;
bool
begin_field
(
string_view
name
,
span
<
const
type_id_t
>
types
,
size_t
index
)
override
;
bool
begin_field
(
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
(
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
(
string_view
x
)
override
;
bool
value
(
const
std
::
u16string
&
x
)
override
;
bool
value
(
const
std
::
u32string
&
x
)
override
;
bool
value
(
span
<
const
byte
>
x
)
override
;
private:
// -- implementation details -------------------------------------------------
template
<
class
T
>
bool
number
(
T
);
using
key_type
=
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
;
string_view
field_type_suffix_
=
json_writer
::
field_type_suffix_default
;
};
}
// namespace caf
libcaf_core/caf/json_object.hpp
0 → 100644
View file @
e0b41065
// 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
<
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
;
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
(
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 @
e0b41065
...
...
@@ -133,6 +133,17 @@ public:
/// @note Implicitly calls `reset`.
bool
load
(
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`.
/// @post The reader is ready for attempting to deserialize another
/// inspectable object.
...
...
libcaf_core/caf/json_value.hpp
0 → 100644
View file @
e0b41065
// 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 "caf/string_view.hpp"
#include <string>
namespace
caf
{
/// Represents an immutable JSON value.
class
CAF_CORE_EXPORT
json_value
{
public:
// -- constructors, destructors, and assignment operators --------------------
/// Creates a @c null JSON value.
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 --------------------------------------------------------------
/// Creates an undefined JSON value. This special state usually indicates that
/// a previous key lookup failed.
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
;
/// Checks whether the value is an @c uint64_t.
bool
is_unsigned
()
const
noexcept
;
/// 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, a @c uint64_t
/// or a @c double.
bool
is_number
()
const
noexcept
;
/// 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 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 -------------------------------------------------------------
/// Converts the value to an @c int64_t or returns @p fallback if the value
/// is not a valid number.
int64_t
to_integer
(
int64_t
fallback
=
0
)
const
;
/// Converts the value to an @c uint64_t or returns @p fallback if the value
/// is not a valid number.
uint64_t
to_unsigned
(
uint64_t
fallback
=
0
)
const
;
/// Converts the value to a @c double or returns @p fallback if the value is
/// not a valid number.
double
to_double
(
double
fallback
=
0.0
)
const
;
/// Converts the value to a @c bool or returns @p fallback if the value is
/// not a valid boolean.
bool
to_bool
(
bool
fallback
=
false
)
const
;
/// Returns the value as a JSON string or an empty string if the value is not
/// a string.
string_view
to_string
()
const
;
/// Returns the value as a JSON string or @p fallback if the value is not a
/// string.
string_view
to_string
(
string_view
fallback
)
const
;
/// Returns the value as a JSON array or an empty array if the value is not
/// an array.
json_array
to_array
()
const
;
/// Returns the value as a JSON array or @p fallback if the value is not an
/// array.
json_array
to_array
(
json_array
fallback
)
const
;
/// Returns the value as a JSON object or an empty object if the value is not
/// an object.
json_object
to_object
()
const
;
/// Returns the value as a JSON object or @p fallback if the value is not an
/// object.
json_object
to_object
(
json_object
fallback
)
const
;
// -- comparison -------------------------------------------------------------
/// Compares two JSON values for equality.
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
(
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
(
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 ---------------------------------------------------------------
/// Prints the JSON value to @p buf.
template
<
class
Buffer
>
void
print_to
(
Buffer
&
buf
,
size_t
indentation_factor
=
0
)
const
{
detail
::
json
::
print_to
(
buf
,
*
val_
,
indentation_factor
);
}
// -- serialization ----------------------------------------------------------
/// Applies the `Inspector` to the JSON value `val`.
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 @
e0b41065
...
...
@@ -210,7 +210,7 @@ private:
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
();
// Enters a new level of nesting.
...
...
@@ -261,11 +261,6 @@ private:
// followed by a newline.
void
sep
();
// Closes a nested structure like lists or objects. Traces back to see if only
// whitespaces were added between the open character and the current position.
// If so, compress the output to produce "[]" instead of "[\n \n]".
void
close_nested
(
char
open
,
char
close
);
// -- member variables -------------------------------------------------------
// The current level of indentation.
...
...
@@ -305,4 +300,12 @@ private:
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
libcaf_core/caf/parser_state.hpp
View file @
e0b41065
...
...
@@ -6,16 +6,18 @@
#include <cctype>
#include <cstdint>
#include <string_view>
#include "caf/fwd.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
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
;
...
...
libcaf_core/caf/pec.hpp
View file @
e0b41065
...
...
@@ -66,6 +66,8 @@ enum class pec : uint8_t {
/// Stopped after running into an invalid parser state. Should never happen
/// and most likely indicates a bug in the implementation.
invalid_state
,
/// Parser stopped after exceeding its maximum supported level of nesting.
nested_too_deeply
,
};
/// @relates pec
...
...
libcaf_core/caf/type_id.hpp
View file @
e0b41065
...
...
@@ -417,6 +417,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
))
...
...
libcaf_core/src/detail/json.cpp
View file @
e0b41065
...
...
@@ -4,8 +4,11 @@
#include "caf/detail/json.hpp"
#include <cstring>
#include <iterator>
#include <memory>
#include <numeric>
#include <streambuf>
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
...
...
@@ -14,11 +17,128 @@
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
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
;
}
caf
::
string_view
as_str_view
(
const
char
*
first
,
const
char
*
last
)
{
return
{
first
,
static_cast
<
size_t
>
(
last
-
first
)};
}
struct
regular_unescaper
{
caf
::
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
caf
::
string_view
{
str_buf
,
len
};
}
auto
unescaped_size
=
do_unescape
(
first
,
last
,
str_buf
);
return
caf
::
string_view
{
str_buf
,
unescaped_size
};
}
};
struct
shallow_unescaper
{
caf
::
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
caf
::
string_view
{
str_buf
,
unescaped_size
};
}
};
struct
in_situ_unescaper
{
caf
::
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
caf
::
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
caf
::
detail
::
parser
{
struct
obj_consumer
;
...
...
@@ -31,6 +151,7 @@ struct val_consumer {
template
<
class
T
>
void
value
(
T
x
)
{
if
constexpr
(
std
::
is_same_v
<
T
,
uint64_t
>
)
puts
(
"GOT AN uint64_t"
);
ptr
->
data
=
x
;
}
...
...
@@ -40,6 +161,7 @@ struct val_consumer {
};
struct
key_consumer
{
monotonic_buffer_resource
*
storage
;
string_view
*
ptr
;
void
value
(
string_view
str
)
{
...
...
@@ -52,7 +174,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
()
{
...
...
@@ -65,8 +187,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
};
}
};
...
...
@@ -74,29 +196,25 @@ 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
,
val_consumer
consumer
);
template
<
class
Consumer
>
void
read_json_null_or_nan
(
string_parser_state
&
ps
,
Consumer
consumer
)
{
template
<
class
ParserState
,
class
Consumer
>
void
read_json_null_or_nan
(
ParserState
&
ps
,
Consumer
consumer
)
{
enum
{
nil
,
is_null
,
is_nan
};
auto
res_type
=
nil
;
auto
g
=
make_scope_guard
([
&
]
{
...
...
@@ -128,15 +246,22 @@ void read_json_null_or_nan(string_parser_state& ps, Consumer consumer) {
transition
(
done
,
'n'
,
res_type
=
is_nan
)
}
term_state
(
done
)
{
transition
(
init
,
"
\t\n
"
)
transition
(
done
,
"
\t\n
"
)
}
fin
();
// clang-format on
}
template
<
class
Consumer
>
void
read_json_string
(
string_parser_state
&
ps
,
Consumer
consumer
)
{
auto
first
=
string_view
::
iterator
{};
// If we have an iterator into a contiguous memory block, we simply store the
// iterator position and use the escaper to decide whether we make regular,
// 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
start
();
state
(
init
)
{
...
...
@@ -145,12 +270,52 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
}
state
(
read_chars
)
{
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
,
consumer
.
value
(
string_view
{
first
,
ps
.
i
}
))
transition
(
done
,
'"'
,
assign_value
(
escaper
,
consumer
,
first
,
ps
.
i
,
false
))
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
)
{
// TODO: Add support for JSON's \uXXXX escaping.
transition
(
read_chars_after_escape
,
"
\"\\
/bfnrtv"
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
}
fin
();
// clang-format on
}
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
,
"
\"\\
/bfnrt"
)
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
"
)
...
...
@@ -159,16 +324,23 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
// clang-format on
}
void
read_member
(
string_parser_state
&
ps
,
member_consumer
consumer
)
{
template
<
class
ParserState
,
class
ScratchSpace
,
class
Unescaper
>
void
read_member
(
ParserState
&
ps
,
ScratchSpace
&
scratch_space
,
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
,
scratch_space
,
unescaper
,
consumer
.
begin_key
()),
after_key
,
'"'
)
}
state
(
after_key
)
{
transition
(
after_key
,
"
\t\n
"
)
fsm_transition
(
read_value
(
ps
,
consumer
.
begin_val
()),
done
,
':'
)
fsm_transition
(
read_value
(
ps
,
scratch_space
,
unescaper
,
nesting_level
,
consumer
.
begin_val
()),
done
,
':'
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
...
...
@@ -177,7 +349,14 @@ void read_member(string_parser_state& ps, member_consumer consumer) {
// clang-format on
}
void
read_json_object
(
string_parser_state
&
ps
,
obj_consumer
consumer
)
{
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
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
ps
.
code
=
pec
::
nested_too_deeply
;
return
;
}
// clang-format off
start
();
state
(
init
)
{
...
...
@@ -186,7 +365,9 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
}
state
(
has_open_brace
)
{
transition
(
has_open_brace
,
"
\t\n
"
)
fsm_epsilon
(
read_member
(
ps
,
consumer
.
begin_member
()),
after_member
,
'"'
)
fsm_epsilon
(
read_member
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_member
()),
after_member
,
'"'
)
transition
(
done
,
'}'
)
}
state
(
after_member
)
{
...
...
@@ -196,7 +377,9 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
}
state
(
after_comma
)
{
transition
(
after_comma
,
"
\t\n
"
)
fsm_epsilon
(
read_member
(
ps
,
consumer
.
begin_member
()),
after_member
,
'"'
)
fsm_epsilon
(
read_member
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_member
()),
after_member
,
'"'
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
...
...
@@ -205,7 +388,14 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
// clang-format on
}
void
read_json_array
(
string_parser_state
&
ps
,
arr_consumer
consumer
)
{
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
)
{
if
(
nesting_level
>=
max_nesting_level
)
{
ps
.
code
=
pec
::
nested_too_deeply
;
return
;
}
// clang-format off
start
();
state
(
init
)
{
...
...
@@ -215,7 +405,9 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
state
(
has_open_brace
)
{
transition
(
has_open_brace
,
"
\t\n
"
)
transition
(
done
,
']'
)
fsm_epsilon
(
read_value
(
ps
,
consumer
.
begin_value
()),
after_value
)
fsm_epsilon
(
read_value
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_value
()),
after_value
)
}
state
(
after_value
)
{
transition
(
after_value
,
"
\t\n
"
)
...
...
@@ -224,7 +416,9 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
}
state
(
after_comma
)
{
transition
(
after_comma
,
"
\t\n
"
)
fsm_epsilon
(
read_value
(
ps
,
consumer
.
begin_value
()),
after_value
)
fsm_epsilon
(
read_value
(
ps
,
scratch_space
,
unescaper
,
nesting_level
+
1
,
consumer
.
begin_value
()),
after_value
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
...
...
@@ -233,17 +427,25 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
// clang-format on
}
void
read_value
(
string_parser_state
&
ps
,
val_consumer
consumer
)
{
template
<
class
ParserState
,
class
ScratchSpace
,
class
Unescaper
>
void
read_value
(
ParserState
&
ps
,
ScratchSpace
&
scratch_space
,
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
,
scratch_space
,
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
,
consumer
.
begin_object
()),
done
,
'{'
)
fsm_epsilon
(
read_json_array
(
ps
,
consumer
.
begin_array
()),
done
,
'['
)
fsm_epsilon
(
read_json_object
(
ps
,
scratch_space
,
unescaper
,
nesting_level
,
consumer
.
begin_object
()),
done
,
'{'
)
fsm_epsilon
(
read_json_array
(
ps
,
scratch_space
,
unescaper
,
nesting_level
,
consumer
.
begin_array
()),
done
,
'['
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
...
...
@@ -260,9 +462,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
*
)
{
...
...
@@ -277,28 +480,103 @@ 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
string_view
realloc
(
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
string_view
{
buf
,
str
.
size
()};
}
string_view
concat
(
std
::
initializer_list
<
string_view
>
xs
,
monotonic_buffer_resource
*
res
)
{
auto
get_size
=
[](
size_t
x
,
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
string_view
{
buf
,
total_size
};
}
value
*
make_value
(
monotonic_buffer_resource
*
storage
)
{
return
make_impl
<
value
>
(
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
)
{
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
};
auto
result
=
new
(
alloc
.
allocate
(
1
))
value
();
parser
::
read_value
(
ps
,
{
storage
,
result
});
parser
::
read_value
(
ps
,
scratch_space
,
unescaper
,
0
,
{
storage
,
result
});
return
result
;
}
...
...
libcaf_core/src/init_global_meta_objects.cpp
View file @
e0b41065
...
...
@@ -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_array.cpp
0 → 100644
View file @
e0b41065
// 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 @
e0b41065
// 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
,
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
(
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
(
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
(
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
(
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
(
byte
x
)
{
return
number
(
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
(
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
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
=
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 @
e0b41065
// 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
(
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 @
e0b41065
...
...
@@ -8,6 +8,8 @@
#include "caf/detail/print.hpp"
#include "caf/string_algorithms.hpp"
#include <fstream>
namespace
{
static
constexpr
const
char
class_name
[]
=
"caf::json_reader"
;
...
...
@@ -50,23 +52,24 @@ std::string type_clash(caf::json_reader::position want,
std
::
string
type_clash
(
caf
::
string_view
want
,
const
caf
::
detail
::
json
::
value
&
got
)
{
using
namespace
caf
::
literals
;
using
namespace
std
::
literals
;
using
caf
::
detail
::
json
::
value
;
switch
(
got
.
data
.
index
())
{
case
value
:
:
integer_index
:
return
type_clash
(
want
,
"json::integer"
_sv
);
case
value
:
:
unsigned_index
:
return
type_clash
(
want
,
"json::integer"
sv
);
case
value
:
:
double_index
:
return
type_clash
(
want
,
"json::real"
_
sv
);
return
type_clash
(
want
,
"json::real"
sv
);
case
value
:
:
bool_index
:
return
type_clash
(
want
,
"json::boolean"
_
sv
);
return
type_clash
(
want
,
"json::boolean"
sv
);
case
value
:
:
string_index
:
return
type_clash
(
want
,
"json::string"
_
sv
);
return
type_clash
(
want
,
"json::string"
sv
);
case
value
:
:
array_index
:
return
type_clash
(
want
,
"json::array"
_
sv
);
return
type_clash
(
want
,
"json::array"
sv
);
case
value
:
:
object_index
:
return
type_clash
(
want
,
"json::object"
_
sv
);
return
type_clash
(
want
,
"json::object"
sv
);
default:
return
type_clash
(
want
,
"json::null"
_
sv
);
return
type_clash
(
want
,
"json::null"
sv
);
}
}
...
...
@@ -145,19 +148,41 @@ json_reader::~json_reader() {
bool
json_reader
::
load
(
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
;
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
;
}
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_
);
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
;
}
void
json_reader
::
revert
()
{
...
...
@@ -166,6 +191,7 @@ void json_reader::revert() {
err_
.
reset
();
st_
->
clear
();
st_
->
emplace_back
(
root_
);
field_
.
clear
();
}
}
...
...
@@ -173,6 +199,7 @@ void json_reader::reset() {
buf_
.
reclaim
();
st_
=
nullptr
;
err_
.
reset
();
field_
.
clear
();
}
// -- interface functions ------------------------------------------------------
...
...
@@ -199,7 +226,7 @@ bool json_reader::fetch_next_object_name(string_view& type_name) {
FN_DECL
;
return
consume
<
false
>
(
fn
,
[
this
,
&
type_name
](
const
detail
::
json
::
value
&
val
)
{
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
object_index
)
{
auto
&
obj
=
get
<
detail
::
json
::
object
>
(
val
.
data
);
auto
&
obj
=
std
::
get
<
detail
::
json
::
object
>
(
val
.
data
);
if
(
auto
mem_ptr
=
find_member
(
&
obj
,
"@type"
))
{
if
(
mem_ptr
->
val
->
data
.
index
()
==
detail
::
json
::
value
::
string_index
)
{
type_name
=
std
::
get
<
string_view
>
(
mem_ptr
->
val
->
data
);
...
...
@@ -227,7 +254,7 @@ bool json_reader::begin_object(type_id_t, string_view) {
FN_DECL
;
return
consume
<
false
>
(
fn
,
[
this
](
const
detail
::
json
::
value
&
val
)
{
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
object_index
)
{
push
(
&
get
<
detail
::
json
::
object
>
(
val
.
data
));
push
(
&
std
::
get
<
detail
::
json
::
object
>
(
val
.
data
));
return
true
;
}
else
{
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
current_field_name
(),
...
...
@@ -260,8 +287,8 @@ bool json_reader::end_object() {
bool
json_reader
::
begin_field
(
string_view
name
)
{
SCOPE
(
position
::
object
);
if
(
auto
member
=
find_member
(
top
<
position
::
object
>
(),
name
))
{
field_
.
push_back
(
name
);
if
(
auto
member
=
find_member
(
top
<
position
::
object
>
(),
name
))
{
push
(
member
->
val
);
return
true
;
}
else
{
...
...
@@ -273,10 +300,10 @@ bool json_reader::begin_field(string_view name) {
bool
json_reader
::
begin_field
(
string_view
name
,
bool
&
is_present
)
{
SCOPE
(
position
::
object
);
field_
.
push_back
(
name
);
if
(
auto
member
=
find_member
(
top
<
position
::
object
>
(),
name
);
member
!=
nullptr
&&
member
->
val
->
data
.
index
()
!=
detail
::
json
::
value
::
null_index
)
{
field_
.
push_back
(
name
);
push
(
member
->
val
);
is_present
=
true
;
}
else
{
...
...
@@ -285,8 +312,8 @@ bool json_reader::begin_field(string_view name, bool& is_present) {
return
true
;
}
bool
json_reader
::
begin_field
(
string_view
name
,
span
<
const
type_id_t
>
types
,
size_t
&
index
)
{
bool
json_reader
::
begin_field
(
string_view
name
,
s
pan
<
const
type_id_t
>
types
,
s
ize_t
&
index
)
{
bool
is_present
=
false
;
if
(
begin_field
(
name
,
is_present
,
types
,
index
))
{
if
(
is_present
)
{
...
...
@@ -304,6 +331,7 @@ bool json_reader::begin_field(string_view name, span<const type_id_t> types,
bool
json_reader
::
begin_field
(
string_view
name
,
bool
&
is_present
,
span
<
const
type_id_t
>
types
,
size_t
&
index
)
{
SCOPE
(
position
::
object
);
field_
.
push_back
(
name
);
if
(
auto
member
=
find_member
(
top
<
position
::
object
>
(),
name
);
member
!=
nullptr
&&
member
->
val
->
data
.
index
()
!=
detail
::
json
::
value
::
null_index
)
{
...
...
@@ -312,7 +340,6 @@ bool json_reader::begin_field(string_view name, bool& is_present,
if
(
auto
i
=
std
::
find
(
types
.
begin
(),
types
.
end
(),
id
);
i
!=
types
.
end
())
{
index
=
static_cast
<
size_t
>
(
std
::
distance
(
types
.
begin
(),
i
));
field_
.
push_back
(
name
);
push
(
member
->
val
);
is_present
=
true
;
return
true
;
...
...
@@ -327,6 +354,7 @@ bool json_reader::end_field() {
SCOPE
(
position
::
object
);
// Note: no pop() here, because the value(s) were already consumed. Only
// update the field_ for debugging.
if
(
!
field_
.
empty
())
field_
.
pop_back
();
return
true
;
}
...
...
@@ -379,7 +407,7 @@ bool json_reader::begin_sequence(size_t& size) {
FN_DECL
;
return
consume
<
false
>
(
fn
,
[
this
,
&
size
](
const
detail
::
json
::
value
&
val
)
{
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
array_index
)
{
auto
&
ls
=
get
<
detail
::
json
::
array
>
(
val
.
data
);
auto
&
ls
=
std
::
get
<
detail
::
json
::
array
>
(
val
.
data
);
size
=
ls
.
size
();
push
(
sequence
{
ls
.
begin
(),
ls
.
end
()});
return
true
;
...
...
@@ -410,7 +438,7 @@ bool json_reader::begin_associative_array(size_t& size) {
FN_DECL
;
return
consume
<
false
>
(
fn
,
[
this
,
&
size
](
const
detail
::
json
::
value
&
val
)
{
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
object_index
)
{
auto
*
obj
=
std
::
addressof
(
get
<
detail
::
json
::
object
>
(
val
.
data
));
auto
*
obj
=
std
::
addressof
(
std
::
get
<
detail
::
json
::
object
>
(
val
.
data
));
pop
();
size
=
obj
->
size
();
push
(
members
{
obj
->
begin
(),
obj
->
end
()});
...
...
@@ -507,11 +535,19 @@ bool json_reader::integer(T& x) {
if
(
detail
::
bounds_checker
<
T
>::
check
(
i64
))
{
x
=
static_cast
<
T
>
(
i64
);
return
true
;
}
else
{
}
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
"
integer out of bounds"
);
"signed
integer out of bounds"
);
return
false
;
}
else
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
unsigned_index
)
{
auto
u64
=
std
::
get
<
uint64_t
>
(
val
.
data
);
if
(
detail
::
bounds_checker
<
T
>::
check
(
u64
))
{
x
=
static_cast
<
T
>
(
u64
);
return
true
;
}
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
"unsigned integer out of bounds"
);
return
false
;
}
else
{
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
current_field_name
(),
type_clash
(
"json::integer"
,
val
));
...
...
@@ -565,10 +601,17 @@ bool json_reader::value(float& x) {
bool
json_reader
::
value
(
double
&
x
)
{
FN_DECL
;
return
consume
<
true
>
(
fn
,
[
this
,
&
x
](
const
detail
::
json
::
value
&
val
)
{
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
double_index
)
{
switch
(
val
.
data
.
index
())
{
case
detail
:
:
json
::
value
::
double_index
:
x
=
std
::
get
<
double
>
(
val
.
data
);
return
true
;
}
else
{
case
detail
:
:
json
::
value
::
integer_index
:
x
=
std
::
get
<
int64_t
>
(
val
.
data
);
return
true
;
case
detail
:
:
json
::
value
::
unsigned_index
:
x
=
std
::
get
<
uint64_t
>
(
val
.
data
);
return
true
;
default:
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
current_field_name
(),
type_clash
(
"json::real"
,
val
));
return
false
;
...
...
libcaf_core/src/json_value.cpp
0 → 100644
View file @
e0b41065
// 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 <cstdint>
#include <fstream>
namespace
caf
{
// -- properties ---------------------------------------------------------------
bool
json_value
::
is_integer
()
const
noexcept
{
return
val_
->
is_integer
()
||
(
val_
->
is_unsigned
()
&&
std
::
get
<
uint64_t
>
(
val_
->
data
)
<=
INT64_MAX
);
}
bool
json_value
::
is_unsigned
()
const
noexcept
{
return
val_
->
is_unsigned
()
||
(
val_
->
is_integer
()
&&
std
::
get
<
int64_t
>
(
val_
->
data
)
>=
0
);
}
bool
json_value
::
is_number
()
const
noexcept
{
switch
(
val_
->
data
.
index
())
{
default:
return
false
;
case
detail
:
:
json
::
value
::
integer_index
:
case
detail
:
:
json
::
value
::
unsigned_index
:
case
detail
:
:
json
::
value
::
double_index
:
return
true
;
}
}
// -- conversion ---------------------------------------------------------------
int64_t
json_value
::
to_integer
(
int64_t
fallback
)
const
{
switch
(
val_
->
data
.
index
())
{
default:
return
fallback
;
case
detail
:
:
json
::
value
::
integer_index
:
return
std
::
get
<
int64_t
>
(
val_
->
data
);
case
detail
:
:
json
::
value
::
unsigned_index
:
{
auto
val
=
std
::
get
<
uint64_t
>
(
val_
->
data
);
if
(
val
<=
INT64_MAX
)
return
static_cast
<
int64_t
>
(
val
);
return
fallback
;
}
case
detail
:
:
json
::
value
::
double_index
:
return
static_cast
<
int64_t
>
(
std
::
get
<
double
>
(
val_
->
data
));
}
}
uint64_t
json_value
::
to_unsigned
(
uint64_t
fallback
)
const
{
switch
(
val_
->
data
.
index
())
{
default:
return
fallback
;
case
detail
:
:
json
::
value
::
integer_index
:
{
auto
val
=
std
::
get
<
int64_t
>
(
val_
->
data
);
if
(
val
>=
0
)
return
static_cast
<
uint64_t
>
(
val
);
return
fallback
;
}
case
detail
:
:
json
::
value
::
unsigned_index
:
return
std
::
get
<
uint64_t
>
(
val_
->
data
);
case
detail
:
:
json
::
value
::
double_index
:
return
static_cast
<
int64_t
>
(
std
::
get
<
double
>
(
val_
->
data
));
}
}
double
json_value
::
to_double
(
double
fallback
)
const
{
switch
(
val_
->
data
.
index
())
{
default:
return
fallback
;
case
detail
:
:
json
::
value
::
integer_index
:
return
static_cast
<
double
>
(
std
::
get
<
int64_t
>
(
val_
->
data
));
case
detail
:
:
json
::
value
::
unsigned_index
:
return
static_cast
<
double
>
(
std
::
get
<
uint64_t
>
(
val_
->
data
));
case
detail
:
:
json
::
value
::
double_index
:
return
std
::
get
<
double
>
(
val_
->
data
);
};
}
bool
json_value
::
to_bool
(
bool
fallback
)
const
{
if
(
is_bool
())
{
return
std
::
get
<
bool
>
(
val_
->
data
);
}
return
fallback
;
}
string_view
json_value
::
to_string
()
const
{
return
to_string
(
string_view
{});
}
string_view
json_value
::
to_string
(
string_view
fallback
)
const
{
if
(
is_string
())
{
return
std
::
get
<
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
(
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
(
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 @
e0b41065
...
...
@@ -7,24 +7,21 @@
#include "caf/detail/append_hex.hpp"
#include "caf/detail/print.hpp"
#include <cctype>
namespace
caf
{
namespace
{
static
constexpr
const
char
class_name
[]
=
"caf::json_writer"
;
constexpr
bool
can_morph
(
json_writer
::
type
from
,
json_writer
::
type
to
)
{
return
from
==
json_writer
::
type
::
element
&&
to
!=
json_writer
::
type
::
member
;
}
constexpr
const
char
*
json_type_names
[]
=
{
"element"
,
"object"
,
"member"
,
constexpr
std
::
string_view
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
)
{
auto
not_ws
=
[](
char
c
)
{
return
!
std
::
isspace
(
c
);
};
auto
last
=
buf
.
rend
();
auto
i
=
std
::
find_if
(
buf
.
rbegin
(),
last
,
not_ws
);
return
(
i
!=
last
)
?
*
i
:
'\0'
;
}
}
// namespace
...
...
@@ -99,7 +96,7 @@ bool json_writer::begin_object(type_id_t id, string_view name) {
pop
();
return
true
;
};
if
(
inside_object
()
||
skip_object_type_annotation_
)
if
(
skip_object_type_annotation_
||
inside_object
()
)
return
begin_associative_array
(
0
);
else
return
begin_associative_array
(
0
)
// Put opening paren, ...
...
...
@@ -135,7 +132,7 @@ bool json_writer::begin_field(string_view name, bool is_present) {
return
true
;
default:
{
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
));
return
false
;
}
...
...
@@ -157,8 +154,8 @@ bool json_writer::begin_field(string_view name, bool is_present) {
}
}
bool
json_writer
::
begin_field
(
string_view
name
,
span
<
const
type_id_t
>
types
,
size_t
index
)
{
bool
json_writer
::
begin_field
(
string_view
name
,
s
pan
<
const
type_id_t
>
types
,
s
ize_t
index
)
{
if
(
index
>=
types
.
size
())
{
emplace_error
(
sec
::
runtime_error
,
"index >= types.size()"
);
return
false
;
...
...
@@ -217,7 +214,7 @@ bool json_writer::begin_key_value_pair() {
return
true
;
default:
{
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
));
return
false
;
}
...
...
@@ -249,7 +246,14 @@ bool json_writer::begin_sequence(size_t) {
bool
json_writer
::
end_sequence
()
{
if
(
pop_if
(
type
::
array
))
{
--
indentation_level_
;
close_nested
(
'['
,
']'
);
// Check whether the array was empty and compress the output in that case.
if
(
last_non_ws_char
(
buf_
)
==
'['
)
{
while
(
std
::
isspace
(
buf_
.
back
()))
buf_
.
pop_back
();
}
else
{
nl
();
}
add
(
']'
);
return
true
;
}
else
{
return
false
;
...
...
@@ -279,7 +283,14 @@ bool json_writer::begin_associative_array(size_t) {
bool
json_writer
::
end_associative_array
()
{
if
(
pop_if
(
type
::
object
))
{
--
indentation_level_
;
close_nested
(
'{'
,
'}'
);
// Check whether the array was empty and compress the output in that case.
if
(
last_non_ws_char
(
buf_
)
==
'{'
)
{
while
(
std
::
isspace
(
buf_
.
back
()))
buf_
.
pop_back
();
}
else
{
nl
();
}
add
(
'}'
);
if
(
!
stack_
.
empty
())
stack_
.
back
().
filled
=
true
;
return
true
;
...
...
@@ -470,12 +481,12 @@ bool json_writer::pop_if(type t) {
return
true
;
}
else
{
std
::
string
str
=
"pop_if failed: expected "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
if
(
stack_
.
empty
())
{
str
+=
", found an empty stack"
;
}
else
{
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
));
return
false
;
...
...
@@ -490,13 +501,13 @@ bool json_writer::pop_if_next(type t) {
return
true
;
}
else
{
std
::
string
str
=
"pop_if_next failed: expected "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
if
(
stack_
.
size
()
<
2
)
{
str
+=
", found a stack of size "
;
detail
::
print
(
str
,
stack_
.
size
());
}
else
{
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
));
return
false
;
...
...
@@ -517,9 +528,9 @@ bool json_writer::morph(type t, type& prev) {
return
true
;
}
else
{
std
::
string
str
=
"cannot convert "
;
str
+=
json_type_name
(
stack_
.
back
().
t
);
str
+=
as_
json_type_name
(
stack_
.
back
().
t
);
str
+=
" to "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
...
...
@@ -536,7 +547,7 @@ void json_writer::unsafe_morph(type t) {
void
json_writer
::
fail
(
type
t
)
{
std
::
string
str
=
"failed to write a "
;
str
+=
json_type_name
(
t
);
str
+=
as_
json_type_name
(
t
);
str
+=
": invalid position (begin/end mismatch?)"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
}
...
...
@@ -570,16 +581,10 @@ void json_writer::sep() {
}
}
void
json_writer
::
close_nested
(
char
open
,
char
close
)
{
auto
not_ws
=
[](
char
c
)
{
return
!
std
::
isspace
(
c
);
};
auto
i
=
std
::
find_if
(
buf_
.
rbegin
(),
buf_
.
rend
(),
not_ws
);
if
(
*
i
==
open
)
{
while
(
std
::
isspace
(
buf_
.
back
()))
buf_
.
pop_back
();
}
else
{
nl
();
}
add
(
close
);
// -- free functions -----------------------------------------------------------
std
::
string_view
as_json_type_name
(
json_writer
::
type
t
)
{
return
json_type_names
[
static_cast
<
uint8_t
>
(
t
)];
}
}
// namespace caf
libcaf_core/test/detail/json.cpp
View file @
e0b41065
...
...
@@ -85,6 +85,10 @@ void stringify(std::string& str, size_t, int64_t val) {
str
+=
std
::
to_string
(
val
);
}
void
stringify
(
std
::
string
&
str
,
size_t
,
uint64_t
val
)
{
str
+=
std
::
to_string
(
val
);
}
void
stringify
(
std
::
string
&
str
,
size_t
,
double
val
)
{
str
+=
std
::
to_string
(
val
);
}
...
...
@@ -106,6 +110,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 @
e0b41065
// 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 @
e0b41065
// 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
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
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 @
e0b41065
// 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
<
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_reader.cpp
View file @
e0b41065
...
...
@@ -113,7 +113,7 @@ fixture::fixture() {
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
json_reader_tests
,
fixture
)
BEGIN_FIXTURE_SCOPE
(
fixture
)
CAF_TEST
(
json
baselines
)
{
size_t
baseline_index
=
0
;
...
...
@@ -179,4 +179,4 @@ SCENARIO("mappers enable custom type names in JSON input") {
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
END_FIXTURE_SCOPE
()
libcaf_core/test/json_value.cpp
0 → 100644
View file @
e0b41065
// 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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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 negative integer"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"-1"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
val
.
is_integer
());
CHECK
(
!
val
.
is_unsigned
());
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
(),
-
1
);
CHECK_EQ
(
val
.
to_unsigned
(),
0u
);
CHECK_EQ
(
val
.
to_double
(),
-
1.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
),
"-1"
);
CHECK_EQ
(
printed
(
val
),
"-1"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from small integer"
)
{
// A small integer can be represented as both int64_t and uint64_t.
auto
val
=
unbox
(
json_value
::
parse
(
"42"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
val
.
is_integer
());
CHECK
(
val
.
is_unsigned
());
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_unsigned
(),
42u
);
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 UINT64_MAX"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
"18446744073709551615"
));
CHECK
(
!
val
.
is_null
());
CHECK
(
!
val
.
is_undefined
());
CHECK
(
!
val
.
is_integer
());
CHECK
(
val
.
is_unsigned
());
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_unsigned
(),
UINT64_MAX
);
CHECK_EQ
(
val
.
to_double
(),
static_cast
<
double
>
(
UINT64_MAX
));
CHECK_EQ
(
val
.
to_bool
(),
false
);
CHECK_EQ
(
val
.
to_string
(),
""
sv
);
CHECK_EQ
(
val
.
to_object
().
size
(),
0u
);
CHECK_EQ
(
to_string
(
val
),
"18446744073709551615"
);
CHECK_EQ
(
printed
(
val
),
"18446744073709551615"
);
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_unsigned
(),
42u
);
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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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_unsigned
(),
0u
);
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
);
}
libcaf_core/test/json_writer.cpp
View file @
e0b41065
...
...
@@ -38,7 +38,7 @@ struct fixture {
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
json_writer_tests
,
fixture
)
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"the JSON writer converts builtin types to strings"
)
{
GIVEN
(
"an integer"
)
{
...
...
@@ -314,4 +314,33 @@ SCENARIO("the JSON writer annotates variant fields") {
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
SCENARIO
(
"the JSON compresses empty lists and objects"
)
{
GIVEN
(
"a map with an empty list value"
)
{
std
::
map
<
std
::
string
,
std
::
vector
<
int
>>
obj
;
obj
[
"xs"
]
=
std
::
vector
<
int
>
{};
obj
[
"ys"
]
=
std
::
vector
<
int
>
{
1
,
2
,
3
};
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON contains a compressed representation of the empty list"
)
{
std
::
string
out
=
R"({
"xs": [],
"ys": [
1,
2,
3
]
})"
;
CHECK_EQ
(
to_json_string
(
obj
,
2
,
true
,
true
),
out
);
}
}
}
GIVEN
(
"an empty map"
)
{
std
::
map
<
std
::
string
,
std
::
vector
<
int
>>
obj
;
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON contains a compressed representation of the empty map"
)
{
CHECK_EQ
(
to_json_string
(
obj
,
2
,
true
,
true
),
"{}"
s
);
}
}
}
}
END_FIXTURE_SCOPE
()
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