BitArray shift operators#14240
BitArray shift operators#14240ianhays merged 4 commits intodotnet:masterfrom AlexRadch:BitArray-Shift
Conversation
|
How add new API to BitArray class? What should I change to remove compilation errors on new methods?
RightShift is new method that I added. |
|
@AlexRadch make sure you update the ref file also (src\System.Collections\ref\System.Collections.cs) as that's what the tests (and any user code) points to. |
|
@danmosemsft I updated this file https://github.com/dotnet/corefx/pull/14240/files#diff-9af4fbb6cc264756bf83c6b96c0144b5 but it is not enough. |
|
@joperezr any idea? I can't see it. |
|
Hi @AlexRadch, first of all thanks for your contribution! You are hitting this issue because we cross compile System.Collections for different frameworks, and one of them is net463 which doesn't use the implementation of BitArray that you modified. This is causing for the error that you called out above, since for net463 build, we don't have these two newly added members. What we need to do in this case, is to condition the reference assembly to only expose these new APIs for .NET Core and UWP, and then make sure that the net463 tests don't include the ones you just wrote, since that API won't be available for that framework. Do you want to give that a try (with some guidance and instructions from me 😄) or do you want me to take over, and build a commit on top of yours? |
|
FYI: I did go ahead and made some changes that should unblock your PR, feel free to either cherrypick them to your branch or to manually apply them. They are here: joperezr@02de163 |
|
@joperezr I merge your changes. Thank you! |
ianhays
left a comment
There was a problem hiding this comment.
FYI to people who commented on the original issue: @Clockwork-Muse @chrisaut @ryantrem
| } | ||
|
|
||
| //[Fact] | ||
| //public static void Shift_Invalid() |
There was a problem hiding this comment.
Why is this one commented out?
There was a problem hiding this comment.
Because I made other direction shift.
There was a problem hiding this comment.
Please remove then we try not to have commented out code
| } | ||
| count = -count; | ||
| // Following trick reduce check with int.MinValue | ||
| if (unchecked((uint)count < (uint)m_length)) |
There was a problem hiding this comment.
Seems needlessly complicated. Why not just have:
if (count == 0)
return this;
if (count < 0)
return LeftShift(-count);
And let LeftShift do the validation to ensure -count is a valid shift.
There was a problem hiding this comment.
if (count < 0)
return LeftShift(-count);Stack overflow with count = int.MinValue -int.MinValue == int.MinValue)
Also one check is better that two. It is trick code.
| { | ||
| if (count == 0) | ||
| { | ||
| _version++; |
There was a problem hiding this comment.
Still be updating the version if no modifications are made?
There was a problem hiding this comment.
Yes, it is usual behavior of all BitArray methods. See Not method when len = 0 for example.
|
@Clockwork-Muse @chrisaut @ryantrem CR please |
| if (unchecked((uint)count < (uint)m_length)) | ||
| { | ||
| var fromIndex = count / BitsPerInt32; | ||
| int shiftCount = count % BitsPerInt32; |
There was a problem hiding this comment.
Perhaps you could use Math.DivRem here? Good discussion about jit behaviour here https://github.com/dotnet/coreclr/issues/3439
There was a problem hiding this comment.
I do not know why but I got compilation error on Math.DivRem: System\Collections\BitArray.cs(432,38): error CS0117: 'Math' does not contain a definition for 'DivRem'
There was a problem hiding this comment.
@AlexRadch I guess that method is not exposed in whatever targetframework the repo is targeting. Either that or the dependency chain is such that BitArray is lower in the dependency chain than the Math implementation. I seriously doubt that though.
I see you've inlined the implementation. I'm not sure that's a good idea as the jitter should eventually recognized div followed by rem and use the results from the hardware immediately (which is why the change to Math.DivRem is temporary https://github.com/stephentoub/coreclr/blob/64edfc9b668d2f4af8c32d7dda2e64e776b40af1/src/mscorlib/src/System/Math.cs#L729).
In any case, I don't think its a big deal either way. Sorry for the confusion.
| if (unchecked((uint)count < (uint)m_length)) | ||
| { | ||
| lastIndex = (m_length - 1) / BitsPerInt32; | ||
| int shiftCount = count % BitsPerInt32; |
|
Sorry I cannot review in detail. But I am happy this is going in, and from a quick glance it looks like a sensible implementation. Good job and thnx for pinging me. |
|
Generally speaking, you shouldn't be pulling from master into a PR, since it confuses what's new. Unless you need something from a recent PR, or there's a merge conflict, just let it get "out of date". If you do need something from master, you should be rebasing instead. |
|
@Clockwork-Muse I made something strange. I try to make rebase. Now I am thinking how to revert all.... |
|
I believe what you want to do is to return to the original state of your PR and then rebase with master. You can try running this commands in order to fix your problem and update this PR accordingly: git checkout BitArray-Shift
git reset --hard 3a2db757adeae49e18148a267723d4a42139ce4a
git fetch upstream master
git rebase upstream/master
(Previous command will give you all of the conflicts, if any. Fix them by calling 'git status' to see which ones you are missing. Once you are done merging conflicts, you should be able to call 'git rebase --continue'. Repeat this process as many times as necessary since it will run 7 times and pause each time you have a conflict)
git push -f origin BitArray-ShiftThat should get you into a good state again. Let me know if you run into any issues. |
|
@AlexRadch not sure if it helps, but I have found a UI (I use SourceTree) helpful to visualize the state of branches when I do something like this. For example after the reset it shows just where your branches are. |
|
The rebase is good @ianhays should sign off on the code/tests |
|
Glad to see this getting some traction! |
| ** Shift all the bit values to right on count bits. When count less zero it | ||
| ** shift all the bit values to left. New bits are be set to zero. The | ||
| ** current instance is updated and returned. | ||
| =========================================================================*/ |
There was a problem hiding this comment.
It seems weird and a bit confusing to me that you can either invoke LeftShift(n) or RightShift(-n) and get the same behavior. I'd think either negative values would not be valid, or there would be a single Shift method that accepts negative or positive values. Ultimately though, to me it makes the most sense to just match the behavior of the primitive shift operators (which this currently does not).
There was a problem hiding this comment.
Shift operations on int and uint just give five bits of the count. In fact they make next operations x shift (count & 0b11111).
BitArray have any length so we can not give some bits of count.
Left shift is like multiplication by 2^n. (x << count) == (x * 2 ^ count)
Right shift is like division by 2^n. (x >> count) == (x / 2 ^ count)
Multiplication and Division are defined for count less than 0 like next:
(x * 2 ^ count) == (x / 2 ^ -count)
So it have a mean to shift in other direction.
Primitive shift operators does not throw exceptions. So if count < 0 we must:
- Throw argument out of range exception.
- Shift on zero bits.
- Shift on -count bits in other direction.
What is you choice?
There was a problem hiding this comment.
int x = 8;
Console.WriteLine(x << 1);
Console.WriteLine(x << -1);
Console.WriteLine(x << 0);
Console.WriteLine(x >> 1);
Console.WriteLine(x >> -1);
Console.WriteLine(x >> 0);gives output,
16
0
8
4
0
8
The c# shift operators just return 0 on negative count. But on BitArray, making it 0 would not be a desired behavior in manipulating the array in place to an unexpected value. Same case for changing the shift direction as well, when the API explicitly mentions right shift, and the array being manipulated in a different shift. I would prefer if an exception is thrown.
There was a problem hiding this comment.
@Priya91 we can not use int shift logic try next in interactive
> int x = 1;
> x << -1
-2147483648
| } | ||
| count = -count; | ||
| // Following trick reduce check with int.MinValue | ||
| if (unchecked((uint)count < (uint)m_length)) |
There was a problem hiding this comment.
Why all the explicit usage of unchecked when this is already the default for non-constants?
There was a problem hiding this comment.
count = -count have one special case. If count equal to int.MinValue than it stay int.MinValue (less zero). So to check this special case in one operation I used such trick code.
@AlexRadch I don't think that the rebase si right, I still see some of your commits twice. Can you try running the commands I pointed out above? |
|
@joperezr Git have me again |
|
@joperezr I do not know what I do but I created only one commit. |
you squashed everything in one commit, which for PRs is ideally better to keep in multiple commits so that we can see the different iterations of the changes, but squashing all to one is not the end of the world 😄 |
|
|
||
| _version++; | ||
| return this; | ||
| } |
There was a problem hiding this comment.
nit: no need to nest the ifs like this
if (count == 0)
{
version++;
return this;
}
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum);
}
There was a problem hiding this comment.
if (count <= 0) {
if (count < 0) { }
}Made 1 comparison in usual flow and 2 in rare flow.
if (count == 0) { }
if (count < 0) { }Made 2 comparison in usual flow.
There was a problem hiding this comment.
You're right, I just prefer the other style as it's a bit easier to read and follows our style throughout the file better. You can leave it like this if you think it's better.
There was a problem hiding this comment.
I like easy to read but this is system library so I use such performance tricks.
| if (count < m_length) | ||
| { | ||
| int shiftCount; | ||
| // int fromIndex = Math.DivRem(count, BitsPerInt32, out shiftCount); // Compilation error |
There was a problem hiding this comment.
Here was review to use Math.DivRem but it can not be compiled. So I inserted this comment.
There was a problem hiding this comment.
We try to avoid commented out code in source. Better to either make it work, remove it, or add an actual comment explaining in detail why we're not using Math.
| int lastIndex = (m_length - 1) / BitsPerInt32; | ||
|
|
||
| int shiftCount; | ||
| //lengthToClear = Math.DivRem(count, BitsPerInt32, out shiftCount); // Compilation error |
| if (count < m_length) | ||
| { | ||
| int shiftCount; | ||
| // We can not use Math.DivRem due compilation error |
There was a problem hiding this comment.
nit: // We can not use Math.DivRem without taking a dependency on System.Runtime.Extensions
| { | ||
| throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); | ||
| } | ||
| Contract.EndContractBlock(); |
There was a problem hiding this comment.
FYI Contract.EndContractBlock(); isn't necessary anymore, so you can leave it out in future additions. It's used in every other method in this file so I see why you added it though.
…untime.Extensions
ianhays
left a comment
There was a problem hiding this comment.
LGTM. I'd like another pair of eyes to take a quick look before merging if possible. If no one bites then it should still be good-to-go though.
|
@dotnet-bot test Innerloop OSX Release Build and Test please (log gone) |
|
@danmosemsft Yeah I think so. I may alter the policy again to keep Pr builds longer than official builds. |
BitArray shift operators Commit migrated from dotnet/corefx@851f91e
The BitArray class in System.Collections should include BitWise right and left shift operations. BitArray is used to represent a collection of on\off values and currently contains And, Or, Xor, and Not operations. Adding RightShift and LeftShift would increase the usefulness of the class and provide a canonical, fast solution to save developers the hassle of coming up with their own.
See https://github.com/dotnet/corefx/issues/8719
Unit tests for the above API get from https://github.com/Clockwork-Muse/corefx/commit/4fc128660ad8c61310bbd10d9d767afda8b22910