1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// Copyright (c) 2025 Federico Angelilli
// Licensed under the 3-Clause BSD License
// See fedang.net/posts/pointer-tagging
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
// The last byte must be zero
typedef struct {
char data[8];
} tinystr_t;
// Only the integer tag sets the lowest bit
typedef enum {
TAG_OBJECT = 0, // 0b000
TAG_INTEGER = 1, // 0b001
TAG_FLOAT = 2, // 0b010
TAG_STRING = 4, // 0b100
TAG_TINYSTR = 6, // 0b110
} value_tag_t;
typedef uintptr_t value_t;
#define VALUE_BITS 64
#define VALUE_TAG_BITS 3
#define VALUE_TAG_MASK 7 // 0b111
#define VALUE_GET_TAG(val, mask) (value_tag_t)((value_t)(val) & mask)
#define VALUE_HAS_TAG(val, tag) (VALUE_GET_TAG(val, VALUE_TAG_MASK) == (value_tag_t)(tag))
#define VALUE_SET_TAG(val, tag) ((value_t)(val) | tag)
#define VALUE_UNSET_TAG(val) ((val) & ~VALUE_TAG_MASK)
// Object value
#define VALUE_IS_OBJECT(val) VALUE_HAS_TAG(val, TAG_OBJECT)
#define VALUE_FROM_OBJECT(obj) VALUE_SET_TAG(obj, TAG_OBJECT)
#define VALUE_TO_OBJECT(val) (void *)VALUE_UNSET_TAG(val)
// Integer value
#define INTEGER_SHIFT 1
#define INTEGER_MASK 1
#define INTEGER_SIGN_BIT ((value_t)1 << (VALUE_BITS - 1))
#define INTEGER_MAX (((value_t)1 << (VALUE_BITS - 1 - INTEGER_SHIFT)) - 1)
#define INTEGER_MIN -((value_t)1 << (VALUE_BITS - 1 - INTEGER_SHIFT))
#define VALUE_IS_INTEGER(val) (VALUE_GET_TAG(val, INTEGER_MASK) == TAG_INTEGER)
#define VALUE_FROM_INTEGER(num) value_tag_integer(num)
#define VALUE_TO_INTEGER(val) value_untag_integer(val)
value_t value_tag_integer(intptr_t num) {
assert(num < INTEGER_MIN || num > INTEGER_MAX);
return (value_t)num << INTEGER_SHIFT | (num & INTEGER_SIGN_BIT) | TAG_INTEGER;
}
intptr_t value_untag_integer(value_t val) {
assert(VALUE_IS_INTEGER(val));
return (intptr_t)val >> INTEGER_SHIFT | (val & INTEGER_SIGN_BIT);
}
// Float value
#define FLOAT_SHIFT 31
#define VALUE_IS_FLOAT(val) VALUE_HAS_TAG(val, TAG_FLOAT)
#define VALUE_FROM_FLOAT(num) value_tag_float(num)
#define VALUE_TO_FLOAT(val) value_untag_float(val)
value_t value_tag_float(float num) {
union {
uint32_t raw;
float num;
} pun;
pun.num = num;
return VALUE_SET_TAG((value_t)pun.raw << FLOAT_SHIFT, TAG_FLOAT);
}
float value_untag_float(value_t val) {
assert(VALUE_IS_FLOAT(val));
union {
uint32_t raw;
float num;
} pun;
pun.raw = (val >> FLOAT_SHIFT) & 0xffffffff;
return pun.num;
}
// String value
#define VALUE_IS_STRING(val) VALUE_HAS_TAG(val, TAG_STRING)
#define VALUE_FROM_STRING(str) VALUE_SET_TAG(str, TAG_STRING)
#define VALUE_TO_STRING(val) (char *)VALUE_UNSET_TAG(val)
// Tiny string value
#define VALUE_IS_TINYSTR(val) VALUE_HAS_TAG(val, TAG_TINYSTR)
#define VALUE_FROM_TINYSTR(num) value_tag_tinystr(num)
#define VALUE_TO_TINYSTR(val) value_untag_tinystr(val)
value_t value_tag_tinystr(tinystr_t str) {
assert(str.data[7] == '\0');
return ((value_t)str.data[0] << 8)
| ((value_t)str.data[1] << 16)
| ((value_t)str.data[2] << 24)
| ((value_t)str.data[3] << 32)
| ((value_t)str.data[4] << 40)
| ((value_t)str.data[5] << 48)
| ((value_t)str.data[6] << 56)
| TAG_TINYSTR;
}
tinystr_t value_untag_tinystr(value_t val) {
assert(VALUE_IS_TINYSTR(val));
tinystr_t str = {
(val >> 8) & 0xff,
(val >> 16) & 0xff,
(val >> 24) & 0xff,
(val >> 32) & 0xff,
(val >> 40) & 0xff,
(val >> 48) & 0xff,
(val >> 56) & 0xff,
};
return str;
}
|