Skip to content
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

[FLINK-6162]Fix bug in ByteArrayOutputStreamWithPos#setPosition #3595

Closed
wants to merge 9 commits into from

Conversation

wenlong88
Copy link
Contributor

@wenlong88 wenlong88 commented Mar 22, 2017

Currently the precondition check in setPosition will fail when the buffer is full:
{{Preconditions.checkArgument(position < getEndPosition(), "Position out of bounds.");}}
We should allow the expected position to be the end position

I have added a test on the fix.

Copy link
Contributor

@greghogan greghogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is not using the array. The only method depending on a positively bounded count is toString.

public class ByteArrayOutputStreamWithPosTest {
@Test
public void setPositionWhenBufferIsFull() throws Exception {
ByteArrayOutputStreamWithPos stream = new ByteArrayOutputStreamWithPos();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialize with a constant size matching the next line.

@wenlong88
Copy link
Contributor Author

@greghogan thanks for the review, I have changed the change test to use explicit buffer size.
you are right that currently there is no dependency on this method. We used the ByteArrayOutputStreamWithPOs in our own scenario and caught the bug.

Copy link
Contributor

@greghogan greghogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some tests since currently we are only checking that no exception is thrown.

public class ByteArrayOutputStreamWithPosTest {
@Test
public void setPositionWhenBufferIsFull() throws Exception {
ByteArrayOutputStreamWithPos stream = new ByteArrayOutputStreamWithPos(32);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is stronger to replace 32 on these lines with a descriptive local variable. After initialization could add an assertion that getBuf().length equals that value.

public void setPositionWhenBufferIsFull() throws Exception {
ByteArrayOutputStreamWithPos stream = new ByteArrayOutputStreamWithPos(32);
stream.write(new byte[32]);
stream.setPosition(stream.getPosition());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert that getPosition is equal to the expected value. Can validate that setPosition now works per the change above and does not change the position, and also check that toString can be executed.

@wenlong88
Copy link
Contributor Author

@greghogan comments addressed

@StefanRRichter
Copy link
Contributor

I would suggest to introduce the analogue check also in ByteArrayInputStreamWithPos.

@wenlong88
Copy link
Contributor Author

hi, @StefanRRichter , I have add check in setPosition in ByteArrayInputSteamWithPos, there is no dependency on this method too, so I rename the method from setPos to setPosition for unifying.

Copy link
Contributor

@greghogan greghogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also call ByteArrayOutputStreamWithPos#toString() at the end of the test (I don't think the output matters, but the call will test that the bounds are consistent).

@@ -118,7 +118,8 @@ public int getPosition() {
return position;
}

public void setPos(int pos) {
public void setPosition(int pos) {
Preconditions.checkArgument(pos < count, "Position out of bounds.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow for equality as with ByteArrayOutputStreamWithPos? And check for non-negative. Should we create a matching getEndPosition() as in ByteArrayOutputStreamWithPos?

@@ -110,7 +110,7 @@ public int getPosition() {
}

public void setPosition(int position) {
Preconditions.checkArgument(position < getEndPosition(), "Position out of bounds.");
Preconditions.checkArgument(position <= getEndPosition(), "Position out of bounds.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for non-negative.

/**
* This tests that the expected position exceeds the capacity of the byte array.
*/
@Test(expected = IllegalArgumentException.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the preference is to instead use ExpectedException which can also take an expected message.

@wenlong88
Copy link
Contributor Author

@greghogan Thanks for the review. I have add non-negative check and update the test with ExpectedException. But I don't think we need to call toString in the test since we have check the position instead I add a test to verify that toString returns the substring with range(0, position) to meet your concern. One update about setPosition in outputStream, I think it is better to auto extend the buffer when setting a position larger than the buffer size, just like the auto extending in write.

@greghogan
Copy link
Contributor

When expanding with write the new capacity (up to position of course) is filled with data. Expanding with setPosition can leave holes in the data and may mask a bug. Since these classes are undocumented it would be safer to leave this behavior unmodified.

@wenlong88
Copy link
Contributor Author

@greghogan Currently we check the position with the EndPosition which is the size of buffer instead of a max written size , so it is still possible to leave holes when keeping unmodified. Enabling auto extending can give the user the flexibility to reserved some space for later filling and does not change current functions logics.

@greghogan
Copy link
Contributor

@wenlong88 you are right that the position can currently be moved beyond written data. This still feels a bit like feature creep as we've added to the PR. Do you need to expand the array is this manner? How did you run across this bug? I'm not disagreeing with the change but would like to be conservative and understand the why.

@wenlong88
Copy link
Contributor Author

@greghogan I come across this bug when we are trying to write some objects to a byte array: at first, I wrote a 0 as initial size and then the serialized object, after that, I seek back to the start position to correct the size and then seek to the end of the written data to continue to write other object. When the array can auto expanding, I can just reserve some hole at first and then seek back and fill the hole.

@wenlong88
Copy link
Contributor Author

hi, @greghogan , do you have any new comment about the changes?

greghogan pushed a commit to greghogan/flink that referenced this pull request Apr 7, 2017
Fix off-by-one error in 'setPosition' and expand array when seeking
outside the original bounds.

This closes apache#3595
@greghogan
Copy link
Contributor

@wenlong88 thanks for the contribution (and the ping)! Merging ...

@asfgit asfgit closed this in af34e5b Apr 8, 2017
p16i pushed a commit to p16i/flink that referenced this pull request Apr 16, 2017
Fix off-by-one error in 'setPosition' and expand array when seeking
outside the original bounds.

This closes apache#3595
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants