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