Open przygienda opened 2 years ago
Could you show an example of what you are trying to achieve and your proposed solution? I'm having trouble picturing it without some code examples.
A more standard use case is building a skip list. Each node has a tower of links, up to a (typically constant bounded) random height. You can make an adapter for links at a specific index across all nodes, by expanding the macro code for intrusive_adapter
, adding a height
(not max height) field to the adapter struct, and using that height to calculate first the offsets within the skip link struct, then the skip link struct within the node array, then the array within the node. To pick one linked list, you initialise it with MyAdapter::new(h)
, and then it walks your nodes using an offset based on h
, threading a list through links at the same height in each.
(And, of course, IRL you would actually build a tower of cursors to navigate through it efficiently using the skip distances.)
Gist for the adapter: https://gist.github.com/cormacrelf/eb54684ab717ad104c4c3f1ddaa2e624
Here's my report on using intrusive linked lists to implement a skip list. I got it to work with some changes.
You can't easily build a skip list using intrusive_collections::LinkedList
as-is because there is no way to pivot a node pointer from one list to another. To efficiently navigate and mutate a skip list you need to take a cursor at some height, and get another cursor at the same node but for the list threaded through at a different height. Equivalently, you could take a cursor at some height, and "advance" it by setting the current node O(1). That advance function can't just be "move_next until current == target" because then navigating the skip list would be slower than just using one linked list.
This can't be done as it stands, because:
unsafe fn advance_to
function to change the current node of a cursorPointerOps::Pointer
out through CursorMut::as_cursor(...).clone_pointer()
, so that's a start&'a mut LinkedList
out of the CursorMut
. You need this to construct a new cursor on the same list with a different current pointer via LinkedList::cursor_mut_from_ptr
.I think unsafe fn advance_to
would be a great addition. Bikeshed the name if you like. I suppose ideally it'd work on *const PointerOps::Value
so users of Rc don't need to rehydrate and dehydrate / bump strong pointers and the like (unless that's not how the pointerops impl works). I've been surprised before at the microsecond-scale performance hit that doing this excessively can cause. It would form a consistent API where the unsafe methods assuming that a node is already linked in a particular list use *const Value
, whereas PointerOps::Pointer
is for nodes being inserted. I'm also sensing that the PointerOps::Pointer
APIs have ownership / ref count implications whereas *const Value
does not.
it is incredibly difficult to write generics code over the current adapters since if an object is linked on multiple queues I may want to write generics saying "manipulate queue[x] link" and now it's almost impossible to express with generics basically collapsing due to all the lifetime constrains on cursors and elements
it would be much simpler if we have array of the same adapter, e.g. LinkList and then I can pass reference to the adapter by using the index into the array where the function interanlly can index it on the structure rather than trying to write a generic over any of those adapters