/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file parcel.h * * @brief Provides classes for the data container implemented in c_utils. * * The Parcel and Parcelable classes and the related memory * allocator are provided. */ #ifndef OHOS_UTILS_PARCEL_H #define OHOS_UTILS_PARCEL_H #include #include #include "nocopyable.h" #include "refbase.h" #include "flat_obj.h" namespace OHOS { class Parcel; /** * @brief Defines a class for which the instance can be written into a parcel. * * @note If this object is remote, its position will be used in * kernel data transaction. */ class Parcelable : public virtual RefBase { public: virtual ~Parcelable() = default; Parcelable(); /** * @brief Creates a `Parcelable` object. * * @param asRemote Specifies whether the object is remote. */ explicit Parcelable(bool asRemote); /** * @brief Writes a `Parcelable` object into a parcel. * * @param parcel Indicates the parcel. * @return Returns `true` if the operation is successful; returns `false` * otherwise. * @note If the `Parcelable` object is remote, its position will be saved * in the parcel. * @note You must implement a static Unmarshalling function to * fetch data from the given parcel into this `Parcelable` object. * See `static TestParcelable *Unmarshalling(Parcel &parcel)` as an example. */ virtual bool Marshalling(Parcel &parcel) const = 0; /** * @brief Enumerates the behavior types of a `Parcelable` object. * * @var IPC Indicate an object that can be used in IPC. * @var RPC Indicate an object that can be used in RPC. * @var HOLD_OBJECT Indicate an object that will be always alive * during data transaction. * */ enum BehaviorFlag { IPC = 0x01, RPC = 0x02, HOLD_OBJECT = 0x10 }; /** * @brief Enables the specified behavior. * * @param b Indicates the behavior. * @see BehaviorFlag. */ inline void SetBehavior(BehaviorFlag b) const { behavior_ |= static_cast(b); } /** * @brief Disables the specified behavior. * * @param b Indicates the behavior. * @see BehaviorFlag. */ inline void ClearBehavior(BehaviorFlag b) const { behavior_ &= static_cast(~b); } /** * @brief Checks whether the specified behavior is enabled. * * @param b Indicates the behavior. * @return Returns `true` if the behavior is enabled; returns `false` * otherwise. * @see BehaviorFlag. */ inline bool TestBehavior(BehaviorFlag b) const { return behavior_ & (static_cast(b)); } public: bool asRemote_; // If the object is remote. mutable uint8_t behavior_; // Behavior of the object. }; /** * @brief Defines a memory allocator for data in `Parcel`. */ class Allocator { public: virtual ~Allocator() = default; virtual void *Realloc(void *data, size_t newSize) = 0; virtual void *Alloc(size_t size) = 0; virtual void Dealloc(void *data) = 0; }; /** * @brief Provides the default implementation for a memory allocator. * * @note A non-default allocator for a parcel must be specified manually. */ class DefaultAllocator : public Allocator { public: /** * @brief Allocates memory for this parcel. * * @param size Indicates the size of the memory to allocate. * @return Returns the void pointer to the memory region. */ void *Alloc(size_t size) override; /** * @brief Deallocates memory for this parcel. * * @param data Indicates the void pointer to the memory region. */ void Dealloc(void *data) override; private: /** * @brief Reallocates memory for this parcel. * * @param data Indicates the void pointer to the existing memory region. * @param newSize Indicates the size of the memory to reallocate. * @return Returns the void pointer to the new memory region. */ void *Realloc(void *data, size_t newSize) override; }; /** * @brief Provides a data/message container. * * This class provides methods for writing and reading data of various types, * including primitives and parcelable objects. * * @note This class is usually used in IPC and RPC scenarios. */ class Parcel { public: Parcel(); /** * @brief Creates a `Parcel` object with the specified memory allocator. * * @param allocator Indicates the memory allocator. */ explicit Parcel(Allocator *allocator); virtual ~Parcel(); /** * @brief Obtains the total size of existing data in this parcel. * * @return Returns the size, in bytes. */ size_t GetDataSize() const; /** * @brief Obtains the pointer to the beginning of data in this parcel. * * @return Returns a pointer of the `uintptr_t` type. */ uintptr_t GetData() const; /** * @brief Obtains the position (offset) of every object written * in this parcel. * * @return Returns a pointer of the `binder_size_t` type to * the first slot of the position array. * @see flat_obj.h */ binder_size_t GetObjectOffsets() const; /** * @brief Obtains the size of the position array. * * @return Returns the size, in bytes. */ size_t GetOffsetsSize() const; /** * @brief Obtains the total number of available bytes to write * into this parcel. * * @return Returns the number of available bytes. */ size_t GetWritableBytes() const; /** * @brief Obtains the total number of available bytes to read * from this parcel. * * @return Returns the number of available bytes. */ size_t GetReadableBytes() const; /** * @brief Obtains the total capacity of this parcel, that is, the size of * the current data region in the parcel. * * @return Returns the capacity, in bytes. */ size_t GetDataCapacity() const; /** * @brief Obtains the maximum capacity of this parcel. * * @return Returns the capacity, in bytes. */ size_t GetMaxCapacity() const; /** * @brief Sets the capacity for this parcel, that is, the size of the * current data region in the parcel. * * @param newCapacity Indicates the capacity to set. * @return Returns `true` if the operation is successful; * returns `false` otherwise. * @note The memory allocator will try to reallocate the data region * with the new capacity. * */ bool SetDataCapacity(size_t newCapacity); /** * @brief Sets the total size of existing data in this parcel. * * @param dataSize Indicates the size, in bytes. * @return Returns `true` if the operation is successful; * returns `false` otherwise. * @note Do not call this function independently; otherwise, it may fail to * return the correct data size. */ bool SetDataSize(size_t dataSize); /** * @brief Sets the maximum capacity for this parcel. * * @param maxCapacity Indicates the maximum capacity to set. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool SetMaxCapacity(size_t maxCapacity); // write primitives in alignment bool WriteBool(bool value); bool WriteInt8(int8_t value); bool WriteInt16(int16_t value); bool WriteInt32(int32_t value); bool WriteInt64(int64_t value); bool WriteUint8(uint8_t value); bool WriteUint16(uint16_t value); bool WriteUint32(uint32_t value); bool WriteUint64(uint64_t value); bool WriteFloat(float value); bool WriteDouble(double value); bool WritePointer(uintptr_t value); /** * @brief Writes a data region (buffer) to this parcel. * * @param data Indicates the void pointer to the buffer. * @param size Indicates the size of the buffer. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteBuffer(const void *data, size_t size); /** * @brief Writes a data region (buffer) to this parcel in alignment * and with the terminator replaced. * * @param data Indicates the void pointer to the buffer. * @param size Indicates the size of the buffer. * @param typeSize Indicates the size of the terminator. * @return Returns `true` if the operation is successful; * returns `false` otherwise. * @note The last several bytes specified by `typeSize` of the aligned data * will be treated as a terminator and replaced by '0b00000000'. */ bool WriteBufferAddTerminator(const void *data, size_t size, size_t typeSize); /** * @brief Writes a data region (buffer) to this parcel. * * Currently, this function provides the same capability as `WriteBuffer()`. * * @param data Indicates the void pointer to the buffer. * @param size Indicates the size of the buffer. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteUnpadBuffer(const void *data, size_t size); /** * @brief Writes a C-style string to this parcel. * * The default terminator `\0` of the C-style string will also be written. * * @param value Indicates a pointer of the char type to a C-style string. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteCString(const char *value); /** * @brief Writes a C++ string (`std::string`) to this parcel. * * The exact length of the string will be written first, and then the string * itself with the appended terminator `\0` will be written. * * @param value Indicates the reference to an `std::string` object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteString(const std::string &value); /** * @brief Writes a C++ UTF-16 encoded string (`std::u16string`) * to this parcel. * * The exact length of the string will be written first, and then the string * itself with the appended terminator `\0` will be written. * * @param value Indicates the reference to an `std::u16string` object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteString16(const std::u16string &value); /** * @brief Writes a UTF-16 encoded string with the specified length * to this parcel. * * An `std::u16string` object will be constructed based on the `char16_t*` * pointer and the length `len` first. Then the input length and the string * data in the `u16string` object with the appended terminator `\0` will * be written. * * @param value Indicates the pointer to a UTF-16 encoded string. * @param len Indicates the exact length of the input string. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteString16WithLength(const char16_t *value, size_t len); /** * @brief Writes a UTF-8 encoded string with the specified length * to this parcel. * * The input length `len` and the string itself * with the appended terminator `\0` will be written. * * @param value Indicates the pointer to a UTF-8 encoded string. * @param len Indicates the exact length of the input string. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteString8WithLength(const char *value, size_t len); /** * @brief Writes a `Parcelable` object to this parcel. * * Call `WriteRemoteObject(const Parcelable *)` to write a remote object. * Call `Marshalling(Parcel &parcel)` to write a non-remote object. * * @param object Indicates the pointer to a `Parcelable` object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. * @note The value '0' of `Int32_t` will be written if a null pointer * is passed in. */ bool WriteParcelable(const Parcelable *object); /** * @brief Writes a `Parcelable` object to this parcel, and enables its * behavior of `HOLD_OBJECT`. * * @param object Indicates the smart pointer to a `Parcelable` object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteStrongParcelable(const sptr &object); /** * @brief Writes a remote object to this parcel. * * @param object Indicates the pointer to a remote object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. * @note If `HOLD_OBJECT` is enabled for the remote object, it will stay * alive as long as this parcel is alive. * */ bool WriteRemoteObject(const Parcelable *object); /** * @brief Writes an object to this parcel. * * Use its own `Marshalling(Parcel &parcel)` when a null pointer is passed * in; in other scenarios, use `WriteRemoteObject(const Parcelable *)`. * * @tparam T Indicates the class type of the object. * @param object Indicates the smart pointer to the object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ template bool WriteObject(const sptr &object); /** * @brief Parses input data by this parcel. * * @param data Indicates the pointer to input data. * @param size Indicates the size of the input data, in bytes. * @return Returns `true` if the operation is successful; * returns `false` otherwise. * @note Only the read operation from this parcel is allowed after * successful calling of this method. */ bool ParseFrom(uintptr_t data, size_t size); bool ReadBool(); int8_t ReadInt8(); int16_t ReadInt16(); int32_t ReadInt32(); int64_t ReadInt64(); uint8_t ReadUint8(); uint16_t ReadUint16(); uint32_t ReadUint32(); uint64_t ReadUint64(); float ReadFloat(); double ReadDouble(); uintptr_t ReadPointer(); bool ReadBool(bool &value); bool ReadInt8(int8_t &value); bool ReadInt16(int16_t &value); bool ReadInt32(int32_t &value); bool ReadInt64(int64_t &value); bool ReadUint8(uint8_t &value); bool ReadUint16(uint16_t &value); bool ReadUint32(uint32_t &value); bool ReadUint64(uint64_t &value); bool ReadFloat(float &value); bool ReadDouble(double &value); /** * @brief Reads a block of data (buffer data) from this parcel. * * @param length Indicates the size of the buffer, in bytes. * @return Returns a pointer of the `uint8_t` type to the buffer. */ const uint8_t *ReadBuffer(size_t length); /** * @brief Read a block of data (buffer data) from this parcel. * * @param length Size of the buffer(Bytes). * @return A `uint8_t` pointer to the buffer. */ const uint8_t *ReadBuffer(size_t length, bool isValidate); /** * @brief Reads a block of data (buffer data) without padding (alignment) * from this parcel. * * This method will read the effective data with the specified * `length` and discard the bytes used for padding. * * @param length Indicates the effective size of the buffer, in bytes. * @return Returns a pointer of the `uint8_t` type to the buffer. * */ const uint8_t *ReadUnpadBuffer(size_t length); /** * @brief Skips the next several bytes specified by `bytes` in the read * operation. * * @param bytes Indicates the number of bytes to skip. */ void SkipBytes(size_t bytes); /** * @brief Reads a C-style string from this parcel. * * @return Returns a pointer of the `char` type to the C-style string. */ const char *ReadCString(); /** * @brief Reads a C++ string (`std::string`) object from this parcel. * * @return Returns a pointer of the `std::string` type to the C-style * string. */ const std::string ReadString(); /** * @brief Reads a C++ string (`std::string`) object from this parcel to * an object. * * @param value Indicates the `std::string` object to hold the data read. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool ReadString(std::string &value); /** * @brief Reads a C++ UTF-16 encoded string (`std::u16string`) object * from this parcel. * * @return Returns a pointer of the `std::u16string` type to the C-style * string. */ const std::u16string ReadString16(); /** * @brief Reads a C++ UTF-16 string (`std::u16string`) object from this * parcel to an object. * * @param value Indicates the `std::u16string` object to hold the data read. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool ReadString16(std::u16string &value); /** * @brief Reads a C++ UTF-16 string (`std::u16string`) object and its length * from this parcel. * * @param len Indicates the reference to a variable of the `int32_t` type * to receive the length. * @return Returns an `std::u16string` object. */ const std::u16string ReadString16WithLength(int32_t &len); /** * @brief Reads a C++ string (`std::string`) object and its length from * this parcel. * * @param len Indicates the reference to a variable of the `int32_t` type * to receive the length. * @return Returns an `std::string` object. */ const std::string ReadString8WithLength(int32_t &len); /** * @brief Sets the read cursor to the specified position. * * @param newPosition Indicates the position, represented by the offset * (in bytes) from the beginning of the data region. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool RewindRead(size_t newPosition); /** * @brief Sets the write cursor to the specified position. * * @param offsets Indicates the position, represented by the offset * (in bytes) from the beginning of the data region. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool RewindWrite(size_t offsets); /** * @brief Obtains the current position of the read cursor. * * @return Returns the position, represented by the offset (in bytes) * from the beginning of the data region. */ size_t GetReadPosition(); /** * @brief Obtains the current position of the write cursor. * * @return Returns the position, represented by the offset (in bytes) * from the beginning of the data region. */ size_t GetWritePosition(); /** * @brief Reads a `Parcelable` object and its child class objects * from this parcel. * * @tparam T Indicates the class type of the output object. * @return Returns the object read. * @note A null pointer will be returned if '0' is read. */ template T *ReadParcelable(); /** * @brief Reads a `Parcelable` object from this parcel and manages it by a * smart pointer. * * @tparam T Indicates the class type of the output object. * @return Returns the object managed by a smart pointer. * @note A null pointer will be returned if '0' is read. */ template sptr ReadStrongParcelable(); /** * @brief Checks whether it is valid to read an object from the current * cursor. * * @return Returns `true` if valid; returns `false` otherwise. */ bool CheckOffsets(); /** * @brief Reads an object from this parcel. * * Call `CheckOffsets()` first to check whether it is valid to read an * object. * * @tparam T Indicates the class type of the output object. * @return Returns the smart pointer to the object. * @note A null pointer will be returned if `CheckOffsets()` fails * to be called. */ template sptr ReadObject(); /** * @brief Sets a memory allocator for this parcel. * * The new allocator will reallocate the data region that has been written. * * @param allocator Indicates the pointer to an `Allocator` object. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool SetAllocator(Allocator *allocator); /** * @brief Records an array of positions of this parcel. * * @param offsets Indicates the pointer to the position array. * @param offsetSize Indicates the size of the position array. * @note The method returns directly if the call fail. */ void InjectOffsets(binder_size_t offsets, size_t offsetSize); /** * @brief Deallocates the data region and resets this parcel. */ void FlushBuffer(); /** * @brief Writes an `std::vector` object to this parcel. * * @tparam T1 Indicates the class type for the vector. * @tparam T2 Indicates the class type for the write method of this parcel. * @param val Indicates the reference to the vector. * @param Write Indicates the `Parcel::Write(T2 value)` method. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ template bool WriteVector(const std::vector &val, bool (Parcel::*Write)(T2)); template bool WriteFixedAlignVector(const std::vector &originVal, bool (Parcel::*SpecialWrite)(T2)); bool WriteBoolVector(const std::vector &val); bool WriteInt8Vector(const std::vector &val); bool WriteInt16Vector(const std::vector &val); bool WriteInt32Vector(const std::vector &val); bool WriteInt64Vector(const std::vector &val); bool WriteUInt8Vector(const std::vector &val); bool WriteUInt16Vector(const std::vector &val); bool WriteUInt32Vector(const std::vector &val); bool WriteUInt64Vector(const std::vector &val); bool WriteFloatVector(const std::vector &val); bool WriteDoubleVector(const std::vector &val); bool WriteStringVector(const std::vector &val); bool WriteString16Vector(const std::vector &val); /** * @brief Reads an `std::vector` object from this parcel. * * @tparam T1 Indicates the class type for the vector. * @tparam T2 Indicates the class type for the read method of this parcel. * @param val Indicates the pointer to the vector. * @param Write Indicates the `Parcel::Read(T2 value)` method. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ template bool ReadVector(std::vector *val, bool (Parcel::*Read)(T &)); template bool ReadFixedAlignVector(std::vector *val, bool (Parcel::*SpecialRead)(T2 &)); bool ReadBoolVector(std::vector *val); bool ReadInt8Vector(std::vector *val); bool ReadInt16Vector(std::vector *val); bool ReadInt32Vector(std::vector *val); bool ReadInt64Vector(std::vector *val); bool ReadUInt8Vector(std::vector *val); bool ReadUInt16Vector(std::vector *val); bool ReadUInt32Vector(std::vector *val); bool ReadUInt64Vector(std::vector *val); bool ReadFloatVector(std::vector *val); bool ReadDoubleVector(std::vector *val); bool ReadStringVector(std::vector *val); bool ReadString16Vector(std::vector *val); // write raw primitive type(i.e. 1byte for bool, 4byte for `int16_t`, etc.) bool WriteBoolUnaligned(bool value); bool WriteInt8Unaligned(int8_t value); bool WriteInt16Unaligned(int16_t value); bool WriteUint8Unaligned(uint8_t value); bool WriteUint16Unaligned(uint16_t value); // read raw primitive type(i.e. 1byte for bool, 4byte for `int16_t`, etc.) bool ReadBoolUnaligned(); bool ReadInt8Unaligned(int8_t &value); bool ReadInt16Unaligned(int16_t &value); bool ReadUint8Unaligned(uint8_t &value); bool ReadUint16Unaligned(uint16_t &value); protected: /** * @brief Records the position of the written object, which is represented * by the offset from the beginning of the data region. * * @param offset Indicates the position. * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool WriteObjectOffset(binder_size_t offset); /** * @brief Ensures that the number of written objects is less than * the capacity of objects. * * If the data region is full, the capacity will be expanded. * * @return Returns `true` if the operation is successful; * returns `false` otherwise. */ bool EnsureObjectsCapacity(); private: DISALLOW_COPY_AND_MOVE(Parcel); template bool Write(T value); template bool Read(T &value); template T Read(); template bool ReadPadded(T &value); inline size_t GetPadSize(size_t size) { const size_t SIZE_OFFSET = 3; return (((size + SIZE_OFFSET) & (~SIZE_OFFSET)) - size); } size_t CalcNewCapacity(size_t minCapacity); bool WriteDataBytes(const void *data, size_t size); void WritePadBytes(size_t padded); bool EnsureWritableCapacity(size_t desireCapacity); bool WriteParcelableOffset(size_t offset); const uint8_t *BasicReadBuffer(size_t length); bool IsReadObjectData(const size_t nextObj, const size_t upperBound); bool ValidateReadData(size_t upperBound); void ClearObjects(); private: uint8_t *data_; size_t readCursor_; size_t writeCursor_; size_t dataSize_; size_t dataCapacity_; size_t maxDataCapacity_; binder_size_t *objectOffsets_; size_t nextObjectIdx_; size_t objectCursor_; size_t objectsCapacity_; Allocator *allocator_; std::vector> objectHolder_; bool writable_ = true; }; template bool Parcel::WriteObject(const sptr &object) { if (object == nullptr) { return T::Marshalling(*this, object); } return WriteRemoteObject(object.GetRefPtr()); } template sptr Parcel::ReadObject() { if (!this->CheckOffsets()) { return nullptr; } sptr res(T::Unmarshalling(*this)); return res; } // Read data from the given parcel into this parcelable object. template T *Parcel::ReadParcelable() { int32_t size = this->ReadInt32(); if (size == 0) { return nullptr; } return T::Unmarshalling(*this); } // Read data from the given parcel into this parcelable object, and return sptr. template sptr Parcel::ReadStrongParcelable() { int32_t size = this->ReadInt32(); if (size == 0) { return nullptr; } sptr res(T::Unmarshalling(*this)); return res; } } // namespace OHOS #endif