• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #ifndef BASE_MESSAGE_H
16 #define BASE_MESSAGE_H
17 #include <functional>
18 #include <vector>
19 
20 #include "securec.h"
21 #include "varint_encode.h"
22 // for struct RandomWriteCtx
23 #include "plugin_module_api.h"
24 
25 namespace OHOS {
26 namespace Developtools {
27 namespace Profiler {
28 namespace ProtoEncoder {
29 // https://developers.google.com/protocol-buffers/docs/encoding
30 // ID   Name    Used For
31 // 0    VARINT  int32, int64, uint32, uint64, sint32, sint64, bool, enum
32 // 1    I64     fixed64, sfixed64, double
33 // 2    LEN     string, bytes, embedded messages, packed repeated fields
34 // 3    SGROUP  group start (deprecated)
35 // 4    EGROUP  group end (deprecated)
36 // 5    I32     fixed32, sfixed32, float
37 enum ProtoMessageType : uint32_t {
38     VARINT = 0,
39     I64 = 1,
40     LEN = 2,
41     I32 = 5,
42 };
43 
44 constexpr uint8_t FIELDID_SHIFT = 3;
45 constexpr uint8_t SIZE_FIXED32 = 4;
46 constexpr uint8_t SIZE_FIXED64 = 8;
47 constexpr uint8_t SIZE_RESERVED_LEN = 4;
48 
49 class MessagePool;
50 
51 class BaseMessage {
52 public:
BaseMessage()53     BaseMessage()
54     {
55         Reset(nullptr, nullptr);
56     }
57 
58     explicit BaseMessage(RandomWriteCtx* writeCtx, MessagePool* messagePool = nullptr)
59     {
60         Reset(writeCtx, messagePool);
61     }
62 
~BaseMessage()63     ~BaseMessage()
64     {
65         if (isWriting_) {
66             Finish();
67         }
68     }
69 
70     void Reset(RandomWriteCtx* writeCtx = nullptr, MessagePool* messagePool = nullptr)
71     {
72         if (subMessage_ != nullptr) {
73             FinishSubMessage();
74         }
75 
76         writeCtx_ = writeCtx;
77         subMessageStack_ = messagePool;
78         backfillOffset_ = 0;
79         if (writeCtx == nullptr) {
80             // can not write
81             isWriting_ = false;
82             size_ = -1;
83             return;
84         }
85         size_ = 0;
86         isWriting_ = true;
87     }
88 
89     // return current size of message has wrote, < 0 if writing failed
Size()90     inline int32_t Size()
91     {
92         return size_;
93     }
94 
95     // finish message and return size of message, < 0 if writing failed
Finish()96     inline int32_t Finish()
97     {
98         if (!isWriting_) {
99             return size_;
100         }
101 
102         if (subMessage_ != nullptr) {
103             FinishSubMessage();
104         }
105 
106         isWriting_ = false;
107         return size_;
108     }
109 
110     // ID Name Used For
111     // 5  I32  fixed32, sfixed32, float
112     template<typename T>
AddFixed32(uint32_t fieldId,T v)113     void AddFixed32(uint32_t fieldId, T v)
114     {
115         static_assert(sizeof(T) == SIZE_FIXED32, "AddFixed32: T is not 32 bits");
116         if (subMessage_ != nullptr) {
117             FinishSubMessage();
118         }
119         if (!isWriting_) {
120             return;
121         }
122 
123         uint8_t* fieldMemory = nullptr;
124         uint32_t fieldOffset = 0;
125         // max field size = varint(fieldId + type) + Fixed32(v)
126         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + SIZE_FIXED32,
127                                   &fieldMemory, &fieldOffset)) {
128             Drop();
129             return;
130         }
131 
132         uint32_t tagSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::I32);
133         // T must be little-endian
134         if (memcpy_s(fieldMemory + tagSize, SIZE_FIXED32, &v, SIZE_FIXED32) != EOK) {
135             Drop();
136             return;
137         }
138 
139         writeCtx_->seek(writeCtx_, fieldOffset + tagSize + SIZE_FIXED32);
140         size_ += tagSize + SIZE_FIXED32;
141     }
142 
143     // ID Name Used For
144     // 1  I64  fixed64, sfixed64, double
145     template<typename T>
AddFixed64(uint32_t fieldId,T v)146     void AddFixed64(uint32_t fieldId, T v)
147     {
148         static_assert(sizeof(T) == SIZE_FIXED64, "AddFixed64: T is not 64 bits");
149         if (subMessage_ != nullptr) {
150             FinishSubMessage();
151         }
152         if (!isWriting_) {
153             return;
154         }
155 
156         uint8_t* fieldMemory = nullptr;
157         uint32_t fieldOffset = 0;
158         // max field size = varint(fieldId + type) + Fixed64(v)
159         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + SIZE_FIXED64,
160                                   &fieldMemory, &fieldOffset)) {
161             Drop();
162             return;
163         }
164 
165         uint32_t tagSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::I64);
166         // T must be little-endian
167         if (memcpy_s(fieldMemory + tagSize, SIZE_FIXED64, &v, SIZE_FIXED64) != EOK) {
168             Drop();
169             return;
170         }
171 
172         writeCtx_->seek(writeCtx_, fieldOffset + tagSize + SIZE_FIXED64);
173         size_ += tagSize + SIZE_FIXED64;
174     }
175 
176     // ID Name   Used For(unsigned and 'intN' varint)
177     // 0  VARINT int32, int64, uint32, uint64, bool, enum
178     template<typename T>
AddVarint(uint32_t fieldId,T v)179     void AddVarint(uint32_t fieldId, T v)
180     {
181         if (subMessage_ != nullptr) {
182             FinishSubMessage();
183         }
184         if (!isWriting_) {
185             return;
186         }
187 
188         uint8_t* fieldMemory = nullptr;
189         uint32_t fieldOffset = 0;
190         // max field size = varint(fieldId + type) + varint(v)
191         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + VARINT_ENCODE_MAX_SIZE,
192                                   &fieldMemory, &fieldOffset)) {
193             Drop();
194             return;
195         }
196 
197         uint32_t fieldSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::VARINT);
198         fieldSize += EncodeVarint(fieldMemory + fieldSize, v);
199         writeCtx_->seek(writeCtx_, fieldOffset + fieldSize);
200         size_ += fieldSize;
201     }
202 
203     // ID Name   Used For('sintN' varint, ZigZag encode)
204     // 0  VARINT sint32, sint64
205     template<typename T>
AddZigZagVarint(uint32_t fieldId,T v)206     void AddZigZagVarint(uint32_t fieldId, T v)
207     {
208         if (subMessage_ != nullptr) {
209             FinishSubMessage();
210         }
211         if (!isWriting_) {
212             return;
213         }
214 
215         uint8_t* fieldMemory = nullptr;
216         uint32_t fieldOffset = 0;
217         // max field size = varint(fieldId + type) + varint(v)
218         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + VARINT_ENCODE_MAX_SIZE,
219                                   &fieldMemory, &fieldOffset)) {
220             Drop();
221             return;
222         }
223 
224         uint32_t fieldSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::VARINT);
225         fieldSize += EncodeZigZagVarint(fieldMemory + fieldSize, v);
226         writeCtx_->seek(writeCtx_, fieldOffset + fieldSize);
227         size_ += fieldSize;
228     }
229 
230     // ID Name Used For
231     // 2  LEN  bytes, string
232     void AddBytes(uint32_t fieldId, const void* data, uint32_t dataSize);
233 
234     // add customize data, return RandomWriteCtx pointer, caller implement:
235     // 1, write data by writeCtx->write() directly;
236     // 2, constructor Message object by writeCtx, and fill data by method of Message
237     RandomWriteCtx* StartAddBytes(uint32_t fieldId);
238     void FinishAddBytes(int32_t size);
239 
240     // get data by getData(), implement in getData() function:
241     // 1, write data by randomWriteCtx->write() directly;
242     // 2, constructor Message object by randomWriteCtx, and fill data by method of Message
243     using GetDataCallback = std::function<int32_t(RandomWriteCtx* randomWriteCtx)>;
244     void AddBytesByCallBack(uint32_t fieldId, GetDataCallback getData);
245 
246     // ID Name Used For
247     // 2  LEN  embedded messages
248     template<typename T>
AddSubMessage(uint32_t fieldId)249     T* AddSubMessage(uint32_t fieldId)
250     {
251         static_assert(std::is_base_of<BaseMessage, T>::value,
252                       "SubMessage must be a derived class of BaseMessage");
253         static_assert(sizeof(T) == sizeof(BaseMessage),
254                       "Size of SubMessage class must be equal to BaseMessage");
255         if (subMessage_ != nullptr) {
256             FinishSubMessage();
257         }
258         if (!isWriting_) {
259             // return message self pointer for business,
260             // business can call message->set_XXX() without checking message is nullptr
261             return static_cast<T*>(this);
262         }
263 
264         uint8_t* fieldMemory = nullptr;
265         // varint(fieldId) + varint(len)
266         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + SIZE_RESERVED_LEN,
267                                   &fieldMemory, &backfillOffset_)) {
268             Drop();
269             return static_cast<T*>(this);
270         }
271 
272         uint32_t tagSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::LEN);
273         backfillOffset_ += tagSize;
274         size_ += tagSize;
275         // reserve length space
276         writeCtx_->seek(writeCtx_, backfillOffset_ + SIZE_RESERVED_LEN);
277         if (!AllocateSubMessage()) {
278             Drop();
279             return static_cast<T*>(this);
280         }
281         return static_cast<T*>(subMessage_);
282     }
283 
284     // ID Name Used For
285     // 2  LEN  packed repeated fields(unsigned and 'intN' varint)
286     template<typename T>
AddPackedVarint(uint32_t fieldId,const T * array,uint32_t arrayCount)287     inline void AddPackedVarint(uint32_t fieldId, const T* array, uint32_t arrayCount)
288     {
289         if (arrayCount == 0) {
290             return;
291         }
292         if (subMessage_ != nullptr) {
293             FinishSubMessage();
294         }
295         if (!isWriting_) {
296             return;
297         }
298 
299         uint32_t maxLen = 0;
300         uint32_t lenSize = GetPackedVarintLenSize(arrayCount, sizeof(T), maxLen);
301         if (lenSize == 0) {
302             Drop();
303             return;
304         }
305 
306         uint8_t* fieldMemory = nullptr;
307         uint32_t fieldOffset = 0;
308         // varint(fieldId) + lenSize + maxLen
309         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + lenSize + maxLen,
310                                   &fieldMemory, &fieldOffset)) {
311             Drop();
312             return;
313         }
314 
315         uint32_t tagSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::LEN);
316         // encode array of varint first
317         uint32_t dataSize = 0;
318         uint8_t* pData = fieldMemory + tagSize + lenSize;
319         for (uint32_t i = 0; i < arrayCount; i++) {
320             dataSize += EncodeVarint(pData + dataSize, *array);
321             array++;
322         }
323         // varint(Length)
324         EncodeVarintPadding(fieldMemory + tagSize, dataSize, lenSize);
325         size_ += tagSize + lenSize + dataSize;
326         // seek to tail
327         writeCtx_->seek(writeCtx_, fieldOffset + tagSize + lenSize + dataSize);
328     }
329 
330     // ID Name Used For
331     // 2  LEN  packed repeated fields(I32 and I64)
332     template<typename T>
AddPackedFixed(uint32_t fieldId,const T * array,uint32_t arrayCount)333     inline void AddPackedFixed(uint32_t fieldId, const T* array, uint32_t arrayCount)
334     {
335         static_assert(sizeof(T) == SIZE_FIXED32 || sizeof(T) == SIZE_FIXED64,
336                       "AddPackedFixed: T is not 32 or 64 bits");
337         if (arrayCount == 0) {
338             return;
339         }
340         if (subMessage_ != nullptr) {
341             FinishSubMessage();
342         }
343         if (!isWriting_) {
344             return;
345         }
346 
347         uint32_t dataSize = arrayCount * sizeof(T);
348         uint8_t* fieldMemory = nullptr;
349         uint32_t fieldOffset = 0;
350         // varint(fieldId) + varint(dataSize) + dataSize
351         if (!writeCtx_->getMemory(writeCtx_, VARINT_ENCODE_MAX_SIZE + SIZE_RESERVED_LEN + dataSize,
352                                   &fieldMemory, &fieldOffset)) {
353             Drop();
354             return;
355         }
356 
357         uint32_t tagSize = EncodeVarint(fieldMemory, (fieldId << FIELDID_SHIFT) | ProtoMessageType::LEN);
358         tagSize += EncodeVarint(fieldMemory + tagSize, dataSize);
359         if (memcpy_s(fieldMemory + tagSize, dataSize, array, dataSize) != EOK) {
360             Drop();
361             return;
362         }
363 
364         size_ += tagSize + dataSize;
365         // seek to tail
366         writeCtx_->seek(writeCtx_, fieldOffset + tagSize + dataSize);
367     }
368 
369 private:
370     RandomWriteCtx* writeCtx_ = nullptr;
371     MessagePool* subMessageStack_ = nullptr;
372     BaseMessage* subMessage_ = nullptr;
373     int32_t size_ = 0;
374     uint32_t backfillOffset_ = 0;
375     bool isWriting_ = true; // false when Finish() or Drop()
376 
377     bool AllocateSubMessage();
378     void FinishSubMessage();
379 
Drop()380     inline void Drop()
381     {
382         isWriting_ = false;
383         size_ = -1;
384         return;
385     }
386 };
387 
388 constexpr uint32_t DEFAULT_SUBMESSAGE_DEPTH = 10;
389 // MessagePool is cache of the BaseMessage's submessage, avoid new and delete multiple times
390 // ONE BaseMessage corresponds to ONE MessagePool
391 class MessagePool {
392 public:
393     explicit MessagePool(uint32_t depth = DEFAULT_SUBMESSAGE_DEPTH)
394     {
395         Reset(depth);
396     }
397 
398     inline void Reset(uint32_t depth = DEFAULT_SUBMESSAGE_DEPTH)
399     {
400         messageCache_.resize(depth);
401         cursor_ = 0;
402     }
403 
Get()404     BaseMessage* Get()
405     {
406         if (cursor_ < messageCache_.size()) {
407             return &messageCache_[cursor_++];
408         } else {
409             auto& msg = messageCache_.emplace_back();
410             cursor_++;
411             return &msg;
412         }
413     }
414 
Release()415     inline void Release()
416     {
417         if (cursor_ > 0) {
418             cursor_--;
419         }
420     }
421 private:
422     std::vector<BaseMessage> messageCache_;
423     uint32_t cursor_ = 0;
424 };
425 } // namespace ProtoEncoder
426 } // namespace Profiler
427 } // namespace Developtools
428 } // namespace OHOS
429 #endif // BASE_MESSAGE_H
430