-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
SpanSearchValue.h
154 lines (132 loc) · 5.7 KB
/
SpanSearchValue.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
* 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