1 /*
2 * Copyright (c) 2022 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 {
ContainerIndex(const string_view name,size_t & pos)37 unsigned long ContainerIndex(const string_view name, size_t& pos)
38 {
39 const char* start = name.substr(pos).data();
40 char* end = nullptr;
41 const unsigned long index = std::strtoul(start, &end, 10); // 10: base
42 // check that conversion stopped at the closing square bracket
43 if (!end || *end != ']') {
44 return ULONG_MAX;
45 }
46 // move past the closing square bracket
47 pos = static_cast<size_t>(end - name.data()) + 1U;
48 return index;
49 }
50
ContainerOffset(const uintptr_t baseOffset,const Property & property,const PropertyData::PropertyOffset & ret,const unsigned long index)51 ptrdiff_t ContainerOffset(const uintptr_t baseOffset, const Property& property, const PropertyData::PropertyOffset& ret,
52 const unsigned long index)
53 {
54 ptrdiff_t offset = PTRDIFF_MAX;
55 // calculate offset to the index. for arrays a direct offset, but for containers need to get the addess
56 // inside the container and compensate the base and member offsets.
57 auto* containerMethods = property.metaData.containerMethods;
58 if (property.type.isArray && (index < property.count)) {
59 offset = static_cast<ptrdiff_t>(index * containerMethods->property.size);
60 } else if (!property.type.isArray && baseOffset && (index < containerMethods->size(property.offset + baseOffset))) {
61 offset = static_cast<ptrdiff_t>(
62 containerMethods->get(property.offset + baseOffset, index) - baseOffset - ret.offset);
63 }
64 return offset;
65 }
66
ParseIndex(const string_view name,const uintptr_t baseOffset,const Property & property,array_view<const Property> & properties,size_t & pos,PropertyData::PropertyOffset & ret)67 bool ParseIndex(const string_view name, const uintptr_t baseOffset, const Property& property,
68 array_view<const Property>& properties, size_t& pos, PropertyData::PropertyOffset& ret)
69 {
70 const unsigned long index = ContainerIndex(name, pos);
71 if (index == ULONG_MAX) {
72 return false;
73 }
74
75 // calculate offset to the index.
76 const ptrdiff_t offset = ContainerOffset(baseOffset, property, ret, index);
77 if (offset == PTRDIFF_MAX) {
78 return false;
79 }
80
81 auto* containerMethods = property.metaData.containerMethods;
82 ret.property = &containerMethods->property;
83 ret.offset = static_cast<uintptr_t>(static_cast<ptrdiff_t>(ret.offset) + offset);
84 ret.index = index;
85
86 if (pos < name.size() && name[pos] == '.') {
87 ++pos;
88 // continue search from the member properties.
89 properties = containerMethods->property.metaData.memberProperties;
90 }
91 return true;
92 }
93
FindProperty(array_view<const Property> properties,const string_view name,const uintptr_t baseOffset)94 PropertyData::PropertyOffset FindProperty(
95 array_view<const Property> properties, const string_view name, const uintptr_t baseOffset)
96 {
97 PropertyData::PropertyOffset ret { nullptr, 0U, 0U };
98
99 // trim down to name without array index or member variable.
100 static constexpr string_view delimiters = ".[";
101
102 size_t pos = 0U;
103 while (pos < name.size()) {
104 const auto delimPos = name.find_first_of(delimiters, pos);
105 auto baseName = name.substr(pos, delimPos - pos);
106 pos = delimPos;
107 if (baseName.empty()) {
108 ret = {};
109 break;
110 }
111
112 auto property = std::find_if(
113 properties.cbegin(), properties.cend(), [baseName](const Property& p) { return p.name == baseName; });
114 if (property != properties.cend()) {
115 // remember what property this was
116 ret.property = &*property;
117 ret.offset += property->offset;
118 ret.index = static_cast<size_t>(property - properties.cbegin());
119
120 // if we have only a name we are done
121 if (pos == string_view::npos) {
122 break;
123 } else if (name[pos] == '.') {
124 // there needs to be at least one character to be a valid name.
125 if ((name.size() - pos) < 2U) {
126 ret = {};
127 break;
128 }
129 ++pos;
130 // continue search from the member properties.
131 properties = property->metaData.memberProperties;
132 } else if (name[pos] == '[') {
133 // there needs to be at least three characters (e.g. [0]) to be a valid array index. the propery must
134 // also be an array.
135 static constexpr size_t minLength = 3U;
136 if (!property->metaData.containerMethods || (pos >= name.size()) || ((name.size() - pos) < minLength)) {
137 ret = {};
138 break;
139 }
140 ++pos;
141
142 if (!ParseIndex(name, baseOffset, *property, properties, pos, ret)) {
143 ret = {};
144 break;
145 }
146 }
147 } else if (!properties.empty()) {
148 ret = {};
149 break;
150 } else {
151 break;
152 }
153 }
154 return ret;
155 }
156 } // namespace
157
PropertyData()158 PropertyData::PropertyData()
159 {
160 Reset();
161 }
162
WLock(IPropertyHandle & handle)163 bool PropertyData::WLock(IPropertyHandle& handle) // no-copy direct-access (Locks the datahandle);
164 {
165 if (dataHandle_ || dataHandleW_) {
166 return false;
167 }
168 dataHandleW_ = &handle;
169 dataHandle_ = dataHandleW_;
170 owner_ = dataHandleW_->Owner();
171 size_ = dataHandleW_->Size();
172 dataW_ = static_cast<uint8_t*>(dataHandleW_->WLock());
173 data_ = dataW_;
174 return true;
175 }
176
WLock(IPropertyHandle & handle,const string_view propertyPath)177 PropertyData::PropertyOffset PropertyData::WLock(IPropertyHandle& handle, const string_view propertyPath)
178 {
179 if (WLock(handle)) {
180 const uintptr_t baseOffset = reinterpret_cast<uintptr_t>(dataW_);
181 if (auto po = FindProperty(Owner()->MetaData(), propertyPath, baseOffset); po) {
182 return po;
183 }
184 }
185 WUnlock(handle);
186 return { nullptr, 0U, 0U };
187 }
188
WUnlock(const IPropertyHandle & handle)189 bool PropertyData::WUnlock(const IPropertyHandle& handle) // (releases the datahandle lock, and removes ref)
190 {
191 BASE_ASSERT(dataHandleW_);
192 BASE_ASSERT(dataHandleW_ == dataHandle_);
193 BASE_ASSERT(dataHandleW_ == &handle);
194 if (dataHandleW_ == &handle) {
195 if (dataHandleW_) {
196 dataHandleW_->WUnlock();
197 Reset();
198 }
199 return true;
200 }
201 return false;
202 }
203
RLock(const IPropertyHandle & handle)204 bool PropertyData::RLock(const IPropertyHandle& handle) // no-copy direct-access (Locks the datahandle);
205 {
206 if (dataHandle_ || dataHandleW_) {
207 return false;
208 }
209 dataHandleW_ = nullptr;
210 dataHandle_ = &handle;
211 owner_ = dataHandle_->Owner();
212 size_ = dataHandle_->Size();
213 data_ = dataHandle_->RLock();
214 dataW_ = nullptr;
215 return true;
216 }
217
RLock(const IPropertyHandle & handle,const string_view propertyPath)218 PropertyData::PropertyOffset PropertyData::RLock(const IPropertyHandle& handle, const string_view propertyPath)
219 {
220 if (RLock(handle)) {
221 const uintptr_t baseOffset = reinterpret_cast<uintptr_t>(data_);
222 if (auto po = FindProperty(Owner()->MetaData(), propertyPath, baseOffset); po) {
223 return po;
224 }
225 }
226 RUnlock(handle);
227 return { nullptr, 0U, 0U };
228 }
229
RUnlock(const IPropertyHandle & handle)230 bool PropertyData::RUnlock(const IPropertyHandle& handle) // (releases the datahandle lock, and removes ref)
231 {
232 BASE_ASSERT(dataHandle_);
233 BASE_ASSERT(dataHandleW_ == nullptr);
234 BASE_ASSERT(dataHandle_ == &handle);
235 if (dataHandle_ == &handle) {
236 if (dataHandle_) {
237 dataHandle_->RUnlock();
238 Reset();
239 }
240 return true;
241 }
242 return false;
243 }
244
FindProperty(const array_view<const Property> properties,const string_view propertyPath,const uintptr_t baseOffset)245 PropertyData::PropertyOffset PropertyData::FindProperty(
246 const array_view<const Property> properties, const string_view propertyPath, const uintptr_t baseOffset)
247 {
248 PropertyData::PropertyOffset offset = ::FindProperty(properties, propertyPath, baseOffset);
249 if (offset) {
250 offset.offset += baseOffset;
251 }
252 return offset;
253 }
254
FindProperty(const array_view<const Property> properties,const string_view propertyPath)255 PropertyData::PropertyOffset PropertyData::FindProperty(
256 const array_view<const Property> properties, const string_view propertyPath)
257 {
258 return ::FindProperty(properties, propertyPath, 0U);
259 }
260
FindProperty(const IPropertyHandle & handle,const string_view propertyPath)261 PropertyData::PropertyOffset PropertyData::FindProperty(const IPropertyHandle& handle, const string_view propertyPath)
262 {
263 if (auto owner = handle.Owner()) {
264 const auto baseAddress = reinterpret_cast<uintptr_t>(handle.RLock());
265 handle.RUnlock();
266 return ::FindProperty(owner->MetaData(), propertyPath, baseAddress);
267 }
268 return {};
269 }
270
~PropertyData()271 PropertyData::~PropertyData()
272 {
273 if (dataHandleW_) {
274 WUnlock(*dataHandleW_);
275 }
276 if (dataHandle_) {
277 RUnlock(*dataHandle_);
278 }
279 }
280
Reset()281 void PropertyData::Reset()
282 {
283 size_ = 0;
284 data_ = nullptr;
285 dataW_ = nullptr;
286 owner_ = nullptr;
287 dataHandle_ = nullptr;
288 dataHandleW_ = nullptr;
289 }
290
MetaData() const291 array_view<const Property> PropertyData::MetaData() const
292 {
293 if (owner_) {
294 return owner_->MetaData();
295 }
296 return {};
297 }
298
MetaData(size_t index) const299 const Property* PropertyData::MetaData(size_t index) const
300 {
301 if (owner_) {
302 const auto& meta = owner_->MetaData();
303 if (index < meta.size()) {
304 return &meta[index];
305 }
306 }
307 return nullptr;
308 }
309
PropertyCount() const310 size_t PropertyData::PropertyCount() const
311 {
312 if (owner_) {
313 const auto& props = owner_->MetaData();
314 return props.size();
315 }
316 return 0;
317 }
318
Owner() const319 const IPropertyApi* PropertyData::Owner() const
320 {
321 return owner_;
322 }
323
Size() const324 size_t PropertyData::Size() const
325 {
326 return size_;
327 }
328
RLock() const329 const void* PropertyData::RLock() const
330 {
331 return data_;
332 }
333
WLock()334 void* PropertyData::WLock()
335 {
336 return dataW_;
337 }
338
RUnlock() const339 void PropertyData::RUnlock() const {}
340
WUnlock()341 void PropertyData::WUnlock() {}
342