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

Make DataWord immutable #1154

Merged
merged 10 commits into from
Aug 17, 2018
Merged

Make DataWord immutable #1154

merged 10 commits into from
Aug 17, 2018

Conversation

zilm13
Copy link
Collaborator

@zilm13 zilm13 commented Aug 13, 2018

Uses #902 which is a bit outdated

  • resolve conflicts
  • few fixes

Should resolve #902 #766 #884 #706
@mkalinin @eugene-shevchenko

@coveralls
Copy link

coveralls commented Aug 13, 2018

Coverage Status

Coverage decreased (-0.2%) to 56.119% when pulling d8bfbef on tonycox-immutableDW into eacfc01 on develop.

@mkalinin mkalinin added this to the 1.9.0-Constantinople milestone Aug 14, 2018
@@ -259,8 +259,8 @@ public HeaderStore headerStore() {
new Serializer<byte[], byte[]>() {
public byte[] serialize(byte[] object) {
DataWord ret = new DataWord(object);
ret.add(new DataWord(1));
return ret.getLast20Bytes();
DataWord addResult = ret.add(new DataWord(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add DataWord.ONE predefined value

public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE);
public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack
private static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256);
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 that access modifiers should stay the same

public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack
private static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256);
private static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE);
private static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
Copy link
Contributor

Choose a reason for hiding this comment

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

ZERO should be represented as an empty array with some constant data, let's use ZERO_EMPTY_ARRAY for that. And since this class became immutable the comment don't push it in to the stack is invalid.


public DataWord() {
public static DataWord zero() {
Copy link
Contributor

Choose a reason for hiding this comment

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

No need. The only thing we need is DataWord.ZERO

}

private DataWord() {
data = ZERO.getData();
Copy link
Contributor

Choose a reason for hiding this comment

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

data = ZERO_EMPTY_ARRAY;

@@ -81,18 +86,26 @@ public DataWord(byte[] data) {
this.data = ByteUtil.EMPTY_BYTE_ARRAY;
Copy link
Contributor

Choose a reason for hiding this comment

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

Better use inner constant here.

else
throw new RuntimeException("Data word can't exceed 32 bytes: " + data);
else if (data.length <= 32) {
byte[] bytes = ZERO.getData();
Copy link
Contributor

Choose a reason for hiding this comment

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

To preserve mutability there should be a brand new empty array allocated and then filled with given data. The same thing is with above case

Copy link
Contributor

Choose a reason for hiding this comment

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

But doing it that way we won't be able to efficiently use this constructor in inner DataWord allocations where DataWord itself cares about immutability. I see two options (i) make this constructor private and use for inner purposes, for outer create a method DataWord.from(byte[] data) with all required checks and copies (ii) do it vise versa

Copy link
Member

Choose a reason for hiding this comment

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

Yep, this looks pretty odd. I see that getData() makes a copy, but new byte[32] suites better here

return Arrays.copyOf(data, data.length);
}

public DataWord insert(int index, byte element) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not needed. We can use program.stackPush(DataWord.ONE) to cover all cases that this method covers


if (this.isZero()) return;
if (this.isZero()) return zero();
Copy link
Contributor

Choose a reason for hiding this comment

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

return this;


if (word.isZero()) {
this.and(ZERO);
return;
return this.and(ZERO);
Copy link
Contributor

Choose a reason for hiding this comment

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

return ZERO;

@zilm13
Copy link
Collaborator Author

zilm13 commented Aug 14, 2018

Second try!

else
throw new RuntimeException("Data word can't exceed 32 bytes: " + data);
else if (data.length <= 32) {
byte[] bytes = ZERO.getData();
Copy link
Member

Choose a reason for hiding this comment

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

Yep, this looks pretty odd. I see that getData() makes a copy, but new byte[32] suites better here

}

public byte[] getData() {
return data;
return Arrays.copyOf(data, data.length);
}
Copy link
Member

Choose a reason for hiding this comment

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

I would leave this as is, just making sure the returned array is not accidentally modified else where (which normally shouldn't be the case for getData() method). If one really needs a copy I'd suggest add special getDataCopy() method

}

public byte[] getNoLeadZeroesData() {
return ByteUtil.stripLeadingZeroes(data);
return ByteUtil.stripLeadingZeroes(getData());
Copy link
Member

Choose a reason for hiding this comment

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

Again getData() makes absolutely unnecessary copy internally

if (word1.value().compareTo(word2.value()) == -1) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
program.stackPush(andResult.insert(31, (byte) 1));
Copy link
Member

Choose a reason for hiding this comment

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

Strange code to push 1 constant

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's outdated

} else {
word1.and(DataWord.ZERO);
program.stackPush(andResult);
Copy link
Member

Choose a reason for hiding this comment

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

Strange code to push 0

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's outdated

if (word1.sValue().compareTo(word2.sValue()) == -1) {
word1.and(DataWord.ZERO);
word1.getData()[31] = 1;
program.stackPush(andResult.insert(31, (byte) 1));
Copy link
Member

Choose a reason for hiding this comment

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

the same

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same )

} else {
word1.and(DataWord.ZERO);
program.stackPush(andResult);
}
Copy link
Member

Choose a reason for hiding this comment

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

the same

Copy link
Member

Choose a reason for hiding this comment

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

And cases below

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

+1

public DataWord and(DataWord w2) {

public DataWord and(DataWord word) {
byte[] newData = this.getData();
Copy link
Member

Choose a reason for hiding this comment

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

It's very confusing to use getData() for making an array copy. It better to make it explicitly or add copyData() private method
The similar below.

}

public byte[] getData() {
return data;
return Arrays.copyOf(data, data.length);
Copy link
Member

Choose a reason for hiding this comment

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

I'm still suggesting returning internal array here (mentioning this in method docs). It's a normal practice (e.g. take a look at java.nio.ByteBuffer.array()

*
* @author Roman Mandeleil
* @since 01.06.2014
*/
public class DataWord implements Comparable<DataWord> {
public final class DataWord implements Comparable<DataWord> {
Copy link
Contributor

Choose a reason for hiding this comment

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

For some reason Github doesn't allow to comment on public boolean isZero() like. But this method lacks a shortcut check for whether this == ZERO or not

return DataWord.ZERO;
}

boolean allExceptLastIsZero = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

You may use 8 * data.length - ByteUtil.numberOfLeadingZeros(data). And check the residual against 0 or 1

ret.add(new DataWord(1));
return ret.getLast20Bytes();
DataWord ret = DataWord.of(object);
DataWord addResult = ret.add(DataWord.of(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use DataWord.ONE here?

}

public void stackPushZero() {
stackPush(new DataWord(0));
stackPush(DataWord.of(0));
Copy link
Contributor

Choose a reason for hiding this comment

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

DataWord.ZERO

}

public void stackPushOne() {
DataWord stackWord = new DataWord(1);
DataWord stackWord = DataWord.of(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

DataWord.ONE

@@ -155,31 +155,31 @@ public void testStorage1() throws Exception {
RepositoryRoot repo = new RepositoryRoot(stateDb, null);
byte[] addr1 = decode("aaaa");
repo.createAccount(addr1);
repo.addStorageRow(addr1, new DataWord(1), new DataWord(111));
repo.addStorageRow(addr1, DataWord.of(1), DataWord.of(111));
Copy link
Contributor

Choose a reason for hiding this comment

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

DataWord.ONE

Copy link
Member

Choose a reason for hiding this comment

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

@mkalinin Here you probably a little bit doebalsa :) This is just a test

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahahaha, maybe just a little bit

Copy link
Contributor

@eugene-shevchenko eugene-shevchenko left a comment

Choose a reason for hiding this comment

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

Nice job. It'll help to avoid mystic bugs =)

else
throw new RuntimeException("Data word can't exceed 32 bytes: " + data);
public static DataWord of(long num) {
return of(ByteBuffer.allocate(8).putLong(num).array());
Copy link
Contributor

Choose a reason for hiding this comment

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

same here Long.BYTES

Copy link
Member

Choose a reason for hiding this comment

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

We've got this already: org.ethereum.util.ByteUtil.longToBytes

public DataWord(ByteArrayWrapper wrappedData){
this(wrappedData.getData());
public static DataWord of(int num) {
return of(ByteBuffer.allocate(4).putInt(num).array());
Copy link
Contributor

Choose a reason for hiding this comment

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

It's better use Integer.BYTES constant.

Copy link
Member

@Nashatyrev Nashatyrev Aug 17, 2018

Choose a reason for hiding this comment

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

We've got this already: org.ethereum.util.ByteUtil.intToBytes

public byte[] getData() {
return data;
return Arrays.copyOf(data, data.length);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it's better to use copyData() and mark getData() as deprecated.

@zilm13 zilm13 deleted the tonycox-immutableDW branch September 20, 2018 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants