/* * Replication Benchmarker * https://github.com/score-team/replication-benchmarker/ * Copyright (C) 2013 LORIA / Inria / SCORE Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package jbenchmarker.logootsplitO; import crdt.Operation; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * * @author Stephane Martin <stephane@stephanemartin.fr> */ public class LogootSDocumentD implements LogootSDoc, Serializable { private int clock = 0; private HashMap<List<Integer>, LogootSBlockLight> mapBaseToBlock = new HashMap<List<Integer>, LogootSBlockLight>(); //for test private ArrayList<LinkBlock> list = new ArrayList<LinkBlock>();//dichotomic ready private StringBuilder view = new StringBuilder(); private int replicaNumber = 0; @Override public void setReplicaNumber(int i) { this.replicaNumber = i; } public static class LinkBlock implements Comparable, Serializable { LogootSBlockLight<Object> block; int offset; public LinkBlock(LogootSBlockLight block, int offset) { this.block = block; this.offset = offset; } @Override public int compareTo(Object t) { if (t instanceof LinkBlock) { this.getID().compareTo(((LinkBlock) t).getID()); } throw new UnsupportedOperationException("Bad comparaison"); //To change body of generated methods, choose Tools | Templates. } public LogootSBlock getBlock() { return block; } public Identifier getID() { return new Identifier(this.block.id.getBase(), offset); } @Override public String toString() { return "L{" + block.id.base + "," + offset + '}'; } } @Override public String view() { return view.toString(); } @Override public int viewLength() { return view.length(); } /** * search a position */ int dicSearch(Identifier id, int min) { //int min = 0; int max = list.size() - 1; while (min <= max) { int i = (int) (min + max) / 2; int p = list.get(i).getID().compareTo(id); //int p = list.get(i).getID().compareTo(block.id. if (p < 0) { min = i + 1; } else if (p > 0) { max = i - 1; } else { min = i; break; } } return min; } /** * * * @param l * @param l2 * @return */ static int maxOffsetBeforeNex(Identifier bI, Identifier nex, int max) { Iterator<Integer> i = bI.base.iterator(); Iterator<Integer> i2 = nex.iterator(); while (i.hasNext() && i2.hasNext()) { if (!i.next().equals(i2.next())) { return max; } } if (i2.hasNext()) { return Math.min(i2.next(), max); } else { return max; } } /** * * @param pos * @param block * @return inserted block */ public void addBlock(LogootSBlockLight block, int begin, List elem) { int offset = begin; int pos = 0; int end = begin + elem.size() - 1; while (offset <= end) { //search the first position pos = dicSearch(block.getId().getBaseId(offset), pos); //computation of offset Max int offsetMax; if (pos < list.size()) { Identifier beginId = new Identifier(block.id.base, offset); offsetMax = maxOffsetBeforeNex(beginId, list.get(pos).getID(), end); } else { offsetMax = end; } add(pos, block, offset, elem.subList(offset - begin, offsetMax - begin + 1)); offset = offsetMax + 1; /* for (; offset <= offsetMax; offset++) { add(pos, block, offset, it.next()); pos++; }*/ } } private ArrayList makeOffsets(LogootSBlockLight block, int offset, List o) { ArrayList l = new ArrayList(o.size()); for (int i = 0; i < o.size(); i++) { l.add(new LinkBlock(block, offset++)); } return l; } /* private LinkBlock[] makeOffsets(LogootSBlock block, int offset, List o) { LinkBlock[] l=new LinkBlock[o.size()]; for (int i = 0; i < o.size(); i++) { l[i]=new LinkBlock(block, offset++); } return l; }*/ private char[] makeChar(List<Character> o) { char[] ret = new char[o.size()]; for (int i = 0; i < o.size(); i++) { ret[i] = o.get(i); } return ret; } private void add(int pos, LogootSBlockLight block, int offset, List o) { list.addAll(pos, makeOffsets(block, offset, o)); view.insert(pos, makeChar(o)); } @Override public void addBlock(Identifier id, List l) { LogootSBlockLight block = mapBaseToBlock.get(id.base); IdentifierInterval idi = new IdentifierInterval(id.base, id.last, id.last + l.size() - 1); if (block == null) { block = new LogootSBlockLight(idi, l.size());//TODO build factory mapBaseToBlock.put(id.base, block); } else { block.addBlock(id.last, l); } addBlock(block, id.last, l); } public void delBlock(LogootSBlock block, int begin, int end) { int offset = begin; int pos = 0; LinkBlock lb; int nbElement = 0; while (offset <= end) { //search the first position. pos = dicSearch(block.getId().getBaseId(offset), pos); if (pos >= list.size()) { break; } lb = list.get(pos); //while we are in block if (lb.getBlock() != block) {//element does not existing pos++; offset++; } else { int b = pos; int e = pos; do { if (lb.offset != offset) { offset = lb.offset; } else { /* list.remove(pos); view.deleteCharAt(pos);*/ e++; offset++; pos++; nbElement++; lb = pos < list.size() ? list.get(pos) : null; } } while (lb != null && lb.getBlock() == block && offset <= end); if (e - b > 0) { list.subList(b, e).clear(); // list.delRange(b, e); view.delete(b, e); pos = b; } } } block.delBlock(begin, end, nbElement); if (block.numberOfElements() == 0) {// little garbage collection this.mapBaseToBlock.remove(block.getId().getBase()); } } @Override public void delBlock(IdentifierInterval id) { LogootSBlock block = mapBaseToBlock.get(id.base); if (block != null) { delBlock(block, id.begin, id.end); } } @Override public void apply(Operation op) { } @Override public LogootSOpAdd insertLocal(int pos, List l) { LinkBlock after = pos < list.size() ? list.get(pos) : null; LinkBlock before = pos > 0 ? list.get(pos - 1) : null; int offset; LogootSBlockLight block; if (after != null && after.block.id.begin==after.offset && after.block.mine && after.block.getId().begin - l.size() > Integer.MIN_VALUE) {// Block in position is mine //add before block block = after.block; //offset = after.offset; offset = block.id.begin - l.size(); block.addBlock(offset, l); } else if (before != null && before.block.id.end==before.offset && before.block.mine && before.block.getId().end + l.size() < Integer.MAX_VALUE) { //add after block block = before.block; offset = block.id.end+1; //offset = block.id.begin - l.size(); block.addBlock(offset, l); } else { // create new block List<Integer> base = IDFactory.createBetweenPosition(before == null ? null : before.getID(), after == null ? null : after.getID(), replicaNumber, clock++); IdentifierInterval id = new IdentifierInterval(base, 0, l.size() - 1); block = new LogootSBlockLight(id);//TODO build factory block.setMine(true); offset = 0; block.addBlock(offset, l); mapBaseToBlock.put(block.getId().getBase(), block); } Identifier idi = new Identifier(block.getId().base, offset); //int i = pos; add(pos, block, offset, l); offset += l.size(); // i++; return new LogootSOpAdd(idi, l); } /** * Delete local begin inclusive to end inclusive * * @param begin * @param end * @return operation to make to other */ @Override public LogootSOpDel delLocal(int begin, int end) { List<IdentifierInterval> li = new LinkedList<IdentifierInterval>(); LinkBlock lb = list.get(begin); LogootSBlock block = lb.getBlock(); int b = lb.offset; int e = b; int nbElement = 0; int i = begin; do { lb = list.get(i); if (lb.block != block) { addDelIdf(block, b, e, li, nbElement); block = lb.block; b = lb.offset; nbElement = 0; } e = lb.offset; i++; nbElement++; } while (i <= end); addDelIdf(block, b, e, li, nbElement); view.delete(begin, end + 1); list.subList(begin, end+1).clear(); //list.delRange(begin, end+1); return new LogootSOpDel(li); } private void addDelIdf(LogootSBlock block, int begin, int end, List<IdentifierInterval> li, int nbElement) { li.add(new IdentifierInterval(block.id.base, begin, end)); block.delBlock(begin, end, nbElement); if (block.numberOfElements() == 0) { mapBaseToBlock.remove(block.getId().getBase()); } } @Override public LogootSDoc create() { return new LogootSDocumentD(); } /** * For test */ public ArrayList<LinkBlock> getList() { return list; } public StringBuilder getView() { return view; } public HashMap<List<Integer>, LogootSBlockLight> getMapBaseToBlock() { return mapBaseToBlock; } }