124 }
125
126 @Override
127 public boolean equals(Object obj) {
128 assert ref != null;
129 if (obj == this) {
130 return true;
131 }
132 if (obj instanceof Data) {
133 Data that = (Data) obj;
134 if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) {
135 return true;
136 }
137 }
138 return false;
139 }
140 }
141
142 private final ArrayList<Data> dataItems = new ArrayList<>();
143
144 private boolean finalLayout;
145 private int sectionAlignment;
146 private int sectionSize;
147
148 @Override
149 public int hashCode() {
150 // DataSection instances should not be used as hash map keys
151 throw new UnsupportedOperationException("hashCode");
152 }
153
154 @Override
155 public String toString() {
156 return identityHashCodeString(this);
157 }
158
159 @Override
160 public boolean equals(Object obj) {
161 if (this == obj) {
162 return true;
163 }
164 if (obj instanceof DataSection) {
165 DataSection that = (DataSection) obj;
166 if (this.finalLayout == that.finalLayout && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) {
167 return true;
168 }
169 }
170 return false;
171 }
172
173 /**
174 * Insert a {@link Data} item into the data section. If the item is already in the data section,
175 * the same {@link DataSectionReference} is returned.
176 *
177 * @param data the {@link Data} item to be inserted
178 * @return a unique {@link DataSectionReference} identifying the {@link Data} item
179 */
180 public DataSectionReference insertData(Data data) {
181 assert !finalLayout;
182 synchronized (data) {
183 if (data.ref == null) {
184 data.ref = new DataSectionReference();
185 dataItems.add(data);
186 }
187 return data.ref;
188 }
189 }
190
191 /**
192 * Transfers all {@link Data} from the provided other {@link DataSection} to this
193 * {@link DataSection}, and empties the other section.
194 */
195 public void addAll(DataSection other) {
196 assert !finalLayout && !other.finalLayout;
197
198 for (Data data : other.dataItems) {
199 assert data.ref != null;
200 dataItems.add(data);
201 }
202 other.dataItems.clear();
203 }
204
205 /**
206 * Compute the layout of the data section. This can be called only once, and after it has been
207 * called, the data section can no longer be modified.
208 */
209 public void finalizeLayout() {
210 assert !finalLayout;
211 finalLayout = true;
212
213 // simple heuristic: put items with larger alignment requirement first
214 dataItems.sort((a, b) -> a.alignment - b.alignment);
215
216 int position = 0;
217 int alignment = 1;
218 for (Data d : dataItems) {
219 alignment = lcm(alignment, d.alignment);
220 position = align(position, d.alignment);
221
222 d.ref.setOffset(position);
223 position += d.size;
224 }
225
226 sectionAlignment = alignment;
227 sectionSize = position;
228 }
229
230 public boolean isFinalized() {
231 return finalLayout;
232 }
233
234 /**
235 * Get the size of the data section. Can only be called after {@link #finalizeLayout}.
236 */
237 public int getSectionSize() {
238 assert finalLayout;
239 return sectionSize;
240 }
241
242 /**
243 * Get the minimum alignment requirement of the data section. Can only be called after
244 * {@link #finalizeLayout}.
245 */
246 public int getSectionAlignment() {
247 assert finalLayout;
248 return sectionAlignment;
249 }
250
251 /**
252 * Build the data section. Can only be called after {@link #finalizeLayout}.
253 *
254 * @param buffer The {@link ByteBuffer} where the data section should be built. The buffer must
255 * hold at least {@link #getSectionSize()} bytes.
256 * @param patch A {@link Consumer} to receive {@link DataPatch data patches} for relocations in
257 * the data section.
258 */
259 public void buildDataSection(ByteBuffer buffer, Consumer<DataPatch> patch) {
260 assert finalLayout;
261 for (Data d : dataItems) {
262 buffer.position(d.ref.getOffset());
263 d.builder.emit(buffer, patch);
264 }
265 }
266
267 public Data findData(DataSectionReference ref) {
268 for (Data d : dataItems) {
269 if (d.ref == ref) {
270 return d;
271 }
272 }
273 return null;
274 }
275
276 public Iterator<Data> iterator() {
277 return dataItems.iterator();
278 }
279
280 public static int lcm(int x, int y) {
283 } else if (y == 0) {
284 return x;
285 }
286
287 int a = Math.max(x, y);
288 int b = Math.min(x, y);
289 while (b > 0) {
290 int tmp = a % b;
291 a = b;
292 b = tmp;
293 }
294
295 int gcd = a;
296 return x * y / gcd;
297 }
298
299 private static int align(int position, int alignment) {
300 return ((position + alignment - 1) / alignment) * alignment;
301 }
302
303 public void clear() {
304 assert !finalLayout;
305 this.dataItems.clear();
306 this.sectionAlignment = 0;
307 this.sectionSize = 0;
308 }
309 }
|
124 }
125
126 @Override
127 public boolean equals(Object obj) {
128 assert ref != null;
129 if (obj == this) {
130 return true;
131 }
132 if (obj instanceof Data) {
133 Data that = (Data) obj;
134 if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) {
135 return true;
136 }
137 }
138 return false;
139 }
140 }
141
142 private final ArrayList<Data> dataItems = new ArrayList<>();
143
144 private boolean closed;
145 private int sectionAlignment;
146 private int sectionSize;
147
148 @Override
149 public int hashCode() {
150 // DataSection instances should not be used as hash map keys
151 throw new UnsupportedOperationException("hashCode");
152 }
153
154 @Override
155 public String toString() {
156 return identityHashCodeString(this);
157 }
158
159 @Override
160 public boolean equals(Object obj) {
161 if (this == obj) {
162 return true;
163 }
164 if (obj instanceof DataSection) {
165 DataSection that = (DataSection) obj;
166 if (this.closed == that.closed && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) {
167 return true;
168 }
169 }
170 return false;
171 }
172
173 /**
174 * Inserts a {@link Data} item into the data section. If the item is already in the data
175 * section, the same {@link DataSectionReference} is returned.
176 *
177 * @param data the {@link Data} item to be inserted
178 * @return a unique {@link DataSectionReference} identifying the {@link Data} item
179 */
180 public DataSectionReference insertData(Data data) {
181 checkOpen();
182 synchronized (data) {
183 if (data.ref == null) {
184 data.ref = new DataSectionReference();
185 dataItems.add(data);
186 }
187 return data.ref;
188 }
189 }
190
191 /**
192 * Transfers all {@link Data} from the provided other {@link DataSection} to this
193 * {@link DataSection}, and empties the other section.
194 */
195 public void addAll(DataSection other) {
196 checkOpen();
197 other.checkOpen();
198
199 for (Data data : other.dataItems) {
200 assert data.ref != null;
201 dataItems.add(data);
202 }
203 other.dataItems.clear();
204 }
205
206 /**
207 * Determines if this object has been {@link #close() closed}.
208 */
209 public boolean closed() {
210 return closed;
211 }
212
213 /**
214 * Computes the layout of the data section and closes this object to further updates.
215 *
216 * This must be called exactly once.
217 */
218 void close() {
219 checkOpen();
220 closed = true;
221
222 // simple heuristic: put items with larger alignment requirement first
223 dataItems.sort((a, b) -> a.alignment - b.alignment);
224
225 int position = 0;
226 int alignment = 1;
227 for (Data d : dataItems) {
228 alignment = lcm(alignment, d.alignment);
229 position = align(position, d.alignment);
230
231 d.ref.setOffset(position);
232 position += d.size;
233 }
234
235 sectionAlignment = alignment;
236 sectionSize = position;
237 }
238
239 /**
240 * Gets the size of the data section.
241 *
242 * This must only be called once this object has been {@linkplain #closed() closed}.
243 */
244 public int getSectionSize() {
245 checkClosed();
246 return sectionSize;
247 }
248
249 /**
250 * Gets the minimum alignment requirement of the data section.
251 *
252 * This must only be called once this object has been {@linkplain #closed() closed}.
253 */
254 public int getSectionAlignment() {
255 checkClosed();
256 return sectionAlignment;
257 }
258
259 /**
260 * Builds the data section into a given buffer.
261 *
262 * This must only be called once this object has been {@linkplain #closed() closed}.
263 *
264 * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must
265 * hold at least {@link #getSectionSize()} bytes.
266 * @param patch a {@link Consumer} to receive {@link DataPatch data patches} for relocations in
267 * the data section
268 */
269 public void buildDataSection(ByteBuffer buffer, Consumer<DataPatch> patch) {
270 checkClosed();
271 for (Data d : dataItems) {
272 buffer.position(d.ref.getOffset());
273 d.builder.emit(buffer, patch);
274 }
275 }
276
277 public Data findData(DataSectionReference ref) {
278 for (Data d : dataItems) {
279 if (d.ref == ref) {
280 return d;
281 }
282 }
283 return null;
284 }
285
286 public Iterator<Data> iterator() {
287 return dataItems.iterator();
288 }
289
290 public static int lcm(int x, int y) {
293 } else if (y == 0) {
294 return x;
295 }
296
297 int a = Math.max(x, y);
298 int b = Math.min(x, y);
299 while (b > 0) {
300 int tmp = a % b;
301 a = b;
302 b = tmp;
303 }
304
305 int gcd = a;
306 return x * y / gcd;
307 }
308
309 private static int align(int position, int alignment) {
310 return ((position + alignment - 1) / alignment) * alignment;
311 }
312
313 private void checkClosed() {
314 if (!closed) {
315 throw new IllegalStateException();
316 }
317 }
318
319 private void checkOpen() {
320 if (closed) {
321 throw new IllegalStateException();
322 }
323 }
324
325 public void clear() {
326 checkOpen();
327 this.dataItems.clear();
328 this.sectionAlignment = 0;
329 this.sectionSize = 0;
330 }
331 }
|