-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Make DataWord immutable #1154
Conversation
@@ -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)); |
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.
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); |
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 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 |
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.
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() { |
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.
No need. The only thing we need is DataWord.ZERO
} | ||
|
||
private DataWord() { | ||
data = ZERO.getData(); |
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.
data = ZERO_EMPTY_ARRAY;
@@ -81,18 +86,26 @@ public DataWord(byte[] data) { | |||
this.data = ByteUtil.EMPTY_BYTE_ARRAY; |
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.
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(); |
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 preserve mutability there should be a brand new empty array allocated and then filled with given data. The same thing is with above case
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.
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
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.
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) { |
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 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(); |
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.
return this;
|
||
if (word.isZero()) { | ||
this.and(ZERO); | ||
return; | ||
return this.and(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.
return ZERO;
…ntiation with 'of' factory
Second try! |
else | ||
throw new RuntimeException("Data word can't exceed 32 bytes: " + data); | ||
else if (data.length <= 32) { | ||
byte[] bytes = ZERO.getData(); |
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.
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); | ||
} |
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 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()); |
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.
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)); |
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.
Strange code to push 1
constant
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.
It's outdated
} else { | ||
word1.and(DataWord.ZERO); | ||
program.stackPush(andResult); |
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.
Strange code to push 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.
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)); |
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 same
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.
same )
} else { | ||
word1.and(DataWord.ZERO); | ||
program.stackPush(andResult); | ||
} |
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 same
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.
And cases below
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.
+1
public DataWord and(DataWord w2) { | ||
|
||
public DataWord and(DataWord word) { | ||
byte[] newData = this.getData(); |
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.
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); |
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'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> { |
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.
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; |
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.
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)); |
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.
Why not use DataWord.ONE
here?
} | ||
|
||
public void stackPushZero() { | ||
stackPush(new DataWord(0)); | ||
stackPush(DataWord.of(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.
DataWord.ZERO
} | ||
|
||
public void stackPushOne() { | ||
DataWord stackWord = new DataWord(1); | ||
DataWord stackWord = DataWord.of(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.
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)); |
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.
DataWord.ONE
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.
@mkalinin Here you probably a little bit doebalsa :) This is just a test
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.
Ahahaha, maybe just a little bit
…shortcuts where it is possible
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.
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()); |
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.
same here Long.BYTES
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.
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()); |
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.
It's better use Integer.BYTES
constant.
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.
We've got this already: org.ethereum.util.ByteUtil.intToBytes
public byte[] getData() { | ||
return data; | ||
return Arrays.copyOf(data, data.length); |
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.
Maybe it's better to use copyData()
and mark getData()
as deprecated.
Uses #902 which is a bit outdated
Should resolve #902 #766 #884 #706
@mkalinin @eugene-shevchenko