| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* bitset.h -- Shared-memory fixed-size bitset for Linux |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* CAS-based atomic per-bit operations on uint64_t words. |
|
5
|
|
|
|
|
|
|
* Lock-free set/clear/test/toggle with popcount. |
|
6
|
|
|
|
|
|
|
*/ |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#ifndef BITSET_H |
|
9
|
|
|
|
|
|
|
#define BITSET_H |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
#include |
|
12
|
|
|
|
|
|
|
#include |
|
13
|
|
|
|
|
|
|
#include |
|
14
|
|
|
|
|
|
|
#include |
|
15
|
|
|
|
|
|
|
#include |
|
16
|
|
|
|
|
|
|
#include |
|
17
|
|
|
|
|
|
|
#include |
|
18
|
|
|
|
|
|
|
#include |
|
19
|
|
|
|
|
|
|
#include |
|
20
|
|
|
|
|
|
|
#include |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#define BS_MAGIC 0x42535431U /* "BST1" */ |
|
23
|
|
|
|
|
|
|
#define BS_VERSION 1 |
|
24
|
|
|
|
|
|
|
#define BS_ERR_BUFLEN 256 |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
/* ================================================================ |
|
27
|
|
|
|
|
|
|
* Header (128 bytes) |
|
28
|
|
|
|
|
|
|
* ================================================================ */ |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
typedef struct { |
|
31
|
|
|
|
|
|
|
uint32_t magic; |
|
32
|
|
|
|
|
|
|
uint32_t version; |
|
33
|
|
|
|
|
|
|
uint64_t capacity; /* 8: number of bits */ |
|
34
|
|
|
|
|
|
|
uint64_t total_size; /* 16 */ |
|
35
|
|
|
|
|
|
|
uint64_t data_off; /* 24 */ |
|
36
|
|
|
|
|
|
|
uint32_t num_words; /* 32: ceil(capacity/64) */ |
|
37
|
|
|
|
|
|
|
uint8_t _pad0[28]; /* 36-63 */ |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
uint64_t stat_sets; /* 64 */ |
|
40
|
|
|
|
|
|
|
uint64_t stat_clears; /* 72 */ |
|
41
|
|
|
|
|
|
|
uint64_t stat_toggles; /* 80 */ |
|
42
|
|
|
|
|
|
|
uint8_t _pad1[40]; /* 88-127 */ |
|
43
|
|
|
|
|
|
|
} BsHeader; |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L |
|
46
|
|
|
|
|
|
|
_Static_assert(sizeof(BsHeader) == 128, "BsHeader must be 128 bytes"); |
|
47
|
|
|
|
|
|
|
#endif |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
typedef struct { |
|
50
|
|
|
|
|
|
|
BsHeader *hdr; |
|
51
|
|
|
|
|
|
|
uint64_t *data; |
|
52
|
|
|
|
|
|
|
size_t mmap_size; |
|
53
|
|
|
|
|
|
|
char *path; |
|
54
|
|
|
|
|
|
|
int backing_fd; |
|
55
|
|
|
|
|
|
|
} BsHandle; |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
/* ================================================================ |
|
58
|
|
|
|
|
|
|
* Bit operations (lock-free CAS) |
|
59
|
|
|
|
|
|
|
* ================================================================ */ |
|
60
|
|
|
|
|
|
|
|
|
61
|
263
|
|
|
|
|
|
static inline int bs_test(BsHandle *h, uint64_t bit) { |
|
62
|
263
|
|
|
|
|
|
uint64_t word = __atomic_load_n(&h->data[bit / 64], __ATOMIC_ACQUIRE); |
|
63
|
263
|
|
|
|
|
|
return (word >> (bit % 64)) & 1; |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
/* Set bit. Returns old value (0 or 1). */ |
|
67
|
18
|
|
|
|
|
|
static inline int bs_set(BsHandle *h, uint64_t bit) { |
|
68
|
18
|
|
|
|
|
|
uint32_t widx = (uint32_t)(bit / 64); |
|
69
|
18
|
|
|
|
|
|
uint64_t mask = (uint64_t)1 << (bit % 64); |
|
70
|
0
|
|
|
|
|
|
for (;;) { |
|
71
|
18
|
|
|
|
|
|
uint64_t word = __atomic_load_n(&h->data[widx], __ATOMIC_RELAXED); |
|
72
|
35
|
100
|
|
|
|
|
if (word & mask) return 1; /* already set */ |
|
73
|
17
|
50
|
|
|
|
|
if (__atomic_compare_exchange_n(&h->data[widx], &word, word | mask, |
|
74
|
|
|
|
|
|
|
1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) { |
|
75
|
17
|
|
|
|
|
|
__atomic_add_fetch(&h->hdr->stat_sets, 1, __ATOMIC_RELAXED); |
|
76
|
17
|
|
|
|
|
|
return 0; |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
/* Clear bit. Returns old value (0 or 1). */ |
|
82
|
2
|
|
|
|
|
|
static inline int bs_clear(BsHandle *h, uint64_t bit) { |
|
83
|
2
|
|
|
|
|
|
uint32_t widx = (uint32_t)(bit / 64); |
|
84
|
2
|
|
|
|
|
|
uint64_t mask = (uint64_t)1 << (bit % 64); |
|
85
|
0
|
|
|
|
|
|
for (;;) { |
|
86
|
2
|
|
|
|
|
|
uint64_t word = __atomic_load_n(&h->data[widx], __ATOMIC_RELAXED); |
|
87
|
3
|
100
|
|
|
|
|
if (!(word & mask)) return 0; /* already clear */ |
|
88
|
1
|
50
|
|
|
|
|
if (__atomic_compare_exchange_n(&h->data[widx], &word, word & ~mask, |
|
89
|
|
|
|
|
|
|
1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) { |
|
90
|
1
|
|
|
|
|
|
__atomic_add_fetch(&h->hdr->stat_clears, 1, __ATOMIC_RELAXED); |
|
91
|
1
|
|
|
|
|
|
return 1; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
/* Toggle bit. Returns new value (0 or 1). */ |
|
97
|
2
|
|
|
|
|
|
static inline int bs_toggle(BsHandle *h, uint64_t bit) { |
|
98
|
2
|
|
|
|
|
|
uint32_t widx = (uint32_t)(bit / 64); |
|
99
|
2
|
|
|
|
|
|
uint64_t mask = (uint64_t)1 << (bit % 64); |
|
100
|
2
|
|
|
|
|
|
uint64_t old = __atomic_fetch_xor(&h->data[widx], mask, __ATOMIC_ACQ_REL); |
|
101
|
2
|
|
|
|
|
|
__atomic_add_fetch(&h->hdr->stat_toggles, 1, __ATOMIC_RELAXED); |
|
102
|
2
|
|
|
|
|
|
return (old & mask) ? 0 : 1; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
/* Population count — total set bits. */ |
|
106
|
11
|
|
|
|
|
|
static inline uint64_t bs_count(BsHandle *h) { |
|
107
|
11
|
|
|
|
|
|
uint64_t total = 0; |
|
108
|
11
|
|
|
|
|
|
uint32_t nw = h->hdr->num_words; |
|
109
|
45
|
100
|
|
|
|
|
for (uint32_t i = 0; i < nw; i++) { |
|
110
|
34
|
|
|
|
|
|
uint64_t word = __atomic_load_n(&h->data[i], __ATOMIC_RELAXED); |
|
111
|
34
|
|
|
|
|
|
total += (uint64_t)__builtin_popcountll(word); |
|
112
|
|
|
|
|
|
|
} |
|
113
|
11
|
|
|
|
|
|
return total; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
2
|
|
|
|
|
|
static inline int bs_any(BsHandle *h) { |
|
117
|
2
|
|
|
|
|
|
uint32_t nw = h->hdr->num_words; |
|
118
|
6
|
100
|
|
|
|
|
for (uint32_t i = 0; i < nw; i++) |
|
119
|
5
|
100
|
|
|
|
|
if (__atomic_load_n(&h->data[i], __ATOMIC_RELAXED)) return 1; |
|
120
|
1
|
|
|
|
|
|
return 0; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
1
|
|
|
|
|
|
static inline int bs_none(BsHandle *h) { return !bs_any(h); } |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
/* Fill all bits. NOT safe concurrently with per-bit CAS ops. */ |
|
126
|
3
|
|
|
|
|
|
static inline void bs_fill(BsHandle *h) { |
|
127
|
3
|
|
|
|
|
|
uint32_t nw = h->hdr->num_words; |
|
128
|
3
|
|
|
|
|
|
uint64_t cap = h->hdr->capacity; |
|
129
|
10
|
100
|
|
|
|
|
for (uint32_t i = 0; i < nw; i++) { |
|
130
|
3
|
100
|
|
|
|
|
uint64_t valid = (i == nw - 1 && cap % 64) |
|
131
|
10
|
100
|
|
|
|
|
? ((uint64_t)1 << (cap % 64)) - 1 : ~(uint64_t)0; |
|
132
|
7
|
|
|
|
|
|
__atomic_store_n(&h->data[i], valid, __ATOMIC_RELEASE); |
|
133
|
|
|
|
|
|
|
} |
|
134
|
3
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
/* Zero all bits. NOT safe concurrently with per-bit CAS ops. */ |
|
137
|
4
|
|
|
|
|
|
static inline void bs_zero(BsHandle *h) { |
|
138
|
4
|
|
|
|
|
|
uint32_t nw = h->hdr->num_words; |
|
139
|
20
|
100
|
|
|
|
|
for (uint32_t i = 0; i < nw; i++) |
|
140
|
16
|
|
|
|
|
|
__atomic_store_n(&h->data[i], 0, __ATOMIC_RELEASE); |
|
141
|
4
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
/* Find first set bit. Returns -1 if none. */ |
|
144
|
3
|
|
|
|
|
|
static inline int64_t bs_first_set(BsHandle *h) { |
|
145
|
3
|
|
|
|
|
|
uint32_t nw = h->hdr->num_words; |
|
146
|
3
|
|
|
|
|
|
uint64_t cap = h->hdr->capacity; |
|
147
|
7
|
100
|
|
|
|
|
for (uint32_t i = 0; i < nw; i++) { |
|
148
|
6
|
|
|
|
|
|
uint64_t word = __atomic_load_n(&h->data[i], __ATOMIC_RELAXED); |
|
149
|
6
|
100
|
|
|
|
|
if (word) { |
|
150
|
2
|
|
|
|
|
|
uint64_t bit = (uint64_t)i * 64 + __builtin_ctzll(word); |
|
151
|
2
|
50
|
|
|
|
|
return bit < cap ? (int64_t)bit : -1; |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
} |
|
154
|
1
|
|
|
|
|
|
return -1; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
/* Find first clear bit. Returns -1 if none. */ |
|
158
|
3
|
|
|
|
|
|
static inline int64_t bs_first_clear(BsHandle *h) { |
|
159
|
3
|
|
|
|
|
|
uint32_t nw = h->hdr->num_words; |
|
160
|
3
|
|
|
|
|
|
uint64_t cap = h->hdr->capacity; |
|
161
|
8
|
100
|
|
|
|
|
for (uint32_t i = 0; i < nw; i++) { |
|
162
|
7
|
|
|
|
|
|
uint64_t word = __atomic_load_n(&h->data[i], __ATOMIC_RELAXED); |
|
163
|
7
|
100
|
|
|
|
|
if (word != ~(uint64_t)0) { |
|
164
|
2
|
|
|
|
|
|
uint64_t bit = (uint64_t)i * 64 + __builtin_ctzll(~word); |
|
165
|
2
|
100
|
|
|
|
|
return bit < cap ? (int64_t)bit : -1; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
} |
|
168
|
1
|
|
|
|
|
|
return -1; |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
/* ================================================================ |
|
172
|
|
|
|
|
|
|
* Create / Open / Close |
|
173
|
|
|
|
|
|
|
* ================================================================ */ |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
#define BS_ERR(fmt, ...) do { if (errbuf) snprintf(errbuf, BS_ERR_BUFLEN, fmt, ##__VA_ARGS__); } while(0) |
|
176
|
|
|
|
|
|
|
|
|
177
|
5
|
|
|
|
|
|
static inline void bs_init_header(void *base, uint64_t total, uint64_t capacity, uint32_t nw) { |
|
178
|
5
|
|
|
|
|
|
BsHeader *hdr = (BsHeader *)base; |
|
179
|
5
|
|
|
|
|
|
memset(base, 0, (size_t)total); |
|
180
|
5
|
|
|
|
|
|
hdr->magic = BS_MAGIC; |
|
181
|
5
|
|
|
|
|
|
hdr->version = BS_VERSION; |
|
182
|
5
|
|
|
|
|
|
hdr->capacity = capacity; |
|
183
|
5
|
|
|
|
|
|
hdr->total_size = total; |
|
184
|
5
|
|
|
|
|
|
hdr->data_off = sizeof(BsHeader); |
|
185
|
5
|
|
|
|
|
|
hdr->num_words = nw; |
|
186
|
5
|
|
|
|
|
|
__atomic_thread_fence(__ATOMIC_SEQ_CST); |
|
187
|
5
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
|
|
189
|
7
|
|
|
|
|
|
static inline BsHandle *bs_setup(void *base, size_t ms, const char *path, int bfd) { |
|
190
|
7
|
|
|
|
|
|
BsHeader *hdr = (BsHeader *)base; |
|
191
|
7
|
|
|
|
|
|
BsHandle *h = (BsHandle *)calloc(1, sizeof(BsHandle)); |
|
192
|
7
|
50
|
|
|
|
|
if (!h) { munmap(base, ms); return NULL; } |
|
193
|
7
|
|
|
|
|
|
h->hdr = hdr; |
|
194
|
7
|
|
|
|
|
|
h->data = (uint64_t *)((uint8_t *)base + hdr->data_off); |
|
195
|
7
|
|
|
|
|
|
h->mmap_size = ms; |
|
196
|
7
|
100
|
|
|
|
|
h->path = path ? strdup(path) : NULL; |
|
197
|
7
|
|
|
|
|
|
h->backing_fd = bfd; |
|
198
|
7
|
|
|
|
|
|
return h; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
|
|
201
|
5
|
|
|
|
|
|
static BsHandle *bs_create(const char *path, uint64_t capacity, char *errbuf) { |
|
202
|
5
|
50
|
|
|
|
|
if (errbuf) errbuf[0] = '\0'; |
|
203
|
5
|
50
|
|
|
|
|
if (capacity == 0) { BS_ERR("capacity must be > 0"); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
204
|
5
|
|
|
|
|
|
uint32_t nw = (uint32_t)((capacity + 63) / 64); |
|
205
|
5
|
50
|
|
|
|
|
if (capacity > (uint64_t)(UINT32_MAX - 63) * 64) { BS_ERR("capacity too large"); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
206
|
5
|
|
|
|
|
|
uint64_t total = sizeof(BsHeader) + (uint64_t)nw * 8; |
|
207
|
|
|
|
|
|
|
|
|
208
|
5
|
|
|
|
|
|
int anonymous = (path == NULL); |
|
209
|
5
|
|
|
|
|
|
int fd = -1; |
|
210
|
|
|
|
|
|
|
size_t map_size; |
|
211
|
|
|
|
|
|
|
void *base; |
|
212
|
|
|
|
|
|
|
|
|
213
|
5
|
100
|
|
|
|
|
if (anonymous) { |
|
214
|
3
|
|
|
|
|
|
map_size = (size_t)total; |
|
215
|
3
|
|
|
|
|
|
base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); |
|
216
|
3
|
50
|
|
|
|
|
if (base == MAP_FAILED) { BS_ERR("mmap: %s", strerror(errno)); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
} else { |
|
218
|
2
|
|
|
|
|
|
fd = open(path, O_RDWR|O_CREAT, 0666); |
|
219
|
3
|
50
|
|
|
|
|
if (fd < 0) { BS_ERR("open: %s", strerror(errno)); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
220
|
2
|
50
|
|
|
|
|
if (flock(fd, LOCK_EX) < 0) { BS_ERR("flock: %s", strerror(errno)); close(fd); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
struct stat st; |
|
222
|
2
|
50
|
|
|
|
|
if (fstat(fd, &st) < 0) { BS_ERR("fstat: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
223
|
2
|
|
|
|
|
|
int is_new = (st.st_size == 0); |
|
224
|
2
|
100
|
|
|
|
|
if (is_new && ftruncate(fd, (off_t)total) < 0) { |
|
|
|
50
|
|
|
|
|
|
|
225
|
0
|
0
|
|
|
|
|
BS_ERR("ftruncate: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
2
|
100
|
|
|
|
|
map_size = is_new ? (size_t)total : (size_t)st.st_size; |
|
228
|
2
|
|
|
|
|
|
base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); |
|
229
|
2
|
50
|
|
|
|
|
if (base == MAP_FAILED) { BS_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
230
|
2
|
100
|
|
|
|
|
if (!is_new) { |
|
231
|
1
|
|
|
|
|
|
BsHeader *hdr = (BsHeader *)base; |
|
232
|
1
|
50
|
|
|
|
|
if (hdr->magic != BS_MAGIC || hdr->version != BS_VERSION || |
|
|
|
50
|
|
|
|
|
|
|
233
|
1
|
50
|
|
|
|
|
hdr->total_size != (uint64_t)st.st_size) { |
|
234
|
0
|
0
|
|
|
|
|
BS_ERR("invalid bitset file"); munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
1
|
|
|
|
|
|
flock(fd, LOCK_UN); close(fd); |
|
237
|
1
|
|
|
|
|
|
return bs_setup(base, map_size, path, -1); |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
} |
|
240
|
4
|
|
|
|
|
|
bs_init_header(base, total, capacity, nw); |
|
241
|
4
|
100
|
|
|
|
|
if (fd >= 0) { flock(fd, LOCK_UN); close(fd); } |
|
242
|
4
|
|
|
|
|
|
return bs_setup(base, map_size, path, -1); |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
1
|
|
|
|
|
|
static BsHandle *bs_create_memfd(const char *name, uint64_t capacity, char *errbuf) { |
|
246
|
1
|
50
|
|
|
|
|
if (errbuf) errbuf[0] = '\0'; |
|
247
|
1
|
50
|
|
|
|
|
if (capacity == 0) { BS_ERR("capacity must be > 0"); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
248
|
1
|
|
|
|
|
|
uint32_t nw = (uint32_t)((capacity + 63) / 64); |
|
249
|
1
|
50
|
|
|
|
|
if (capacity > (uint64_t)(UINT32_MAX - 63) * 64) { BS_ERR("capacity too large"); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
250
|
1
|
|
|
|
|
|
uint64_t total = sizeof(BsHeader) + (uint64_t)nw * 8; |
|
251
|
1
|
50
|
|
|
|
|
int fd = memfd_create(name ? name : "bitset", MFD_CLOEXEC); |
|
252
|
1
|
50
|
|
|
|
|
if (fd < 0) { BS_ERR("memfd_create: %s", strerror(errno)); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
253
|
1
|
50
|
|
|
|
|
if (ftruncate(fd, (off_t)total) < 0) { BS_ERR("ftruncate: %s", strerror(errno)); close(fd); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
254
|
1
|
|
|
|
|
|
void *base = mmap(NULL, (size_t)total, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); |
|
255
|
1
|
50
|
|
|
|
|
if (base == MAP_FAILED) { BS_ERR("mmap: %s", strerror(errno)); close(fd); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
256
|
1
|
|
|
|
|
|
bs_init_header(base, total, capacity, nw); |
|
257
|
1
|
|
|
|
|
|
return bs_setup(base, (size_t)total, NULL, fd); |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
1
|
|
|
|
|
|
static BsHandle *bs_open_fd(int fd, char *errbuf) { |
|
261
|
1
|
50
|
|
|
|
|
if (errbuf) errbuf[0] = '\0'; |
|
262
|
|
|
|
|
|
|
struct stat st; |
|
263
|
1
|
50
|
|
|
|
|
if (fstat(fd, &st) < 0) { BS_ERR("fstat: %s", strerror(errno)); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
264
|
1
|
50
|
|
|
|
|
if ((uint64_t)st.st_size < sizeof(BsHeader)) { BS_ERR("too small"); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
265
|
1
|
|
|
|
|
|
size_t ms = (size_t)st.st_size; |
|
266
|
1
|
|
|
|
|
|
void *base = mmap(NULL, ms, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); |
|
267
|
1
|
50
|
|
|
|
|
if (base == MAP_FAILED) { BS_ERR("mmap: %s", strerror(errno)); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
268
|
1
|
|
|
|
|
|
BsHeader *hdr = (BsHeader *)base; |
|
269
|
1
|
50
|
|
|
|
|
if (hdr->magic != BS_MAGIC || hdr->version != BS_VERSION || |
|
|
|
50
|
|
|
|
|
|
|
270
|
1
|
50
|
|
|
|
|
hdr->total_size != (uint64_t)st.st_size) { |
|
271
|
0
|
0
|
|
|
|
|
BS_ERR("invalid bitset"); munmap(base, ms); return NULL; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
1
|
|
|
|
|
|
int myfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); |
|
274
|
1
|
50
|
|
|
|
|
if (myfd < 0) { BS_ERR("fcntl: %s", strerror(errno)); munmap(base, ms); return NULL; } |
|
|
|
0
|
|
|
|
|
|
|
275
|
1
|
|
|
|
|
|
return bs_setup(base, ms, NULL, myfd); |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
7
|
|
|
|
|
|
static void bs_destroy(BsHandle *h) { |
|
279
|
7
|
50
|
|
|
|
|
if (!h) return; |
|
280
|
7
|
100
|
|
|
|
|
if (h->backing_fd >= 0) close(h->backing_fd); |
|
281
|
7
|
50
|
|
|
|
|
if (h->hdr) munmap(h->hdr, h->mmap_size); |
|
282
|
7
|
|
|
|
|
|
free(h->path); |
|
283
|
7
|
|
|
|
|
|
free(h); |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
0
|
|
|
|
|
|
static void bs_msync(BsHandle *h) { msync(h->hdr, h->mmap_size, MS_SYNC); } |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
#endif /* BITSET_H */ |