Skip to content

Instantly share code, notes, and snippets.

@Frityet
Created December 18, 2024 03:02
Show Gist options
  • Save Frityet/43a7df0713f0f6d405526b59796cd91a to your computer and use it in GitHub Desktop.
Save Frityet/43a7df0713f0f6d405526b59796cd91a to your computer and use it in GitHub Desktop.
// 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, &currentEncoding);
}
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