Newer
Older
honey-os / src / userland / buffersTests / main.c
#include <hlib.h>
#include <buffers.h>

// see https://github.com/msgpack/msgpack/blob/master/spec.md

#define FORMATS_STRUCTS_X(_name, _dataType, _min, _max, _readType, _readTypeParameter) \
    { .name = #_name, .dataType = TYPE_##_dataType, .readType = _readType, .readTypeParameter = _readTypeParameter, .min = _min, .max = _max }

FormatInfo formatInfo[] = {
    FORMATS(FORMATS_STRUCTS_X, COMMA)
};

Formats FirstByteToFormat[256];

uint32_t readLength(void *data, uint32_t size) {
    switch (size) {
    case 1:
        return *((uint8_t *)data);
    case 2:
        return *((uint16_t *)data);
    case 4:
        return *((uint32_t *)data);
    // TODO: 64-bit numbers!
    }
    printf("cannot read length of size %i!\n", size);
    return 0;
}

int32_t readInteger(uint8_t *data, Formats format, FormatInfo *info) {
    if (format == FORMAT_NEGATIVE_FIXINT) {
        return ((int8_t) (*data | ~info->readTypeParameter));
    }
    if (format == FORMAT_POSITIVE_FIXINT ||
        format == FORMAT_UINT8 ||
        format == FORMAT_UINT16 ||
        format == FORMAT_UINT32 ||
        format == FORMAT_UINT64) {

        return *(uint32_t *)data;
    }
    if (data[info->readTypeParameter-1] & 0x80) {
        for (uint8_t i = info->readTypeParameter; i < 8; i++) {
            data[i] = 0xFF;
        }
    }
    return *(int32_t *)data;
}

void *dumpPack(uint8_t *data, uint32_t indent) {
    uint8_t firstByte = data[0];
    Formats format = FirstByteToFormat[firstByte];
    FormatInfo *info = &formatInfo[format];
    uint32_t bytesToRead = 1;
    uint32_t dataOffset = 0, dataSize = 0;
    switch (info->readType) {
    case Inline:
        break;
    case InlineLength:
        bytesToRead += firstByte & info->readTypeParameter;
        dataOffset = 1;
        dataSize = firstByte & info->readTypeParameter;
        break;
    case FixedLength:
        bytesToRead += info->readTypeParameter;
        dataOffset = 1;
        dataSize = info->readTypeParameter;
        break;
    case ReadLength:
        uint32_t length = readLength(data + 1, info->readTypeParameter);
        bytesToRead += info->readTypeParameter + length;
        dataOffset = 1 + info->readTypeParameter;
        dataSize = length;
        break;
    case ElementsInline:
        dataOffset = 1;
        break;
    case ReadElements:
        bytesToRead += info->readTypeParameter;
        dataOffset = 1;
        dataSize = info->readTypeParameter;
    }

    char *hexData = malloc(3*bytesToRead);
    for (uint32_t i = 0; i < bytesToRead; i++) {
        sprintf(hexData + 3*i, "%x ", data[i]);
    }
    hexData[3*bytesToRead - 1] = 0;
    char *indentData = malloc(indent + 1);
    memset(indentData, ' ', indent);
    indentData[indent] = 0;
    printf("%s%s: ", indentData, hexData);
    void *buffer = malloc(MAX(bytesToRead + 1, 8));
    switch (info->readType) {
    case Inline:
    case ElementsInline:
        *((uint8_t *)buffer) = firstByte & info->readTypeParameter; break;
    case InlineLength:
    case FixedLength:
    case ReadLength:
    case ReadElements:
        memcpy(data + dataOffset, buffer, dataSize); break;
    default: break;
    }
    uint32_t size;
    void *next = data + bytesToRead;
    switch (info->dataType) {
    case TYPE_NIL:
        printf("NIL"); break;
    case TYPE_INTEGER:
        printf("int(%i)", readInteger(buffer, format, info)); break;
    case TYPE_BOOLEAN:
        printf("bool(%s)", readInteger(buffer, format, info) ? "true" : "false"); break;
    // can't even print a float yet...
    case TYPE_STRING:
        ((uint8_t *)buffer)[bytesToRead] = 0;
        printf("str(\"%s\")", buffer); break;
    case TYPE_ARRAY:
        size = readInteger(buffer, format, info);
        printf("array(%i) (%s)\n", size, info->name);
        for (uint32_t i = 0; i < size; i++) {
            next = dumpPack(next, indent + 2);
        }
        return next;
    case TYPE_MAP:
        size = readInteger(buffer, format, info);
        printf("map(%i) (%s)\n", size, info->name);
        for (uint32_t i = 0; i < size; i++) {
            next = dumpPack(next, indent + 1);
            next = dumpPack(next, indent + 2);
        }
        return next;

    default:
        printf("unknown"); break;
    }
    printf(" (%s)\n", info->name);
    free(hexData);
    free(indentData);
    free(buffer);
    return next;
}

void fillSpots(uint16_t from, uint16_t to, Formats value) {
    for (uint16_t i = from; i <= to; i++) {
        FirstByteToFormat[i] = value;
    }
}

#define FILL_SPOTS_X(name, dataType, min, max, readType, readTypeParameter) fillSpots(min, max, FORMAT_##name);
void initialize() {
    FORMATS(FILL_SPOTS_X, NOTHING);
}

uint32_t stringLength(uint32_t strlength) {
    if ((strlength & 0x1F) == strlength) {
        // fixstr
        return 1 + strlength;
    }
    if ((strlength & 0xFF) == strlength) {
        // str8
        return 2 + strlength;
    }
    if ((strlength & 0xFFFF) == strlength) {
        // str16
        return 3 + strlength;
    }
    // str32
    return 5 + strlength;
}

void *stringWrite(void *buffer, char *string) {
    uint32_t length = strlen(string);
    uint8_t *bufferByte = buffer;
    if ((length & 0x1F) == length) {
        *bufferByte = formatInfo[FORMAT_FIXSTR].min + (uint8_t) length;
        buffer++;
    } else if ((length & 0xFF) == length) {
        *bufferByte = formatInfo[FORMAT_STR8].min;
        *(uint8_t *)(buffer + 1) = (uint8_t) length;
        buffer += 2;
    } else if ((length & 0xFFFF) == length) {
        *bufferByte = formatInfo[FORMAT_STR16].min;
        *(uint16_t *)(buffer + 1) = (uint16_t) length;
        buffer += 3;
    } else {
        *bufferByte = formatInfo[FORMAT_STR32].min;
        *(uint32_t *)(buffer + 1) = (uint32_t) length;
        buffer += 5;
    }
    memcpy(string, buffer, length);
    return buffer + length;
}

uint32_t integerLength(int32_t value, IntegerType integerType) {
    if ((value & 0x7F) == value || ((~value) & 0x1F) == ~value) {
        // fixint
        return 1;
    }
    value = ABS(value);
    if ((value & 0xFF) == value) {
        // int8
        return 2;
    }
    if ((value & 0xFFFF) == value) {
        // int16
        return 3;
    }
    if ((value & 0xFFFFFFFF) == value) {
        // int32
        return 5;
    }
    // int64
    return 9;
}

void *integerWrite(void *buffer, int32_t x, IntegerType type) {
    if (x < 0 && type != Signed) {
        printf("integerWrite: %i is negative but type is Unsigned!\n", x);
        return buffer;
    }
    uint8_t *bufferByte = buffer;
    if ((x & 0x7F) == x) {
        *bufferByte = (uint8_t)x;
        // fixint
        return buffer + 1;
    }
    if (((~x) & 0x1F) == ~x) {
        *bufferByte = (int8_t)x;
        // negative fixint
        return buffer + 1;
    }
    if ((uint8_t)x == x) {
        *bufferByte = formatInfo[FORMAT_UINT8].min;
        *(uint8_t *)(buffer + 1) = (uint8_t) x;
        return buffer + 2;
    }
    if ((int8_t) x == x) {
        *bufferByte = formatInfo[FORMAT_INT8].min;
        *(int8_t *)(buffer + 1) = (int8_t) x;
        return buffer + 2;
    }
    if ((uint16_t)x == x) {
        *bufferByte = formatInfo[FORMAT_UINT16].min;
        *(uint16_t *)(buffer + 1) = (uint16_t) x;
        return buffer + 3;
    }
    if ((int16_t) x == x) {
        *bufferByte = formatInfo[FORMAT_INT16].min;
        *(int16_t *)(buffer + 1) = (int16_t) x;
        return buffer + 3;
    }
    if ((uint32_t)x == x) {
        *bufferByte = formatInfo[FORMAT_UINT32].min;
        *(uint32_t *)(buffer + 1) = (uint32_t) x;
        return buffer + 5;
    }
    if ((int32_t) x == x) {
        *bufferByte = formatInfo[FORMAT_INT32].min;
        *(int32_t *)(buffer + 1) = (int32_t) x;
        return buffer + 5;
    }
    // TODO: 64 bit numbers
    return buffer;
}

uint32_t arrayLength(uint32_t elementCount) {
    if ((elementCount & formatInfo[FORMAT_FIXARRAY].readTypeParameter) == elementCount) {
        return 1;
    }
    if ((uint16_t)elementCount == elementCount) {
        return 3;
    }
    if ((uint32_t)elementCount == elementCount) {
        return 5;
    }
    // TODO: 64 bit numbers
    return 1;
}

void *arrayWrite(void *buffer, uint32_t elementCount) {
    uint8_t *bufferByte = buffer;
    if ((elementCount & formatInfo[FORMAT_FIXARRAY].readTypeParameter) == elementCount) {
        *bufferByte = formatInfo[FORMAT_FIXARRAY].min + elementCount;
        return buffer + 1;
    }
    if ((uint16_t)elementCount == elementCount) {
        *bufferByte = formatInfo[FORMAT_ARRAY16].min;
        *(uint16_t *)(buffer + 1) = (uint16_t) elementCount;
        return buffer + 3;
    }
    if ((uint32_t)elementCount == elementCount) {
        *bufferByte = formatInfo[FORMAT_ARRAY32].min;
        *(uint32_t *)(buffer + 1) = (uint32_t) elementCount;
        return buffer + 5;
    }
    // TODO: 64 bit numbers
    return buffer;
}

uint32_t mapLength(uint32_t elementCount) {
    if (elementCount % 2) {
        printf("map: bad element count %i\n", elementCount);
        return 0;
    }
    return arrayLength(elementCount / 2);
}

void *mapWrite(void *buffer, uint32_t elementCount) {
    uint8_t *bufferByte = buffer;
    if (elementCount % 2) {
        printf("map: bad element count %i\n", elementCount);
        return buffer;
    }
    elementCount >>= 1;
    if ((elementCount & 0x0F) == elementCount) {
        *bufferByte = formatInfo[FORMAT_FIXMAP].min + elementCount;
        // fixmap
        return buffer + 1;
    }
    if ((elementCount & 0xFFFF) == elementCount) {
        *bufferByte = formatInfo[FORMAT_MAP16].min;
        *(uint16_t *)(buffer + 1) = (uint16_t) elementCount;
        return buffer + 3;
    }
    *bufferByte = formatInfo[FORMAT_MAP32].min;
    *(uint32_t *)(buffer + 1) = elementCount;
    return buffer + 5;
}

#define SAMPLE_1(X) \
    X(INTEGER, -500, Signed)

#define SAMPLE_2_ARRAY_CONTENT(X, S) \
    X(INTEGER, 1) S \
    X(STRING, "hi") S \
    X(INTEGER, 500, Signed)

#define SAMPLE_2(X) \
    X(ARRAY, SAMPLE_2_ARRAY_CONTENT)

#define SAMPLE_3_MAP_CONTENTS(X, S) \
    X(INTEGER, 1) S \
    X(ARRAY, SAMPLE_2_ARRAY_CONTENT) S \
    X(STRING, "hello") S \
    X(STRING, "world")

#define SAMPLE_3(X) \
    X(MAP, SAMPLE_3_MAP_CONTENTS)

#define EXPECT(data, _dataType) \
    if (formatInfo[FirstByteToFormat[*((uint8_t *)data)]].dataType != TYPE_##_dataType) { \
        printf("failed EXPECT, expected %s, got %s\n", #_dataType, formatInfo[FirstByteToFormat[*((uint8_t *)data)]].name); \
    } else

// for reading values from a buffer: malloc is very slow, so only use it sparingly, when reutrning a value.

intmax_t readInt(void *data) {
    uint8_t *buffer = (uint8_t *) data;
    uint8_t format = FirstByteToFormat[*buffer];
    FormatInfo *info = &formatInfo[format];
    if (info->dataType != TYPE_INTEGER) {
        printf("readInt: cannot convert %s to int\n", info->name);
        return 0;
    }
    if (format < FORMAT_INT8) {
        // definietly working with a uint
        if (format == FORMAT_POSITIVE_FIXINT) {
            return *((uint8_t *)data) & info->readTypeParameter;
        }
        if (format == FORMAT_UINT8) {
            return *((uint8_t *)(data + 1));
        }
        if (format == FORMAT_UINT16) {
            return *((uint16_t *)(data + 1));
        }
        if (format == FORMAT_UINT32) {
            return *((uint32_t *)(data + 1));
        }
        goto fail;
    }
    if (format == FORMAT_NEGATIVE_FIXINT) {
        return (intmax_t) (int8_t) (*buffer);
    }
    if (format == FORMAT_INT8) {
        return (intmax_t) *((int8_t *)data);
    }
    if (format == FORMAT_INT16) {
        return (intmax_t) *((int16_t *)(data + 1));
    }
    if (format == FORMAT_INT32) {
        return (intmax_t) *((int32_t *)(data + 1));
    }
fail:
    // TODO: 64-bit numbers
    printf("readUint: cannot read %s\n", info->name);
    return 0;
}

uint32_t readUint(void *data) {
    intmax_t asInt = readInt(data);
    if (asInt < 0) {
        printf("readUint: value %i is negative\n", asInt);
        return 0;
    }
    return asInt;

}

char *readStr(void *data) {
    uint8_t *buffer = (uint8_t *) data;
    uint8_t format = FirstByteToFormat[*buffer];
    FormatInfo *info = &formatInfo[format];
    if (info->dataType != TYPE_STRING) {
        printf("readString: cannot convert %s to string\n", info->name);
        return NULL;
    }
    uintmax_t size = 0;
    uint8_t offset = 0;
    if (format == FORMAT_FIXSTR) {
        size = *buffer & info->readTypeParameter;
        offset = 1;
    } else if (format == FORMAT_STR8) {
        size = *((uint8_t *)(data + 1));
        offset = 2;
    } else if (format == FORMAT_STR16) {
        size = *((uint16_t *)(data + 1));
        offset = 3;
    } else if (format == FORMAT_STR32) {
        size = *((uint32_t *)(data + 1));
        offset = 5;
    }
    printf("size : %i\n", size);
    char *str = malloc(size + 1);
    memcpy(data + offset, str, size);
    str[size] = 0;
    return str;
}

uintmax_t readArraySize(void *data, void **firstElement) {
    uint8_t *buffer = (uint8_t *) data;
    uint8_t format = FirstByteToFormat[*buffer];
    FormatInfo *info = &formatInfo[format];
    if (info->dataType != TYPE_ARRAY) {
        printf("readArraySize: cannot convert %s to array\n", info->name);
        return 0;
    }
    switch (format) {
    case FORMAT_FIXARRAY:
        *firstElement = data + 1;
        return *buffer & info->readTypeParameter;
    case FORMAT_ARRAY16:
        *firstElement = data + 3;
        return *((uint16_t *)(data + 1));
    case FORMAT_ARRAY32:
        *firstElement = data + 5;
        return *((uint32_t *)(data + 1));
    }
    printf("readArraySize: cannot read %s\n", info->name);
    return 0;
}

void *seek(void *data) {
    uint8_t *buffer = (uint8_t *) data; 
    uint8_t format = FirstByteToFormat[*buffer];
    FormatInfo *info = &formatInfo[format];
    uint32_t elementCount;
    switch (info->readType) {
    case Inline:
        return data + 1;
    case InlineLength:
        return data + 1 + (*buffer & info->readTypeParameter);
    case FixedLength:
        return data + 1 + info->readTypeParameter;
    case ReadLength:
        switch (info->readTypeParameter) {
        case 1:
            return data + 1 + *((uint8_t *)(data + 1));
        case 2:
            return data + 1 + *((uint16_t *)(data + 1));
        case 4:
            return data + 1 + *((uint32_t *)(data + 1));
        }
    case ElementsInline:
        data++;
        for (uint8_t i = 0; i < (*buffer &info->readTypeParameter); i++) {
            data = seek(data);
        }
        return data;
    case ReadElements:
        switch (info->readTypeParameter) {
        case 1:
            elementCount = *((uint8_t *)(data + 1));
        case 2:
            elementCount = *((uint16_t *)(data + 1));
        case 4:
            elementCount = *((uint32_t *)(data + 1));
        }
        data += 1 + info->readTypeParameter;
        for (uint8_t i = 0; i < elementCount; i++) {
            data = seek(data);
        }
        return data;
    }
    printf("seek: cannot read %s\n", info->name);
    return NULL;
}

#define _AS_INT(data, catchError) \
    ({ \
        uint8_t *buffer = (uint8_t *) data; \
        uint8_t type = FirstByteToFormat[*buffer]; \
        if (formatInfo[type].dataType != TYPE_INTEGER) catchError \
        readInt(data); \
    })

#define _AS_UINT(data, catchError, catchNegative) \
    ({ \
        uint8_t *buffer = (uint8_t *) data; \
        uint8_t type = FirstByteToFormat[*buffer]; \
        if (formatInfo[type].dataType != TYPE_INTEGER) catchError \
        intmax_t asInt = readInt(data); \
        if (asInt < 0) catchNegative \
        (uint32_t) asInt; \
    })

#define _AS_STRING(data, catchError) \
    ({ \
        uint8_t *buffer = (uint8_t *) data; \
        uint8_t type = FirstByteToFormat[*buffer]; \
        if (formatInfo[type].dataType != TYPE_STRING) catchError \
        readStr(data); \
    })

#define AS_INT(data, retval, ...) \
    _AS_INT(data, ##__VA_ARGS__, { printf("AS_INT: cannot convert '" #data "' to an integer\n"); return retval; })

#define AS_UINT(data, retval, ...) \
    _AS_UINT(data, ##__VA_ARGS__, { printf("AS_UINT: cannot convert '" #data "' to an integer\n"); return retval; }, { printf("AS_UINT: '" #data "' is negative!\n"); asInt = 0; })

#define AS_STRING(data, retval, ...) \
    _AS_STRING(data, ##__VA_ARGS__, { printf("AS_STRING: cannot convert '" #data "' to a string\n"); return retval; })

#define ARRAY_LOOP(data, retval, elementName, action) \
    { \
        uint8_t *buffer = (uint8_t *) data; \
        uint8_t type = FirstByteToFormat[*buffer]; \
        if (formatInfo[type].dataType != TYPE_ARRAY) { \
            printf("ARRAY_LOOP: cannot convert '" #data "' to an array\n"); \
            return retval; \
        } \
        void *elementName; \
        uint32_t maxElement = readArraySize(data, &elementName); \
        for (uint32_t i = 0; i < maxElement; i++) { \
            action \
            elementName = seek(elementName); \
        } \
    }

int32_t main() {
    static bool intitialized = false;
    if (!intitialized) {
        intitialized = true;
        initialize();
    }
    CREATE(test, SAMPLE_2);
    ARRAY_LOOP(test, -1, element, {
        dumpPack(element, 0);
    })
    dumpPack(test, 0);
    free(test);
}