blob: e11a9e64c0050d3975c9dde4eb2e471d16d1a6ae [file] [log] [blame]
/*
* Copyright (c) 2024 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstddef>
#include <lib/support/Span.h>
#include <optional>
namespace chip {
/// represents a wrapper around a type `T` that contains internal
/// `Span<...>` values of other sub-types. It allows searching within the container sub-spans
/// to create new containers.
///
/// The use case is that we very often search within nested containers, like "find-endpoint" + "find-cluster" + "find-attribute"
/// and we generally only care about "does the last element exist or not"
///
/// A typical example of the way this class is used looks like this:
///
/// SpanSearchValue container(somePointer);
///
/// const AcceptedCommandData * value =
/// container
/// .Find<ByEndpoint>(path.mEndpointId, mEndpointIndexHint)
/// .Find<ByServerCluster>(path.mClusterId, mServerClusterHint)
/// .Find<ByAcceptedCommand>(path.mCommandId, mAcceptedCommandHint)
/// .Value();
///
/// Where a `ByFoo` structure looks like:
///
/// struct ByFoo {
/// using Key = int; // the KEY inside a type
/// using Type = SomeValueType; // The type that is indexed by `Key`
///
/// /// Allows getting the "Span of Type" from an underlying structure.
/// /// A `SpanSearchValue<Foo>` will require a `GetSpan(Foo&)`
/// static Span<Type> GetSpan(ContainerType & data) { /* return ... */ }
///
/// /// Checks that the `Type` value has the given `Key` or not
/// static bool HasKey(const Key & id, const Type & instance) { /* return "instance has key id" */ }
/// }
///
/// Where we define:
/// - how to get a "span of sub-elements" for an object (`GetSpan`)
/// - how to determine if a given sub-element has the "correct key"
template <typename T>
class SpanSearchValue
{
public:
SpanSearchValue() : mValue(nullptr) {}
SpanSearchValue(std::nullptr_t) : mValue(nullptr) {}
explicit SpanSearchValue(T * value) : mValue(value) {}
/// Returns nullptr if such an element does not exist or non-null valid value if the element exists
T * Value() const { return mValue; }
/// Gets the first element of `TYPE::Type`
template <typename TYPE>
SpanSearchValue<typename TYPE::Type> First(unsigned & indexHint)
{
// if no value, searching more also yields no value
VerifyOrReturnValue(mValue != nullptr, nullptr);
Span<typename TYPE::Type> value_span = TYPE::GetSpan(*mValue);
VerifyOrReturnValue(!value_span.empty(), nullptr);
// found it, save the hint
indexHint = 0;
return SpanSearchValue<typename TYPE::Type>(&value_span[0]);
}
/// Find the value corresponding to `key`
template <typename TYPE>
SpanSearchValue<typename TYPE::Type> Find(typename TYPE::Key key, unsigned & indexHint)
{
VerifyOrReturnValue(mValue != nullptr, nullptr);
Span<typename TYPE::Type> value_span = TYPE::GetSpan(*mValue);
if (!FindIndexUsingHint(key, value_span, indexHint, TYPE::HasKey))
{
return nullptr;
}
return SpanSearchValue<typename TYPE::Type>(&value_span[indexHint]);
}
/// Finds the value that occurs after `key` in the underlying collection.
template <typename TYPE>
SpanSearchValue<typename TYPE::Type> Next(typename TYPE::Key key, unsigned & indexHint)
{
VerifyOrReturnValue(mValue != nullptr, nullptr);
Span<typename TYPE::Type> value_span = TYPE::GetSpan(*mValue);
if (!FindIndexUsingHint(key, value_span, indexHint, TYPE::HasKey))
{
return nullptr;
}
VerifyOrReturnValue((indexHint + 1) < value_span.size(), nullptr);
indexHint++;
return SpanSearchValue<typename TYPE::Type>(&value_span[indexHint]);
}
private:
T * mValue = nullptr; // underlying value, NULL if such a value does not exist
/// Search for the index where `needle` is located inside `haystack`
///
/// using `haystackValueMatchesNeedle` to find if a given haystack value matches the given needle
///
/// `in_out_hint` contains a start search point at the start and will contain the found index
/// location (if found) at the end.
///
/// Returns true on success (index found) false on failure (index not found). If returning
/// false, the value of `in_out_hint` is unchanged
template <typename N, typename H>
static bool FindIndexUsingHint(const N & needle, Span<H> haystack, unsigned & in_out_hint,
bool (*haystackValueMatchesNeedle)(const N &, const typename std::remove_const<H>::type &))
{
// search starts at `hint` rather than 0
const unsigned haystackSize = static_cast<unsigned>(haystack.size());
unsigned checkIndex = (in_out_hint < haystackSize) ? in_out_hint : 0;
for (unsigned i = 0; i < haystackSize; i++, checkIndex++)
{
if (haystackValueMatchesNeedle(needle, haystack[checkIndex % haystackSize]))
{
in_out_hint = checkIndex % haystackSize;
return true;
}
}
return false;
}
};
} // namespace chip