Created
February 15, 2025 01:31
-
-
Save bynect/295422de677d7bb38e93d07ce8f5d4a1 to your computer and use it in GitHub Desktop.
topbit.c
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
#include <stdint.h> | |
#include <string.h> | |
//static_assert(sizeof(void *) == sizeof(uintptr_t), "Funky libc not allowed"); | |
//static_assert(sizeof(void *) == sizeof(uint64_t), "Only works on amd64"); | |
// We want to represent different values | |
// | |
// we have 64-48 => 16 bits at our disposal | |
// or in the case of 57 addressing | |
// 64-58 => 6 bits | |
// we leave the most significant bit | |
// and use 5 bit (62 to 57) | |
// Keeping the zeroes configuration for object/pointers | |
// we can use up to 63 possible tags! | |
// | |
// | x | 1 | 1 | 1 | 1 | 1 | | |
typedef uintptr_t value_t; | |
typedef enum { | |
TAG_OBJECT = 0, | |
TAG_STRING = 1, | |
TAG_INTEGER = 2, | |
TAG_FLOAT = 3, | |
TAG_TINYSTR = 4, | |
TAG_INVALID = 0x3f, | |
} value_tag_t; | |
typedef struct { | |
// Needs to be zero terminated! | |
char data[8]; | |
} tinystr_t; | |
#define TAG_SHIFT (63 - 6) | |
#define TAG_MASK ((uintptr_t)TAG_INVALID << TAG_SHIFT) | |
#define VALUE_SIGN_BIT ((uintptr_t)1 << (TAG_SHIFT - 1)) | |
#define VALUE_MS_BIT ((uintptr_t)1 << 63) | |
#define VALUE_GET_TAG(value) (value_tag_t)(((uintptr_t)(value) & TAG_MASK) >> TAG_SHIFT) | |
#define VALUE_SET_TAG(value, tag) (value_t)((uintptr_t)(value) | (uintptr_t)tag << TAG_SHIFT) | |
#define VALUE_UNTAG(value) (value_t)((uintptr_t)(value) & ~TAG_MASK) | |
#define VALUE_GET_OBJECT(value) (void *)(VALUE_UNTAG(value)) | |
#define VALUE_GET_STRING(value) (char *)(VALUE_UNTAG(value)) | |
#define VALUE_TAG_OBJECT(value) (value_t)(value); | |
#define VALUE_TAG_STRING(value) (VALUE_SET_TAG(value, TAG_STRING)) | |
value_t value_tag_integer(intptr_t num) { | |
// We are effectively truncating this 64 bit integer to 58 bit, and the | |
// sign bit must be handled accordingly! | |
uintptr_t val = *(uintptr_t *)# | |
val &= ~((uintptr_t)0x7f << (TAG_SHIFT - 1)); | |
// Move the sign bit | |
if (num < 0) | |
val |= VALUE_SIGN_BIT; | |
return VALUE_SET_TAG(val, TAG_INTEGER); | |
} | |
intptr_t value_untag_integer(value_t val) { | |
uintptr_t num = (uintptr_t)val & ~((uintptr_t)0x7f << (TAG_SHIFT - 1)); | |
// If the number is negative, pad with 1's to adjust the two's complement | |
if ((uintptr_t)val & VALUE_SIGN_BIT) | |
num |= (uintptr_t)0x7f << (TAG_SHIFT - 1); | |
return *(intptr_t *)# | |
} | |
value_t value_tag_float(float num) { | |
union { | |
value_t val; | |
float num; | |
} pun; | |
pun.num = num; | |
return VALUE_SET_TAG(pun.val, TAG_FLOAT); | |
} | |
float value_untag_float(value_t val) { | |
union { | |
value_t val; | |
float num; | |
} pun; | |
pun.val = val; | |
return pun.num; | |
} | |
value_t value_tag_tinystr(tinystr_t str) { | |
uintptr_t val = 0; | |
uint8_t *ptr = (uint8_t *)&val; | |
memcpy(ptr + 1, str.data, 7); | |
return VALUE_SET_TAG(val, TAG_TINYSTR); | |
} | |
tinystr_t value_untag_tinystr(value_t val) { | |
tinystr_t str = { 0 }; | |
uint8_t *ptr = (uint8_t *)&val; | |
memcpy(str.data, ptr + 1, 7); | |
return str; | |
} | |
#include <stdio.h> | |
void print_value(value_t val) { | |
switch (VALUE_GET_TAG(val)) { | |
case TAG_OBJECT: | |
printf("Object %p\n", VALUE_GET_OBJECT(val)); | |
break; | |
case TAG_STRING: | |
printf("String %s\n", VALUE_GET_STRING(val)); | |
break; | |
case TAG_INTEGER: | |
printf("Integer %ld\n", value_untag_integer(val)); | |
break; | |
case TAG_FLOAT: | |
printf("Float %f\n", value_untag_float(val)); | |
break; | |
case TAG_TINYSTR: | |
printf("Tinystr %s\n", value_untag_tinystr(val).data); | |
break; | |
default: | |
printf("Invalid value\n"); | |
} | |
} | |
#define VALUE_INT_MAX (((uintptr_t)1 << (TAG_SHIFT - 1)) - 1) | |
#define VALUE_INT_MIN -((uintptr_t)1 << (TAG_SHIFT - 1)) | |
int main() { | |
tinystr_t s = { "Hello" }; | |
tinystr_t s2 = { "" }; | |
value_t vs[] = { | |
value_tag_integer(100169), | |
value_tag_float(20.49), | |
value_tag_integer(424242), | |
VALUE_TAG_STRING("long string works!"), | |
value_tag_tinystr(s), | |
value_tag_tinystr(s2), | |
value_tag_integer(-1000), | |
value_tag_float(-1.29930e3), | |
value_tag_integer(VALUE_INT_MIN), | |
value_tag_integer(VALUE_INT_MAX), | |
}; | |
printf("MAX %ld\nMIN %ld\n", VALUE_INT_MAX, VALUE_INT_MIN); | |
for (size_t i = 0; i < sizeof(vs)/sizeof(*vs); i++) | |
print_value(vs[i]); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment