Algorithm question: in-place multi-bringToFront?

tsbockman thomas.bockman at gmail.com
Fri May 29 05:21:34 UTC 2020


On Friday, 29 May 2020 at 03:29:05 UTC, tsbockman wrote:
> It could be made pure @safe nothrow @nogc fairly easily, except 
> that maintaining proper attribute inference based on the 
> attributes of implicitly called methods of T is a pain.

void cutToFront(T)(scope T[] queue, scope const(size_t[]) 
cutterXs ...) {
     import core.bitop : bsr;
     import core.memory : pureCalloc, pureFree;
     import std.algorithm : move, moveEmplace, sort;

     // Allocate scratch space:
     const(size_t) memSize = (cutterXs.length + 1) * 
(size_t.sizeof + T.sizeof);
     void* mem = () @trusted { return pureCalloc(memSize, 1); }();
     if(mem == null)
         assert(0, "Allocation failed - possibly out of memory?");
     scope(exit) { () @trusted {
         /* IMPORTANT: T must not throw from inside move or 
moveEmplace, below,
         because it's too hard to figure out which item's 
destructors should be called for cutters. */
         pureFree(mem);
     }(); }

     // Prepare scratch slices:
     size_t[] cutterXSet = () @trusted {
         static assert(size_t.alignof == size_t(1) << 
bsr(size_t.alignof));
         auto ptr = cast(size_t*) ((size_t.alignof - 1 + 
cast(size_t) mem) & -size_t.alignof);
         return ptr[0 .. cutterXs.length];
     }();
     T[] cutters = () @trusted {
         static assert(T.alignof == size_t(1) << bsr(T.alignof));
         auto ptr = cast(T*) ((((cutterXSet.length + 1) * 
size_t.sizeof) + T.alignof - 1 + cast(size_t) mem) & -T.alignof);
         return ptr[0 .. cutterXSet.length];
     }();
     assert((cutterXSet.length * typeof(cutterXSet[0]).sizeof + 
cast(void*) cutterXSet.ptr) < cast(void*) cutters.ptr
           && (cutters.length * typeof(cutters[0]).sizeof + 
cast(void*) cutters.ptr) < (memSize + mem));

     // Sort cutterXs:
     cutterXSet[] = cutterXs[];
     sort(cutterXSet);

     // Convert cutterXs into a set:
     size_t cutterCount = 0, lastCutterX = 0;
     for(size_t cutterXX = 0; cutterXX < cutterXSet.length; 
++cutterXX) {
         const cutterX = cutterXSet[cutterXX];
         if(cutterX != lastCutterX) {
             lastCutterX = cutterX;
             cutterXSet[cutterCount] = cutterX;
             ++cutterCount;
         }
     }
     cutterXSet = cutterXSet[0 .. cutterCount];

     if(cutterCount > 0) {
         // Validate the indices:
         if(lastCutterX >= queue.length)
             assert(0, "Invalid cutter index.");

         // Pull the cutters out of the queue temporarily, sliding 
other items back as needed:
         const(size_t) lastCutterXX = cutterCount - 1;
         size_t gap = 0, oldX = lastCutterX, oldCutterX = 
cutterXSet[lastCutterXX];
         while(true) {
             T* dest;
             if(oldX == oldCutterX) {
                 // If T makes moveEmplace!T unsafe, then the 
move!T call below should also be unsafe.
                 () @trusted nothrow { moveEmplace(queue[oldX], 
cutters[lastCutterXX - gap]); }();
                 ++gap;
                 if(gap <= lastCutterXX)
                     oldCutterX = cutterXSet[lastCutterXX - gap];
             } else {
                 const newX = oldX + gap;
                 () nothrow { move(queue[oldX], queue[newX]); }();
             }

             if(oldX <= 0)
                 break;
             --oldX; // Iterate backwards to avoid overwriting 
later entries before they have been moved elsewhere.
         }

         // Finally, place the cutters at the front of the line:
         size_t x = lastCutterXX;
         while(true) {
             () nothrow { move(cutters[x], queue[x]); }();
             if(x <= 0)
                 break;
             --x; // Iterate through queue[newX] in the same 
direction as the loop above to avoid confusing the prefetcher.
         }
     }
}



More information about the Digitalmars-d mailing list