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 "PropertyTools/property_data.h"
17 
18 #include <PropertyTools/property_value.h>
19 #include <algorithm>
20 #include <climits>
21 #include <cstdlib>
22 
23 #include <base/containers/array_view.h>
24 #include <base/containers/iterator.h>
25 #include <base/containers/string.h>
26 #include <base/containers/string_view.h>
27 #include <base/util/compile_time_hashes.h>
28 #include <core/log.h>
29 #include <core/property/intf_property_api.h>
30 #include <core/property/intf_property_handle.h>
31 #include <core/property/property.h>
32 
33 using namespace CORE_NS;
34 using BASE_NS::array_view;
35 using BASE_NS::FNV1aHash;
36 using BASE_NS::string;
37 using BASE_NS::string_view;
38 
39 namespace {
ParseIndex(const string_view name,const uintptr_t baseOffset,const Property & property,array_view<const Property> & properties,size_t & pos,PropertyData::PropertyOffset & ret)40 bool ParseIndex(const string_view name, const uintptr_t baseOffset, const Property& property,
41     array_view<const Property>& properties, size_t& pos, PropertyData::PropertyOffset& ret)
42 {
43     // there needs to be at least three characters to be a valid array index. the propery must also be an
44     // array.
45     if (((name.size() - pos) < 3U) || !property.metaData.containerMethods) { // 3: min length e.g. [0]
46         ret = {};
47         return false;
48     }
49     ret.propertyPath = name.substr(0, pos);
50     ++pos;
51 
52     const char* start = name.substr(pos).data();
53     char* end = nullptr;
54     const unsigned long index = strtoul(start, &end, 10); // 10: base
55     // check that conversion stopped at the closing square bracket
56     if (!end || *end != ']') {
57         ret = {};
58         return false;
59     }
60     // move past the closing square bracket and store the path so far
61     pos = static_cast<size_t>(end - name.data()) + 1U;
62     ret.propertyPath = name.substr(0, pos);
63 
64     auto* containerMethods = property.metaData.containerMethods;
65 
66     // calculate offset to the index. for arrays a direct offset, but for containers need to get the addess
67     // inside the container and compensate the base and member offsets.
68     ptrdiff_t offset = PTRDIFF_MAX;
69     if (property.type.isArray && (index < property.count)) {
70         offset = static_cast<ptrdiff_t>(index * containerMethods->property.size);
71     } else if (!property.type.isArray && baseOffset) {
72         if (index < containerMethods->size(property.offset + baseOffset)) {
73             offset = static_cast<ptrdiff_t>(
74                 containerMethods->get(property.offset + baseOffset, index) - baseOffset - ret.offset);
75         }
76     }
77 
78     if (offset != PTRDIFF_MAX) {
79         ret.property = &containerMethods->property;
80         ret.offset = static_cast<uintptr_t>(static_cast<ptrdiff_t>(ret.offset) + offset);
81         ret.index = index;
82 
83         if (pos < name.size() && name[pos] == '.') {
84             ++pos;
85             // continue search from the member properties.
86             properties = containerMethods->property.metaData.memberProperties;
87         }
88     } else {
89         ret = {};
90         return false;
91     }
92     return true;
93 }
94 
FindProperty(array_view<const Property> properties,const string_view name,const uintptr_t baseOffset)95 PropertyData::PropertyOffset FindProperty(
96     array_view<const Property> properties, const string_view name, const uintptr_t baseOffset)
97 {
98     PropertyData::PropertyOffset ret { nullptr, 0U, 0U, {} };
99 
100     // trim down to name without array index or member variable.
101     constexpr const string_view delimiters = ".[";
102 
103     size_t pos = 0U;
104     while (pos < name.size()) {
105         const auto delimPos = name.find_first_of(delimiters, pos);
106         auto baseName = name.substr(pos, delimPos - pos);
107         pos = delimPos;
108         if (baseName.empty()) {
109             ret = {};
110             break;
111         }
112 
113         auto property = std::find_if(
114             properties.cbegin(), properties.cend(), [baseName](const Property& p) { return p.name == baseName; });
115         if (property != properties.cend()) {
116             // remember what property this was
117             ret.property = &*property;
118             ret.offset += property->offset;
119             ret.index = static_cast<size_t>(std::distance(properties.cbegin(), property));
120 
121             // if we have only a name we are done
122             if (pos == string_view::npos) {
123                 ret.propertyPath = name;
124             } else if (name[pos] == '.') {
125                 // there needs to be at least one character to be a valid name.
126                 if ((name.size() - pos) < 2U) {
127                     ret = {};
128                     break;
129                 }
130                 ret.propertyPath = name.substr(0, pos);
131                 ++pos;
132                 // continue search from the member properties.
133                 properties = property->metaData.memberProperties;
134             } else if (name[pos] == '[') {
135                 if (!ParseIndex(name, baseOffset, *property, properties, pos, ret)) {
136                     break;
137                 }
138             }
139         } else if (!properties.empty()) {
140             ret = {};
141             break;
142         } else {
143             break;
144         }
145     }
146     return ret;
147 }
148 } // namespace
149 
PropertyData()150 PropertyData::PropertyData()
151 {
152     Reset();
153 }
154 
WLock(IPropertyHandle & handle)155 bool PropertyData::WLock(IPropertyHandle& handle) // no-copy direct-access (Locks the datahandle);
156 {
157     CORE_ASSERT(dataHandle_ == nullptr);
158     CORE_ASSERT(dataHandleW_ == nullptr);
159     dataHandleW_ = &handle;
160     dataHandle_ = dataHandleW_;
161     owner_ = dataHandleW_->Owner();
162     size_ = dataHandleW_->Size();
163     dataW_ = static_cast<uint8_t*>(dataHandleW_->WLock());
164     data_ = dataW_;
165     return true;
166 }
167 
WLock(IPropertyHandle & handle,const string_view propertyPath)168 PropertyData::PropertyOffset PropertyData::WLock(IPropertyHandle& handle, const string_view propertyPath)
169 {
170     if (WLock(handle)) {
171         const uintptr_t baseOffset = reinterpret_cast<uintptr_t>(dataW_);
172         if (auto po = FindProperty(Owner()->MetaData(), propertyPath, baseOffset); po) {
173             return po;
174         }
175     }
176     WUnlock(handle);
177     return { nullptr, 0U, 0U, {} };
178 }
179 
WUnlock(const IPropertyHandle & handle)180 bool PropertyData::WUnlock(const IPropertyHandle& handle) // (releases the datahandle lock, and removes ref)
181 {
182     CORE_ASSERT(dataHandleW_);
183     CORE_ASSERT(dataHandleW_ == dataHandle_);
184     CORE_ASSERT(dataHandleW_ == &handle);
185     if (dataHandleW_ == &handle) {
186         if (dataHandleW_) {
187             dataHandleW_->WUnlock();
188             Reset();
189         }
190         return true;
191     }
192     return false;
193 }
194 
RLock(const IPropertyHandle & handle)195 bool PropertyData::RLock(const IPropertyHandle& handle) // no-copy direct-access (Locks the datahandle);
196 {
197     CORE_ASSERT(dataHandle_ == nullptr);
198     CORE_ASSERT(dataHandleW_ == nullptr);
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     CORE_ASSERT(dataHandle_);
223     CORE_ASSERT(dataHandleW_ == nullptr);
224     CORE_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 
Get(size_t index)290 PropertyValue PropertyData::Get(size_t index)
291 {
292     if (owner_ && dataW_) {
293         const auto& props = owner_->MetaData();
294         if (index < props.size()) {
295             const auto& meta = props[index];
296             return PropertyValue(&meta, static_cast<void*>(dataW_ + meta.offset), meta.count);
297         }
298     }
299     return PropertyValue();
300 }
301 
Get(size_t index) const302 PropertyValue PropertyData::Get(size_t index) const
303 {
304     if (owner_ && data_) {
305         const auto& props = owner_->MetaData();
306         if (index < props.size()) {
307             const auto& meta = props[index];
308             return PropertyValue(&meta,
309                 const_cast<void*>(static_cast<const void*>(static_cast<const uint8_t*>(data_) + meta.offset)),
310                 meta.count);
311         }
312     }
313     return PropertyValue();
314 }
315 
PropertyCount() const316 size_t PropertyData::PropertyCount() const
317 {
318     if (owner_) {
319         const auto& props = owner_->MetaData();
320         return props.size();
321     }
322     return 0;
323 }
324 
Get(const string_view name)325 PropertyValue PropertyData::Get(const string_view name)
326 {
327     if (owner_ && dataW_) {
328         const auto& props = owner_->MetaData();
329         if (!props.empty()) {
330             const auto nameHash = FNV1aHash(name.data(), name.size());
331             for (const auto& meta : props) {
332                 if (meta.hash == nameHash && meta.name == name) {
333                     return PropertyValue(&meta, static_cast<void*>(dataW_ + meta.offset), meta.count);
334                 }
335             }
336         }
337     }
338     return PropertyValue();
339 }
340 
Get(const string_view name) const341 PropertyValue PropertyData::Get(const string_view name) const
342 {
343     if (owner_ && data_) {
344         const auto& props = owner_->MetaData();
345         if (!props.empty()) {
346             const auto nameHash = FNV1aHash(name.data(), name.size());
347             for (const auto& meta : props) {
348                 if (meta.hash == nameHash && meta.name == name) {
349                     return PropertyValue(&meta,
350                         const_cast<void*>(static_cast<const void*>(static_cast<const uint8_t*>(data_) + meta.offset)),
351                         meta.count);
352                 }
353             }
354         }
355     }
356     return PropertyValue();
357 }
358 
operator [](size_t index)359 PropertyValue PropertyData::operator[](size_t index)
360 {
361     return Get(index);
362 }
363 
operator [](size_t index) const364 PropertyValue PropertyData::operator[](size_t index) const
365 {
366     return Get(index);
367 }
368 
369 PropertyValue PropertyData::operator[](const string_view& name)
370 {
371     return Get(name);
372 }
373 
operator [](const string_view & name) const374 PropertyValue PropertyData::operator[](const string_view& name) const
375 {
376     return Get(name);
377 }
378 
379 const IPropertyApi* PropertyData::Owner() const
__anonf853e2af0502(const string_view& name) 380 {
381     return owner_;
382 }
383 
384 size_t PropertyData::Size() const
__anonf853e2af0602(const string_view& name) 385 {
386     return size_;
387 }
388 
389 const void* PropertyData::RLock() const
__anonf853e2af0702(const string_view& name) 390 {
391     return data_;
392 }
393 
394 void* PropertyData::WLock()
395 {
396     return dataW_;
397 }
398 
RUnlock() const399 void PropertyData::RUnlock() const {}
400 
WUnlock()401 void PropertyData::WUnlock() {}
402