2025-08-10 01:34:16 +02:00

920 lines
23 KiB
C

#include "Serialization.h"
#include "common/Common.h"
#include <os/OsDeps.h>
bool __Serialization_deserializeNestedField(DeserializeCtx* ctx, DeserializeCtx* outCtx)
{
unsigned length;
if(!Serialization_deserializeUInt(ctx, &length) )
return false;
length -= Serialization_serialLenUInt();
if (ctx->length < length)
return false;
outCtx->data = ctx->data;
outCtx->length = length;
ctx->data += length;
ctx->length -= length;
return true;
}
/**
* @return 0 on error (e.g. strLen is greater than bufLen), used buffer size otherwise
*/
void Serialization_serializeStr(SerializeCtx* ctx, unsigned strLen, const char* strStart)
{
// write length field
Serialization_serializeUInt(ctx, strLen);
// write raw string
Serialization_serializeBlock(ctx, strStart, strLen);
// write termination char
Serialization_serializeChar(ctx, 0);
}
/**
* @return false on error (e.g. strLen is greater than bufLen)
*/
bool Serialization_deserializeStr(DeserializeCtx* ctx,
unsigned* outStrLen, const char** outStrStart)
{
// length field
if(unlikely(!Serialization_deserializeUInt(ctx, outStrLen) ) )
return false;
// check length and terminating zero
if(unlikely( (ctx->length < *outStrLen + 1) || ( (ctx->data)[*outStrLen] != 0) ) )
return false;
// string start
*outStrStart = ctx->data;
ctx->data += *outStrLen + 1;
ctx->length -= *outStrLen + 1;
return true;
}
/**
* @return 0 on error (e.g. arrLen is greater than bufLen), used buffer size otherwise
*/
void Serialization_serializeCharArray(SerializeCtx* ctx, unsigned arrLen, const char* arrStart)
{
// elem count info field
Serialization_serializeUInt(ctx, arrLen);
// copy raw array data
Serialization_serializeBlock(ctx, arrStart, arrLen);
}
/**
* @return false on error (e.g. arrLen is greater than bufLen)
*/
bool Serialization_deserializeCharArray(DeserializeCtx* ctx,
unsigned* outElemNum, const char** outArrStart, unsigned* outLen)
{
if(unlikely(!Serialization_deserializeUInt(ctx, outElemNum) ) )
return false;
if(ctx->length < *outElemNum)
return false;
// array start
*outArrStart = ctx->data;
ctx->data += *outElemNum;
ctx->length -= *outElemNum;
return true;
}
/**
* Note: adds padding to achieve 4 byte alignment
*
* @return 0 on error (e.g. strLen is greater than bufLen), used buffer size otherwise
*/
void Serialization_serializeStrAlign4(SerializeCtx* ctx, unsigned strLen, const char* strStart)
{
// write length field
Serialization_serializeUInt(ctx, strLen);
// write raw string
Serialization_serializeBlock(ctx, strStart, strLen);
// write termination char
Serialization_serializeChar(ctx, 0);
// align to 4b length
if( (strLen + 1) % 4)
Serialization_serializeBlock(ctx, "\0\0\0", 4 - (strLen + 1) % 4);
}
/**
* @return false on error (e.g. strLen is greater than bufLen)
*/
bool Serialization_deserializeStrAlign4(DeserializeCtx* ctx,
unsigned* outStrLen, const char** outStrStart)
{
const char* const bufAtStart = ctx->data;
unsigned padding;
// length field
if(unlikely(!Serialization_deserializeUInt(ctx, outStrLen) ) )
return false;
// check length and terminating zero
if(unlikely( (ctx->length < *outStrLen + 1) || ( (ctx->data)[*outStrLen] != 0) ) )
return false;
// string start
*outStrStart = ctx->data;
ctx->data += *outStrLen + 1;
ctx->length -= *outStrLen + 1;
padding = (ctx->data - bufAtStart) % 4;
if(padding != 0)
{
if(unlikely(ctx->length < padding) )
return false;
ctx->data += 4 - padding;
ctx->length -= 4 - padding;
}
return true;
}
/**
* Serialization of a NicList.
*
* note: 4 byte aligned.
*
* @return 0 on error, used buffer size otherwise
*/
void Serialization_serializeNicList(SerializeCtx* ctx, NicAddressList* nicList)
{
unsigned nicListSize = NicAddressList_length(nicList);
NicAddressListIter iter;
unsigned startOffset = ctx->length;
// length field placeholder
Serialization_serializeUInt(ctx, 0xFFFFFFFF);
// elem count info field
Serialization_serializeUInt(ctx, nicListSize);
// serialize each element of the nicList
NicAddressListIter_init(&iter, nicList);
for(unsigned i = 0; i < nicListSize; i++, NicAddressListIter_next(&iter) )
{
NicAddress* nicAddr = NicAddressListIter_value(&iter);
const size_t minNameSize = MIN(sizeof(nicAddr->name), SERIALIZATION_NICLISTELEM_NAME_SIZE);
// We currently only support and store ipv4 addresses internally, so we always set the
// protocol field to 4.
Serialization_serializeUInt8(ctx, 4);
{ // ipAddress
Serialization_serializeBlock(ctx, &nicAddr->ipAddr.s_addr, sizeof(nicAddr->ipAddr.s_addr) );
}
{ // name
Serialization_serializeBlock(ctx, nicAddr->name, minNameSize);
}
{ // nicType
Serialization_serializeBlock(ctx, &nicAddr->nicType, sizeof(uint8_t) );
}
{ // 2 bytes padding (for 4 byte alignment)
Serialization_serializeBlock(ctx, "\0\0", 2);
}
}
// Overwrite placeholder with length
if (ctx->data)
put_unaligned_le32(ctx->length - startOffset, ctx->data + startOffset);
}
/**
* Pre-processes a serialized NicList for deserialization via deserializeNicList().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeNicListPreprocess(DeserializeCtx* ctx, RawList* outList)
{
if(!Serialization_deserializeUInt(ctx, &outList->length) )
return false;
if(!Serialization_deserializeUInt(ctx, &outList->elemCount) )
return false;
outList->data = ctx->data;
// We need the length of the actual list without the two header fields above (which are included
// in the sequence length field)
outList->length -= 8;
if(unlikely(ctx->length < outList->length) )
return false;
ctx->data += outList->length;
ctx->length -= outList->length;
return true;
}
/**
* Deserializes a NicList.
* (requires pre-processing via nicListBufToMsg() )
*/
void Serialization_deserializeNicList(const RawList* inList, NicAddressList* outNicList)
{
const char* currentNicListPos = inList->data;
unsigned skipped = 0;
for(unsigned i = 0; i < inList->elemCount; i++)
{
uint8_t protocol;
NicAddress* nicAddr = os_kmalloc(sizeof(NicAddress) );
const size_t minNameSize = MIN(sizeof(nicAddr->name), SERIALIZATION_NICLISTELEM_NAME_SIZE);
memset(nicAddr, 0, sizeof(NicAddress) ); // clear unused fields
{ // protocol
protocol = (uint8_t) get_unaligned((char*)currentNicListPos);
currentNicListPos += 1;
}
if (protocol == 4)
{ // ipAddress
// Ipv4 address
nicAddr->ipAddr.s_addr = get_unaligned((unsigned*)currentNicListPos);
currentNicListPos += 4;
} else {
// If this is an ipv6 address, skip it as it is not supported yet.
// 16 bytes ipv6 address + name + nicType + padding
currentNicListPos += 16 + SERIALIZATION_NICLISTELEM_NAME_SIZE + 1 + 2;
skipped += 1;
continue;
}
{ // name
memcpy(&nicAddr->name, currentNicListPos, minNameSize);
(nicAddr->name)[minNameSize-1] = 0; // make sure the string is zero-terminated
currentNicListPos += SERIALIZATION_NICLISTELEM_NAME_SIZE;
}
{ // nicType
nicAddr->nicType = (NicAddrType_t) get_unaligned((char*)currentNicListPos);
currentNicListPos += 1;
}
{ // 2 bytes padding (for 4 byte alignment)
currentNicListPos += 2;
}
NicAddressList_append(outNicList, nicAddr);
}
if (skipped > 0) {
printk_fhgfs(KERN_INFO, "Skipped deserializing %d unsupported IPv6 Nics", skipped);
}
}
/**
* Pre-processes a serialized NodeList for deserialization via deserializeNodeList().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeNodeListPreprocess(DeserializeCtx* ctx, RawList* outList)
{
unsigned i;
if(!Serialization_deserializeUInt(ctx, &outList->elemCount) )
return false;
// store start pos for later deserialize()
outList->data = ctx->data;
for(i=0; i < outList->elemCount; i++)
{
{// nodeID
unsigned nodeIDLen = 0;
const char* nodeID = NULL;
if(unlikely(!Serialization_deserializeStr(ctx, &nodeIDLen, &nodeID) ) )
return false;
}
{// nicList (4b aligned)
RawList nicList;
if(unlikely(!Serialization_deserializeNicListPreprocess(ctx, &nicList) ) )
return false;
}
{// nodeNumID
NumNodeID nodeNumID = {0};
if(unlikely(!NumNodeID_deserialize(ctx, &nodeNumID) ) )
return false;
}
{// portUDP
uint16_t portUDP = 0;
if(unlikely(!Serialization_deserializeUShort(ctx, &portUDP) ) )
return false;
}
{// portTCP
uint16_t portTCP = 0;
if(unlikely(!Serialization_deserializeUShort(ctx, &portTCP) ) )
return false;
}
{// nodeType
char nodeType = 0;
if(unlikely(!Serialization_deserializeChar(ctx, &nodeType) ) )
return false;
}
}
return true;
}
/**
* Deserializes a NodeList.
* (requires pre-processing)
*
* Note: Nodes will be constructed and need to be deleted later
*/
void Serialization_deserializeNodeList(App* app, const RawList* inList, NodeList* outNodeList)
{
unsigned i;
DeserializeCtx ctx = {
.data = inList->data,
.length = -1,
};
for(i=0; i < inList->elemCount; i++)
{
NicAddressList nicList;
const char* nodeID = NULL;
NumNodeID nodeNumID = (NumNodeID){0};
uint16_t portUDP = 0;
uint16_t portTCP = 0;
char nodeType = 0;
NicAddressList_init(&nicList);
{// nodeID
unsigned nodeIDLen = 0;
Serialization_deserializeStr(&ctx, &nodeIDLen, &nodeID);
}
{// nicList (4b aligned)
RawList rawNicList;
Serialization_deserializeNicListPreprocess(&ctx, &rawNicList);
Serialization_deserializeNicList(&rawNicList, &nicList);
}
// nodeNumID
NumNodeID_deserialize(&ctx, &nodeNumID);
// portUDP
Serialization_deserializeUShort(&ctx, &portUDP);
// portTCP
Serialization_deserializeUShort(&ctx, &portTCP);
// nodeType
Serialization_deserializeChar(&ctx, &nodeType);
{// construct node
Node* node;
App_lockNicList(app);
node = Node_construct(app, nodeID, nodeNumID, portUDP, portTCP, &nicList,
nodeType == NODETYPE_Meta || nodeType == NODETYPE_Storage? App_getLocalRDMANicListLocked(app) : NULL);
App_unlockNicList(app);
Node_setNodeAliasAndType(node, NULL, (NodeType)nodeType);
// append node to outList
NodeList_append(outNodeList, node);
}
// cleanup
ListTk_kfreeNicAddressListElems(&nicList);
NicAddressList_uninit(&nicList);
}
}
/**
* Serialization of a StrCpyList.
*
* Note: We keep the serialiazation format compatible with string vec serialization
*/
void Serialization_serializeStrCpyList(SerializeCtx* ctx, StrCpyList* list)
{
SerializeCtx startCtx = *ctx;
StrCpyListIter iter;
size_t listSize = StrCpyList_length(list);
size_t i;
// totalBufLen info field
Serialization_serializeUInt(ctx, 0);
// elem count info field
Serialization_serializeUInt(ctx, listSize);
// store each element of the list as a raw zero-terminated string
StrCpyListIter_init(&iter, list);
for(i=0; i < listSize; i++, StrCpyListIter_next(&iter) )
{
char* currentElem = StrCpyListIter_value(&iter);
size_t serialElemLen = strlen(currentElem) + 1; // +1 for the terminating zero
Serialization_serializeBlock(ctx, currentElem, serialElemLen);
}
// fix totalBufLen info field
Serialization_serializeUInt(&startCtx, ctx->length - startCtx.length);
}
/**
* Pre-processes a serialized StrCpyList.
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeStrCpyListPreprocess(DeserializeCtx* ctx, RawList* outList)
{
DeserializeCtx outCtx;
if(!__Serialization_deserializeNestedField(ctx, &outCtx) )
return false;
// elem count field
if(unlikely(!Serialization_deserializeUInt(&outCtx, &outList->elemCount) ) )
return false;
if(unlikely(outList->elemCount && ( outCtx.data[outCtx.length - 1] != 0) ) )
return false;
outList->data = outCtx.data;
outList->length = outCtx.length;
return true;
}
/**
* Deserializes a StrCpyList.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeStrCpyList(const RawList* inList, StrCpyList* outList)
{
const char* listStart = inList->data;
unsigned listBufLen = inList->length;
unsigned i;
// read each list element as a raw zero-terminated string
// and make sure that we do not read beyond the specified end position
for(i = 0; (i < inList->elemCount) && (listBufLen > 0); i++)
{
size_t elemLen = strlen(listStart);
StrCpyList_append(outList, listStart);
listStart += elemLen + 1; // +1 for the terminating zero
listBufLen -= elemLen + 1; // +1 for the terminating zero
}
return i == inList->elemCount;
}
/**
* Serialization of a StrCpyVec.
*
* Note: We keep the serialiazation format compatible with string list serialization
*/
void Serialization_serializeStrCpyVec(SerializeCtx* ctx, StrCpyVec* vec)
{
Serialization_serializeStrCpyList(ctx, &vec->strCpyList);
}
/**
* Pre-processes a serialized StrCpyVec.
*
* Note: We keep the serialization format compatible to the string list format.
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeStrCpyVecPreprocess(DeserializeCtx* ctx, RawList* outList)
{
// serialization format is 100% compatible to the list version, so we can just use the list
// version code here...
return Serialization_deserializeStrCpyListPreprocess(ctx, outList);
}
/**
* Deserializes a StrCpyVec.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeStrCpyVec(const RawList* inList, StrCpyVec* outVec)
{
const char* listStart = inList->data;
unsigned listBufLen = inList->length;
unsigned i;
// read each list element as a raw zero-terminated string
// and make sure that we do not read beyond the specified end position
for(i = 0; (i < inList->elemCount) && (listBufLen > 0); i++)
{
size_t elemLen = strlen(listStart);
StrCpyVec_append(outVec, listStart);
listStart += elemLen + 1; // +1 for the terminating zero
listBufLen -= elemLen + 1; // +1 for the terminating zero
}
return i == inList->elemCount;
}
/**
* Pre-processes a serialized UInt8List().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt8ListPreprocess(DeserializeCtx* ctx, RawList* outList)
{
DeserializeCtx outCtx;
if(!__Serialization_deserializeNestedField(ctx, &outCtx) )
return false;
// elem count field
if(unlikely(!Serialization_deserializeUInt(&outCtx, &outList->elemCount) ) )
return false;
if(outCtx.length != outList->elemCount * Serialization_serialLenUInt8() )
return false;
outList->data = outCtx.data;
outList->length = outCtx.length;
return true;
}
/**
* Deserializes a UInt8List.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt8List(const RawList* inList, UInt8List* outList)
{
DeserializeCtx ctx = { inList->data, inList->length };
unsigned i;
// read each list element
for(i=0; i < inList->elemCount; i++)
{
uint8_t value;
if(!Serialization_deserializeUInt8(&ctx, &value) )
return false;
UInt8List_append(outList, value);
}
return true;
}
/**
* Pre-processes a serialized UInt8Vec().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt8VecPreprocess(DeserializeCtx* ctx, RawList* outList)
{
// serialization format is 100% compatible to the list version, so we can just use the list
// version code here...
return Serialization_deserializeUInt8ListPreprocess(ctx, outList);
}
/**
* Deserializes a UInt8Vec.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt8Vec(const RawList* inList, UInt8Vec* outVec)
{
DeserializeCtx ctx = { inList->data, inList->length };
unsigned i;
// read each list element
for(i=0; i < inList->elemCount; i++)
{
uint8_t value;
if(!Serialization_deserializeUInt8(&ctx, &value) )
return false;
UInt8Vec_append(outVec, value);
}
return true;
}
/**
* Serialization of a UInt16List.
*
* Note: We keep the serialization format compatible with int vec serialization
*/
void Serialization_serializeUInt16List(SerializeCtx* ctx, UInt16List* list)
{
SerializeCtx startCtx = *ctx;
UInt16ListIter iter;
unsigned i;
unsigned listSize = UInt16List_length(list);
// totalBufLen info field
Serialization_serializeUInt(ctx, 0);
// elem count info field
Serialization_serializeUInt(ctx, listSize);
// store each element of the list
UInt16ListIter_init(&iter, list);
for(i=0; i < listSize; i++, UInt16ListIter_next(&iter) )
{
uint16_t currentValue = UInt16ListIter_value(&iter);
Serialization_serializeUShort(ctx, currentValue);
}
// fix totalBufLen info field
Serialization_serializeUInt(&startCtx, ctx->length - startCtx.length);
}
/**
* Pre-processes a serialized UInt16List().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt16ListPreprocess(DeserializeCtx* ctx, RawList* outList)
{
DeserializeCtx outCtx;
if(!__Serialization_deserializeNestedField(ctx, &outCtx) )
return false;
// elem count field
if(unlikely(!Serialization_deserializeUInt(&outCtx, &outList->elemCount) ) )
return false;
if(outCtx.length != outList->elemCount * Serialization_serialLenUShort() )
return false;
outList->data = outCtx.data;
outList->length = outCtx.length;
return true;
}
/**
* Deserializes a UInt16List.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt16List(const RawList* inList, UInt16List* outList)
{
DeserializeCtx ctx = { inList->data, inList->length };
unsigned i;
// read each list element
for(i=0; i < inList->elemCount; i++)
{
uint16_t value;
if(!Serialization_deserializeUShort(&ctx, &value) )
return false;
UInt16List_append(outList, value);
}
return true;
}
/**
* Note: We keep the serialiazation format compatible with string list serialization
*/
void Serialization_serializeUInt16Vec(SerializeCtx* ctx, UInt16Vec* vec)
{
// UInt16Vecs are derived from UInt16Lists, so we can just do a cast here and use the list
// version code...
Serialization_serializeUInt16List(ctx, (UInt16List*)vec);
}
/**
* Pre-processes a serialized UInt16Vec().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt16VecPreprocess(DeserializeCtx* ctx, RawList* outList)
{
// serialization format is 100% compatible to the list version, so we can just use the list
// version code here...
return Serialization_deserializeUInt16ListPreprocess(ctx, outList);
}
/**
* Deserializes a UInt16Vec.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeUInt16Vec(const RawList* inList, UInt16Vec* outVec)
{
DeserializeCtx ctx = { inList->data, inList->length };
unsigned i;
// read each list element
for(i=0; i < inList->elemCount; i++)
{
uint16_t value;
if(!Serialization_deserializeUShort(&ctx, &value) )
return false;
UInt16Vec_append(outVec, value);
}
return true;
}
/**
* Serialization of a Int64CpyList.
*
* Note: We keep the serialization format compatible with int vec serialization
*/
void Serialization_serializeInt64CpyList(SerializeCtx* ctx, Int64CpyList* list)
{
SerializeCtx startCtx = *ctx;
Int64CpyListIter iter;
unsigned i;
unsigned listSize = Int64CpyList_length(list);
// totalBufLen info field
Serialization_serializeUInt(ctx, 0);
// elem count info field
Serialization_serializeUInt(ctx, listSize);
// store each element of the list
Int64CpyListIter_init(&iter, list);
for(i=0; i < listSize; i++, Int64CpyListIter_next(&iter) )
{
int64_t currentValue = Int64CpyListIter_value(&iter);
Serialization_serializeInt64(ctx, currentValue);
}
// fix totalBufLen info field
Serialization_serializeUInt(&startCtx, ctx->length - startCtx.length);
}
/**
* Pre-processes a serialized Int64CpyList().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeInt64CpyListPreprocess(DeserializeCtx* ctx, RawList* outList)
{
DeserializeCtx outCtx;
if(!__Serialization_deserializeNestedField(ctx, &outCtx) )
return false;
// elem count field
if(unlikely(!Serialization_deserializeUInt(&outCtx, &outList->elemCount) ) )
return false;
if(outCtx.length != (outList->elemCount * Serialization_serialLenInt64() ) )
return false;
outList->data = outCtx.data;
outList->length = outCtx.length;
return true;
}
/**
* Deserializes a Int64CpyList.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeInt64CpyList(const RawList* inList, Int64CpyList* outList)
{
DeserializeCtx ctx = { inList->data, inList->length };
unsigned i;
// read each list element
for(i=0; i < inList->elemCount; i++)
{
int64_t value;
if(!Serialization_deserializeInt64(&ctx, &value) )
return false;
Int64CpyList_append(outList, value);
}
return true;
}
/**
* Note: We keep the serialiazation format compatible with string list serialization
*/
void Serialization_serializeInt64CpyVec(SerializeCtx* ctx, Int64CpyVec* vec)
{
// Int64CpyVecs are derived from Int64CpyLists, so we can just do a cast here and use the list
// version code...
Serialization_serializeInt64CpyList(ctx, (Int64CpyList*)vec);
}
/**
* Pre-processes a serialized Int64CpyVec().
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeInt64CpyVecPreprocess(DeserializeCtx* ctx, RawList* outList)
{
// serialization format is 100% compatible to the list version, so we can just use the list
// version code here...
return Serialization_deserializeInt64CpyListPreprocess(ctx, outList);
}
/**
* Deserializes a Int64CpyVec.
* (requires pre-processing)
*
* @return false on error or inconsistency
*/
bool Serialization_deserializeInt64CpyVec(const RawList* inList, Int64CpyVec* outVec)
{
DeserializeCtx ctx = { inList->data, inList->length };
unsigned i;
// read each list element
for(i=0; i < inList->elemCount; i++)
{
int64_t value;
if(!Serialization_deserializeInt64(&ctx, &value) )
return false;
Int64CpyVec_append(outVec, value);
}
return true;
}