You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-17 01:12:23 +02:00
Add support for more Pack types.
Since the pack type was stored in 4 bits, only 15 values were allowed (0 was reserved). Allow virtually unlimited types by storing type info in a base-128 encoded integer following the tag when the type bits in the tag are set to 0xF. Also separate the type IDs used in the pack (PackTypeMap) from those presented to the user (PackType). The prior PackType enum exposed implementation details to the user, e.g. pckTypeUnknown.
This commit is contained in:
@ -57,10 +57,10 @@ my $rhBuild =
|
||||
####################################################################################################################################
|
||||
# Format pack tag
|
||||
####################################################################################################################################
|
||||
use constant PCK_TYPE_ARRAY => 'pckTypeArray';
|
||||
use constant PCK_TYPE_BOOL => 'pckTypeBool';
|
||||
use constant PCK_TYPE_OBJ => 'pckTypeObj';
|
||||
use constant PCK_TYPE_STR => 'pckTypeStr';
|
||||
use constant PCK_TYPE_ARRAY => 1;
|
||||
use constant PCK_TYPE_BOOL => 2;
|
||||
use constant PCK_TYPE_OBJ => 5;
|
||||
use constant PCK_TYPE_STR => 7;
|
||||
|
||||
# Pack an unsigned 64-bit integer to base-128 varint encoding and output to hex. This is a simplified version of
|
||||
# pckWriteUInt64Internal() so see that function for more information.
|
||||
@ -86,7 +86,7 @@ sub packIntFormat
|
||||
sub packTagFormat
|
||||
{
|
||||
my $strName = shift;
|
||||
my $strType = shift;
|
||||
my $iType = shift;
|
||||
my $iDelta = shift;
|
||||
my $xData = shift;
|
||||
my $iIndent = shift;
|
||||
@ -97,7 +97,7 @@ sub packTagFormat
|
||||
my $iValue = undef;
|
||||
my $iBits = undef;
|
||||
|
||||
if ($strType eq PCK_TYPE_STR || $strType eq PCK_TYPE_BOOL)
|
||||
if ($iType == PCK_TYPE_STR || $iType == PCK_TYPE_BOOL)
|
||||
{
|
||||
$iBits = $iDelta & 0x3;
|
||||
$iDelta >>= 2;
|
||||
@ -107,7 +107,7 @@ sub packTagFormat
|
||||
$iBits |= 0x4;
|
||||
}
|
||||
|
||||
if ($strType eq PCK_TYPE_STR)
|
||||
if ($iType == PCK_TYPE_STR)
|
||||
{
|
||||
$iBits |= 0x8;
|
||||
$iValue = length($xData);
|
||||
@ -118,7 +118,7 @@ sub packTagFormat
|
||||
undef($xData);
|
||||
}
|
||||
}
|
||||
elsif ($strType eq PCK_TYPE_ARRAY || $strType eq PCK_TYPE_OBJ)
|
||||
elsif ($iType == PCK_TYPE_ARRAY || $iType == PCK_TYPE_OBJ)
|
||||
{
|
||||
$iBits |= $iDelta & 0x7;
|
||||
$iDelta >>= 3;
|
||||
@ -130,14 +130,7 @@ sub packTagFormat
|
||||
}
|
||||
|
||||
# Output pack type and bits
|
||||
my $strResult = "${strIndent}${strType} << 4";
|
||||
|
||||
if ($iBits != 0)
|
||||
{
|
||||
$strResult .= sprintf(" | 0x%02X", $iBits);
|
||||
}
|
||||
|
||||
$strResult .= ',';
|
||||
my $strResult = sprintf("${strIndent}0x%02X,", ($iType << 4) | $iBits);
|
||||
|
||||
# Output additional id delta when present
|
||||
if ($iDelta > 0)
|
||||
|
@ -13,6 +13,21 @@
|
||||
|
||||
<release-list>
|
||||
<release date="XXXX-XX-XX" version="2.35dev" title="UNDER DEVELOPMENT">
|
||||
<release-core-list>
|
||||
<release-development-list>
|
||||
<release-item>
|
||||
<github-pull-request id="1421"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="cynthia.shang"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Add support for more <code>Pack</code> types.</p>
|
||||
</release-item>
|
||||
</release-development-list>
|
||||
</release-core-list>
|
||||
|
||||
<release-test-list>
|
||||
<release-improvement-list>
|
||||
<release-item>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,14 @@
|
||||
/***********************************************************************************************************************************
|
||||
Pack Type
|
||||
|
||||
Each pack field begins with a one byte tag. The four high order bits of the tag contain the field type (PackType). The four lower
|
||||
order bits vary by type.
|
||||
Each pack field begins with a one byte tag. The four high order bits of the tag contain the field type (PackTypeMap). To allow more
|
||||
types than four bits will allow, the type bits can be set to 0xF and the rest of the type (- 0xF) will be stored by a base-128
|
||||
encoded integer immediately following the tag. The four lower order bits vary by type.
|
||||
|
||||
When the "more ID delta" indicator is set then the tag will be followed by a base-128 encoded integer with the higher order ID delta
|
||||
bits. The ID delta represents the delta from the ID of the previous field. When the "more value indicator" then the tag (and the ID
|
||||
delta, if any) will be followed by a base-128 encoded integer with the high order value bits, i.e. the bits that were not stored
|
||||
directly in the tag.
|
||||
When the "more ID delta" indicator is set then the tag (and type, if any) will be followed by a base-128 encoded integer with the
|
||||
higher order ID delta bits. The ID delta represents the delta from the ID of the previous field. When the "more value indicator"
|
||||
then the tag (and the type and ID delta, if any) will be followed by a base-128 encoded integer with the high order value bits, i.e.
|
||||
the bits that were not stored directly in the tag.
|
||||
|
||||
For integer types the value is the integer being stored but for string and binary types the value is 1 if the size is greater than 0
|
||||
and 0 if the size is 0. When the size is greater than 0 the tag is immediately followed by (or after the delta ID if "more ID delta"
|
||||
@ -17,7 +18,7 @@ prevents storing an additional byte when the string/binary length is zero.
|
||||
|
||||
The following are definitions for the pack tag field and examples of how it is interpretted.
|
||||
|
||||
Integer types (packTypeData[type].valueMultiBit) when an unsigned value is <= 1 or a signed value is >= -1 and <= 0:
|
||||
Integer types (packTypeMapData[type].valueMultiBit) when an unsigned value is <= 1 or a signed value is >= -1 and <= 0:
|
||||
3 - more value indicator bit set to 0
|
||||
2 - value low order bit
|
||||
1 - more ID delta indicator bit
|
||||
@ -37,7 +38,7 @@ Integer types (packTypeData[type].valueMultiBit) when an unsigned value is <= 1
|
||||
add back the "ID delta low order bit" to give a binary representation of 1 0 0 1 = 9. Add back the 1 which is never
|
||||
recorded and the ID gap is 10.
|
||||
|
||||
Integer types (packTypeData[type].valueMultiBit) when an unsigned value is > 1 or a signed value is < -1 or > 0:
|
||||
Integer types (packTypeMapData[type].valueMultiBit) when an unsigned value is > 1 or a signed value is < -1 or > 0:
|
||||
3 - more value indicator bit set to 1
|
||||
2 - more ID delta indicator bit
|
||||
0-1 - ID delta low order bits
|
||||
@ -54,7 +55,7 @@ Integer types (packTypeData[type].valueMultiBit) when an unsigned value is > 1 o
|
||||
the bit representation would be 1 0 1 0 which is ten (10) so the gap between the IDs is 11.
|
||||
1f = signed, zigzag representation of -16 (the actual value)
|
||||
|
||||
String, binary types, and boolean (packTypeData[type].valueSingleBit):
|
||||
String, binary types, and boolean (packTypeMapData[type].valueSingleBit):
|
||||
3 - value bit
|
||||
2 - more ID delta indicator bit
|
||||
0-1 - ID delta low order bits
|
||||
@ -109,101 +110,120 @@ Constants
|
||||
#define PACK_UINT64_SIZE_MAX 10
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Type data
|
||||
Map PackType types to the types that will be written into the pack. This hides the details of the type IDs from the user and allows
|
||||
the IDs used in the pack to differ from the IDs the user sees.
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct PackTypeData
|
||||
typedef enum
|
||||
{
|
||||
pckTypeMapUnknown = 0, // Used internally when the type is not known
|
||||
pckTypeMapArray = 1, // Maps to pckTypeArray
|
||||
pckTypeMapBool = 2, // Maps to pckTypeBool
|
||||
pckTypeMapI32 = 3, // Maps to pckTypeI32
|
||||
pckTypeMapI64 = 4, // Maps to pckTypeI64
|
||||
pckTypeMapObj = 5, // Maps to pckTypeObj
|
||||
pckTypeMapPtr = 6, // Maps to pckTypePtr
|
||||
pckTypeMapStr = 7, // Maps to pckTypeStr
|
||||
pckTypeMapU32 = 8, // Maps to pckTypeU32
|
||||
pckTypeMapU64 = 9, // Maps to pckTypeU64
|
||||
|
||||
// The empty positions before 15 can be used for new types that will be encoded entirely in the tag
|
||||
|
||||
pckTypeMapTime = 15, // Maps to pckTypeTime
|
||||
pckTypeMapBin = 16, // Maps to pckTypeBin
|
||||
} PackTypeMap;
|
||||
|
||||
typedef struct PackTypeMapData
|
||||
{
|
||||
PackType type; // Data type
|
||||
bool valueSingleBit; // Can the value be stored in a single bit (e.g. bool)
|
||||
bool valueMultiBit; // Can the value require multiple bits (e.g. integer)
|
||||
bool size; // Does the type require a size (e.g. string)
|
||||
const String *const name; // Type name used in error messages
|
||||
} PackTypeData;
|
||||
} PackTypeMapData;
|
||||
|
||||
static const PackTypeData packTypeData[] =
|
||||
static const PackTypeMapData packTypeMapData[] =
|
||||
{
|
||||
{
|
||||
.type = pckTypeUnknown,
|
||||
.name = STRDEF("unknown"),
|
||||
},
|
||||
// Unknown type map data should not be used
|
||||
{0},
|
||||
|
||||
// Formats that can be encoded entirely in the tag
|
||||
{
|
||||
.type = pckTypeArray,
|
||||
.name = STRDEF("array"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeBin,
|
||||
.valueSingleBit = true,
|
||||
.size = true,
|
||||
.name = STRDEF("bin"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeBool,
|
||||
.valueSingleBit = true,
|
||||
.name = STRDEF("bool"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeI32,
|
||||
.valueMultiBit = true,
|
||||
.name = STRDEF("i32"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeI64,
|
||||
.valueMultiBit = true,
|
||||
.name = STRDEF("i64"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeObj,
|
||||
.name = STRDEF("obj"),
|
||||
},
|
||||
{
|
||||
.type = pckTypePtr,
|
||||
.valueMultiBit = true,
|
||||
.name = STRDEF("ptr"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeStr,
|
||||
.valueSingleBit = true,
|
||||
.size = true,
|
||||
.name = STRDEF("str"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeTime,
|
||||
.valueMultiBit = true,
|
||||
.name = STRDEF("time"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeU32,
|
||||
.valueMultiBit = true,
|
||||
.name = STRDEF("u32"),
|
||||
},
|
||||
{
|
||||
.type = pckTypeU64,
|
||||
.valueMultiBit = true,
|
||||
.name = STRDEF("u64"),
|
||||
},
|
||||
|
||||
// Placeholders for unused types that can be encoded entirely in the tag
|
||||
{0},
|
||||
{0},
|
||||
{0},
|
||||
{0},
|
||||
{0},
|
||||
|
||||
// Formats that require an extra byte to encode
|
||||
{
|
||||
.type = pckTypeTime,
|
||||
.valueMultiBit = true,
|
||||
},
|
||||
{
|
||||
.type = pckTypeBin,
|
||||
.valueSingleBit = true,
|
||||
.size = true,
|
||||
},
|
||||
};
|
||||
|
||||
#define PACK_TYPE_MAP_SIZE (sizeof(packTypeMapData) / sizeof(PackTypeMapData))
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object types
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct PackTagStack
|
||||
{
|
||||
PackType type;
|
||||
unsigned int idLast;
|
||||
unsigned int nullTotal;
|
||||
PackTypeMap typeMap; // Tag type map
|
||||
unsigned int idLast; // Last id in the container
|
||||
unsigned int nullTotal; // Total nulls since last tag written
|
||||
} PackTagStack;
|
||||
|
||||
struct PackRead
|
||||
{
|
||||
MemContext *memContext; // Mem context
|
||||
IoRead *read; // Read pack from
|
||||
Buffer *buffer; // Buffer to contain read data
|
||||
Buffer *buffer; // Buffer containing read data
|
||||
const uint8_t *bufferPtr; // Pointer to buffer
|
||||
size_t bufferPos; // Position in the buffer
|
||||
size_t bufferUsed; // Amount of data in the buffer
|
||||
|
||||
unsigned int tagNextId; // Next tag id
|
||||
PackType tagNextType; // Next tag type
|
||||
PackTypeMap tagNextTypeMap; // Next tag type map
|
||||
uint64_t tagNextValue; // Next tag value
|
||||
|
||||
List *tagStack; // Stack of object/array tags
|
||||
@ -239,7 +259,7 @@ pckReadNewInternal(void)
|
||||
.tagStack = lstNewP(sizeof(PackTagStack)),
|
||||
};
|
||||
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.type = pckTypeObj});
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
@ -394,10 +414,15 @@ pckReadTagNext(PackRead *this)
|
||||
else
|
||||
{
|
||||
// Read field type (e.g. int64, string)
|
||||
this->tagNextType = tag >> 4;
|
||||
this->tagNextTypeMap = tag >> 4;
|
||||
|
||||
if (this->tagNextTypeMap == 0xF)
|
||||
this->tagNextTypeMap = (unsigned int)pckReadU64Internal(this) + 0xF;
|
||||
|
||||
CHECK(this->tagNextTypeMap < PACK_TYPE_MAP_SIZE && packTypeMapData[this->tagNextTypeMap].type != 0);
|
||||
|
||||
// If the value can contain multiple bits (e.g. integer)
|
||||
if (packTypeData[this->tagNextType].valueMultiBit)
|
||||
if (packTypeMapData[this->tagNextTypeMap].valueMultiBit)
|
||||
{
|
||||
// If the value is stored following the tag (value > 1 bit)
|
||||
if (tag & 0x8)
|
||||
@ -427,7 +452,7 @@ pckReadTagNext(PackRead *this)
|
||||
}
|
||||
}
|
||||
// Else the value is a single bit (e.g. boolean)
|
||||
else if (packTypeData[this->tagNextType].valueSingleBit)
|
||||
else if (packTypeMapData[this->tagNextTypeMap].valueSingleBit)
|
||||
{
|
||||
// Read low order bits of the field ID delta
|
||||
this->tagNextId = tag & 0x3;
|
||||
@ -469,18 +494,18 @@ Read field tag
|
||||
Some tags and data may be skipped based on the value of the id parameter.
|
||||
***********************************************************************************************************************************/
|
||||
static uint64_t
|
||||
pckReadTag(PackRead *this, unsigned int *id, PackType type, bool peek)
|
||||
pckReadTag(PackRead *this, unsigned int *id, PackTypeMap typeMap, bool peek)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(PACK_READ, this);
|
||||
FUNCTION_TEST_PARAM_P(UINT, id);
|
||||
FUNCTION_TEST_PARAM(ENUM, type);
|
||||
FUNCTION_TEST_PARAM(ENUM, typeMap);
|
||||
FUNCTION_TEST_PARAM(BOOL, peek); // Look at the next tag without advancing the field id
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(id != NULL);
|
||||
ASSERT((peek && type == pckTypeUnknown) || (!peek && type != pckTypeUnknown));
|
||||
ASSERT((peek && typeMap == pckTypeMapUnknown) || (!peek && typeMap != pckTypeMapUnknown));
|
||||
|
||||
// Increment the id by one if no id was specified
|
||||
if (*id == 0)
|
||||
@ -506,14 +531,15 @@ pckReadTag(PackRead *this, unsigned int *id, PackType type, bool peek)
|
||||
// Else the id exists
|
||||
else if (*id == this->tagNextId)
|
||||
{
|
||||
// When not peeking the next tag (just to see what it is) then error if the type is not as specified
|
||||
// When not peeking the next tag (just to see what it is) then error if the type map is not as specified
|
||||
if (!peek)
|
||||
{
|
||||
if (this->tagNextType != type)
|
||||
if (this->tagNextTypeMap != typeMap)
|
||||
{
|
||||
THROW_FMT(
|
||||
FormatError, "field %u is type '%s' but expected '%s'", this->tagNextId,
|
||||
strZ(packTypeData[this->tagNextType].name), strZ(packTypeData[type].name));
|
||||
strZ(strIdToStr(packTypeMapData[this->tagNextTypeMap].type)),
|
||||
strZ(strIdToStr(packTypeMapData[typeMap].type)));
|
||||
}
|
||||
|
||||
this->tagStackTop->idLast = this->tagNextId;
|
||||
@ -524,7 +550,7 @@ pckReadTag(PackRead *this, unsigned int *id, PackType type, bool peek)
|
||||
}
|
||||
|
||||
// Read data for the field being skipped if this is not the field requested
|
||||
if (packTypeData[this->tagNextType].size && this->tagNextValue != 0)
|
||||
if (packTypeMapData[this->tagNextTypeMap].size && this->tagNextValue != 0)
|
||||
{
|
||||
size_t sizeExpected = (size_t)pckReadU64Internal(this);
|
||||
|
||||
@ -588,7 +614,7 @@ pckReadNullInternal(PackRead *this, unsigned int *id)
|
||||
ASSERT(id != NULL);
|
||||
|
||||
// Read tag at specified id
|
||||
pckReadTag(this, id, pckTypeUnknown, true);
|
||||
pckReadTag(this, id, pckTypeMapUnknown, true);
|
||||
|
||||
// If the field is NULL then set idLast (to avoid rechecking the same id on the next call) and return true
|
||||
if (*id < this->tagNextId)
|
||||
@ -624,7 +650,7 @@ pckReadType(PackRead *this)
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
FUNCTION_TEST_RETURN(this->tagNextType);
|
||||
FUNCTION_TEST_RETURN(packTypeMapData[this->tagNextTypeMap].type);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -639,10 +665,10 @@ pckReadArrayBegin(PackRead *this, PackIdParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
// Read array begin
|
||||
pckReadTag(this, ¶m.id, pckTypeArray, false);
|
||||
pckReadTag(this, ¶m.id, pckTypeMapArray, false);
|
||||
|
||||
// Add array to the tag stack so IDs can be tracked separately from the parent container
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.type = pckTypeArray});
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapArray});
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
}
|
||||
@ -656,12 +682,12 @@ pckReadArrayEnd(PackRead *this)
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (lstSize(this->tagStack) == 1 || this->tagStackTop->type != pckTypeArray)
|
||||
if (lstSize(this->tagStack) == 1 || this->tagStackTop->typeMap != pckTypeMapArray)
|
||||
THROW(FormatError, "not in array");
|
||||
|
||||
// Make sure we are at the end of the array
|
||||
unsigned int id = UINT_MAX - 1;
|
||||
pckReadTag(this, &id, pckTypeUnknown, true);
|
||||
pckReadTag(this, &id, pckTypeMapUnknown, true);
|
||||
|
||||
// Pop array off the stack
|
||||
lstRemoveLast(this->tagStack);
|
||||
@ -690,7 +716,7 @@ pckReadBin(PackRead *this, PckReadBinParam param)
|
||||
Buffer *result = NULL;
|
||||
|
||||
// If buffer size > 0
|
||||
if (pckReadTag(this, ¶m.id, pckTypeBin, false))
|
||||
if (pckReadTag(this, ¶m.id, pckTypeMapBin, false))
|
||||
{
|
||||
// Get the buffer size
|
||||
result = bufNew((size_t)pckReadU64Internal(this));
|
||||
@ -725,7 +751,7 @@ pckReadBool(PackRead *this, PckReadBoolParam param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(param.defaultValue);
|
||||
|
||||
FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeBool, false));
|
||||
FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeMapBool, false));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -743,7 +769,7 @@ pckReadI32(PackRead *this, PckReadI32Param param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(param.defaultValue);
|
||||
|
||||
FUNCTION_TEST_RETURN(cvtInt32FromZigZag((uint32_t)pckReadTag(this, ¶m.id, pckTypeI32, false)));
|
||||
FUNCTION_TEST_RETURN(cvtInt32FromZigZag((uint32_t)pckReadTag(this, ¶m.id, pckTypeMapI32, false)));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -761,7 +787,7 @@ pckReadI64(PackRead *this, PckReadI64Param param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(param.defaultValue);
|
||||
|
||||
FUNCTION_TEST_RETURN(cvtInt64FromZigZag(pckReadTag(this, ¶m.id, pckTypeI64, false)));
|
||||
FUNCTION_TEST_RETURN(cvtInt64FromZigZag(pckReadTag(this, ¶m.id, pckTypeMapI64, false)));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -776,10 +802,10 @@ pckReadObjBegin(PackRead *this, PackIdParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
// Read object begin
|
||||
pckReadTag(this, ¶m.id, pckTypeObj, false);
|
||||
pckReadTag(this, ¶m.id, pckTypeMapObj, false);
|
||||
|
||||
// Add object to the tag stack so IDs can be tracked separately from the parent container
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.type = pckTypeObj});
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
}
|
||||
@ -793,12 +819,12 @@ pckReadObjEnd(PackRead *this)
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (lstSize(this->tagStack) == 1 || ((PackTagStack *)lstGetLast(this->tagStack))->type != pckTypeObj)
|
||||
if (lstSize(this->tagStack) == 1 || ((PackTagStack *)lstGetLast(this->tagStack))->typeMap != pckTypeMapObj)
|
||||
THROW(FormatError, "not in object");
|
||||
|
||||
// Make sure we are at the end of the object
|
||||
unsigned id = UINT_MAX - 1;
|
||||
pckReadTag(this, &id, pckTypeUnknown, true);
|
||||
pckReadTag(this, &id, pckTypeMapUnknown, true);
|
||||
|
||||
// Pop object off the stack
|
||||
lstRemoveLast(this->tagStack);
|
||||
@ -824,7 +850,7 @@ pckReadPtr(PackRead *this, PckReadPtrParam param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(NULL);
|
||||
|
||||
FUNCTION_TEST_RETURN((void *)(uintptr_t)pckReadTag(this, ¶m.id, pckTypePtr, false));
|
||||
FUNCTION_TEST_RETURN((void *)(uintptr_t)pckReadTag(this, ¶m.id, pckTypeMapPtr, false));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -845,7 +871,7 @@ pckReadStr(PackRead *this, PckReadStrParam param)
|
||||
String *result = NULL;
|
||||
|
||||
// If string size > 0
|
||||
if (pckReadTag(this, ¶m.id, pckTypeStr, false))
|
||||
if (pckReadTag(this, ¶m.id, pckTypeMapStr, false))
|
||||
{
|
||||
// Read the string size
|
||||
size_t sizeExpected = (size_t)pckReadU64Internal(this);
|
||||
@ -882,7 +908,7 @@ pckReadTime(PackRead *this, PckReadTimeParam param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(param.defaultValue);
|
||||
|
||||
FUNCTION_TEST_RETURN((time_t)cvtInt64FromZigZag(pckReadTag(this, ¶m.id, pckTypeTime, false)));
|
||||
FUNCTION_TEST_RETURN((time_t)cvtInt64FromZigZag(pckReadTag(this, ¶m.id, pckTypeMapTime, false)));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -900,7 +926,7 @@ pckReadU32(PackRead *this, PckReadU32Param param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(param.defaultValue);
|
||||
|
||||
FUNCTION_TEST_RETURN((uint32_t)pckReadTag(this, ¶m.id, pckTypeU32, false));
|
||||
FUNCTION_TEST_RETURN((uint32_t)pckReadTag(this, ¶m.id, pckTypeMapU32, false));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -918,7 +944,7 @@ pckReadU64(PackRead *this, PckReadU64Param param)
|
||||
if (pckReadNullInternal(this, ¶m.id))
|
||||
FUNCTION_TEST_RETURN(param.defaultValue);
|
||||
|
||||
FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeU64, false));
|
||||
FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeMapU64, false));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -936,7 +962,7 @@ pckReadEnd(PackRead *this)
|
||||
{
|
||||
// Make sure we are at the end of the container
|
||||
unsigned int id = UINT_MAX - 1;
|
||||
pckReadTag(this, &id, pckTypeUnknown, true);
|
||||
pckReadTag(this, &id, pckTypeMapUnknown, true);
|
||||
|
||||
// Remove from stack
|
||||
lstRemoveLast(this->tagStack);
|
||||
@ -953,7 +979,7 @@ pckReadToLog(const PackRead *this)
|
||||
{
|
||||
return strNewFmt(
|
||||
"{depth: %u, idLast: %u, tagNextId: %u, tagNextType: %u, tagNextValue %" PRIu64 "}", lstSize(this->tagStack),
|
||||
this->tagStackTop->idLast, this->tagNextId, this->tagNextType, this->tagNextValue);
|
||||
this->tagStackTop->idLast, this->tagNextId, this->tagNextTypeMap, this->tagNextValue);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -975,7 +1001,7 @@ pckWriteNewInternal(void)
|
||||
.tagStack = lstNewP(sizeof(PackTagStack)),
|
||||
};
|
||||
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.type = pckTypeObj});
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
@ -1112,11 +1138,11 @@ pckWriteU64Internal(PackWrite *this, uint64_t value)
|
||||
Write field tag
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
pckWriteTag(PackWrite *this, PackType type, unsigned int id, uint64_t value)
|
||||
pckWriteTag(PackWrite *this, PackTypeMap typeMap, unsigned int id, uint64_t value)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(PACK_WRITE, this);
|
||||
FUNCTION_TEST_PARAM(ENUM, type);
|
||||
FUNCTION_TEST_PARAM(ENUM, typeMap);
|
||||
FUNCTION_TEST_PARAM(UINT, id);
|
||||
FUNCTION_TEST_PARAM(UINT64, value);
|
||||
FUNCTION_TEST_END();
|
||||
@ -1138,11 +1164,11 @@ pckWriteTag(PackWrite *this, PackType type, unsigned int id, uint64_t value)
|
||||
// Calculate field ID delta
|
||||
unsigned int tagId = id - this->tagStackTop->idLast - 1;
|
||||
|
||||
// Write field type (e.g. int64, string)
|
||||
uint64_t tag = type << 4;
|
||||
// Write field type map (e.g. int64, string)
|
||||
uint64_t tag = typeMap >= 0xF ? 0xF0 : typeMap << 4;
|
||||
|
||||
// If the value can contain multiple bits (e.g. integer)
|
||||
if (packTypeData[type].valueMultiBit)
|
||||
if (packTypeMapData[typeMap].valueMultiBit)
|
||||
{
|
||||
// If the value is stored in the tag (value == 1 bit)
|
||||
if (value < 2)
|
||||
@ -1175,7 +1201,7 @@ pckWriteTag(PackWrite *this, PackType type, unsigned int id, uint64_t value)
|
||||
}
|
||||
}
|
||||
// Else the value is a single bit (e.g. boolean)
|
||||
else if (packTypeData[type].valueSingleBit)
|
||||
else if (packTypeMapData[typeMap].valueSingleBit)
|
||||
{
|
||||
// Write value
|
||||
tag |= (value & 0x1) << 3;
|
||||
@ -1207,6 +1233,10 @@ pckWriteTag(PackWrite *this, PackType type, unsigned int id, uint64_t value)
|
||||
uint8_t tagByte = (uint8_t)tag;
|
||||
pckWriteBuffer(this, BUF(&tagByte, 1));
|
||||
|
||||
// Write remaining type map
|
||||
if (typeMap >= 0xF)
|
||||
pckWriteU64Internal(this, typeMap - 0xF);
|
||||
|
||||
// Write low order bits of the field ID delta
|
||||
if (tagId > 0)
|
||||
pckWriteU64Internal(this, tagId);
|
||||
@ -1271,10 +1301,10 @@ pckWriteArrayBegin(PackWrite *this, PackIdParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
// Write the array tag
|
||||
pckWriteTag(this, pckTypeArray, param.id, 0);
|
||||
pckWriteTag(this, pckTypeMapArray, param.id, 0);
|
||||
|
||||
// Add array to the tag stack so IDs can be tracked separately from the parent container
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.type = pckTypeArray});
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapArray});
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1288,7 +1318,7 @@ pckWriteArrayEnd(PackWrite *this)
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(lstSize(this->tagStack) != 1);
|
||||
ASSERT(((PackTagStack *)lstGetLast(this->tagStack))->type == pckTypeArray);
|
||||
ASSERT(((PackTagStack *)lstGetLast(this->tagStack))->typeMap == pckTypeMapArray);
|
||||
|
||||
// Write end of array tag
|
||||
pckWriteU64Internal(this, 0);
|
||||
@ -1317,7 +1347,7 @@ pckWriteBin(PackWrite *this, const Buffer *value, PckWriteBinParam param)
|
||||
ASSERT(value != NULL);
|
||||
|
||||
// Write buffer size if > 0
|
||||
pckWriteTag(this, pckTypeBin, param.id, !bufEmpty(value));
|
||||
pckWriteTag(this, pckTypeMapBin, param.id, !bufEmpty(value));
|
||||
|
||||
// Write buffer data if size > 0
|
||||
if (!bufEmpty(value))
|
||||
@ -1345,7 +1375,7 @@ pckWriteBool(PackWrite *this, bool value, PckWriteBoolParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
|
||||
pckWriteTag(this, pckTypeBool, param.id, value);
|
||||
pckWriteTag(this, pckTypeMapBool, param.id, value);
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1365,7 +1395,7 @@ pckWriteI32(PackWrite *this, int32_t value, PckWriteI32Param param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
|
||||
pckWriteTag(this, pckTypeI32, param.id, cvtInt32ToZigZag(value));
|
||||
pckWriteTag(this, pckTypeMapI32, param.id, cvtInt32ToZigZag(value));
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1385,7 +1415,7 @@ pckWriteI64(PackWrite *this, int64_t value, PckWriteI64Param param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
|
||||
pckWriteTag(this, pckTypeI64, param.id, cvtInt64ToZigZag(value));
|
||||
pckWriteTag(this, pckTypeMapI64, param.id, cvtInt64ToZigZag(value));
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1402,10 +1432,10 @@ pckWriteObjBegin(PackWrite *this, PackIdParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
// Write the object tag
|
||||
pckWriteTag(this, pckTypeObj, param.id, 0);
|
||||
pckWriteTag(this, pckTypeMapObj, param.id, 0);
|
||||
|
||||
// Add object to the tag stack so IDs can be tracked separately from the parent container
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.type = pckTypeObj});
|
||||
this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1419,7 +1449,7 @@ pckWriteObjEnd(PackWrite *this)
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(lstSize(this->tagStack) != 1);
|
||||
ASSERT(((PackTagStack *)lstGetLast(this->tagStack))->type == pckTypeObj);
|
||||
ASSERT(((PackTagStack *)lstGetLast(this->tagStack))->typeMap == pckTypeMapObj);
|
||||
|
||||
// Write end of object tag
|
||||
pckWriteU64Internal(this, 0);
|
||||
@ -1445,7 +1475,7 @@ pckWritePtr(PackWrite *this, const void *value, PckWritePtrParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == NULL))
|
||||
pckWriteTag(this, pckTypePtr, param.id, (uintptr_t)value);
|
||||
pckWriteTag(this, pckTypeMapPtr, param.id, (uintptr_t)value);
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1467,7 +1497,7 @@ pckWriteStr(PackWrite *this, const String *value, PckWriteStrParam param)
|
||||
ASSERT(value != NULL);
|
||||
|
||||
// Write string size if > 0
|
||||
pckWriteTag(this, pckTypeStr, param.id, strSize(value) > 0);
|
||||
pckWriteTag(this, pckTypeMapStr, param.id, strSize(value) > 0);
|
||||
|
||||
// Write string data if size > 0
|
||||
if (strSize(value) > 0)
|
||||
@ -1495,7 +1525,7 @@ pckWriteTime(PackWrite *this, time_t value, PckWriteTimeParam param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
|
||||
pckWriteTag(this, pckTypeTime, param.id, cvtInt64ToZigZag(value));
|
||||
pckWriteTag(this, pckTypeMapTime, param.id, cvtInt64ToZigZag(value));
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1515,7 +1545,7 @@ pckWriteU32(PackWrite *this, uint32_t value, PckWriteU32Param param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
|
||||
pckWriteTag(this, pckTypeU32, param.id, value);
|
||||
pckWriteTag(this, pckTypeMapU32, param.id, value);
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1535,7 +1565,7 @@ pckWriteU64(PackWrite *this, uint64_t value, PckWriteU64Param param)
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
|
||||
pckWriteTag(this, pckTypeU64, param.id, value);
|
||||
pckWriteTag(this, pckTypeMapU64, param.id, value);
|
||||
|
||||
FUNCTION_TEST_RETURN(this);
|
||||
}
|
||||
@ -1573,16 +1603,3 @@ pckWriteToLog(const PackWrite *this)
|
||||
{
|
||||
return strNewFmt("{depth: %u, idLast: %u}", lstSize(this->tagStack), this->tagStackTop->idLast);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
const String *
|
||||
pckTypeToStr(PackType type)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(ENUM, type);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(type < sizeof(packTypeData) / sizeof(PackTypeData));
|
||||
|
||||
FUNCTION_TEST_RETURN(packTypeData[type].name);
|
||||
}
|
||||
|
@ -98,25 +98,24 @@ typedef struct PackWrite PackWrite;
|
||||
#include "common/io/read.h"
|
||||
#include "common/io/write.h"
|
||||
#include "common/type/object.h"
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/stringId.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Pack data type
|
||||
***********************************************************************************************************************************/
|
||||
typedef enum
|
||||
{
|
||||
pckTypeUnknown = 0,
|
||||
pckTypeArray,
|
||||
pckTypeBin,
|
||||
pckTypeBool,
|
||||
pckTypeI32,
|
||||
pckTypeI64,
|
||||
pckTypeObj,
|
||||
pckTypePtr,
|
||||
pckTypeStr,
|
||||
pckTypeTime,
|
||||
pckTypeU32,
|
||||
pckTypeU64,
|
||||
pckTypeArray = STRID5("array", 0x190ca410),
|
||||
pckTypeBin = STRID5("bin", 0x39220),
|
||||
pckTypeBool = STRID5("bool", 0x63de20),
|
||||
pckTypeI32 = STRID6("i32", 0x1e7c91),
|
||||
pckTypeI64 = STRID6("i64", 0x208891),
|
||||
pckTypeObj = STRID5("obj", 0x284f0),
|
||||
pckTypePtr = STRID5("ptr", 0x4a900),
|
||||
pckTypeStr = STRID5("str", 0x4a930),
|
||||
pckTypeTime = STRID5("time", 0x2b5340),
|
||||
pckTypeU32 = STRID6("u32", 0x1e7d51),
|
||||
pckTypeU64 = STRID6("u64", 0x208951),
|
||||
} PackType;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@ -478,11 +477,6 @@ pckWriteFree(PackWrite *const this)
|
||||
objFree(this);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Helper Functions
|
||||
***********************************************************************************************************************************/
|
||||
const String *pckTypeToStr(PackType type);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -39,13 +39,10 @@ String *hrnPackToStr(PackRead *read)
|
||||
PackType type = pckReadType(read);
|
||||
unsigned int id = pckReadId(read);
|
||||
|
||||
strCatFmt(result, "%u:%s:", id, strZ(pckTypeToStr(type)));
|
||||
strCatFmt(result, "%u:%s:", id, strZ(strIdToStr(type)));
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case pckTypeUnknown:
|
||||
THROW_FMT(AssertError, "invalid type %s", strZ(pckTypeToStr(type)));
|
||||
|
||||
case pckTypeArray:
|
||||
pckReadArrayBeginP(read, .id = id);
|
||||
strCatFmt(result, "[%s]", strZ(hrnPackToStr(read)));
|
||||
|
@ -17,6 +17,12 @@ testRun(void)
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("PackWrite and PackRead"))
|
||||
{
|
||||
TEST_TITLE("type size");
|
||||
|
||||
TEST_RESULT_UINT(sizeof(PackType), 4, "PackType");
|
||||
TEST_RESULT_UINT(sizeof(PackTypeMapData), 8, "PackTypeMapData");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("write pack");
|
||||
|
||||
Buffer *pack = bufNew(0);
|
||||
@ -140,46 +146,46 @@ testRun(void)
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
bufHex(pack),
|
||||
"b8e803" // 1, u64, 750
|
||||
"b8fd9fad8f07" // 2, u64, 1911246845
|
||||
"bc01ffffffffffffffffff01" // 7, u64, 0xFFFFFFFFFFFFFFFF
|
||||
"b601" // 10, u64, 1
|
||||
"b84d" // 11, u64, 77
|
||||
"a87f" // 12, u32, 127
|
||||
"54" // 13, i64, -1
|
||||
"44" // 14, i32, -1
|
||||
"38" // 15, bool, true
|
||||
"3401" // 20, bool, false
|
||||
"67" // 28, obj begin
|
||||
"38" // 1, bool
|
||||
"30" // 2, bool
|
||||
"98e803" // 1, u64, 750
|
||||
"98fd9fad8f07" // 2, u64, 1911246845
|
||||
"9c01ffffffffffffffffff01" // 7, u64, 0xFFFFFFFFFFFFFFFF
|
||||
"9601" // 10, u64, 1
|
||||
"984d" // 11, u64, 77
|
||||
"887f" // 12, u32, 127
|
||||
"44" // 13, i64, -1
|
||||
"34" // 14, i32, -1
|
||||
"28" // 15, bool, true
|
||||
"2401" // 20, bool, false
|
||||
"57" // 28, obj begin
|
||||
"28" // 1, bool
|
||||
"20" // 2, bool
|
||||
"00" // obj end
|
||||
"1801" // 37, array begin
|
||||
"b0" // 1, u64, 0
|
||||
"b4" // 2, u64, 1
|
||||
"b802" // 3, u64, 2
|
||||
"b803" // 4, u64, 3
|
||||
"90" // 1, u64, 0
|
||||
"94" // 2, u64, 1
|
||||
"9802" // 3, u64, 2
|
||||
"9803" // 4, u64, 3
|
||||
"00" // array end
|
||||
"880673616d706c65" // 38, str, sample
|
||||
"8816656e6f756768746f696e637265617365627566666572" // 39, str, enoughtoincreasebuffer
|
||||
"80" // 40, str, zero length
|
||||
"8805736d616c6c" // 41, str, small
|
||||
"80" // 42, str, zero length
|
||||
"82" // 45, str, zero length
|
||||
"a1" // 47, u32, 0
|
||||
"780673616d706c65" // 38, str, sample
|
||||
"7816656e6f756768746f696e637265617365627566666572" // 39, str, enoughtoincreasebuffer
|
||||
"70" // 40, str, zero length
|
||||
"7805736d616c6c" // 41, str, small
|
||||
"70" // 42, str, zero length
|
||||
"72" // 45, str, zero length
|
||||
"81" // 47, u32, 0
|
||||
"10" // 48, array begin
|
||||
"60" // 1, obj begin
|
||||
"48d608" // 1, i32, 555
|
||||
"49920c" // 3, i32, 777
|
||||
"5902" // 5, i64, 1
|
||||
"b5" // 7, u64, 1
|
||||
"50" // 1, obj begin
|
||||
"38d608" // 1, i32, 555
|
||||
"39920c" // 3, i32, 777
|
||||
"4902" // 5, i64, 1
|
||||
"95" // 7, u64, 1
|
||||
"00" // obj end
|
||||
"890141" // 3, str, A
|
||||
"9942" // 5, time, 33
|
||||
"988401" // 6, time, 66
|
||||
"790141" // 3, str, A
|
||||
"f90042" // 5, time, 33
|
||||
"f8008401" // 6, time, 66
|
||||
"00" // array end
|
||||
"2806050403020100" // 49, bin, 0x050403020100
|
||||
"21" // 51, bin, zero length
|
||||
"f80106050403020100" // 49, bin, 0x050403020100
|
||||
"f101" // 51, bin, zero length
|
||||
"00", // end
|
||||
"check pack hex");
|
||||
|
||||
|
Reference in New Issue
Block a user