@@ -9594,6 +9594,388 @@ auto refRange(R)(R* range)
95949594 foo(r);
95959595}
95969596
9597+ /**
9598+ Bitwise adapter over integral type ranges. Consumes the range elements bit by bit.
9599+
9600+ Params:
9601+ R = an input range to iterate over
9602+
9603+ Returns:
9604+ At minimum, an input range. If `R` was a forward, bidirectional or random
9605+ access range, `Bitwise!R` will be as well.
9606+ */
9607+ struct Bitwise (R)
9608+ if (isInputRange! R && isIntegral! (ElementType! R))
9609+ {
9610+ private :
9611+ alias ElemType = ElementType! R;
9612+ alias UnsignedElemType = Unsigned! ElemType;
9613+
9614+ R parent;
9615+ enum bitsNum = ElemType.sizeof * 8 ;
9616+ size_t maskPos = bitsNum;
9617+
9618+ static if (isBidirectionalRange! R)
9619+ {
9620+ size_t backMaskPos = 1 ;
9621+ }
9622+
9623+ public :
9624+ this ()(auto ref R range)
9625+ {
9626+ parent = range;
9627+ }
9628+
9629+ /**
9630+ * Check if the range is empty
9631+ *
9632+ * Returns: a boolean true or false
9633+ */
9634+ bool empty ()
9635+ {
9636+ static if (hasLength! R)
9637+ {
9638+ return length () == 0 ;
9639+ }
9640+ else static if (isBidirectionalRange! R)
9641+ {
9642+ bool isOverlapping = parent.empty;
9643+ if (! parent.empty)
9644+ {
9645+ /*
9646+ If we have consumed the last element of the range both from
9647+ the front and the back, then the masks positions will overlap
9648+ */
9649+ R parentCopy = parent.save;
9650+ parentCopy.popFront;
9651+ isOverlapping = parentCopy.empty && (maskPos < backMaskPos);
9652+ }
9653+ return parent.empty || isOverlapping;
9654+ }
9655+ else
9656+ {
9657+ /*
9658+ If we consumed the last element of the range, but not all the
9659+ bits in the last element
9660+ */
9661+ return parent.empty;
9662+ }
9663+ }
9664+
9665+ bool front ()
9666+ {
9667+ assert (! empty());
9668+ return (parent.front & mask(maskPos)) != 0 ;
9669+ }
9670+
9671+ void popFront ()
9672+ {
9673+ -- maskPos;
9674+ if (maskPos == 0 )
9675+ {
9676+ parent.popFront;
9677+ maskPos = bitsNum;
9678+ }
9679+ }
9680+
9681+ static if (hasLength! R)
9682+ {
9683+ size_t length ()
9684+ {
9685+ auto len = parent.length * bitsNum - (bitsNum - maskPos);
9686+ static if (isBidirectionalRange! R)
9687+ {
9688+ len -= (backMaskPos - 1 );
9689+ }
9690+ return len;
9691+ }
9692+
9693+ alias opDollar = length;
9694+ }
9695+
9696+ static if (isForwardRange! R)
9697+ {
9698+ typeof (this ) save ()
9699+ {
9700+ auto result = this ;
9701+ result.parent = parent.save;
9702+ return result;
9703+ }
9704+ }
9705+
9706+ static if (isBidirectionalRange! R)
9707+ {
9708+ bool back ()
9709+ {
9710+ assert (! empty());
9711+ return (parent.back & mask(backMaskPos)) != 0 ;
9712+ }
9713+
9714+ void popBack ()
9715+ {
9716+ ++ backMaskPos;
9717+ if (backMaskPos > bitsNum)
9718+ {
9719+ parent.popBack;
9720+ backMaskPos = 1 ;
9721+ }
9722+ }
9723+ }
9724+
9725+ static if (isRandomAccessRange! R)
9726+ {
9727+ /**
9728+ Return the `n`th bit within the range
9729+ */
9730+ bool opIndex (size_t n)
9731+ in
9732+ {
9733+ /*
9734+ If it does not have the length property, it means that R is
9735+ an infinite range
9736+ */
9737+ static if (hasLength! R)
9738+ {
9739+ assert (n < length(), __PRETTY_FUNCTION__ ~ " : Index out of bounds" );
9740+ }
9741+ }
9742+ body
9743+ {
9744+ // If n >= maskPos, then the bit sign will be 1, otherwise 0
9745+ immutable sizediff_t sign = (maskPos - n - 1 ) >> (sizediff_t .sizeof * 8 - 1 );
9746+ /*
9747+ By truncating n with maskPos bits we have skipped the remaining
9748+ maskPos bits in parent[0], so we need to add 1 to elemIndex.
9749+
9750+ Because bitsNum is a power of 2, n / bitsNum == n >> bitsNum.bsf
9751+ */
9752+ import core.bitop : bsf;
9753+ immutable size_t elemIndex = sign * (((n - maskPos) >> bitsNum.bsf) + 1 );
9754+
9755+ /*
9756+ Since the indexing is from MSB to LSB, we need to subtract the
9757+ remainder from the number of bits that the element type has.
9758+
9759+ Because bitsNum is a power of 2, n % bitsNum == n & (bitsNum - 1)
9760+ */
9761+ immutable size_t elemMaskPos = (sign ^ 1 ) * (maskPos - n)
9762+ + sign * (bitsNum - ((n - maskPos) & (bitsNum - 1 )));
9763+
9764+ return (parent[elemIndex] & mask(elemMaskPos)) != 0 ;
9765+ }
9766+
9767+ /**
9768+ Assignes `flag` to the `n`th bit within the range
9769+ */
9770+ void opIndexAssign (bool flag, size_t n)
9771+ in
9772+ {
9773+ static if (hasLength! R)
9774+ {
9775+ assert (n < length(), __PRETTY_FUNCTION__ ~ " : Index out of bounds" );
9776+ }
9777+ }
9778+ body
9779+ {
9780+ import core.bitop : bsf;
9781+
9782+ immutable sizediff_t sign = (maskPos - n - 1 ) >> (sizediff_t .sizeof * 8 - 1 );
9783+ immutable size_t elemIndex = sign * (((n - maskPos) >> bitsNum.bsf) + 1 );
9784+ immutable size_t elemMaskPos = (sign ^ 1 ) * (maskPos - n)
9785+ + sign * (bitsNum - ((n - maskPos) & (bitsNum - 1 )));
9786+
9787+ auto elem = parent[elemIndex];
9788+ auto elemMask = mask(elemMaskPos);
9789+ parent[elemIndex] = cast (UnsignedElemType)(flag * (elem | elemMask)
9790+ + (flag ^ 1 ) * (elem & ~ elemMask));
9791+ }
9792+
9793+ Bitwise! R opSlice ()
9794+ {
9795+ return this ;
9796+ }
9797+
9798+ Bitwise! R opSlice (size_t start, size_t end)
9799+ in
9800+ {
9801+ assert (start < end, __PRETTY_FUNCTION__ ~ " : Invalid bounds: end <= start" );
9802+ }
9803+ body
9804+ {
9805+ import core.bitop : bsf;
9806+
9807+ sizediff_t sign = (maskPos - start - 1 ) >> (sizediff_t .sizeof * 8 - 1 );
9808+ immutable size_t startElemIndex = sign * (((start - maskPos) >> bitsNum.bsf) + 1 );
9809+ immutable size_t startElemMaskPos = (sign ^ 1 ) * (maskPos - start)
9810+ + sign * (bitsNum - (start & (bitsNum - 1 )));
9811+
9812+ immutable size_t sliceLen = end - start - 1 ;
9813+ sign = (maskPos - sliceLen - 1 ) >> (sizediff_t .sizeof * 8 - 1 );
9814+ immutable size_t endElemIndex = sign * (((sliceLen - maskPos) >> bitsNum.bsf) + 1 );
9815+ immutable size_t endElemMaskPos = (sign ^ 1 ) * (maskPos - sliceLen)
9816+ + sign * (bitsNum - ((sliceLen - maskPos) & (bitsNum - 1 )));
9817+
9818+ typeof (return ) result;
9819+ // Get the slice to be returned from the parent
9820+ result.parent = (parent[startElemIndex .. endElemIndex + 1 ]).save;
9821+ result.maskPos = startElemMaskPos;
9822+ static if (isBidirectionalRange! R)
9823+ {
9824+ result.backMaskPos = endElemMaskPos;
9825+ }
9826+ return result;
9827+ }
9828+ }
9829+
9830+ private :
9831+ auto mask (size_t maskPos)
9832+ {
9833+ return (1UL << (maskPos - 1UL ));
9834+ }
9835+ }
9836+
9837+ // Test all range types over all integral types
9838+ // /
9839+ @safe unittest
9840+ {
9841+ import std.internal.test.dummyrange ;
9842+
9843+ alias IntegralTypes = AliasSeq! (byte , ubyte , short , ushort , int , uint ,
9844+ long , ulong );
9845+ foreach (IntegralType; IntegralTypes)
9846+ {
9847+ foreach (T; AllDummyRangesType! (IntegralType[]))
9848+ {
9849+ T a;
9850+ auto bw = Bitwise! T(a);
9851+
9852+ static if (isForwardRange! T)
9853+ {
9854+ auto bw2 = bw.save;
9855+ }
9856+
9857+ static if (isBidirectionalRange! T)
9858+ {
9859+ auto bw3 = bw.save;
9860+ auto bw4 = bw.save;
9861+ }
9862+
9863+ static if (hasLength! T)
9864+ {
9865+ auto bwLength = bw.length;
9866+ assert (bw.length == (IntegralType.sizeof * 8 * a.length));
9867+ static if (isForwardRange! T)
9868+ {
9869+ assert (bw.length == bw2.length);
9870+ }
9871+ }
9872+
9873+ // Make sure front and back are not the mechanisms that modify the range
9874+ long numCalls = 42 ;
9875+ bool initialFrontValue;
9876+
9877+ if (! bw.empty())
9878+ {
9879+ initialFrontValue = bw.front;
9880+ }
9881+
9882+ while (! bw.empty() && (-- numCalls))
9883+ {
9884+ bw.front;
9885+ assert (bw.front == initialFrontValue);
9886+ }
9887+
9888+ /*
9889+ Check that empty works properly and that popFront does not get called
9890+ more times than it should
9891+ */
9892+ numCalls = 0 ;
9893+ while (! bw.empty())
9894+ {
9895+ ++ numCalls;
9896+
9897+ static if (hasLength! T)
9898+ {
9899+ assert (bw.length == bwLength);
9900+ -- bwLength;
9901+ }
9902+
9903+ static if (isForwardRange! T)
9904+ {
9905+ assert (bw.front == bw2.front);
9906+ bw2.popFront();
9907+ }
9908+
9909+ static if (isBidirectionalRange! T)
9910+ {
9911+ assert (bw3.front == bw4.front);
9912+ bw3.popBack();
9913+ bw4.popBack();
9914+ }
9915+ bw.popFront();
9916+ }
9917+
9918+ auto rangeLen = numCalls / (IntegralType.sizeof * 8 );
9919+ assert (numCalls == (IntegralType.sizeof * 8 * rangeLen));
9920+ assert (bw.empty());
9921+ static if (isForwardRange! T)
9922+ {
9923+ assert (bw2.empty());
9924+ }
9925+
9926+ static if (isBidirectionalRange! T)
9927+ {
9928+ assert (bw3.empty());
9929+ }
9930+ }
9931+ }
9932+ }
9933+
9934+ // Test opIndex and opSlice
9935+ // /
9936+ @system unittest
9937+ {
9938+ alias IntegralTypes = AliasSeq! (byte , ubyte , short , ushort , int , uint ,
9939+ long , ulong );
9940+ foreach (IntegralType; IntegralTypes)
9941+ {
9942+ size_t bitsNum = IntegralType.sizeof * 8 ;
9943+
9944+ auto first = cast (IntegralType)(1 );
9945+
9946+ // 2 ^ (bitsNum - 1)
9947+ auto second = cast (IntegralType)(cast (IntegralType)(1 ) << (bitsNum - 2 ));
9948+
9949+ IntegralType[] a = [first, second];
9950+ auto bw = Bitwise! (IntegralType[])(a);
9951+
9952+ // Check against lsb of a[0]
9953+ assert (bw[bitsNum - 1 ] == true );
9954+ // Check against msb - 1 of a[1]
9955+ assert (bw[bitsNum + 1 ] == true );
9956+
9957+ bw.popFront();
9958+ assert (bw[bitsNum] == true );
9959+
9960+ import core.exception : Error;
9961+ import std.exception : assertThrown;
9962+
9963+ // Check out of bounds error
9964+ assertThrown! Error(bw[2 * bitsNum - 1 ]);
9965+
9966+ bw[2 ] = true ;
9967+ assert (bw[2 ] == true );
9968+ bw.popFront();
9969+ assert (bw[1 ] == true );
9970+
9971+ auto bw2 = bw[0 .. $ - 5 ];
9972+ auto bw3 = bw2[];
9973+ assert (bw2.length == (bw.length - 5 ));
9974+ assert (bw2.length == bw3.length);
9975+ bw2.popFront();
9976+ assert (bw2.length != bw3.length);
9977+ }
9978+ }
95979979
95989980/* ********************************
95999981 * An OutputRange that discards the data it receives.
0 commit comments