1
0
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:
David Steele
2021-06-08 12:55:00 -04:00
committed by GitHub
parent 3f9fbc3c24
commit 4a075b7252
7 changed files with 770 additions and 748 deletions

View File

@ -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)

View File

@ -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

View File

@ -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, &param.id, pckTypeArray, false);
pckReadTag(this, &param.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, &param.id, pckTypeBin, false))
if (pckReadTag(this, &param.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, &param.id))
FUNCTION_TEST_RETURN(param.defaultValue);
FUNCTION_TEST_RETURN(pckReadTag(this, &param.id, pckTypeBool, false));
FUNCTION_TEST_RETURN(pckReadTag(this, &param.id, pckTypeMapBool, false));
}
/**********************************************************************************************************************************/
@ -743,7 +769,7 @@ pckReadI32(PackRead *this, PckReadI32Param param)
if (pckReadNullInternal(this, &param.id))
FUNCTION_TEST_RETURN(param.defaultValue);
FUNCTION_TEST_RETURN(cvtInt32FromZigZag((uint32_t)pckReadTag(this, &param.id, pckTypeI32, false)));
FUNCTION_TEST_RETURN(cvtInt32FromZigZag((uint32_t)pckReadTag(this, &param.id, pckTypeMapI32, false)));
}
/**********************************************************************************************************************************/
@ -761,7 +787,7 @@ pckReadI64(PackRead *this, PckReadI64Param param)
if (pckReadNullInternal(this, &param.id))
FUNCTION_TEST_RETURN(param.defaultValue);
FUNCTION_TEST_RETURN(cvtInt64FromZigZag(pckReadTag(this, &param.id, pckTypeI64, false)));
FUNCTION_TEST_RETURN(cvtInt64FromZigZag(pckReadTag(this, &param.id, pckTypeMapI64, false)));
}
/**********************************************************************************************************************************/
@ -776,10 +802,10 @@ pckReadObjBegin(PackRead *this, PackIdParam param)
ASSERT(this != NULL);
// Read object begin
pckReadTag(this, &param.id, pckTypeObj, false);
pckReadTag(this, &param.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, &param.id))
FUNCTION_TEST_RETURN(NULL);
FUNCTION_TEST_RETURN((void *)(uintptr_t)pckReadTag(this, &param.id, pckTypePtr, false));
FUNCTION_TEST_RETURN((void *)(uintptr_t)pckReadTag(this, &param.id, pckTypeMapPtr, false));
}
/**********************************************************************************************************************************/
@ -845,7 +871,7 @@ pckReadStr(PackRead *this, PckReadStrParam param)
String *result = NULL;
// If string size > 0
if (pckReadTag(this, &param.id, pckTypeStr, false))
if (pckReadTag(this, &param.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, &param.id))
FUNCTION_TEST_RETURN(param.defaultValue);
FUNCTION_TEST_RETURN((time_t)cvtInt64FromZigZag(pckReadTag(this, &param.id, pckTypeTime, false)));
FUNCTION_TEST_RETURN((time_t)cvtInt64FromZigZag(pckReadTag(this, &param.id, pckTypeMapTime, false)));
}
/**********************************************************************************************************************************/
@ -900,7 +926,7 @@ pckReadU32(PackRead *this, PckReadU32Param param)
if (pckReadNullInternal(this, &param.id))
FUNCTION_TEST_RETURN(param.defaultValue);
FUNCTION_TEST_RETURN((uint32_t)pckReadTag(this, &param.id, pckTypeU32, false));
FUNCTION_TEST_RETURN((uint32_t)pckReadTag(this, &param.id, pckTypeMapU32, false));
}
/**********************************************************************************************************************************/
@ -918,7 +944,7 @@ pckReadU64(PackRead *this, PckReadU64Param param)
if (pckReadNullInternal(this, &param.id))
FUNCTION_TEST_RETURN(param.defaultValue);
FUNCTION_TEST_RETURN(pckReadTag(this, &param.id, pckTypeU64, false));
FUNCTION_TEST_RETURN(pckReadTag(this, &param.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);
}

View File

@ -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
***********************************************************************************************************************************/

View File

@ -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)));

View File

@ -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");