Created
December 18, 2024 03:02
-
-
Save Frityet/43a7df0713f0f6d405526b59796cd91a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Helper function to calculate the size of a type based on its encoding | |
size_t sizeForEncoding(const char *encoding) { | |
switch (*encoding) { | |
case 'c': return sizeof(char); | |
case 's': return sizeof(short); | |
case 'i': return sizeof(int); | |
case 'q': return sizeof(long); | |
case 'Q': return sizeof(size_t); | |
case 'f': return sizeof(float); | |
case 'd': return sizeof(double); | |
case 'D': return sizeof(long double); | |
case 'B': return sizeof(bool); | |
case '*': return sizeof(char *); | |
case '^': return sizeof(void *); // Size of any pointer | |
case '[': { | |
// Array | |
int count; | |
sscanf(encoding + 1, "%d", &count); | |
const char *end = strchr(encoding, ']'); | |
size_t elementSize = sizeForEncoding(encoding + 1 + (int)log10(count) + 1); // Skip count | |
return count * elementSize; | |
} | |
case '{': { | |
// Structure | |
const char *start = strchr(encoding, '='); | |
if (!start) return 0; // Invalid structure encoding | |
start++; | |
size_t totalSize = 0; | |
while (*start && *start != '}') { | |
if (*start == 'r') start++; | |
size_t memberSize = sizeForEncoding(start); | |
if (memberSize == 0) { | |
return 0; | |
} | |
totalSize += memberSize; | |
if (*start == '{' || *start == '[') { | |
start = strchr(start, *start == '{' ? '}' : ']'); | |
if (!start) return 0; | |
} | |
start++; | |
} | |
return totalSize; | |
} | |
case 'r': | |
return sizeForEncoding(encoding + 1); | |
default: return 0; // Unknown encoding | |
} | |
} | |
// Helper function to skip to the next type in a structure encoding | |
const char *nillable skipEncoding(const char *encoding) { | |
if (*encoding == 'r') encoding++; | |
if (*encoding == '{' || *encoding == '[') { | |
encoding = strchr(encoding, *encoding == '{' ? '}' : ']'); | |
if (!encoding) return NULL; | |
encoding++; | |
} else { | |
encoding++; | |
} | |
return encoding; | |
} | |
// Serialize data based on the given encoding | |
void *nillable serialise(void *data, const char *encoding) { | |
// 1. Calculate the total size needed | |
size_t encodingLength = strlen(encoding) + 1; // +1 for null terminator | |
size_t dataSize = sizeForEncoding(encoding); | |
if (dataSize == 0) { | |
OFLog(@"Error: Invalid or unsupported encoding."); | |
return NULL; | |
} | |
size_t totalSize = encodingLength + dataSize; | |
// 2. Allocate memory for the serialized data | |
void *serializedData = malloc(totalSize); | |
if (!serializedData) { | |
OFLog(@"Error: Memory allocation failed."); | |
return NULL; | |
} | |
// 3. Copy the encoding string | |
memcpy(serializedData, encoding, encodingLength); | |
// 4. Serialize the data based on the encoding | |
void *dataPtr = (char *)serializedData + encodingLength; | |
const char *currentEncoding = encoding; | |
void *inputPtr = data; | |
while (*currentEncoding) { | |
if (*currentEncoding == 'r') { | |
currentEncoding++; | |
continue; | |
} | |
size_t elementSize = sizeForEncoding(currentEncoding); | |
switch (*currentEncoding) { | |
case '^': { | |
// Pointer | |
void *ptr = *(void **)inputPtr; | |
if (ptr == NULL) { | |
// Handle null pointers | |
memset(dataPtr, 0, sizeof(void*)); | |
} else { | |
// Handle non-null pointers | |
if (strcmp(currentEncoding, "^v") != 0) { | |
// If not a void pointer, serialize the pointed-to data | |
void* inner_ptr = serialise(*(void**)inputPtr, currentEncoding + 1); | |
memcpy(dataPtr, &inner_ptr, sizeof(void*)); | |
} else { | |
// If a void pointer, just copy the address | |
memcpy(dataPtr, inputPtr, sizeof(void*)); | |
} | |
} | |
inputPtr = (char*)inputPtr + sizeof(void*); | |
dataPtr = (char*)dataPtr + sizeof(void*); | |
if (*(currentEncoding + 1) == '{' || *(currentEncoding + 1) == '[') { | |
currentEncoding = strchr(currentEncoding + 1, *(currentEncoding + 1) == '{' ? '}' : ']'); | |
if (!currentEncoding) return NULL; | |
} | |
currentEncoding++; | |
break; | |
} | |
case '[': { | |
// Array | |
int count; | |
sscanf(currentEncoding + 1, "%d", &count); | |
const char *end = strchr(currentEncoding, ']'); | |
const char *elementTypeEncoding = currentEncoding + 1 + (int)log10(count) + 1; | |
size_t elementSize = sizeForEncoding(elementTypeEncoding); | |
for (int i = 0; i < count; i++) { | |
memcpy(dataPtr, inputPtr, elementSize); | |
dataPtr = (char*)dataPtr + elementSize; | |
inputPtr = (char*)inputPtr + elementSize; | |
} | |
currentEncoding = end + 1; | |
break; | |
} | |
case '{': { | |
// Structure | |
const char *start = strchr(currentEncoding, '='); | |
if (!start) { | |
free(serializedData); | |
return NULL; // Invalid structure encoding | |
} | |
start++; | |
while (*start && *start != '}') { | |
if (*start == 'r') start++; | |
size_t memberSize = sizeForEncoding(start); | |
if (memberSize == 0) { | |
free(serializedData); | |
return NULL; | |
} | |
memcpy(dataPtr, inputPtr, memberSize); | |
dataPtr = (char*)dataPtr + memberSize; | |
inputPtr = (char*)inputPtr + memberSize; | |
start = skipEncoding(start); | |
} | |
currentEncoding = strchr(currentEncoding, '}') + 1; | |
break; | |
} | |
default: | |
memcpy(dataPtr, inputPtr, elementSize); | |
dataPtr = (char*)dataPtr + elementSize; | |
inputPtr = (char*)inputPtr + elementSize; | |
currentEncoding++; | |
} | |
} | |
return serializedData; | |
} | |
// Deserialize data based on the encoding stored in the data | |
void *nillable deserialise(void *data, const char **encoding_out) { | |
// 1. Extract the encoding string | |
const char *encoding = (const char *)data; | |
size_t encodingLength = strlen(encoding) + 1; | |
// 2. Calculate the size of the data based on the encoding | |
size_t dataSize = sizeForEncoding(encoding); | |
if (dataSize == 0) { | |
OFLog(@"Error: Invalid or unsupported encoding."); | |
return NULL; | |
} | |
// 3. Allocate memory for the deserialized data | |
void *deserializedData = malloc(dataSize); | |
if (!deserializedData) { | |
OFLog(@"Error: Memory allocation failed."); | |
return NULL; | |
} | |
// 4. Deserialize the data | |
void *dataPtr = (char *)data + encodingLength; | |
const char *currentEncoding = encoding; | |
void *outputPtr = deserializedData; | |
while (*currentEncoding) { | |
if (*currentEncoding == 'r') { | |
currentEncoding++; | |
continue; | |
} | |
size_t elementSize = sizeForEncoding(currentEncoding); | |
switch (*currentEncoding) { | |
case '^': { | |
// Pointer | |
void **ptrLocation = (void **)outputPtr; | |
void* serialized_ptr = *(void**)dataPtr; | |
if (serialized_ptr == NULL) { | |
*ptrLocation = NULL; | |
} else { | |
*ptrLocation = deserialise(serialized_ptr, ¤tEncoding); | |
} | |
dataPtr = (char*)dataPtr + sizeof(void*); | |
outputPtr = (char*)outputPtr + sizeof(void*); | |
break; | |
} | |
case '[': { | |
// Array | |
int count; | |
sscanf(currentEncoding + 1, "%d", &count); | |
const char *end = strchr(currentEncoding, ']'); | |
const char *elementTypeEncoding = currentEncoding + 1 + (int)log10(count) + 1; | |
size_t elementSize = sizeForEncoding(elementTypeEncoding); | |
for (int i = 0; i < count; i++) { | |
memcpy(outputPtr, dataPtr, elementSize); | |
dataPtr = (char*)dataPtr + elementSize; | |
outputPtr = (char*)outputPtr + elementSize; | |
} | |
currentEncoding = end + 1; | |
break; | |
} | |
case '{': { | |
// Structure | |
const char *start = strchr(currentEncoding, '='); | |
if (!start) { | |
free(deserializedData); | |
return NULL; // Invalid structure encoding | |
} | |
start++; | |
while (*start && *start != '}') { | |
if (*start == 'r') start++; | |
size_t memberSize = sizeForEncoding(start); | |
if (memberSize == 0) { | |
free(deserializedData); | |
return NULL; | |
} | |
memcpy(outputPtr, dataPtr, memberSize); | |
dataPtr = (char*)dataPtr + memberSize; | |
outputPtr = (char*)outputPtr + memberSize; | |
start = skipEncoding(start); | |
} | |
currentEncoding = strchr(currentEncoding, '}') + 1; | |
break; | |
} | |
default: | |
memcpy(outputPtr, dataPtr, elementSize); | |
dataPtr = (char*)dataPtr + elementSize; | |
outputPtr = (char*)outputPtr + elementSize; | |
currentEncoding++; | |
} | |
} | |
// 5. Optionally, return the encoding | |
if (encoding_out) { | |
*encoding_out = strdup(encoding); // Caller must free this | |
} | |
return deserializedData; | |
} | |
struct [[gnu::packed]] Point { | |
int a, b; | |
}; | |
struct [[gnu::packed]] Complicated { | |
struct Point p; | |
struct Point q; | |
struct { | |
float f; | |
const char *s; | |
long double d; | |
} nested; | |
char arr[10]; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment