Data-Intern-Shared
view release on metacpan or search on metacpan
if (backing_fd >= 0) close(backing_fd);
return NULL;
}
h->hdr = hdr;
h->reader_slots = (SiReaderSlot *)((uint8_t *)base + hdr->reader_slots_off);
h->slots = (SiSlot *)((uint8_t *)base + hdr->hash_off);
h->reverse = (uint32_t *)((uint8_t *)base + hdr->reverse_off);
h->arena = (uint8_t *)base + hdr->arena_off;
h->mmap_size = map_size;
h->path = path ? strdup(path) : NULL;
h->backing_fd = backing_fd;
h->my_slot_idx = UINT32_MAX;
return h;
}
/* Validate a mapped header (shared by si_create reopen and si_open_fd). */
static inline int si_validate_header(const SiHeader *hdr, uint64_t file_size) {
if (hdr->magic != SI_MAGIC) return 0;
if (hdr->version != SI_VERSION) return 0;
if (hdr->max_strings == 0 || hdr->max_strings > SI_MAX_STRINGS) return 0;
if (hdr->hash_slots == 0 || (hdr->hash_slots & (hdr->hash_slots - 1)) != 0) return 0; /* pow2 */
if (hdr->hash_slots <= hdr->max_strings) return 0; /* probe termination: an empty slot always exists */
if (hdr->arena_bytes == 0) return 0;
if (hdr->total_size != file_size) return 0;
if (hdr->total_size != si_total_size(hdr->hash_slots, hdr->max_strings, hdr->arena_bytes)) return 0;
SiLayout L = si_layout(hdr->hash_slots, hdr->max_strings);
if (hdr->reader_slots_off != L.reader_slots) return 0;
if (hdr->hash_off != L.hash) return 0;
if (hdr->reverse_off != L.reverse) return 0;
if (hdr->arena_off != L.arena) return 0;
if (hdr->count > hdr->max_strings) return 0;
if (hdr->arena_used > hdr->arena_bytes) return 0;
return 1;
}
/* validate args + compute the hash-slot count and (if 0) a default arena size */
static int si_validate_create_args(uint32_t max_strings, uint32_t *arena_bytes_io,
uint32_t *hash_slots, char *errbuf) {
if (errbuf) errbuf[0] = '\0';
if (max_strings == 0) { SI_ERR("max_strings must be > 0"); return 0; }
if (max_strings > SI_MAX_STRINGS) { SI_ERR("max_strings too large (max %u)", SI_MAX_STRINGS); return 0; }
uint64_t want = (uint64_t)max_strings * 10 / 7 + 1; /* hash load factor ~0.7 */
/* next_pow2(want) is always strictly > max_strings, so a probe always finds
an empty slot (lookup misses cannot loop forever). With max_strings capped
at 2^30, want <= ~1.43*2^30 < 2^31, whose next_pow2 is 2^31 -- fits uint32. */
*hash_slots = si_next_pow2((uint32_t)want);
if (*arena_bytes_io == 0) { /* default arena: 32 bytes/string */
uint64_t a = (uint64_t)max_strings * 32;
if (a > SI_MAX_ARENA) a = SI_MAX_ARENA;
if (a < 64) a = 64;
*arena_bytes_io = (uint32_t)a;
}
return 1;
}
/* Securely obtain a fd: create exclusively (O_CREAT|O_EXCL|O_NOFOLLOW at mode,
* default 0600), or attach an existing file (O_RDWR|O_NOFOLLOW, no O_CREAT). */
static int si_secure_open(const char *path, mode_t mode, char *errbuf) {
for (int attempt = 0; attempt < 100; attempt++) {
int fd = open(path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC, mode);
if (fd >= 0) { (void)fchmod(fd, mode); return fd; } /* exact mode: umask narrowed the O_EXCL create */
if (errno != EEXIST) { SI_ERR("create %s: %s", path, strerror(errno)); return -1; }
fd = open(path, O_RDWR|O_NOFOLLOW|O_CLOEXEC);
if (fd >= 0) return fd;
if (errno == ENOENT) continue; /* creator unlinked between our two opens; retry */
SI_ERR("open %s: %s", path, strerror(errno)); /* ELOOP => symlink rejected */
return -1;
}
SI_ERR("open %s: create/attach kept racing", path);
return -1;
}
static SiHandle *si_create(const char *path, uint32_t max_strings, uint32_t arena_bytes, mode_t mode, char *errbuf) {
uint32_t hash_slots;
if (!si_validate_create_args(max_strings, &arena_bytes, &hash_slots, errbuf)) return NULL;
uint64_t total = si_total_size(hash_slots, max_strings, arena_bytes);
int anonymous = (path == NULL);
int fd = -1;
size_t map_size;
void *base;
if (anonymous) {
map_size = (size_t)total;
base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if (base == MAP_FAILED) { SI_ERR("mmap: %s", strerror(errno)); return NULL; }
} else {
fd = si_secure_open(path, mode, errbuf);
if (fd < 0) return NULL;
if (flock(fd, LOCK_EX) < 0) { SI_ERR("flock: %s", strerror(errno)); close(fd); return NULL; }
struct stat st;
if (fstat(fd, &st) < 0) { SI_ERR("fstat: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
int is_new = (st.st_size == 0);
if (!is_new && (uint64_t)st.st_size < sizeof(SiHeader)) {
SI_ERR("%s: file too small (%lld)", path, (long long)st.st_size);
flock(fd, LOCK_UN); close(fd); return NULL;
}
if (is_new && ftruncate(fd, (off_t)total) < 0) {
SI_ERR("ftruncate: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL;
}
map_size = is_new ? (size_t)total : (size_t)st.st_size;
base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) { SI_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
if (!is_new) {
if (!si_validate_header((SiHeader *)base, (uint64_t)st.st_size)) {
SI_ERR("invalid intern file"); munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
}
flock(fd, LOCK_UN); close(fd);
return si_setup(base, map_size, path, -1);
}
}
si_init_header(base, max_strings, hash_slots, arena_bytes, total);
if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
return si_setup(base, map_size, path, -1);
}
static SiHandle *si_create_memfd(const char *name, uint32_t max_strings, uint32_t arena_bytes, char *errbuf) {
uint32_t hash_slots;
if (!si_validate_create_args(max_strings, &arena_bytes, &hash_slots, errbuf)) return NULL;
uint64_t total = si_total_size(hash_slots, max_strings, arena_bytes);
( run in 2.328 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )