New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Minor Vector cleanup #5545
Minor Vector cleanup #5545
Conversation
977ffa5
to
228da21
Compare
@@ -88,7 +83,7 @@ override def companion: GenericCompanion[Vector] = Vector | |||
|
|||
override def lengthCompare(len: Int): Int = length - len | |||
|
|||
private[collection] final def initIterator[B >: A](s: VectorIterator[B]) { | |||
private[collection] def initIterator[B >: A](s: VectorIterator[B]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can get away with finality, we should leave it in. In general, immutable data structures do not lend themselves well to subclassing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the whole class is final, does it make sense to keep final methods also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. No. Sorry, I missed that. I had somehow assumed, without any evidence, that it was in VectorPointer.
// can still be improved | ||
override /*SeqLike*/ | ||
def reverseIterator: Iterator[A] = new AbstractIterator[A] { | ||
override /*SeqLike*/ def reverseIterator: Iterator[A] = new AbstractIterator[A] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is harder to read. It's better the original way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in other places it was formatted like this. should I change the rest also, or just revert this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you don't mind changing the others, may as well change them. The override /* SeqLike */
thing is arguably the least important part of the signature, so having it push other stuff out of view on the right, potentially, is a bad idea. Two lines solve the problem nicely.
val shiftBlocks = freeSpace >>> 5*(depth-1) // number of top-level blocks | ||
val freeSpace = ((1 << (5 * depth)) - endIndex) // free space at the right given the current tree-structure depth | ||
val shift = (freeSpace & ~((1 << (5 * (depth - 1))) - 1)) // number of elements by which we'll shift right (only move at top level) | ||
val shiftBlocks = (freeSpace >>> (5 * (depth - 1))) // number of top-level blocks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces are good. Gratuitious parens around the entire expression are not good--just visual clutter that doesn't aid anything. Internal parens that make one not rely upon knowing operator precedence are good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
understood, will remove external ones in other places also :)
val shiftBlocks = startIndex >>> 5*(depth-1) | ||
|
||
//println("----- appendBack " + value + " at " + endIndex + " reached block end") | ||
val shift = startIndex & ~((1 << 5 * (depth - 1))-1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed spaces around the last -
|
||
//println("----- appendBack " + value + " at " + endIndex + " reached block end") | ||
val shift = startIndex & ~((1 << 5 * (depth - 1))-1) | ||
val shiftBlocks = startIndex >>> 5 * (depth - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should surround multiplication with parens for clarity
s.cleanLeftEdge(cutIndex-shift) | ||
s | ||
*/ | ||
val shift = (cutIndex & ~((1 << (5 * d)) - 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need outer parens
@@ -639,13 +572,8 @@ override def companion: GenericCompanion[Vector] = Vector | |||
val blockIndex = (cutIndex - 1) & ~31 | |||
val xor = startIndex ^ (cutIndex - 1) | |||
val d = requiredDepth(xor) | |||
val shift = (startIndex & ~((1 << (5*d))-1)) | |||
val shift = (startIndex & ~((1 << (5 * d)) - 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need outer parens
@@ -1123,16 +1041,13 @@ private[immutable] trait VectorPointer[T] { | |||
} | |||
|
|||
|
|||
|
|||
|
|||
// USED IN APPEND | |||
// create a new block at the bottom level (and possibly nodes on its path) and prepares for writing | |||
|
|||
// requires structure is clean and at pos oldIndex, | |||
// ensures structure is dirty and at pos newIndex and writable at level 0 | |||
private[immutable] final def gotoFreshPosWritable0(oldIndex: Int, newIndex: Int, xor: Int): Unit = { // goto block start pos | |||
if (xor < (1 << 5)) { // level = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should leave a comment that we're already at the block start.
This looks like a welcome improvement overall; just a few aspects that could be even tidier if you're tidying anyway. |
228da21
to
4279bbc
Compare
Thanks @Ichoran! |
display3((index >> 15) & 31) = display2 | ||
display2((index >> 10) & 31) = display1 | ||
display1((index >> 5) & 31) = display0 | ||
display5((index >>> 25) & 31) = display4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indices are inherently positive or zero
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is error-checking to make sure they're positive or zero, then there is no reason to bother changing. If there is not error-checking, an exception could be replaced with a silently wrong result. I think they should stay signed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error checking is in checkRangeConvert and >>>
was already used in many other places, it's the reason why I unified it: https://github.com/paplorinc/scala/blob/4279bbc9f467ef6d8a0ddf4190f2dd947f13d580/src/library/scala/collection/immutable/Vector.scala#L482
val a2 = new Array[AnyRef](array.length) | ||
java.lang.System.arraycopy(array, 0, a2, 0, right) | ||
a2 | ||
private[immutable] def copyLeft(array: Array[AnyRef], right: Int): Array[AnyRef] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to unify with copyOf
4279bbc
to
7bddc2c
Compare
getElem(idx, idx ^ focus) | ||
} | ||
|
||
private def checkRangeConvert(index: Int) = { | ||
val idx = index + startIndex | ||
if (0 <= index && idx < endIndex) | ||
if (index >= 0 && idx < endIndex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Ichoran, this makes sure all indices are strictly non-negative.
(switched the comparison order to make it more intuitive - it's also easier to see that different variables are compared for boundaries)
ff0eae6
to
6ee5e12
Compare
There's nothing to rebase against yet, all 2.12.x builds are currently broken. I've created #5549 to fix this. |
6ee5e12
to
2963129
Compare
@szeiger, I still have a MiMa failure (not sure where to view the failure's reason). |
@paplorinc Go to the details of
Unlike most projects we require both backwards and forwards binary compatibility for Scala itself, so you can never add or remove a method. Some cases are really not binary compatible but we're OK with it because it only affects internal code that could not be accessed from outside the standard library without subverting Scala's access restrictions. Package-private elements generally fall into this category. Once you have convinced yourself that the binary-incompatible changes are harmless you can whitelist them. See the log for the ready-to-copy-and-paste whitelisting configuration. Note that it's always shown under backwards compatibility even when it really affects forwards compatibility (and therefore has to go into |
83fd253
to
009f2fc
Compare
009f2fc
to
82ed1d1
Compare
val shiftBlocks = startIndex >>> 5*(depth-1) | ||
|
||
//println("----- appendBack " + value + " at " + endIndex + " reached block end") | ||
val shift = startIndex & ~((1 << (5 * (depth - 1))) - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not sufficiently similar to the next computation for alignment to be a good idea. It suggests that the two are closely related, when in fact they are in detail rather different computations. So I would just go shift = startIndex
without the extra spaces.
with VectorPointer[A @uncheckedVariance] { | ||
extends AbstractIterator[A] | ||
with Iterator[A] | ||
with VectorPointer[A @uncheckedVariance] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This style makes it hard to tell at a glance where the class signature ends and where the body begins. The style is all over the place in practice, but I recommend left-justifying everything; vertical whitespace should always be used to separate classes so there is no need to indent them to indicate that they all belong to VectorIterator
. Failing that, the previous version was preferable because at least all the extended classes/traits are lined up.
} else if (xor < (1 << 25)) { // level = 4 | ||
display4((index >>> 20) & 31).asInstanceOf[Array[AnyRef]]((index >>> 15) & 31).asInstanceOf[Array[AnyRef]]((index >>> 10) & 31).asInstanceOf[Array[AnyRef]]((index >>> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] | ||
} else if (xor < (1 << 30)) { // level = 5 | ||
display5((index >>> 25) & 31).asInstanceOf[Array[AnyRef]]((index >>> 20) & 31).asInstanceOf[Array[AnyRef]]((index >>> 15) & 31).asInstanceOf[Array[AnyRef]]((index >>> 10) & 31).asInstanceOf[Array[AnyRef]]((index >>> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an awful mess even with the improvements unless you have a very wide screen. If you're up for it maybe you can rewrite it in the style
display5((index >>> 25) & 31).asInstanceOf[Array[AnyRef]].
apply((index >>> 20) & 31).asInstanceOf[Array[AnyRef]].
apply((index >>> 15) & 31) . . .
The alternative--lining up the matching part of the displays--pushes the earlier ones so far right that they disappear, so that's probably not a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about
var array = display5
array = array((index >>> 25) & 31).asInstanceOf[Array[AnyRef]]
array = array((index >>> 20) & 31).asInstanceOf[Array[AnyRef]]
array = array((index >>> 15) & 31).asInstanceOf[Array[AnyRef]]
array = array((index >>> 10) & 31).asInstanceOf[Array[AnyRef]]
array = array((index >>> 5) & 31).asInstanceOf[Array[AnyRef]]
array(index & 31).asInstanceOf[T]
or would that be slower than the direct reference?
|
||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a newline at the end to keep things happy that like newlines at the end!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:)) sir, yes sir!
array = array((index >>> 10) & 31).asInstanceOf[Array[AnyRef]] | ||
array = array((index >>> 5) & 31).asInstanceOf[Array[AnyRef]] | ||
array(index & 31).asInstanceOf[T] | ||
} else { // level = 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have witnessed cases where allocating a temporary var produces worse performance than chaining methods, and at this point in time I think chaining is slightly more idiomatic than repeated assignment to vars. So I'd just chain them as I indicated before. If you don't like the apply
, you can do it this way:
array((index >>> 25) & 31).
asInstanceOf[Array[AnyRef]]((index >>> 20) & 31).
asInstanceOf[Array[AnyRef]]((index >>> 15) & 31).
...
You miss the alignment on the first shift, but I guess you could add whitespace until it was aligned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, resolved it via parenthesizing everything to make it multilinable, making alignment trivial also :)
c97b11b
to
1f330ec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@retronym, could you please review this also, it's related to https://issues.scala-lang.org/browse/SI-10049 as well? |
Unified, since indices are always positive and unsigned shift was already used in other places
1f330ec
to
74db1d3
Compare
/nothingtoseehere The CLA checker had some hickups recently. CLA confirmed. |
thanks @paplorinc! you're killing it lately |
Thank you @Ichoran, @szeiger and @SethTisue! :) |
My original intention didn't work out, but since I created these simple style unification commits, I though I'll share them. :)