1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
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 "proto_reader.h"
17#include <cinttypes>
18#include "optimize.h"
19
20namespace SysTuning {
21namespace ProtoReader {
22std::map<ProtoWireType, ProtoReaderBase::ParseDataAreaValueByType> ProtoReaderBase::DATA_AREA_TYPE_TO_PARSE_FUNC_MAP = {
23    {ProtoWireType::kVarInt, ProtoReaderBase::ParseVarIntValue},
24    {ProtoWireType::kLengthDelimited, ProtoReaderBase::ParseLengthDelimitedValue},
25    {ProtoWireType::kFixed32, ProtoReaderBase::ParseFixed32Value},
26    {ProtoWireType::kFixed64, ProtoReaderBase::ParseFixed64Value},
27};
28ProtoReaderBase::ProtoReaderBase(DataArea *storage, uint32_t dataAreasCount, const uint8_t *buffer, size_t length)
29    : startAddr_(buffer),
30      endAddr_(startAddr_ + length),
31      currentReadAddr_(startAddr_),
32      dataAreas_(storage),
33      dataAreasCount_(dataAreasCount),
34      size_(dataAreasCount),
35      volume_(dataAreasCount)
36{
37    auto dataAreasSize = sizeof(DataArea) * dataAreasCount_;
38    (void)memset_s(dataAreas_, dataAreasSize, 0, dataAreasSize);
39}
40
41// return next parse addr and dataAreaTag. if failed returns nullptr
42const uint8_t *ProtoReaderBase::GetNextProtoTag(const uint8_t *const startAddr,
43                                                const uint8_t *const endAddr,
44                                                uint64_t *dataAreaTag)
45{
46    const uint8_t *cursor = startAddr;
47    if (*cursor & BYTE_HIGHEST_BIT_MARK) {
48        const uint8_t *nextAddr = VarIntDecode(cursor, endAddr, dataAreaTag);
49        if (cursor == nextAddr) {
50            return nullptr;
51        }
52        cursor = nextAddr;
53    } else {
54        *dataAreaTag = *(cursor++);
55    }
56    if (cursor >= endAddr) {
57        return nullptr;
58    }
59    return cursor;
60}
61
62bool ProtoReaderBase::ParseVarIntValue(ParseDataAreaResult &result,
63                                       const uint8_t *startAddr,
64                                       const uint8_t *const endAddr)
65{
66    uint64_t intValue = 0;
67    auto cursor = VarIntDecode(startAddr, endAddr, &intValue);
68    if (cursor == startAddr) {
69        return false;
70    }
71    result.dataArea.SetDataAreaIntValue(intValue);
72    result.next = cursor;
73    result.status = OK;
74    return true;
75}
76
77bool ProtoReaderBase::ParseLengthDelimitedValue(ParseDataAreaResult &result,
78                                                const uint8_t *startAddr,
79                                                const uint8_t *const endAddr)
80{
81    uint64_t length = 0;
82    auto cursor = VarIntDecode(startAddr, endAddr, &length);
83    if (cursor == startAddr || length > static_cast<uint64_t>(endAddr - cursor)) {
84        return false;
85    }
86    const uintptr_t dataStartAddr = reinterpret_cast<uintptr_t>(cursor);
87    result.dataArea.SetDataAreaIntValue(dataStartAddr);
88    result.dataArea.SetDataAreaSize(length);
89    result.next = cursor + length;
90    if (length > kMaxMessageLength) {
91        TS_LOGD("Skip this data, because it is too large. length: %" PRIu64 "", length);
92        result.status = SKIP;
93        return true;
94    }
95    result.status = OK;
96    return true;
97}
98
99bool ProtoReaderBase::ParseFixed64Value(ParseDataAreaResult &result,
100                                        const uint8_t *startAddr,
101                                        const uint8_t *const endAddr)
102{
103    uint64_t intValue = 0;
104    auto cursor = startAddr + sizeof(uint64_t);
105    if (cursor > endAddr) {
106        return false;
107    }
108
109    (void)memcpy_s(&intValue, sizeof(uint64_t), startAddr, sizeof(uint64_t));
110    result.dataArea.SetDataAreaIntValue(intValue);
111    result.next = cursor;
112    result.status = OK;
113    return true;
114}
115
116bool ProtoReaderBase::ParseFixed32Value(ParseDataAreaResult &result,
117                                        const uint8_t *startAddr,
118                                        const uint8_t *const endAddr)
119{
120    uint64_t intValue = 0;
121    auto cursor = startAddr + sizeof(uint32_t);
122    if (cursor > endAddr) {
123        return false;
124    }
125    (void)memcpy_s(&intValue, sizeof(uint64_t), startAddr, sizeof(uint32_t));
126    result.dataArea.SetDataAreaIntValue(intValue);
127    result.next = cursor;
128    result.status = OK;
129    return true;
130}
131
132ParseDataAreaResult ProtoReaderBase::ParseOneDataArea(const uint8_t *const startAddr, const uint8_t *const endAddr)
133{
134    ParseDataAreaResult result = {ABORT, startAddr, DataArea{}};
135    if (!startAddr || startAddr >= endAddr) {
136        return result;
137    }
138    uint64_t dataAreaTag = 0;
139    auto cursor = GetNextProtoTag(startAddr, endAddr, &dataAreaTag);
140    if (!cursor) {
141        return result;
142    }
143    uint32_t dataAreaId = static_cast<uint32_t>(dataAreaTag >> DATA_AREA_TYPE_BITS);
144    if (dataAreaId == 0) {
145        return result;
146    }
147    if (TS_UNLIKELY(dataAreaId > std::numeric_limits<uint16_t>::max())) {
148        TS_LOGD("Skip dataArea %d because its too big", dataAreaId);
149        result.status = ParseProtoStatus::SKIP;
150        result.next = cursor;
151        return result;
152    }
153    result.dataArea.SetDataAreaId(dataAreaId);
154
155    auto dataAreaType = static_cast<uint8_t>(dataAreaTag) & DATA_AREA_TYPE_VALUE;
156    result.dataArea.SetDataAreaType(dataAreaType);
157
158    auto itor = DATA_AREA_TYPE_TO_PARSE_FUNC_MAP.find(static_cast<ProtoWireType>(dataAreaType));
159    if (itor == DATA_AREA_TYPE_TO_PARSE_FUNC_MAP.end()) {
160        return result;
161    }
162    itor->second(result, cursor, endAddr_);
163    return result;
164}
165
166DataArea ProtoReaderBase::FindDataArea(uint32_t dataAreaId)
167{
168    DataArea dataArea{};
169    auto temp = currentReadAddr_;
170    currentReadAddr_ = startAddr_;
171    for (auto nextDataArea = ReadNextDataArea(); nextDataArea.DataAreaValid(); nextDataArea = ReadNextDataArea()) {
172        if (nextDataArea.DataAreaId() == dataAreaId) {
173            dataArea = nextDataArea;
174            break;
175        }
176    }
177    currentReadAddr_ = temp;
178    return dataArea;
179}
180TS_INLINE
181DataArea ProtoReaderBase::ReadNextDataArea()
182{
183    ParseDataAreaResult result = {ABORT, currentReadAddr_, DataArea{}};
184    do {
185        result = ParseOneDataArea(currentReadAddr_, endAddr_);
186        currentReadAddr_ = result.next;
187    } while (result.status == ParseProtoStatus::SKIP);
188    return result.dataArea;
189}
190
191void ProtoReaderBase::ParseAllDataAreas()
192{
193    const uint8_t *cur = startAddr_;
194    ParseDataAreaResult result = {ABORT, startAddr_, DataArea{}};
195    while (true) {
196        result = ParseOneDataArea(cur, endAddr_);
197        cur = result.next;
198        if (result.status == ParseProtoStatus::SKIP) {
199            continue;
200        } else if (result.status == ParseProtoStatus::ABORT) {
201            break;
202        }
203        auto dataAreaId = result.dataArea.DataAreaId();
204        if (dataAreaId >= dataAreasCount_) {
205            continue;
206        }
207
208        DataArea *dataArea = &dataAreas_[dataAreaId];
209        if (!dataArea->DataAreaValid()) {
210            *dataArea = std::move(result.dataArea);
211        } else {
212            if (size_ >= volume_) {
213                MoveToLargerHeapStorage();
214                dataArea = &dataAreas_[dataAreaId];
215            }
216            dataAreas_[size_++] = *dataArea;
217            *dataArea = std::move(result.dataArea);
218        }
219    }
220    currentReadAddr_ = result.next;
221}
222
223void ProtoReaderBase::MoveToLargerHeapStorage()
224{
225    uint32_t largerVolume = volume_ << 1;
226    std::unique_ptr<DataArea[]> largerVolumeStorage = std::make_unique<DataArea[]>(largerVolume);
227    (void)memcpy_s(&largerVolumeStorage[0], sizeof(DataArea) * largerVolume, dataAreas_, sizeof(DataArea) * size_);
228    lagerHeapStorage_ = std::move(largerVolumeStorage);
229    dataAreas_ = &lagerHeapStorage_[0];
230    volume_ = largerVolume;
231}
232} // namespace ProtoReader
233} // namespace SysTuning
234