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 33using namespace CORE_NS; 34using BASE_NS::array_view; 35using BASE_NS::FNV1aHash; 36using BASE_NS::string; 37using BASE_NS::string_view; 38 39namespace { 40bool 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 95PropertyData::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 150PropertyData::PropertyData() 151{ 152 Reset(); 153} 154 155bool 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 168PropertyData::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 180bool 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 195bool 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 208PropertyData::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 220bool 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 235PropertyData::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 245PropertyData::PropertyOffset PropertyData::FindProperty( 246 const array_view<const Property> properties, const string_view propertyPath) 247{ 248 return ::FindProperty(properties, propertyPath, 0U); 249} 250 251PropertyData::~PropertyData() 252{ 253 if (dataHandleW_) { 254 WUnlock(*dataHandleW_); 255 } 256 if (dataHandle_) { 257 RUnlock(*dataHandle_); 258 } 259} 260 261void PropertyData::Reset() 262{ 263 size_ = 0; 264 data_ = nullptr; 265 dataW_ = nullptr; 266 owner_ = nullptr; 267 dataHandle_ = nullptr; 268 dataHandleW_ = nullptr; 269} 270 271array_view<const Property> PropertyData::MetaData() const 272{ 273 if (owner_) { 274 return owner_->MetaData(); 275 } 276 return {}; 277} 278 279const 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 290PropertyValue 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 302PropertyValue 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 316size_t PropertyData::PropertyCount() const 317{ 318 if (owner_) { 319 const auto& props = owner_->MetaData(); 320 return props.size(); 321 } 322 return 0; 323} 324 325PropertyValue 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 341PropertyValue 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 359PropertyValue PropertyData::operator[](size_t index) 360{ 361 return Get(index); 362} 363 364PropertyValue PropertyData::operator[](size_t index) const 365{ 366 return Get(index); 367} 368 369PropertyValue PropertyData::operator[](const string_view& name) 370{ 371 return Get(name); 372} 373 374PropertyValue PropertyData::operator[](const string_view& name) const 375{ 376 return Get(name); 377} 378 379const IPropertyApi* PropertyData::Owner() const 380{ 381 return owner_; 382} 383 384size_t PropertyData::Size() const 385{ 386 return size_; 387} 388 389const void* PropertyData::RLock() const 390{ 391 return data_; 392} 393 394void* PropertyData::WLock() 395{ 396 return dataW_; 397} 398 399void PropertyData::RUnlock() const {} 400 401void PropertyData::WUnlock() {} 402