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