package com.chap.memo.memoNodes.storage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import com.chap.memo.memoNodes.bus.MemoReadBus;
import com.chap.memo.memoNodes.model.ArcOp;
import com.chap.memo.memoNodes.model.ArcOpBuffer;
import com.chap.memo.memoNodes.model.OpsType;
import com.eaio.uuid.UUID;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ObjectArrays;
public final class ArcOpShard extends MemoStorable {
private static final long serialVersionUID = 7712775430540649570L;
final ArcOp[] parentArray;
final ArcOp[] childArray;
final ArcOp[] rootParentArray;
final ArcOp[] rootChildArray;
transient private static final ChildCmp childCmp = new ChildCmp();
transient private static final ParentCmp parentCmp = new ParentCmp();
private static void addCmp (ArcOp op,HashMap<UUIDTuple,ArcOp> map){
UUIDTuple tup = new UUIDTuple(op.getParent(),op.getChild());
if (!map.containsKey(tup) || map.get(tup).getTimestamp_long()< op.getTimestamp_long()){
map.put(tup,op);
}
}
public static void dropHistory(ArcOpShard shard) {
MemoReadBus ReadBus = MemoReadBus.getBus();
System.out.println("Before:"+shard.parentArray.length+":"+shard.childArray.length+":"+shard.rootParentArray.length+":"+shard.rootChildArray.length);
LinkedHashMap<UUIDTuple,ArcOp> map = new LinkedHashMap<UUIDTuple,ArcOp>(shard.getSize()/(2* ArcOpBuffer.BYTESPEROP));
for (ArcOp op : shard.parentArray){
addCmp(op,map);
}
List<ArcOp> parents = ImmutableList.copyOf(map.values());
map.clear();
for (ArcOp op : shard.rootParentArray){
addCmp(op,map);
}
List<ArcOp> rootParents = ImmutableList.copyOf(map.values());
map.clear();
for (ArcOp op : shard.childArray){
addCmp(op,map);
}
List<ArcOp> children = ImmutableList.copyOf(map.values());
map.clear();
for (ArcOp op : shard.rootChildArray){
addCmp(op,map);
}
List<ArcOp> rootChildren = ImmutableList.copyOf(map.values());
ArcOpShard newShard = new ArcOpShard(parents,children,rootParents,rootChildren);
ArcOpIndex index = new ArcOpIndex(newShard);
ReadBus.addOpsIndex(index, newShard);
System.out.println("After:"+newShard.parentArray.length+":"+newShard.childArray.length+":"+newShard.rootParentArray.length+":"+newShard.rootChildArray.length);
ArcOpIndex idx = ReadBus.removeArcOpIndexByShard(shard
.getMyKey());
if (idx != null) idx.delete();
ReadBus.delShard(shard);
shard.delete();
}
public static void devideAndMerge(ArcOpShard[] shards){
if (shards.length <= 1) return;
System.out.println("Merging "+shards.length+" arcOp shards");
MemoReadBus ReadBus = MemoReadBus.getBus();
ArrayList<ArcOp> rootParentList = new ArrayList<ArcOp>();
ArrayList<ArcOp> rootChildList = new ArrayList<ArcOp>();
ArrayList<ArcOp> parents = new ArrayList<ArcOp>();
ArrayList<ArcOp> children = new ArrayList<ArcOp>();
for (ArcOpShard shard : shards){
parents.ensureCapacity(parents.size()+shard.parentArray.length);
parents.addAll(Arrays.asList(shard.parentArray));
children.ensureCapacity(children.size()+shard.childArray.length);
children.addAll(Arrays.asList(shard.childArray));
rootParentList.ensureCapacity(rootParentList.size()+shard.rootParentArray.length);
rootParentList.addAll(Arrays.asList(shard.rootParentArray));
rootChildList.ensureCapacity(rootChildList.size()+shard.rootChildArray.length);
rootChildList.addAll(Arrays.asList(shard.rootChildArray));
}
Collections.sort(parents,parentCmp);
Collections.sort(children,childCmp);
int maxSize = ArcOpBuffer.STORESIZE*ArcOpBuffer.COMPRESSION_RATIO/ArcOpBuffer.BYTESPEROP;
ArrayList<ArcOp> newParents = new ArrayList<ArcOp>(Math.min(maxSize,parents.size()));
ArrayList<ArcOp> newChildren = new ArrayList<ArcOp>(Math.min(maxSize,children.size()));
Iterator<ArcOp> parIter = parents.iterator();
Iterator<ArcOp> chldIter = children.iterator();
while (parIter.hasNext() || chldIter.hasNext()){
int count = maxSize-(newParents.size()+newChildren.size());
ArcOp parent = parIter.hasNext()?parIter.next():null;
ArcOp child = chldIter.hasNext()?chldIter.next():null;
while (count > 0 && parent != null && (child == null || parent.getParentTime()<= child.getChildTime())){
newParents.add(parent);
parIter.remove();
parent = parIter.hasNext()?parIter.next():null;
count--;
}
while (count > 0 && child != null && (parent == null || child.getChildTime() <= parent.getParentTime())){
newChildren.add(child);
chldIter.remove();
child = chldIter.hasNext()?chldIter.next():null;
count--;
}
if (count <= 0){
ArcOpShard shard = new ArcOpShard(newParents,newChildren,new ArrayList<ArcOp>(0),new ArrayList<ArcOp>(0));
ArcOpIndex index = new ArcOpIndex(shard);
ReadBus.addOpsIndex(index, shard);
newParents.clear();
newChildren.clear();
}
if (parent != null) newParents.add(parent);
if (child != null) newChildren.add(child);
}
ArrayList<ArcOp> newRootParents = new ArrayList<ArcOp>(Math.min(maxSize,rootParentList.size()));
ArrayList<ArcOp> newRootChildren = new ArrayList<ArcOp>(Math.min(maxSize,rootChildList.size()));
parIter = rootParentList.iterator();
chldIter = rootChildList.iterator();
while (parIter.hasNext() || chldIter.hasNext()){
ArcOp parent = parIter.hasNext()?parIter.next():null;
ArcOp child = chldIter.hasNext()?chldIter.next():null;
int count = maxSize-(newParents.size()+newChildren.size())-(newRootParents.size()+newRootChildren.size());
while(count>0 && parent != null){
newRootParents.add(parent);
parent = parIter.hasNext()?parIter.next():null;
count--;
}
while(count>0 && child != null){
newRootChildren.add(child);
child = chldIter.hasNext()?chldIter.next():null;
count--;
}
if (count<=0){
ArcOpShard shard = new ArcOpShard(newParents,newChildren,newRootParents,newRootChildren);
ArcOpIndex index = new ArcOpIndex(shard);
ReadBus.addOpsIndex(index, shard);
newParents.clear();
newChildren.clear();
newRootParents.clear();
newRootChildren.clear();
}
if (parent != null) newRootParents.add(parent);
if (child != null) newRootChildren.add(child);
}
if (newParents.size()>0 || newChildren.size()>0||newRootParents.size()>0||newRootChildren.size()>0){
ArcOpShard shard = new ArcOpShard(newParents,newChildren,newRootParents,newRootChildren);
ArcOpIndex index = new ArcOpIndex(shard);
ReadBus.addOpsIndex(index, shard);
}
for (ArcOpShard other : shards) {
ArcOpIndex idx = ReadBus.removeArcOpIndexByShard(other
.getMyKey());
if (idx != null)
idx.delete();
ReadBus.delShard(other);
other.delete();
}
}
public void setSpread(){
long parTime = parentArray.length>0?parentArray[parentArray.length-1].getParentTime():0;
long chldTime = childArray.length>0?childArray[childArray.length-1].getChildTime():0;
long newest = Math.max(parTime, chldTime);
parTime = parentArray.length>0?parentArray[0].getParentTime():newest;
chldTime = childArray.length>0?childArray[0].getChildTime():newest;
long oldest = Math.min(parTime, chldTime);
this.storeTime = newest;
this.spread = newest-oldest;
}
public ArcOpShard(List<ArcOp> parentList,List<ArcOp> childList,List<ArcOp> rootParentList, List<ArcOp> rootChildList){
parentArray = parentList.toArray(new ArcOp[0]);
childArray = childList.toArray(new ArcOp[0]);
rootParentArray = rootParentList.toArray(new ArcOp[0]);
rootChildArray = rootChildList.toArray(new ArcOp[0]);
setSpread();
}
public ArcOpShard(ArcOpBuffer buffer){
ArcOp[] tmp = buffer.getParentOps().toArray(new ArcOp[0]);
Arrays.sort(tmp,parentCmp);
int i=0;
while(tmp[i].getParent().time == 0)i++;
if (i>0){
rootParentArray = Arrays.copyOf(tmp,i);
parentArray = Arrays.copyOfRange(tmp,i,tmp.length);
} else {
rootParentArray = new ArcOp[0];
parentArray = tmp;
}
tmp = buffer.getChildOps().toArray(new ArcOp[0]);
Arrays.sort(tmp,childCmp);
i=0;
while(tmp[i].getChild().time==0)i++;
if (i>0){
rootChildArray = Arrays.copyOf(tmp,i);
childArray = Arrays.copyOfRange(tmp,i,tmp.length);
} else {
rootChildArray = new ArcOp[0];
childArray = tmp;
}
setSpread();
}
public ImmutableList<ArcOp> getChildOps(UUID id) {
if (id.time == 0){
return ImmutableList.copyOf(rootChildArray);
}
int pivot = Arrays.binarySearch(childArray, new ArcOp(OpsType.ADD,id,id,0),childCmp);
if (pivot<0) {
// System.out.println("Ops not found:"+pivot);
return ImmutableList.of();
}
Builder<ArcOp> resBuilder = ImmutableList.builder();
while (pivot>0 && childArray[pivot-1].getChild().time == id.time) pivot--;
while (pivot<childArray.length && childArray[pivot].getChild().time == id.time){
ArcOp op = childArray[pivot];
if (op.getChild().equals(id)){
resBuilder.add(op);
}
pivot++;
}
return resBuilder.build();
}
public ImmutableList<ArcOp> getParentOps(UUID id) {
if (id.time == 0){
return ImmutableList.copyOf(rootParentArray);
}
int pivot = Arrays.binarySearch(parentArray, new ArcOp(OpsType.ADD,id,id,0),parentCmp);
if (pivot<0) {
// System.out.println("Ops not found:"+pivot);
return ImmutableList.of();
}
Builder<ArcOp> resBuilder = ImmutableList.builder();
while (pivot>0 && parentArray[pivot-1].getParent().time == id.time) pivot--;
while (pivot<parentArray.length && parentArray[pivot].getParent().time == id.time){
ArcOp op = parentArray[pivot];
if (op.getParent().equals(id)){
resBuilder.add(op);
}
pivot++;
}
return resBuilder.build();
}
public ArcOp[] getOps(){
return ObjectArrays.concat(childArray,rootChildArray,ArcOp.class);
}
public boolean hasParentRoot(){
return rootParentArray.length>0;
}
public boolean hasChildRoot(){
return rootChildArray.length>0;
}
@Override
public int getSize(){
return (parentArray.length+rootParentArray.length+childArray.length+rootChildArray.length) * ArcOpBuffer.BYTESPEROP;
}
}
class ParentCmp implements Comparator<ArcOp>{
public int compare(ArcOp a,ArcOp b) {
return a.getParent().time==b.getParent().time?0:(a.getParentTime()>b.getParentTime()?1:-1);
}
}
class ChildCmp implements Comparator<ArcOp>{
public int compare(ArcOp a,ArcOp b) {
return a.getChild().time==b.getChild().time?0:(a.getChildTime()>b.getChildTime()?1:-1);
}
}
class UUIDTuple {
public UUID parent=null;
public UUID child =null;
public UUIDTuple(UUID parent, UUID child){
this.parent=parent;
this.child=child;
}
public int hashCode(){
return (parent.hashCode()+child.hashCode())%Integer.MAX_VALUE;
}
public boolean equals(Object o){
if (o instanceof UUIDTuple){
UUIDTuple other = (UUIDTuple) o;
if (other.parent.equals(parent) && other.child.equals(child)) return true;
}
return false;
}
}