1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <algorithm>
17 #include <climits>
18 #include <cstdlib>
19
20 #include <base/containers/array_view.h>
21 #include <base/containers/iterator.h>
22 #include <base/containers/string.h>
23 #include <base/containers/string_view.h>
24 #include <base/util/compile_time_hashes.h>
25 #include <core/property/intf_property_api.h>
26 #include <core/property/intf_property_handle.h>
27 #include <core/property/property.h>
28 #include <core/property_tools/property_data.h>
29
30 using namespace CORE_NS;
31 using BASE_NS::array_view;
32 using BASE_NS::FNV1aHash;
33 using BASE_NS::string;
34 using BASE_NS::string_view;
35
36 namespace {
ParseIndex(const string_view name,const uintptr_t baseOffset,const Property & property,array_view<const Property> & properties,size_t & pos,PropertyData::PropertyOffset & ret)37 bool ParseIndex(const string_view name, const uintptr_t baseOffset, const Property& property,
38 array_view<const Property>& properties, size_t& pos, PropertyData::PropertyOffset& ret)
39 {
40 // there needs to be at least three characters (e.g. [0]) to be a valid array index. the propery must also be an
41 // array.
42 static constexpr size_t minLength = 3U;
43 if ((pos >= name.size()) || ((name.size() - pos) < minLength) || !property.metaData.containerMethods) {
44 ret = {};
45 return false;
46 }
47 ret.propertyPath = name.substr(0, pos);
48 ++pos;
49
50 const char* start = name.substr(pos).data();
51 char* end = nullptr;
52 const unsigned long index = strtoul(start, &end, 10); // 10: base
53 // check that conversion stopped at the closing square bracket
54 if (!end || *end != ']') {
55 ret = {};
56 return false;
57 }
58 // move past the closing square bracket and store the path so far
59 pos = static_cast<size_t>(end - name.data()) + 1U;
60 ret.propertyPath = name.substr(0, pos);
61
62 auto* containerMethods = property.metaData.containerMethods;
63
64 // calculate offset to the index. for arrays a direct offset, but for containers need to get the addess
65 // inside the container and compensate the base and member offsets.
66 ptrdiff_t offset = PTRDIFF_MAX;
67 if (property.type.isArray && (index < property.count)) {
68 offset = static_cast<ptrdiff_t>(index * containerMethods->property.size);
69 } else if (!property.type.isArray && baseOffset) {
70 if (index < containerMethods->size(property.offset + baseOffset)) {
71 offset = static_cast<ptrdiff_t>(
72 containerMethods->get(property.offset + baseOffset, index) - baseOffset - ret.offset);
73 }
74 }
75
76 if (offset != PTRDIFF_MAX) {
77 ret.property = &containerMethods->property;
78 ret.offset = static_cast<uintptr_t>(static_cast<ptrdiff_t>(ret.offset) + offset);
79 ret.index = index;
80
81 if (pos < name.size() && name[pos] == '.') {
82 ++pos;
83 // continue search from the member properties.
84 properties = containerMethods->property.metaData.memberProperties;
85 }
86 } else {
87 ret = {};
88 return false;
89 }
90 return true;
91 }
92
FindProperty(array_view<const Property> properties,const string_view name,const uintptr_t baseOffset)93 PropertyData::PropertyOffset FindProperty(
94 array_view<const Property> properties, const string_view name, const uintptr_t baseOffset)
95 {
96 PropertyData::PropertyOffset ret { nullptr, 0U, 0U, {} };
97
98 // trim down to name without array index or member variable.
99 constexpr const string_view delimiters = ".[";
100
101 size_t pos = 0U;
102 while (pos < name.size()) {
103 const auto delimPos = name.find_first_of(delimiters, pos);
104 auto baseName = name.substr(pos, delimPos - pos);
105 pos = delimPos;
106 if (baseName.empty()) {
107 ret = {};
108 break;
109 }
110
111 auto property = std::find_if(
112 properties.cbegin(), properties.cend(), [baseName](const Property& p) { return p.name == baseName; });
113 if (property != properties.cend()) {
114 // remember what property this was
115 ret.property = &*property;
116 ret.offset += property->offset;
117 ret.index = static_cast<size_t>(std::distance(properties.cbegin(), property));
118
119 // if we have only a name we are done
120 if (pos == string_view::npos) {
121 ret.propertyPath = name;
122 } else if (name[pos] == '.') {
123 // there needs to be at least one character to be a valid name.
124 if ((name.size() - pos) < 2U) {
125 ret = {};
126 break;
127 }
128 ret.propertyPath = name.substr(0, pos);
129 ++pos;
130 // continue search from the member properties.
131 properties = property->metaData.memberProperties;
132 } else if (name[pos] == '[') {
133 if (!ParseIndex(name, baseOffset, *property, properties, pos, ret)) {
134 break;
135 }
136 }
137 } else if (!properties.empty()) {
138 ret = {};
139 break;
140 } else {
141 break;
142 }
143 }
144 return ret;
145 }
146 } // namespace
147
PropertyData()148 PropertyData::PropertyData()
149 {
150 Reset();
151 }
152
WLock(IPropertyHandle & handle)153 bool PropertyData::WLock(IPropertyHandle& handle) // no-copy direct-access (Locks the datahandle);
154 {
155 if (dataHandle_ || dataHandleW_) {
156 return false;
157 }
158 dataHandleW_ = &handle;
159 dataHandle_ = dataHandleW_;
160 owner_ = dataHandleW_->Owner();
161 size_ = dataHandleW_->Size();
162 dataW_ = static_cast<uint8_t*>(dataHandleW_->WLock());
163 data_ = dataW_;
164 return true;
165 }
166
WLock(IPropertyHandle & handle,const string_view propertyPath)167 PropertyData::PropertyOffset PropertyData::WLock(IPropertyHandle& handle, const string_view propertyPath)
168 {
169 if (WLock(handle)) {
170 const uintptr_t baseOffset = reinterpret_cast<uintptr_t>(dataW_);
171 if (auto po = FindProperty(Owner()->MetaData(), propertyPath, baseOffset); po) {
172 return po;
173 }
174 }
175 WUnlock(handle);
176 return { nullptr, 0U, 0U, {} };
177 }
178
WUnlock(const IPropertyHandle & handle)179 bool PropertyData::WUnlock(const IPropertyHandle& handle) // (releases the datahandle lock, and removes ref)
180 {
181 BASE_ASSERT(dataHandleW_);
182 BASE_ASSERT(dataHandleW_ == dataHandle_);
183 BASE_ASSERT(dataHandleW_ == &handle);
184 if (dataHandleW_ == &handle) {
185 if (dataHandleW_) {
186 dataHandleW_->WUnlock();
187 Reset();
188 }
189 return true;
190 }
191 return false;
192 }
193
RLock(const IPropertyHandle & handle)194 bool PropertyData::RLock(const IPropertyHandle& handle) // no-copy direct-access (Locks the datahandle);
195 {
196 if (dataHandle_ || dataHandleW_) {
197 return false;
198 }
199 dataHandleW_ = nullptr;
200 dataHandle_ = &handle;
201 owner_ = dataHandle_->Owner();
202 size_ = dataHandle_->Size();
203 data_ = dataHandle_->RLock();
204 dataW_ = nullptr;
205 return true;
206 }
207
RLock(const IPropertyHandle & handle,const string_view propertyPath)208 PropertyData::PropertyOffset PropertyData::RLock(const IPropertyHandle& handle, const string_view propertyPath)
209 {
210 if (RLock(handle)) {
211 const uintptr_t baseOffset = reinterpret_cast<uintptr_t>(data_);
212 if (auto po = FindProperty(Owner()->MetaData(), propertyPath, baseOffset); po) {
213 return po;
214 }
215 }
216 RUnlock(handle);
217 return { nullptr, 0U, 0U, {} };
218 }
219
RUnlock(const IPropertyHandle & handle)220 bool PropertyData::RUnlock(const IPropertyHandle& handle) // (releases the datahandle lock, and removes ref)
221 {
222 BASE_ASSERT(dataHandle_);
223 BASE_ASSERT(dataHandleW_ == nullptr);
224 BASE_ASSERT(dataHandle_ == &handle);
225 if (dataHandle_ == &handle) {
226 if (dataHandle_) {
227 dataHandle_->RUnlock();
228 Reset();
229 }
230 return true;
231 }
232 return false;
233 }
234
FindProperty(const array_view<const Property> properties,const string_view propertyPath,const uintptr_t baseOffset)235 PropertyData::PropertyOffset PropertyData::FindProperty(
236 const array_view<const Property> properties, const string_view propertyPath, const uintptr_t baseOffset)
237 {
238 PropertyData::PropertyOffset offset = ::FindProperty(properties, propertyPath, baseOffset);
239 if (offset) {
240 offset.offset += baseOffset;
241 }
242 return offset;
243 }
244
FindProperty(const array_view<const Property> properties,const string_view propertyPath)245 PropertyData::PropertyOffset PropertyData::FindProperty(
246 const array_view<const Property> properties, const string_view propertyPath)
247 {
248 return ::FindProperty(properties, propertyPath, 0U);
249 }
250
~PropertyData()251 PropertyData::~PropertyData()
252 {
253 if (dataHandleW_) {
254 WUnlock(*dataHandleW_);
255 }
256 if (dataHandle_) {
257 RUnlock(*dataHandle_);
258 }
259 }
260
Reset()261 void PropertyData::Reset()
262 {
263 size_ = 0;
264 data_ = nullptr;
265 dataW_ = nullptr;
266 owner_ = nullptr;
267 dataHandle_ = nullptr;
268 dataHandleW_ = nullptr;
269 }
270
MetaData() const271 array_view<const Property> PropertyData::MetaData() const
272 {
273 if (owner_) {
274 return owner_->MetaData();
275 }
276 return {};
277 }
278
MetaData(size_t index) const279 const Property* PropertyData::MetaData(size_t index) const
280 {
281 if (owner_) {
282 const auto& meta = owner_->MetaData();
283 if (index < meta.size()) {
284 return &meta[index];
285 }
286 }
287 return nullptr;
288 }
289
PropertyCount() const290 size_t PropertyData::PropertyCount() const
291 {
292 if (owner_) {
293 const auto& props = owner_->MetaData();
294 return props.size();
295 }
296 return 0;
297 }
298
Owner() const299 const IPropertyApi* PropertyData::Owner() const
300 {
301 return owner_;
302 }
303
Size() const304 size_t PropertyData::Size() const
305 {
306 return size_;
307 }
308
RLock() const309 const void* PropertyData::RLock() const
310 {
311 return data_;
312 }
313
WLock()314 void* PropertyData::WLock()
315 {
316 return dataW_;
317 }
318
RUnlock() const319 void PropertyData::RUnlock() const {}
320
WUnlock()321 void PropertyData::WUnlock() {}
322