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
cad4baef
Commit
cad4baef
authored
May 01, 2023
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/neverlord/json-unsigned'
parents
6bbcebf8
7271c19d
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
690 additions
and
173 deletions
+690
-173
CHANGELOG.md
CHANGELOG.md
+15
-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
+9
-1
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
+35
-15
libcaf_core/caf/detail/parser/read_number.hpp
libcaf_core/caf/detail/parser/read_number.hpp
+211
-98
libcaf_core/caf/detail/parser/read_number_or_timespan.hpp
libcaf_core/caf/detail/parser/read_number_or_timespan.hpp
+9
-0
libcaf_core/caf/detail/type_traits.hpp
libcaf_core/caf/detail/type_traits.hpp
+4
-0
libcaf_core/caf/json_value.hpp
libcaf_core/caf/json_value.hpp
+35
-7
libcaf_core/src/detail/parse.cpp
libcaf_core/src/detail/parse.cpp
+0
-1
libcaf_core/src/json_reader.cpp
libcaf_core/src/json_reader.cpp
+27
-14
libcaf_core/src/json_value.cpp
libcaf_core/src/json_value.cpp
+64
-12
libcaf_core/test/detail/bounds_checker.cpp
libcaf_core/test/detail/bounds_checker.cpp
+4
-2
libcaf_core/test/detail/json.cpp
libcaf_core/test/detail/json.cpp
+4
-0
libcaf_core/test/detail/parser/read_number.cpp
libcaf_core/test/detail/parser/read_number.cpp
+13
-0
libcaf_core/test/json_reader.cpp
libcaf_core/test/json_reader.cpp
+51
-1
libcaf_core/test/json_value.cpp
libcaf_core/test/json_value.cpp
+81
-1
No files found.
CHANGELOG.md
View file @
cad4baef
...
...
@@ -5,6 +5,21 @@ is based on [Keep a Changelog](https://keepachangelog.com).
## [Unreleased]
### Added
-
The class
`json_value`
can now hold unsigned 64-bit integer values. This
allows it to store values that would otherwise overflow a signed integer.
Values that can be represented in both integer types will return
`true`
for
`is_integer()`
as well as for the new
`is_unsigned()`
function. Users can
obtain the stored value as
`uint64_t`
via
`to_unsigned()`
.
### Changed
-
With the addition of the unsigned type to
`json_value`
, there is now a new
edge case where
`is_number()`
returns
`true`
but neither
`is_integer()`
nor
`is_double()`
return
`true`
: integer values larger than
`INT64_MAX`
will only
return true for
`is_unsigned()`
.
### Fixed
-
Fix flow setup for servers that use
`web_socket::with`
. This bug caused
...
...
libcaf_core/caf/detail/bounds_checker.hpp
View file @
cad4baef
...
...
@@ -10,26 +10,28 @@
namespace
caf
::
detail
{
template
<
class
To
,
bool
LargeUnsigned
=
sizeof
(
To
)
>
=
sizeof
(
int64_t
)
&&
std
::
is_unsigned
<
To
>::
value
>
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
To
>
struct
bounds_checker
<
To
,
true
>
{
static
constexpr
bool
check
(
int64_t
x
)
noexcept
{
return
x
>=
0
;
struct
bounds_checker
{
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
;
}
}
};
...
...
libcaf_core/caf/detail/config_consumer.hpp
View file @
cad4baef
...
...
@@ -102,7 +102,15 @@ public:
template
<
class
T
>
pec
value
(
T
&&
x
)
{
return
value_impl
(
config_value
{
std
::
forward
<
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
()
{
...
...
libcaf_core/caf/detail/consumer.hpp
View file @
cad4baef
...
...
@@ -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 @
cad4baef
...
...
@@ -333,24 +333,26 @@ public:
using
object_allocator
=
object
::
allocator_type
;
using
data_type
=
std
::
variant
<
null_t
,
int64_t
,
double
,
bool
,
using
data_type
=
std
::
variant
<
null_t
,
int64_t
,
uint64_t
,
double
,
bool
,
std
::
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
undefined_index
=
7
;
static
constexpr
size_t
object_index
=
7
;
static
constexpr
size_t
undefined_index
=
8
;
data_type
data
;
...
...
@@ -362,6 +364,10 @@ public:
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
;
}
...
...
@@ -470,10 +476,10 @@ bool save(Serializer& sink, const value& val) {
if
(
!
sink
.
begin_object
(
type_id_v
<
json_value
>
,
type_name_v
<
json_value
>
))
return
false
;
// Maps our type indexes to their public API counterpart.
type_id_t
mapping
[]
=
{
type_id_v
<
unit_t
>
,
type_id_v
<
int64_t
>
,
type_id_v
<
double
>
,
type_id_v
<
bool
>
,
type_id_v
<
std
::
string
>
,
type_id_v
<
json_array
>
,
type_id_v
<
json_object
>
,
type_id_v
<
none_t
>
};
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
))
...
...
@@ -484,6 +490,10 @@ bool save(Serializer& sink, const value& val) {
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
;
...
...
@@ -552,10 +562,10 @@ bool load(Deserializer& source, value& val, monotonic_buffer_resource* res) {
if
(
!
source
.
begin_object
(
type_id_v
<
json_value
>
,
type_name_v
<
json_value
>
))
return
false
;
// Maps our type indexes to their public API counterpart.
type_id_t
mapping
[]
=
{
type_id_v
<
unit_t
>
,
type_id_v
<
int64_t
>
,
type_id_v
<
double
>
,
type_id_v
<
bool
>
,
type_id_v
<
std
::
string
>
,
type_id_v
<
json_array
>
,
type_id_v
<
json_object
>
,
type_id_v
<
none_t
>
};
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
))
...
...
@@ -572,6 +582,13 @@ bool load(Deserializer& source, value& val, monotonic_buffer_resource* res) {
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
))
...
...
@@ -738,6 +755,9 @@ void print_to(Buffer& buf, const value& val, size_t indentation_factor,
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
;
...
...
libcaf_core/caf/detail/parser/read_number.hpp
View file @
cad4baef
This diff is collapsed.
Click to expand it.
libcaf_core/caf/detail/parser/read_number_or_timespan.hpp
View file @
cad4baef
...
...
@@ -35,6 +35,8 @@ void read_number_or_timespan(State& ps, Consumer& consumer,
Consumer
*
outer
=
nullptr
;
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
;
...
...
@@ -48,6 +50,13 @@ void read_number_or_timespan(State& ps, Consumer& consumer,
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
;
}
...
...
libcaf_core/caf/detail/type_traits.hpp
View file @
cad4baef
...
...
@@ -1056,6 +1056,10 @@ constexpr bool is_string_or_cstring_v
=
std
::
is_convertible_v
<
T
,
const
char
*>
||
std
::
is_same_v
<
std
::
string
,
std
::
decay_t
<
T
>>
;
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/json_value.hpp
View file @
cad4baef
...
...
@@ -19,6 +19,7 @@ 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
}
...
...
@@ -39,6 +40,8 @@ public:
// -- 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
};
}
...
...
@@ -57,19 +60,19 @@ public:
}
/// Checks whether the value is an @c int64_t.
bool
is_integer
()
const
noexcept
{
return
val_
->
is_integer
();
}
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 or a @c double.
bool
is_number
()
const
noexcept
{
return
is_integer
()
||
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
{
...
...
@@ -93,26 +96,49 @@ public:
// -- 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.
std
::
string_view
to_string
()
const
;
/// Returns the value as a JSON string or @p fallback if the value is not a
/// string.
std
::
string_view
to_string
(
std
::
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 ----------------------------------------------------------------
...
...
@@ -143,6 +169,7 @@ public:
// -- 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
);
...
...
@@ -150,6 +177,7 @@ public:
// -- 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
)
{
...
...
libcaf_core/src/detail/parse.cpp
View file @
cad4baef
...
...
@@ -12,7 +12,6 @@
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/read_ipv4_address.hpp"
#include "caf/detail/parser/read_ipv6_address.hpp"
#include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/detail/parser/read_timespan.hpp"
#include "caf/detail/parser/read_unsigned_integer.hpp"
...
...
libcaf_core/src/json_reader.cpp
View file @
cad4baef
...
...
@@ -56,6 +56,7 @@ std::string type_clash(std::string_view want,
using
caf
::
detail
::
json
::
value
;
switch
(
got
.
data
.
index
())
{
case
value
:
:
integer_index
:
case
value
:
:
unsigned_index
:
return
type_clash
(
want
,
"json::integer"
sv
);
case
value
:
:
double_index
:
return
type_clash
(
want
,
"json::real"
sv
);
...
...
@@ -534,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"
);
return
false
;
}
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
"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
,
"integer out of bounds"
);
return
false
;
}
else
{
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
current_field_name
(),
type_clash
(
"json::integer"
,
val
));
...
...
@@ -592,16 +601,20 @@ 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
)
{
x
=
std
::
get
<
double
>
(
val
.
data
);
return
true
;
}
else
if
(
val
.
data
.
index
()
==
detail
::
json
::
value
::
integer_index
)
{
x
=
std
::
get
<
int64_t
>
(
val
.
data
);
return
true
;
}
else
{
emplace_error
(
sec
::
runtime_error
,
class_name
,
fn
,
current_field_name
(),
type_clash
(
"json::real"
,
val
));
return
false
;
switch
(
val
.
data
.
index
())
{
case
detail
:
:
json
::
value
::
double_index
:
x
=
std
::
get
<
double
>
(
val
.
data
);
return
true
;
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
View file @
cad4baef
...
...
@@ -10,30 +10,82 @@
#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
{
if
(
is_integer
())
{
return
std
::
get
<
int64_t
>
(
val_
->
data
);
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
));
}
if
(
is_double
())
{
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
));
}
return
fallback
;
}
double
json_value
::
to_double
(
double
fallback
)
const
{
if
(
is_double
())
{
return
std
::
get
<
double
>
(
val_
->
data
);
}
if
(
is_integer
())
{
return
static_cast
<
double
>
(
std
::
get
<
int64_t
>
(
val_
->
data
));
}
return
fallback
;
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
{
...
...
libcaf_core/test/detail/bounds_checker.cpp
View file @
cad4baef
...
...
@@ -10,8 +10,8 @@
namespace
{
template
<
class
T
>
bool
check
(
int64_t
x
)
{
template
<
class
T
,
class
U
>
bool
check
(
U
x
)
{
return
caf
::
detail
::
bounds_checker
<
T
>::
check
(
x
);
}
...
...
@@ -39,5 +39,7 @@ CAF_TEST(small integers) {
CAF_TEST
(
large
unsigned
integers
)
{
CHECK_EQ
(
check
<
uint64_t
>
(
-
1
),
false
);
CHECK_EQ
(
check
<
uint64_t
>
(
0
),
true
);
CHECK_EQ
(
check
<
uint64_t
>
(
0u
),
true
);
CHECK_EQ
(
check
<
uint64_t
>
(
std
::
numeric_limits
<
int64_t
>::
max
()),
true
);
CHECK_EQ
(
check
<
uint64_t
>
(
std
::
numeric_limits
<
uint64_t
>::
max
()),
true
);
}
libcaf_core/test/detail/json.cpp
View file @
cad4baef
...
...
@@ -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
);
}
...
...
libcaf_core/test/detail/parser/read_number.cpp
View file @
cad4baef
...
...
@@ -30,6 +30,12 @@ struct number_consumer {
void
value
(
int64_t
y
)
{
x
=
y
;
}
void
value
(
uint64_t
y
)
{
if
(
y
<
INT64_MAX
)
value
(
static_cast
<
int64_t
>
(
y
));
else
CAF_FAIL
(
"number consumer called with a uint64_t larger than INT64_MAX"
);
}
};
struct
range_consumer
{
...
...
@@ -40,6 +46,12 @@ struct range_consumer {
void
value
(
int64_t
y
)
{
xs
.
emplace_back
(
y
);
}
void
value
(
uint64_t
y
)
{
if
(
y
<
INT64_MAX
)
value
(
static_cast
<
int64_t
>
(
y
));
else
CAF_FAIL
(
"range consumer called with a uint64_t larger than INT64_MAX"
);
}
};
struct
res_t
{
...
...
@@ -328,6 +340,7 @@ CAF_TEST(ranges can use signed integers) {
CAF_TEST
(
the
parser
rejects
invalid
step
values
)
{
CHECK_ERR
(
"-2..+2..-2"
,
pec
::
invalid_range_expression
);
CHECK_ERR
(
"+2..-2..+2"
,
pec
::
invalid_range_expression
);
CHECK_ERR
(
"+2..-2..0"
,
pec
::
invalid_range_expression
);
}
END_FIXTURE_SCOPE
()
libcaf_core/test/json_reader.cpp
View file @
cad4baef
...
...
@@ -17,6 +17,7 @@ using namespace std::literals;
namespace
{
struct
fixture
{
// Adds a test case for a given input and expected output.
template
<
class
T
>
void
add_test_case
(
std
::
string_view
input
,
T
val
)
{
auto
f
=
[
this
,
input
,
obj
{
std
::
move
(
val
)}]()
->
bool
{
...
...
@@ -36,6 +37,20 @@ struct fixture {
test_cases
.
emplace_back
(
std
::
move
(
f
));
}
// Adds a test case that should fail.
template
<
class
T
>
void
add_neg_test_case
(
std
::
string_view
input
)
{
auto
f
=
[
this
,
input
]()
->
bool
{
auto
tmp
=
T
{};
auto
res
=
reader
.
load
(
input
)
// parse JSON
&&
reader
.
apply
(
tmp
);
// deserialize object
if
(
res
)
MESSAGE
(
"got unexpected output: "
<<
tmp
);
return
!
res
;
};
test_cases
.
emplace_back
(
std
::
move
(
f
));
}
template
<
class
T
,
class
...
Ts
>
std
::
vector
<
T
>
ls
(
Ts
...
xs
)
{
std
::
vector
<
T
>
result
;
...
...
@@ -67,7 +82,6 @@ fixture::fixture() {
add_test_case
(
R"_(true)_"
,
true
);
add_test_case
(
R"_(false)_"
,
false
);
add_test_case
(
R"_([true, false])_"
,
ls
<
bool
>
(
true
,
false
));
add_test_case
(
R"_(42)_"
,
int32_t
{
42
});
add_test_case
(
R"_([1, 2, 3])_"
,
ls
<
int32_t
>
(
1
,
2
,
3
));
add_test_case
(
R"_([[1, 2], [3], []])_"
,
ls
<
i32_list
>
(
ls
<
int32_t
>
(
1
,
2
),
ls
<
int32_t
>
(
3
),
ls
<
int32_t
>
()));
...
...
@@ -109,6 +123,42 @@ fixture::fixture() {
R"({"top-left": {"x": 10, "y": 10}, )"
R"("bottom-right": {"x": 20, "y": 20}}})"
,
widget
{
"blue"
,
rectangle
{{
10
,
10
},
{
20
,
20
}}});
// Test cases for integers that are in bound.
add_test_case
(
R"_(-128)_"
,
int8_t
{
INT8_MIN
});
add_test_case
(
R"_(127)_"
,
int8_t
{
INT8_MAX
});
add_test_case
(
R"_(-32768)_"
,
int16_t
{
INT16_MIN
});
add_test_case
(
R"_(32767)_"
,
int16_t
{
INT16_MAX
});
add_test_case
(
R"_(-2147483648)_"
,
int32_t
{
INT32_MIN
});
add_test_case
(
R"_(2147483647)_"
,
int32_t
{
INT32_MAX
});
add_test_case
(
R"_(-9223372036854775808)_"
,
int64_t
{
INT64_MIN
});
add_test_case
(
R"_(9223372036854775807)_"
,
int64_t
{
INT64_MAX
});
// Test cases for unsigned integers that are in bound.
add_test_case
(
R"_(0)_"
,
uint8_t
{
0
});
add_test_case
(
R"_(255)_"
,
uint8_t
{
UINT8_MAX
});
add_test_case
(
R"_(0)_"
,
uint16_t
{
0
});
add_test_case
(
R"_(65535)_"
,
uint16_t
{
UINT16_MAX
});
add_test_case
(
R"_(0)_"
,
uint32_t
{
0
});
add_test_case
(
R"_(4294967295)_"
,
uint32_t
{
UINT32_MAX
});
add_test_case
(
R"_(0)_"
,
uint64_t
{
0
});
add_test_case
(
R"_(18446744073709551615)_"
,
uint64_t
{
UINT64_MAX
});
// Test cases for integers that are out of bound.
add_neg_test_case
<
int8_t
>
(
R"_(-129)_"
);
add_neg_test_case
<
int8_t
>
(
R"_(128)_"
);
add_neg_test_case
<
int16_t
>
(
R"_(-32769)_"
);
add_neg_test_case
<
int16_t
>
(
R"_(32768)_"
);
add_neg_test_case
<
int32_t
>
(
R"_(-2147483649)_"
);
add_neg_test_case
<
int32_t
>
(
R"_(2147483648)_"
);
add_neg_test_case
<
int64_t
>
(
R"_(-9223372036854775809)_"
);
add_neg_test_case
<
int64_t
>
(
R"_(9223372036854775808)_"
);
// Test cases for unsigned integers that are out of bound.
add_neg_test_case
<
uint8_t
>
(
R"_(-1)_"
);
add_neg_test_case
<
uint8_t
>
(
R"_(256)_"
);
add_neg_test_case
<
uint16_t
>
(
R"_(-1)_"
);
add_neg_test_case
<
uint16_t
>
(
R"_(65536)_"
);
add_neg_test_case
<
uint32_t
>
(
R"_(-1)_"
);
add_neg_test_case
<
uint32_t
>
(
R"_(4294967296)_"
);
add_neg_test_case
<
uint64_t
>
(
R"_(-1)_"
);
add_neg_test_case
<
uint64_t
>
(
R"_(18446744073709551616)_"
);
}
}
// namespace
...
...
libcaf_core/test/json_value.cpp
View file @
cad4baef
...
...
@@ -39,6 +39,7 @@ TEST_CASE("default-constructed") {
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
);
...
...
@@ -61,6 +62,7 @@ TEST_CASE("from undefined") {
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
);
...
...
@@ -70,11 +72,36 @@ TEST_CASE("from undefined") {
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
TEST_CASE
(
"from integer"
)
{
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
());
...
...
@@ -82,6 +109,7 @@ TEST_CASE("from integer") {
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
);
...
...
@@ -91,6 +119,29 @@ TEST_CASE("from integer") {
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
());
...
...
@@ -103,6 +154,7 @@ TEST_CASE("from double") {
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
);
...
...
@@ -124,6 +176,7 @@ TEST_CASE("from bool") {
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
);
...
...
@@ -145,6 +198,7 @@ TEST_CASE("from 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
);
...
...
@@ -166,6 +220,7 @@ TEST_CASE("from empty array") {
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
);
...
...
@@ -188,6 +243,7 @@ TEST_CASE("from array of size 1") {
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
);
...
...
@@ -210,6 +266,7 @@ TEST_CASE("from array of size 3") {
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
);
...
...
@@ -232,6 +289,7 @@ TEST_CASE("from empty object") {
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
);
...
...
@@ -253,6 +311,7 @@ TEST_CASE("from non-empty object") {
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
);
...
...
@@ -261,3 +320,24 @@ TEST_CASE("from non-empty object") {
CHECK_EQ
(
printed
(
val
),
"{
\n
\"
foo
\"
:
\"
bar
\"\n
}"
);
CHECK_EQ
(
deep_copy
(
val
),
val
);
}
namespace
{
std
::
string_view
json_with_boundary_values
=
R"_({
"min-int64": -9223372036854775808,
"max-int64": 9223372036854775807,
"min-uint64": 0,
"max-uint64": 18446744073709551615
})_"
;
}
// namespace
TEST_CASE
(
"non-empty object with nested values"
)
{
auto
val
=
unbox
(
json_value
::
parse
(
json_with_boundary_values
));
CHECK
(
val
.
is_object
());
auto
obj
=
val
.
to_object
();
CHECK_EQ
(
obj
.
value
(
"min-int64"
).
to_integer
(),
INT64_MIN
);
CHECK_EQ
(
obj
.
value
(
"max-int64"
).
to_integer
(),
INT64_MAX
);
CHECK_EQ
(
obj
.
value
(
"min-uint64"
).
to_unsigned
(),
0u
);
CHECK_EQ
(
obj
.
value
(
"max-uint64"
).
to_unsigned
(),
UINT64_MAX
);
}
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