Skip to content

Commit

Permalink
Small changes and scalar test
Browse files Browse the repository at this point in the history
  • Loading branch information
Anilm3 committed Oct 2, 2024
1 parent 4653fd8 commit bcc99d8
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 88 deletions.
124 changes: 36 additions & 88 deletions src/object_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,41 +37,52 @@ class object_view {
object_view &operator=(const object_view &) = default;
object_view &operator=(object_view &&) = default;

[[nodiscard]] const detail::object *ptr() const { return obj_; }
bool operator==(const object_view other) const { return ptr() == other.ptr(); }
bool operator!=(const object_view other) const { return ptr() != other.ptr(); }

// The unchecked API assumes that the caller has already verified that the
// method preconditions are met:
// - The underlying object is non-null
// - When using at, the accessed indexed is within bounds
// - When using as, the accessed field matches the underlying object type
//
// The checked API (without suffix), always validates preconditions so it is
// safer but introduces small overheads.

[[nodiscard]] bool has_value() const noexcept { return obj_ != nullptr; }

[[nodiscard]] object_type type() const noexcept
{
return obj_ != nullptr ? static_cast<object_type>(obj_->type) : object_type::invalid;
}
// Return the type without checking whether the internal object is NULL,
// this function should only be called if the object_view is known to have
// a non-null object, or after checking for nullability with has_value
[[nodiscard]] object_type type_unchecked() const noexcept
{
return static_cast<object_type>(obj_->type);
}
[[nodiscard]] std::size_t size() const noexcept

[[nodiscard]] object_type type() const noexcept
{
return obj_ != nullptr ? static_cast<std::size_t>(obj_->nbEntries) : 0;
return obj_ != nullptr ? type_unchecked() : object_type::invalid;
}

[[nodiscard]] std::size_t size_unchecked() const noexcept
{
return static_cast<std::size_t>(obj_->nbEntries);
}
[[nodiscard]] std::size_t capacity() const noexcept
{
return obj_ != nullptr ? static_cast<std::size_t>(obj_->nbEntries) : 0;
}
[[nodiscard]] bool empty() const noexcept

[[nodiscard]] std::size_t size() const noexcept
{
return obj_ != nullptr ? obj_->nbEntries == 0 : true;
return obj_ != nullptr ? size_unchecked() : 0;
}

[[nodiscard]] bool empty_unchecked() const noexcept { return obj_->nbEntries == 0; }

[[nodiscard]] bool empty() const noexcept { return obj_ != nullptr ? empty_unchecked() : true; }

template <typename T> [[nodiscard]] bool is() const noexcept
{
return is_compatible_type<T>(type());
}
[[nodiscard]] bool is_valid() const noexcept { return type() != object_type::invalid; }
[[nodiscard]] bool is_invalid() const noexcept { return type() == object_type::invalid; }

// These is_* methods provide further checks for consistency, albeit these
// perhaps should be replaced by assertions.
[[nodiscard]] bool is_container() const noexcept
{
return (type() & container_object_type) != 0 && obj_->array != nullptr;
Expand All @@ -81,18 +92,6 @@ class object_view {
{
return type() == object_type::string && obj_->stringValue != nullptr;
}
bool operator==(const object_view other) const { return ptr() == other.ptr(); }
bool operator!=(const object_view other) const { return ptr() != other.ptr(); }

[[nodiscard]] const detail::object *ptr() const { return obj_; }

[[nodiscard]] std::pair<std::string_view, object_view> at(std::size_t index) const noexcept
{
if (!is_container() || index > static_cast<std::size_t>(obj_->nbEntries)) {
[[unlikely]] return {};
}
return at_unchecked(index);
}

[[nodiscard]] std::pair<std::string_view, object_view> at_unchecked(
std::size_t index) const noexcept
Expand All @@ -106,6 +105,14 @@ class object_view {
return {{}, object_view{&slot}};
}

[[nodiscard]] std::pair<std::string_view, object_view> at(std::size_t index) const noexcept
{
if (!is_container() || index > static_cast<std::size_t>(obj_->nbEntries)) {
[[unlikely]] return {};
}
return at_unchecked(index);
}

template <typename T> [[nodiscard]] std::optional<T> as() const noexcept
{
if (!is_compatible_type<T>(type())) {
Expand Down Expand Up @@ -172,65 +179,6 @@ class object_view {

template <typename T> T convert() const { return converter<T>{*this}(); }

class iterator {
public:
iterator() = default;

explicit iterator(object_view view, size_t index = 0) : type(view.type())
{
if (view.is_container()) {
end_ = view.obj_->array + view.size();
if (index >= view.size()) {
current_ = end_;
} else {
current_ = view.obj_->array + index;
}
}
}

bool operator!=(const iterator &rhs) const noexcept { return current_ != rhs.current_; }

[[nodiscard]] std::string_view key() const noexcept
{
if (current_ == end_ || type != object_type::map) {
return {};
}
return {
current_->parameterName, static_cast<std::size_t>(current_->parameterNameLength)};
}

std::pair<std::string_view, object_view> operator*() const noexcept
{
if (current_ == end_) {
return {{}, nullptr};
}
return {key(), value()};
}

[[nodiscard]] object_view value() const noexcept
{
if (current_ == end_) {
return nullptr;
}
return {current_};
}

iterator &operator++() noexcept
{
if (current_ != end_) {
current_++;
}
return *this;
}

[[nodiscard]] bool is_valid() const noexcept { return current_ != end_; }

protected:
object_type type{object_type::invalid};
detail::object *current_{nullptr};
detail::object *end_{nullptr};
};

class array {
public:
array() = default;
Expand Down
131 changes: 131 additions & 0 deletions tests/object_view_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Unless explicitly stated otherwise all files in this repository are
// dual-licensed under the Apache-2.0 License or BSD-3-Clause License.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2021 Datadog, Inc.

#include "object_view.hpp"
#include "test_utils.hpp"

using namespace ddwaf;

namespace {

TEST(TestObjectView, InvalidObject)
{
detail::object original;
ddwaf_object_invalid(&original);

object_view view(&original);
EXPECT_TRUE(view.has_value());

EXPECT_EQ(view.type(), object_type::invalid);
EXPECT_EQ(view.type_unchecked(), object_type::invalid);

EXPECT_EQ(view.size(), 0);
EXPECT_TRUE(view.empty());

EXPECT_FALSE(view.as<bool>());
EXPECT_FALSE(view.as<int64_t>());
EXPECT_FALSE(view.as<uint64_t>());
EXPECT_FALSE(view.as<double>());
EXPECT_FALSE(view.as<std::string>());
}

TEST(TestObjectView, NullObject)
{
detail::object original;
ddwaf_object_invalid(&original);

object_view view(&original);
EXPECT_TRUE(view.has_value());

EXPECT_EQ(view.type(), object_type::invalid);
EXPECT_EQ(view.type_unchecked(), object_type::invalid);

EXPECT_EQ(view.size(), 0);
EXPECT_TRUE(view.empty());

EXPECT_FALSE(view.as<bool>());
EXPECT_FALSE(view.as<int64_t>());
EXPECT_FALSE(view.as<uint64_t>());
EXPECT_FALSE(view.as<double>());
EXPECT_FALSE(view.as<std::string>());
}

TEST(TestObjectView, BooleanObject)
{
detail::object original;
ddwaf_object_bool(&original, true);

object_view view(&original);
EXPECT_TRUE(view.has_value());

EXPECT_EQ(view.type(), object_type::boolean);
EXPECT_EQ(view.type_unchecked(), object_type::boolean);

EXPECT_EQ(view.size(), 0);
EXPECT_TRUE(view.empty());

EXPECT_TRUE(view.as<bool>());
EXPECT_EQ(view.as<bool>().value(), true);

EXPECT_EQ(view.as_unchecked<bool>(), true);

EXPECT_FALSE(view.as<int64_t>());
EXPECT_FALSE(view.as<uint64_t>());
EXPECT_FALSE(view.as<double>());
EXPECT_FALSE(view.as<std::string>());
}

TEST(TestObjectView, SignedObject)
{
detail::object original;
ddwaf_object_signed(&original, -20);

object_view view(&original);
EXPECT_TRUE(view.has_value());

EXPECT_EQ(view.type(), object_type::int64);
EXPECT_EQ(view.type_unchecked(), object_type::int64);

EXPECT_EQ(view.size(), 0);
EXPECT_TRUE(view.empty());

EXPECT_TRUE(view.as<int64_t>());
EXPECT_EQ(view.as<int64_t>().value(), -20);

EXPECT_EQ(view.as_unchecked<int64_t>(), -20);

EXPECT_FALSE(view.as<bool>());
EXPECT_FALSE(view.as<uint64_t>());
EXPECT_FALSE(view.as<double>());
EXPECT_FALSE(view.as<std::string>());
}

TEST(TestObjectView, UnsignedObject)
{
detail::object original;
ddwaf_object_unsigned(&original, 20);

object_view view(&original);
EXPECT_TRUE(view.has_value());

EXPECT_EQ(view.type(), object_type::uint64);
EXPECT_EQ(view.type_unchecked(), object_type::uint64);

EXPECT_EQ(view.size(), 0);
EXPECT_TRUE(view.empty());

EXPECT_TRUE(view.as<uint64_t>());
EXPECT_EQ(view.as<uint64_t>().value(), 20);

EXPECT_EQ(view.as_unchecked<uint64_t>(), 20);

EXPECT_FALSE(view.as<bool>());
EXPECT_FALSE(view.as<int64_t>());
EXPECT_FALSE(view.as<double>());
EXPECT_FALSE(view.as<std::string>());
}

} // namespace

0 comments on commit bcc99d8

Please sign in to comment.