package org.radargun.stages.cache.background; import java.io.Serializable; import java.util.Arrays; import java.util.HashSet; import java.util.Map; /** * Log value that can be written by multiple stressors. * * @author Radim Vansa <rvansa@redhat.com> */ public class SharedLogValue implements Serializable { private final int[] threadIds; private final long[] operationIds; public SharedLogValue(int threadId, long operationId) { threadIds = new int[] {threadId}; operationIds = new long[] {operationId}; } protected SharedLogValue(int[] threadIds, long[] operationIds) { this.threadIds = threadIds; this.operationIds = operationIds; } public SharedLogValue with(int threadId, long operationId) { int[] newThreadIds = new int[threadIds.length + 1]; System.arraycopy(threadIds, 0, newThreadIds, 0, threadIds.length); newThreadIds[threadIds.length] = threadId; long[] newOperationIds = new long[operationIds.length + 1]; System.arraycopy(operationIds, 0, newOperationIds, 0, operationIds.length); newOperationIds[operationIds.length] = operationId; return new SharedLogValue(newThreadIds, newOperationIds); } public SharedLogValue with(int threadId, long operationId, Map<Integer, Long> checkedOperationIds) { int toRemoveCount = 0; for (int i = 0; i < threadIds.length; ++i) { long checked = checkedOperationIds.get(threadIds[i]); if (operationIds[i] <= checked) { ++toRemoveCount; } } int[] newThreadIds = new int[threadIds.length - toRemoveCount + 1]; long[] newOperationIds = new long[operationIds.length - toRemoveCount + 1]; for (int i = 0, j = 0; i < threadIds.length; ++i) { long checked = checkedOperationIds.get(threadIds[i]); if (operationIds[i] > checked) { newThreadIds[j] = threadIds[i]; newOperationIds[j] = operationIds[i]; ++j; } } newThreadIds[newThreadIds.length - 1] = threadId; newOperationIds[newOperationIds.length - 1] = operationId; return new SharedLogValue(newThreadIds, newOperationIds); } public SharedLogValue join(SharedLogValue other) { // reserve enough space // the values will likely have the same beginning int commonIndex; int minLength = Math.min(threadIds.length, other.threadIds.length); for (commonIndex = 0; commonIndex < minLength; ++commonIndex) { if (threadIds[commonIndex] != other.threadIds[commonIndex] || operationIds[commonIndex] != other.operationIds[commonIndex]) { break; } } if (commonIndex == threadIds.length) { return other; } else if (commonIndex == other.threadIds.length) { return this; } else { HashSet<ThreadOperation> operations = new HashSet<ThreadOperation>(); for (int i = commonIndex; i < threadIds.length; ++i) { operations.add(new ThreadOperation(threadIds[i], operationIds[i])); } for (int i = commonIndex; i < other.threadIds.length; ++i) { operations.add(new ThreadOperation(other.threadIds[i], other.operationIds[i])); } int[] newThreadIds = new int[commonIndex + operations.size()]; long[] newOperationIds = new long[commonIndex + operations.size()]; System.arraycopy(threadIds, 0, newThreadIds, 0, commonIndex); System.arraycopy(operationIds, 0, newOperationIds, 0, commonIndex); for (ThreadOperation operation : operations) { newThreadIds[commonIndex] = operation.threadId; newOperationIds[commonIndex] = operation.operationId; ++commonIndex; } return new SharedLogValue(newThreadIds, newOperationIds); } } public long minFrom(int threadId) { long operationId = Long.MAX_VALUE; for (int i = 0; i < threadIds.length; ++i) { if (threadIds[i] == threadId) operationId = Math.min(operationIds[i], operationId); } return operationId; } public boolean contains(int threadId, long operationId) { for (int i = threadIds.length - 1; i >= 0; i--) { if (threadIds[i] == threadId && operationIds[i] == operationId) return true; } return false; } public int size() { return threadIds.length; } public int getThreadId(int index) { return threadIds[index]; } public long getOperationId(int index) { return operationIds[index]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SharedLogValue that = (SharedLogValue) o; if (!Arrays.equals(operationIds, that.operationIds)) return false; if (!Arrays.equals(threadIds, that.threadIds)) return false; return true; } @Override public int hashCode() { int result = Arrays.hashCode(threadIds); result = 31 * result + Arrays.hashCode(operationIds); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder("[ #").append(threadIds.length).append(": "); for (int i = 0; i < threadIds.length; ++i) { sb.append(threadIds[i]).append('~').append(operationIds[i]).append(", "); } return sb.append(']').toString(); } private static class ThreadOperation { public final int threadId; public final long operationId; private ThreadOperation(int threadId, long operationId) { this.threadId = threadId; this.operationId = operationId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ThreadOperation that = (ThreadOperation) o; if (operationId != that.operationId) return false; if (threadId != that.threadId) return false; return true; } @Override public int hashCode() { int result = threadId; result = 31 * result + (int) (operationId ^ (operationId >>> 32)); return result; } } }