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
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
...
...
@@ -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,15 +33,13 @@ 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
=
{},
EnableRange
=
{})
{
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
;
// Our result when reading an integer number.
...
...
@@ -48,36 +47,98 @@ 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
=
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
)
}
// 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
(
neg_dot
)
{
fsm_transition_static_if
(
enable_range
,
read_number_range
(
ps
,
consumer
,
result
),
done
,
'.'
,
g
.
disable
())
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
result
},
true
),
done
,
any_char
,
g
.
disable
())
epsilon
(
done
)
}
term_state
(
done
)
{
// nop
}
// "+" or "-" alone aren't numbers.
state
(
has_plus
)
{
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.
}),
done
,
'.'
,
g
.
disable
())
transition
(
pos_zero
,
'0'
)
epsilon
(
pos_dec
)
}
state
(
has_minus
)
{
fsm_epsilon_static_if
(
enable_float
,
read_floating_point
(
ps
,
consumer
,
odbl
{
0.
},
true
),
done
,
'.'
,
g
.
disable
())
transition
(
neg_zero
,
'0'
)
epsilon
(
neg_dec
)
}
// Disambiguate base.
term_state
(
pos_zero
)
{
transition
(
start_pos_bin
,
"bB"
)
...
...
@@ -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
)
{
std
::
optional
<
int64_t
>
end
;
std
::
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
(
min_val
<
max_val
)
{
auto
step_val
=
step
.
value_or
(
1
);
if
(
step_val
<=
0
)
{
code
=
pec
::
invalid_range_expression
;
return
;
}
if
(
s
==
0
||
(
n
>
m
&&
s
>
0
)
||
(
n
<
m
&&
s
<
0
))
{
ps
.
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
(
max_val
-
i
<
s
)
// protect against overflows
return
;
i
+=
s
;
}
if
(
n
<=
m
)
for
(
auto
i
=
n
;
i
<=
m
;
i
+=
s
)
consumer
.
value
(
i
);
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
)
{
ps
.
code
=
pec
::
invalid_range_expression
;
}
else
if
(
!
step
)
{
generate_2
(
begin
,
*
end
);
}
else
{
generate_3
(
begin
,
*
end
,
*
step
);
}
auto
fn
=
[
&
](
auto
end_val
)
{
if
constexpr
(
std
::
is_same_v
<
decltype
(
end_val
),
none_t
>
)
{
ps
.
code
=
pec
::
invalid_range_expression
;
}
else
{
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 @
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