Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 28 additions & 20 deletions common/values/parsed_map_field_value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "extensions/protobuf/internal/map_reflection.h"
#include "internal/json.h"
#include "internal/message_equality.h"
#include "internal/number.h"
#include "internal/status_macros.h"
#include "internal/well_known_types.h"
#include "google/protobuf/arena.h"
Expand Down Expand Up @@ -197,11 +198,15 @@ absl::optional<int32_t> ValueAsInt32(const Value& value) {
uint_value &&
uint_value->NativeValue() <= std::numeric_limits<int32_t>::max()) {
return static_cast<int32_t>(uint_value->NativeValue());
} else if (auto double_value = value.AsDouble();
double_value &&
static_cast<double>(static_cast<int32_t>(
double_value->NativeValue())) == double_value->NativeValue()) {
return static_cast<int32_t>(double_value->NativeValue());
} else if (auto double_value = value.AsDouble(); double_value) {
auto number = internal::Number::FromDouble(double_value->NativeValue());
if (number.LosslessConvertibleToInt()) {
int64_t wide_value = number.AsInt();
if (wide_value >= std::numeric_limits<int32_t>::min() &&
wide_value <= std::numeric_limits<int32_t>::max()) {
return static_cast<int32_t>(wide_value);
}
}
}
return absl::nullopt;
}
Expand All @@ -213,11 +218,11 @@ absl::optional<int64_t> ValueAsInt64(const Value& value) {
uint_value &&
uint_value->NativeValue() <= std::numeric_limits<int64_t>::max()) {
return static_cast<int64_t>(uint_value->NativeValue());
} else if (auto double_value = value.AsDouble();
double_value &&
static_cast<double>(static_cast<int64_t>(
double_value->NativeValue())) == double_value->NativeValue()) {
return static_cast<int64_t>(double_value->NativeValue());
} else if (auto double_value = value.AsDouble(); double_value) {
auto number = internal::Number::FromDouble(double_value->NativeValue());
if (number.LosslessConvertibleToInt()) {
return number.AsInt();
}
}
return absl::nullopt;
}
Expand All @@ -231,11 +236,14 @@ absl::optional<uint32_t> ValueAsUInt32(const Value& value) {
uint_value && uint_value->NativeValue() <=
std::numeric_limits<uint32_t>::max()) {
return static_cast<uint32_t>(uint_value->NativeValue());
} else if (auto double_value = value.AsDouble();
double_value &&
static_cast<double>(static_cast<uint32_t>(
double_value->NativeValue())) == double_value->NativeValue()) {
return static_cast<uint32_t>(double_value->NativeValue());
} else if (auto double_value = value.AsDouble(); double_value) {
auto number = internal::Number::FromDouble(double_value->NativeValue());
if (number.LosslessConvertibleToUint()) {
uint64_t wide_value = number.AsUint();
if (wide_value <= std::numeric_limits<uint32_t>::max()) {
return static_cast<uint32_t>(wide_value);
}
}
}
return absl::nullopt;
}
Expand All @@ -246,11 +254,11 @@ absl::optional<uint64_t> ValueAsUInt64(const Value& value) {
return static_cast<uint64_t>(int_value->NativeValue());
} else if (auto uint_value = value.AsUint(); uint_value) {
return uint_value->NativeValue();
} else if (auto double_value = value.AsDouble();
double_value &&
static_cast<double>(static_cast<uint64_t>(
double_value->NativeValue())) == double_value->NativeValue()) {
return static_cast<uint64_t>(double_value->NativeValue());
} else if (auto double_value = value.AsDouble(); double_value) {
auto number = internal::Number::FromDouble(double_value->NativeValue());
if (number.LosslessConvertibleToUint()) {
return number.AsUint();
}
}
return absl::nullopt;
}
Expand Down
72 changes: 72 additions & 0 deletions common/values/parsed_map_field_value_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <limits>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -225,6 +226,77 @@ TEST_F(ParsedMapFieldValueTest, Find) {
IsOkAndHolds(Eq(absl::nullopt)));
}

TEST_F(ParsedMapFieldValueTest, FindDoubleKeyConversion) {
// Lookups on integer-keyed maps coerce double keys following CEL's numeric
// semantics ({1: 'x'}[1.0] resolves to key 1). A double that is out of range,
// negative for an unsigned key, or non-finite must be reported as absent
// rather than narrowed to the key type: converting such a double straight to
// an integer with static_cast is undefined behaviour.
ParsedMapFieldValue int64_map(
DynamicParseTextProto<TestAllTypesProto3>(R"pb(
map_int64_int64 { key: 1 value: 100 }
)pb"),
DynamicGetField<TestAllTypesProto3>("map_int64_int64"), arena());
EXPECT_THAT(int64_map.Find(DoubleValue(1.0), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Optional(IntValueIs(100))));
EXPECT_THAT(int64_map.Find(DoubleValue(1e19), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));
EXPECT_THAT(int64_map.Find(DoubleValue(-1e19), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));
EXPECT_THAT(int64_map.Find(DoubleValue(std::numeric_limits<double>::infinity()),
descriptor_pool(), message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));
EXPECT_THAT(
int64_map.Find(DoubleValue(std::numeric_limits<double>::quiet_NaN()),
descriptor_pool(), message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));

ParsedMapFieldValue uint64_map(
DynamicParseTextProto<TestAllTypesProto3>(R"pb(
map_uint64_uint64 { key: 1 value: 100 }
)pb"),
DynamicGetField<TestAllTypesProto3>("map_uint64_uint64"), arena());
EXPECT_THAT(uint64_map.Find(DoubleValue(1.0), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Optional(UintValueIs(100))));
EXPECT_THAT(uint64_map.Find(DoubleValue(-1.0), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));
EXPECT_THAT(uint64_map.Find(DoubleValue(1e20), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));

ParsedMapFieldValue int32_map(
DynamicParseTextProto<TestAllTypesProto3>(R"pb(
map_int32_int32 { key: 1 value: 100 }
)pb"),
DynamicGetField<TestAllTypesProto3>("map_int32_int32"), arena());
EXPECT_THAT(int32_map.Find(DoubleValue(1.0), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Optional(IntValueIs(100))));
EXPECT_THAT(int32_map.Find(DoubleValue(1e19), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));

ParsedMapFieldValue uint32_map(
DynamicParseTextProto<TestAllTypesProto3>(R"pb(
map_uint32_uint32 { key: 1 value: 100 }
)pb"),
DynamicGetField<TestAllTypesProto3>("map_uint32_uint32"), arena());
EXPECT_THAT(uint32_map.Find(DoubleValue(1.0), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Optional(UintValueIs(100))));
EXPECT_THAT(uint32_map.Find(DoubleValue(-1.0), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));
EXPECT_THAT(uint32_map.Find(DoubleValue(1e19), descriptor_pool(),
message_factory(), arena()),
IsOkAndHolds(Eq(absl::nullopt)));
}

TEST_F(ParsedMapFieldValueTest, Has) {
ParsedMapFieldValue value(
DynamicParseTextProto<TestAllTypesProto3>(R"pb(
Expand Down