Coverage Summary for Class: Memory (org.ethereum.vm.program)
Class |
Class, %
|
Method, %
|
Line, %
|
Memory |
100%
(1/1)
|
73.3%
(11/15)
|
66%
(62/94)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.vm.program;
21
22 import org.ethereum.vm.DataWord;
23 import org.ethereum.vm.program.listener.ProgramListener;
24 import org.ethereum.vm.program.listener.ProgramListenerAware;
25
26 import java.util.LinkedList;
27 import java.util.List;
28
29 import static java.lang.Math.ceil;
30 import static java.lang.Math.min;
31 import static java.lang.String.format;
32 import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
33 import static org.ethereum.util.ByteUtil.oneByteToHexString;
34
35 public class Memory implements ProgramListenerAware {
36
37 private static final int CHUNK_SIZE = 1024;
38 private static final int WORD_SIZE = 32;
39
40 private List<byte[]> chunks = new LinkedList<>();
41 private int softSize;
42 private ProgramListener traceListener;
43
44 @Override
45 public void setTraceListener(ProgramListener traceListener) {
46 this.traceListener = traceListener;
47 }
48
49 public byte[] read(int address, int size) {
50 if (size <= 0) {
51 return EMPTY_BYTE_ARRAY;
52 }
53
54 extend(address, size);
55 byte[] data = new byte[size];
56
57 int chunkIndex = address / CHUNK_SIZE;
58 int chunkOffset = address % CHUNK_SIZE;
59
60 int toGrab = data.length;
61 int start = 0;
62
63 while (toGrab > 0) {
64 int copied = grabMax(chunkIndex, chunkOffset, toGrab, data, start);
65
66 // read next chunk from the start
67 ++chunkIndex;
68 chunkOffset = 0;
69
70 // mark remind
71 toGrab -= copied;
72 start += copied;
73 }
74
75 return data;
76 }
77
78 public void write(int address, byte[] data, int dataSize, boolean limited) {
79 if (data.length < dataSize) {
80 dataSize = data.length;
81 }
82
83 if (!limited) {
84 extend(address, dataSize);
85 }
86
87 int chunkIndex = address / CHUNK_SIZE;
88 int chunkOffset = address % CHUNK_SIZE;
89
90 int toCapture = 0;
91 if (limited) {
92 toCapture = (address + dataSize > softSize) ? softSize - address : dataSize;
93 } else {
94 toCapture = dataSize;
95 }
96
97 int start = 0;
98 while (toCapture > 0) {
99 int captured = captureMax(chunkIndex, chunkOffset, toCapture, data, start);
100
101 // capture next chunk
102 ++chunkIndex;
103 chunkOffset = 0;
104
105 // mark remind
106 toCapture -= captured;
107 start += captured;
108 }
109
110 if (traceListener != null) {
111 traceListener.onMemoryWrite(address, data, dataSize);
112 }
113 }
114
115
116 public void extendAndWrite(int address, int allocSize, byte[] data) {
117 extend(address, allocSize);
118 write(address, data, data.length, false);
119 }
120
121 public void extend(int address, int size) {
122 if (size <= 0) {
123 return;
124 }
125
126 final int newSize = address + size;
127
128 int toAllocate = newSize - internalSize();
129 if (toAllocate > 0) {
130 addChunks((int) ceil((double) toAllocate / CHUNK_SIZE));
131 }
132
133 toAllocate = newSize - softSize;
134 if (toAllocate > 0) {
135 toAllocate = (int) ceil((double) toAllocate / WORD_SIZE) * WORD_SIZE;
136 softSize += toAllocate;
137
138 if (traceListener != null) {
139 traceListener.onMemoryExtend(toAllocate);
140 }
141 }
142 }
143
144 public DataWord readWord(int address) {
145 return DataWord.valueOf(read(address, 32));
146 }
147
148 // just access expecting all data valid
149 public byte readByte(int address) {
150
151 int chunkIndex = address / CHUNK_SIZE;
152 int chunkOffset = address % CHUNK_SIZE;
153
154 byte[] chunk = chunks.get(chunkIndex);
155
156 return chunk[chunkOffset];
157 }
158
159 @Override
160 public String toString() {
161
162 StringBuilder memoryData = new StringBuilder();
163 StringBuilder firstLine = new StringBuilder();
164 StringBuilder secondLine = new StringBuilder();
165
166 for (int i = 0; i < softSize; ++i) {
167
168 byte value = readByte(i);
169
170 // Check if value is ASCII
171 String character = ((byte) 0x20 <= value && value <= (byte) 0x7e) ? new String(new byte[]{value}) : "?";
172 firstLine.append(character).append("");
173 secondLine.append(oneByteToHexString(value)).append(" ");
174
175 if ((i + 1) % 8 == 0) {
176 String tmp = format("%4s", Integer.toString(i - 7, 16)).replace(" ", "0");
177 memoryData.append("").append(tmp).append(" ");
178 memoryData.append(firstLine).append(" ");
179 memoryData.append(secondLine);
180 if (i + 1 < softSize) {
181 memoryData.append("\n");
182 }
183 firstLine.setLength(0);
184 secondLine.setLength(0);
185 }
186 }
187
188 return memoryData.toString();
189 }
190
191 public int size() {
192 return softSize;
193 }
194
195 public int internalSize() {
196 return chunks.size() * CHUNK_SIZE;
197 }
198
199 public List<byte[]> getChunks() {
200 return new LinkedList<>(chunks);
201 }
202
203 private int captureMax(int chunkIndex, int chunkOffset, int size, byte[] src, int srcPos) {
204
205 byte[] chunk = chunks.get(chunkIndex);
206 int toCapture = min(size, chunk.length - chunkOffset);
207
208 System.arraycopy(src, srcPos, chunk, chunkOffset, toCapture);
209 return toCapture;
210 }
211
212 private int grabMax(int chunkIndex, int chunkOffset, int size, byte[] dest, int destPos) {
213
214 byte[] chunk = chunks.get(chunkIndex);
215 int toGrab = min(size, chunk.length - chunkOffset);
216
217 System.arraycopy(chunk, chunkOffset, dest, destPos, toGrab);
218
219 return toGrab;
220 }
221
222 private void addChunks(int num) {
223 for (int i = 0; i < num; ++i) {
224 chunks.add(new byte[CHUNK_SIZE]);
225 }
226 }
227 }