0

I am trying to build a system for slab allocation, where slabs are contiguous of memory of int data that are constantly recycled. A slab is allocated independently, and stored in a block. If I am done with the int, I want to mark it as done for recycling / garbage collection / reference counting. To find out what slab the int pointer is in, I have decided that I will do a binary search on the memory addresses of the blocks. The blocks are arranged in order by their memory addresses, except block zero, which is the active block.

struct block { size_t size; int *slab; };
struct blocks { struct block *data; size_t size, capacity; };

/** @return Index of block that is higher than `x` in `blocks`.
 @order \O(\log `blocks`) */
static size_t upper(const struct blocks *const blocks,
    const int *const x) {
    const struct block *const base = blocks->data;
    size_t n, b0, b1;
    if(!(n = blocks->size)) return 0;
    if(!--n) return 1;
    /* The last one is a special case: it doesn't have an upper bound. */
    for(b0 = 1, --n; n; n /= 2) {
        b1 = b0 + n / 2;
        if(x < base[b1].slab) { continue; }
        else if(base[b1 + 1].slab <= x) { b0 = b1 + 1; n--; continue; }
        else { return b1 + 1; }
    }
    return b0 + (x >= base[slots->size - 1].slab);
}

static size_t slab_idx(const struct blocks *const blocks,
    const int *const x) {
    struct block *const base = blocks->data;
    /* One chunk, assume it's in that chunk; first slab is `capacity`. */
    if(blocks->size == 1
        || (x >= base[0].slab && x < base[0].slab + blocks->capacity))
        return 0;
    return upper(blocks, x) - 1;
}

The C11 standard 6.5.8¶5, reads,

When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression P points to an element of an array object and the expression Q points to the last element of the same array object, the pointer expression Q+1 compares greater than P. In all other cases, the behavior is undefined.

This works, but invokes undefined behaviour. Would converting them into uintptr_t work? Specifically any solutions for C90?

Neil
  • 1,767
  • 2
  • 16
  • 22
  • _Side note:_ For some [simple] examples of how to do a memory pool, see my recent answer: https://stackoverflow.com/questions/71313784/cs50-pset5-speller-optimisation/71316468#71316468 – Craig Estey Mar 06 '22 at 23:11
  • @CraigEstey I'm one of the ones that upvoted that answer. In that one, one uses a linked-list. I'm trying to organize it in ascending order and use a binary search. I'm not sure how I can do it in a standards-complaint way because the restrictions placed on pointers. – Neil Mar 06 '22 at 23:22
  • I'm not sure where you think you have undefined behaviour. I don't see any comparison between integers and pointers, where `uintptr_t` could help. Also clang 12.0 with `-Wall -Wextra -std=c90 -ansi -pedantic` does not complain, neither does GCC 10.2.1. Please, elaborate. – nickie Mar 06 '22 at 23:28
  • _Side note:_ Well, thank you. I've done comments with links to my answers before, but this is the first time I've done this on a page where the OP previously upvoted the linked answer. – Craig Estey Mar 06 '22 at 23:33
  • AFAICT, your code [as is] doesn't have any UB (from desk checking). Doing a binary search on `int *` pointers is fine (e.g. the comparison operations are okay). What are the units for (e.g.) `size` in both structs (e.g. bytes, count of `int`, count of slot structs)? Are you getting a runtime error? – Craig Estey Mar 06 '22 at 23:38
  • I'm assuming that `data` points to an array of slots that is _sorted_ and the sort key is `chunk`? And, that `upper` is supposed to return the slot number/offset in `data` that is spanned by the range `chunk,chunk+size` of the given slot? I'm having trouble understanding your search algorithm in terms of a classic binary search. I'd expect two vars: `size_t lo = 0, hi = slots->capacity - 1;` And, then, in the loop (e.g.) `mid = (lo + hi) / 2;`, etc. – Craig Estey Mar 06 '22 at 23:54
  • 1
    You lose nothing by applying the standard-compliant approach which is to cast pointers to `uintptr_t` and compare those. – paddy Mar 07 '22 at 00:55
  • @paddy That's essentially what I want to know: does casting them to `uintptr_t` really help, and why? I've enclosed the calling function; sorry, the logic of the binary search doesn't make much sense without it. – Neil Mar 07 '22 at 01:16
  • I mean, you lose ANSI C89 compliance by including ``; which means, among other things, anything less then MSVC2019 may have troubles compiling it. – Neil Mar 07 '22 at 04:40
  • 1
    @Neil Because you are comparing `x` against a variety of `slab` pointers, and for at most one of those comparisons do the `x` and `slab` come from the same array (or one past the end of the same array). For all the other comparisons, you are comparing pointers that came from different arrays, and none of the clauses in the paragraph apply except for the last one: "In all other cases, the behavior is undefined." (The behavior is UB for a reason: [Segmented architectures](https://stackoverflow.com/a/39161283).) Using `uintptr_t` avoids UB; the behavior is implementation-defined. – Raymond Chen Mar 07 '22 at 04:44
  • @RaymondChen very interesting post. Practically, is it possible to have a pointer not from an object test erroneously within the object? – Neil Mar 08 '22 at 02:20
  • 1
    The language does not guarantee it, and compilers do optimize based on the requirement that pointers compared for less-than/greater-than must point to the same array, so you would be best to compare `uintptr_t` values instead of pointers. – Raymond Chen Mar 08 '22 at 03:33

0 Answers0