11 Star 46 Fork 141

OpenHarmony / commonlibrary_c_utils

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
c-utils-guide-parcel.md 15.80 KB
一键复制 编辑 原始数据 按行查看 历史
lijincheng 提交于 2024-01-31 18:07 . Update document

使用Parcel作为数据容器

概述

Parcel对象是一个数据容器,其提供一个内存空间以供数据写入。Parcel对象不仅支持诸如int,float,double等基本类型的写入,同时也支持扁平化地写入一个继承了Parcelable类的子类对象。可用于IPC中以实现数据通信功能。

涉及功能

OHOS::Parcel

数据/消息的容器类。

具体描述

class OHOS::Parcel;

包含用于写入以及读出多种类型的数据,包括基本类型、Parcelable对象等。

#include <parcel.h>

Public Functions

返回类型 名称
Parcel()
Parcel(Allocator* allocator)
构造Parcel对象,并指定内存分配器Allcator。
virtual ~Parcel()
bool CheckOffsets()
检查从当前光标读取对象的操作是否可行。
void FlushBuffer()
释放parcel中的数据区域,并重置该parcel状态。
uintptr_t GetData() const
获取指向当前parcel中数据起始位置的指针。
size_t GetDataCapacity() const
获取当前parcel的总体容量(字节),即parcel中数据区域的当前总大小。
size_t GetDataSize() const
获取当前parcel中已存在数据的总大小。
size_t GetMaxCapacity() const
binder_size_t GetObjectOffsets() const
获取写入至当前parcel的每一个oject的具体位置。
size_t GetOffsetsSize() const
获取当前存储的所有object的位置的总大小。
size_t GetReadableBytes() const
获取剩余可从当前parcel读出的总字节数。
size_t GetReadPosition()
获取当前读光标位置。
size_t GetWritableBytes() const
获取剩余可向当前parcel写入的总字节数。
size_t GetWritePosition()
获取当前写光标位置。
void InjectOffsets(binder_size_t offsets, size_t offsetSize)
记录一个数组,该数组内包含多个对象的位置偏移量。
bool ParseFrom(uintptr_t data, size_t size)
使用当前parcel读入输入数据。
bool ReadBool()
bool ReadBool(bool& value)
bool ReadBoolUnaligned()
bool ReadBoolVector(std::vector< bool >* val)
const uint8_t* ReadBuffer(size_t length)
从当前parcel中读出一块数据(一块缓存区的数据)。
const char* ReadCString()
从当前parcel中读出C-风格的字符串。
double ReadDouble()
bool ReadDouble(double& value)
bool ReadDoubleVector(std::vector< double >* val)
float ReadFloat()
bool ReadFloat(float& value)
bool ReadFloatVector(std::vector< float >* val)
int16_t ReadInt16()
bool ReadInt16(int16_t& value)
bool ReadInt16Unaligned(int16_t& value)
bool ReadInt16Vector(std::vector< int16_t >* val)
int32_t ReadInt32()
bool ReadInt32(int32_t& value)
bool ReadInt32Vector(std::vector< int32_t >* val)
int64_t ReadInt64()
bool ReadInt64(int64_t& value)
bool ReadInt64Vector(std::vector< int64_t >* val)
int8_t ReadInt8()
bool ReadInt8(int8_t& value)
bool ReadInt8Unaligned(int8_t& value)
bool ReadInt8Vector(std::vector< int8_t >* val)
template <typename T >
sptr< T >
ReadObject()
从当前parcel读出某一具体对象。
template <typename T >
T*
ReadParcelable()
从当前parcel读出一个Parcelable(及其子类)对象。
uintptr_t ReadPointer()
const std::string ReadString()
从当前parcel中读出一个C++字符串(std::string)对象。
bool ReadString(std::string& value)
从当前parcel读出C++std::string字符串对象,并存入输入对象中。
const std::u16string ReadString16()
从当前parcel中读出一个UTF-16编码的C++字符串(std::u16string)对象。
bool ReadString16(std::u16string& value)
从当前parcel读出一个UTF-16编码的C++std::u16string字符串对象,并存入输入对象中。
bool ReadString16Vector(std::vector< std::u16string >* val)
const std::u16string ReadString16WithLength(int32_t& len)
从当前parcel中读出UTF-16编码的C++字符串(std::u16string)对象以及其对应长度。
const std::string ReadString8WithLength(int32_t& len)
从当前parcel中读出C++字符串(std::string)对象以及其对应长度。
bool ReadStringVector(std::vector< std::string >* val)
template <typename T >
sptr< T >
ReadStrongParcelable()
从当前parcel读出一个Parcelable对象,并使用智能指针管理该对象。
uint16_t ReadUint16()
bool ReadUint16(uint16_t& value)
bool ReadUint16Unaligned(uint16_t& value)
bool ReadUInt16Vector(std::vector< uint16_t >* val)
uint32_t ReadUint32()
bool ReadUint32(uint32_t& value)
bool ReadUInt32Vector(std::vector< uint32_t >* val)
uint64_t ReadUint64()
bool ReadUint64(uint64_t& value)
bool ReadUInt64Vector(std::vector< uint64_t >* val)
uint8_t ReadUint8()
bool ReadUint8(uint8_t& value)
bool ReadUint8Unaligned(uint8_t& value)
bool ReadUInt8Vector(std::vector< uint8_t >* val)
const uint8_t* ReadUnpadBuffer(size_t length)
从当前parcel中读出一块数据(一块缓存区的数据)。
template <typename T >
bool
ReadVector(std::vector< T >* val, bool(Parcel::*)(T&) Read)
从当前parcel读出一个std::vector对象。
bool RewindRead(size_t newPosition)
将读光标置于指定位置。
bool RewindWrite(size_t offsets)
将写光标置于指定位置。
bool SetAllocator(Allocator* allocator)
设置当前parcel的内存分配器Allocator对象。
bool SetDataCapacity(size_t newCapacity)
设置当前parcel的以字节数为单位的容量大小,即parcel内数据区域的大小。
bool SetDataSize(size_t dataSize)
设置当前parcel的已存在数据大小(字节)。
bool SetMaxCapacity(size_t maxCapacity)
设置当前parcel的以字节为单位的最大容量。
void SkipBytes(size_t bytes)
在读操作中,跳过接下来由bytes指定的几个字节。
bool WriteBool(bool value)
bool WriteBoolUnaligned(bool value)
bool WriteBoolVector(const std::vector< bool >& val)
bool WriteBuffer(const void* data, size_t size)
bool WriteBufferAddTerminator(const void* data, size_t size, size_t typeSize)
bool WriteCString(const char* value)
向当前parcel写入一个C风格的字符串。
bool WriteDouble(double value)
bool WriteDoubleVector(const std::vector< double >& val)
bool WriteFloat(float value)
bool WriteFloatVector(const std::vector< float >& val)
bool WriteInt16(int16_t value)
bool WriteInt16Unaligned(int16_t value)
bool WriteInt16Vector(const std::vector< int16_t >& val)
bool WriteInt32(int32_t value)
bool WriteInt32Vector(const std::vector< int32_t >& val)
bool WriteInt64(int64_t value)
bool WriteInt64Vector(const std::vector< int64_t >& val)
bool WriteInt8(int8_t value)
bool WriteInt8Unaligned(int8_t value)
bool WriteInt8Vector(const std::vector< int8_t >& val)
template <typename T >
bool
WriteObject(const sptr< T >& object)
向当前parcel写入某一具体对象。
bool WriteParcelable(const Parcelable* object)
向当前parcel写入Parcelable对象。
bool WritePointer(uintptr_t value)
bool WriteRemoteObject(const Parcelable* object)
向当前parcel中写入remote对象。
bool WriteString(const std::string& value)
向当前parcel写入一个C++的std::string字符串。
bool WriteString16(const std::u16string& value)
向当前parcel写入一个C++的std::string字符串。
bool WriteString16Vector(const std::vector< std::u16string >& val)
bool WriteString16WithLength(const char16_t* value, size_t len)
向当前parcel写入一个UTF-16编码的字符串。
bool WriteString8WithLength(const char* value, size_t len)
向当前parcel写入一个UTF-8编码的字符串。
bool WriteStringVector(const std::vector< std::string >& val)
bool WriteStrongParcelable(const sptr< Parcelable >& object)
将Parcelable对象的HOLD_OBJECT行为开启后写入当前parcel。
bool WriteUint16(uint16_t value)
bool WriteUint16Unaligned(uint16_t value)
bool WriteUInt16Vector(const std::vector< uint16_t >& val)
bool WriteUint32(uint32_t value)
bool WriteUInt32Vector(const std::vector< uint32_t >& val)
bool WriteUint64(uint64_t value)
bool WriteUInt64Vector(const std::vector< uint64_t >& val)
bool WriteUint8(uint8_t value)
bool WriteUint8Unaligned(uint8_t value)
bool WriteUInt8Vector(const std::vector< uint8_t >& val)
bool WriteUnpadBuffer(const void* data, size_t size)
基于数据区指针及数据长度写入一段数据,功能与WriteBuffer完全相同,注:该接口内部会自动计算并写入对齐长度
template <typename T1 ,typename T2 >
bool
WriteVector(const std::vector< T1 >& val, bool(Parcel::*)(T2) Write)
向当前parcel写入一个std::vector对象。

Protected Functions

返回类型 名称
bool EnsureObjectsCapacity()
确保当前写入对象数量小于对象容量。
bool WriteObjectOffset(binder_size_t offset)
记录待写入对象的具体位置,该位置表示为相对于数据区域起始处的偏移量。

OHOS::Parcelable

定义了实例可被写入至某一Parcel的类接口。

具体描述

class OHOS::Parcelable;

提示: 如果当前对象为remote,其地址将被用于在内核中进行数据传输。

#include <parcel.h>

继承自 OHOS::RefBase

Public Types

名称
enum BehaviorFlag { IPC = 0x01, RPC = 0x02, HOLD_OBJECT = 0x10}
用于描述Parcelable对象具体行为的枚举类。

Public Functions

返回类型 名称
Parcelable()
Parcelable(bool asRemote)
构造一个Parcelable对象。
virtual ~Parcelable() = default
void ClearBehavior(BehaviorFlag b) const
清除指定行为标志位。
virtual bool Marshalling(Parcel& parcel) const =0
向指定Parcel对象中写入当前Parcelable对象。
void SetBehavior(BehaviorFlag b) const
设置指定行为标志位。
bool TestBehavior(BehaviorFlag b) const
检查指定行为是否已开启。

Public Attributes

名称
bool asRemote_
指明对象是否为remote的标志位。
uint8_t behavior_
指定已开启行为的具体值。

使用示例

  1. 使用方法(伪代码)
// 写入端以某种顺序写入数据
struct TestData {
    bool booltest;
    int8_t int8test;
    int16_t int16test;
    int32_t int32test;
    uint8_t uint8test;
    uint16_t uint16test;
    uint32_t uint32test;
};

...

Parcel parcel(nullptr);
struct TestData data = { true, -0x34, 0x5634, -0x12345678, 0x34, 0x5634, 0x12345678 };
bool result = false;

result = parcel.WriteBool(data.booltest);
if (!result) {
    // 写失败处理
}

result = parcel.WriteInt8(data.int8test);
if (!result) {
    // 写失败处理
}

result = parcel.WriteInt16(data.int16test);
if (!result) {
    // 写失败处理
}

result = parcel.WriteInt32(data.int32test);
if (!result) {
    // 写失败处理
}

result = parcel.WriteUint8(data.uint8test);
if (!result) {
    // 写失败处理
}

result = parcel.WriteUint16(data.uint16test);
if (!result) {
    // 写失败处理
}

result = parcel.WriteUint32(data.uint32test);
if (!result) {
    // 写失败处理
}
// 接收端根据写入端写入顺序读取数据
bool readbool = parcel.ReadBool();

int8_t readint8 = parcel.ReadInt8();

int16_t readint16 = parcel.ReadInt16();

int32_t readint32 = parcel.ReadInt32();

uint8_t readuint8 = parcel.ReadUint8();

uint16_t readuint16 = parcel.ReadUint16();

uint32_t readuint32 = parcel.ReadUint32();
  1. 常见接口限制及使用误区
  • ReadBuffer/ReadUnpadBuffer/WriteBuffer/WriteUnpadBuffer

    不推荐ReadBuffer与WriteBuffer/WriteUnpadBuffer对应配合使用,可能因为对齐问题导致ReadBuffer后的Read操作从错误的偏移位置进行读取,进而导致读取异常; ReadUnpadBuffer与WriteBuffer/WriteUnpadBuffer配合使用为正确的使用方式。

// ReadBuffer: 读取buffer,且内部数据区按参数设置长度偏移,不考虑数据对齐
// ReadUnpadBuffer: 读取buffer,内部数据区基于读取长度自动计算对齐并偏移,将数据对齐考虑在内
// WriteBuffer: 写入数据,内部数据区会基于写入长度计算对齐长度并自动偏移
// WriteUnpadBuffer: 与WriteBuffer完全相同

struct Padded {
    char title;
    int32_t handle;
    uint64_t cookie;
};

struct Unpadded {
    char tip;
};

Parcel parcel(nullptr);
const struct Padded pad = { 'p', 0x34567890, -0x2345678998765432 };
const struct Unpadded unpad = { 'u' };
// CASE 1:写入对齐数据
// 后续代码为单case下不同情况的使用代码,并非真实连续调用
parcel.WriteBuffer(static_cast<const void *>(&pad), sizeof(struct Padded));
parcel.WriteInt32(1);

// 错误使用但结果正常:
parcel.ReadBuffer(sizeof(struct Padded)); // 可以正常读取buffer内容
parcel.ReadInt32(); // 后续读取内容正常

// 正确使用:
parcel.ReadUnpadBuffer(sizeof(struct Padded)); // 可以正常读取buffer内容
parcel.ReadInt32(); // 后续读取内容正常

// CASE 2:写入非对齐数据
// 后续代码为单case下不同情况的使用代码,并非真实连续调用
parcel.WriteBuffer(static_cast<const void *>(&unpad), sizeof(struct Unpadded));
parcel.WriteInt32(1);

// 错误使用,结果异常:
parcel.ReadBuffer(sizeof(struct Unpadded)); // 可以正常读取buffer内容
parcel.ReadInt32(); // 后续读取内容异常

// 正确使用:
parcel.ReadUnpadBuffer(sizeof(struct Unpadded)); // 可以正常读取buffer内容
parcel.ReadInt32(); // 后续读取内容正常
  • 基础类型的Read接口,如ReadInt32,ReadFloat等读取失败

    当前在基础Read接口内加入了安全校验机制,当发现基础类型的读操作在读取Object对象数据内容时,该行为会被拦截

// 伪代码:

Parcel parcel(nullptr);
Parcelable object;
parcel.WriteRemoteObject(object);
parcel.ReadInt32(); // False
  • 使用WriteBuffer写入字符串,忽略结束符导致读取字符串长度异常

    WriteBuffer接口并非专门处理字符串写入的接口,因此错误传递写入长度,可能会导致字符串的结束符丢失

// 伪代码:

string str = "abcdefg";
Parcel parcel(nullptr);
char *strPtr = str.c_str();
auto len = str.length();
parcel.WriteBuffer(strPtr, len);

parcel.ReadBuffer(len); // 读取字符串长度异常
  1. 测试用例编译运行方法
  • 测试用例代码参见 base/test/unittest/common/utils_parcel_test.cpp

  • 使用开发者自测试框架,使用方法参见:开发自测试执行框架-测试用例执行

  • 使用以下具体命令以运行parcel.h对应测试用例

run -t UT -tp utils -ts UtilsParcelTest
1
https://gitee.com/openharmony/commonlibrary_c_utils.git
git@gitee.com:openharmony/commonlibrary_c_utils.git
openharmony
commonlibrary_c_utils
commonlibrary_c_utils
master

搜索帮助