Data-SortedSet-Shared

 view release on metacpan or  search on metacpan

sortedset.h  view on Meta::CPAN

    SsHandle *h = (SsHandle *)calloc(1, sizeof(SsHandle));
    if (!h) {
        munmap(base, map_size);
        if (backing_fd >= 0) close(backing_fd);
        return NULL;
    }
    h->hdr          = hdr;
    h->reader_slots = (SsReaderSlot *)((uint8_t *)base + hdr->reader_slots_off);
    h->index        = (SsIdxSlot *)((uint8_t *)base + hdr->index_off);
    h->nodes        = (SsNode *)((uint8_t *)base + hdr->nodes_off);
    h->mmap_size    = map_size;
    h->path         = path ? strdup(path) : NULL;
    h->notify_fd    = -1;
    h->backing_fd   = backing_fd;
    h->my_slot_idx  = UINT32_MAX;
    return h;
}

/* Validate a mapped header (shared by ss_create reopen and ss_open_fd). */
static inline int ss_validate_header(const SsHeader *hdr, uint64_t file_size) {
    if (hdr->magic != SS_MAGIC) return 0;
    if (hdr->version != SS_VERSION) return 0;
    if (hdr->max_entries == 0 || hdr->max_entries >= SS_MAX_CAPACITY) return 0;
    if (hdr->index_slots == 0 || (hdr->index_slots & (hdr->index_slots - 1)) != 0) return 0; /* pow2 */
    if (hdr->node_capacity == 0) return 0;
    if (hdr->total_size != file_size) return 0;
    if (hdr->total_size != ss_total_size(hdr->index_slots, hdr->node_capacity)) return 0;
    SsLayout L = ss_layout(hdr->index_slots);
    if (hdr->reader_slots_off != L.reader_slots) return 0;
    if (hdr->index_off        != L.index)        return 0;
    if (hdr->nodes_off        != L.nodes)        return 0;
    if (hdr->count > hdr->max_entries) return 0;
    if (hdr->root != SS_NONE && hdr->root >= hdr->node_capacity) return 0;
    if (hdr->root == SS_NONE && hdr->count != 0) return 0;
    return 1;
}


/* validate max_entries and compute the index-slot count + node-pool capacity
   (shared by ss_create + ss_create_memfd) */
static int ss_validate_create_args(uint32_t max_entries, uint32_t *index_slots,
                                   uint32_t *node_capacity, char *errbuf) {
    if (errbuf) errbuf[0] = '\0';
    if (max_entries == 0) { SS_ERR("max_entries must be > 0"); return 0; }
    /* must be strictly < SS_MAX_CAPACITY: at exactly 2^30 the index cap below makes
       index_slots == max_entries (load factor 1.0), so ss_idx_find never finds an
       empty slot on an absent lookup when full and spins forever under the lock. */
    if (max_entries >= SS_MAX_CAPACITY) { SS_ERR("max_entries too large (max %u)", SS_MAX_CAPACITY - 1); return 0; }
    uint64_t want = (uint64_t)max_entries * 10 / 7 + 1;        /* index load factor ~0.7 */
    if (want > SS_MAX_CAPACITY) want = SS_MAX_CAPACITY;
    *index_slots   = ss_next_pow2((uint32_t)want);
    *node_capacity = (uint32_t)((uint64_t)max_entries / (SS_MIN - 1) + 64); /* worst-case fill + slack */
    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 ss_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) { SS_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 */
        SS_ERR("open %s: %s", path, strerror(errno));  /* ELOOP => symlink rejected */
        return -1;
    }
    SS_ERR("open %s: create/attach kept racing", path);
    return -1;
}

static SsHandle *ss_create(const char *path, uint32_t max_entries, mode_t mode, char *errbuf) {
    uint32_t index_slots, node_capacity;
    if (!ss_validate_create_args(max_entries, &index_slots, &node_capacity, errbuf)) return NULL;

    uint64_t total = ss_total_size(index_slots, node_capacity);
    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) { SS_ERR("mmap: %s", strerror(errno)); return NULL; }
    } else {
        fd = ss_secure_open(path, mode, errbuf);
        if (fd < 0) return NULL;
        if (flock(fd, LOCK_EX) < 0) { SS_ERR("flock: %s", strerror(errno)); close(fd); return NULL; }
        struct stat st;
        if (fstat(fd, &st) < 0) { SS_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(SsHeader)) {
            SS_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) {
            SS_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) { SS_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
        if (!is_new) {
            if (!ss_validate_header((SsHeader *)base, (uint64_t)st.st_size)) {
                SS_ERR("invalid sorted-set file"); munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
            }
            flock(fd, LOCK_UN); close(fd);
            return ss_setup(base, map_size, path, -1);
        }
    }
    ss_init_header(base, max_entries, index_slots, node_capacity, total);
    if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
    return ss_setup(base, map_size, path, -1);
}

static SsHandle *ss_create_memfd(const char *name, uint32_t max_entries, char *errbuf) {
    uint32_t index_slots, node_capacity;
    if (!ss_validate_create_args(max_entries, &index_slots, &node_capacity, errbuf)) return NULL;

    uint64_t total = ss_total_size(index_slots, node_capacity);



( run in 2.085 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )