diff --git a/src/main/java/de/hechler/patrick/utils/cc/ModifiableString.java b/src/main/java/de/hechler/patrick/utils/cc/ModifiableString.java
index c1db6b0de25f4b8979132ad8102cbd64ba79d399..c30e145d6641d32a9011b6cc8715a0532a6fd6f2 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/ModifiableString.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/ModifiableString.java
@@ -36,4 +36,10 @@ public interface ModifiableString extends CharSequence {
 		return toString().getBytes(cs);
 	}
 
+	@Override
+	int hashCode();
+
+	@Override
+	boolean equals(Object obj);
+
 }
diff --git a/src/main/java/de/hechler/patrick/utils/cc/file/CCPath.java b/src/main/java/de/hechler/patrick/utils/cc/file/CCPath.java
index f54b3a5419fd6003b429655346b255698886194a..d2e4a0d3d3411060566d8c87beda51be5675a9c2 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/file/CCPath.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/file/CCPath.java
@@ -3,17 +3,12 @@ package de.hechler.patrick.utils.cc.file;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Objects;
 
-public class CCPath implements CCFile {
+public record CCPath(Path path) implements CCFile {
 
-	private final Path path;
-
-	public CCPath(Path path) {
-		this.path = path;
-	}
-
-	public Path path() {
-		return path;
+	public CCPath {
+		Objects.requireNonNull(path, "path");
 	}
 	
 	@Override
@@ -26,38 +21,9 @@ public class CCPath implements CCFile {
 		return Files.readAllBytes(path);
 	}
 
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((path == null) ? 0 : path.hashCode());
-		return result;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		CCPath other = (CCPath) obj;
-		if (path == null) {
-			if (other.path != null)
-				return false;
-		} else if (!path.equals(other.path))
-			return false;
-		return true;
-	}
-
 	@Override
 	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		builder.append("CCPath [path=");
-		builder.append(path);
-		builder.append("]");
-		return builder.toString();
+		return path.toString();
 	}
 
 }
diff --git a/src/main/java/de/hechler/patrick/utils/cc/file/CCSrcDstPath.java b/src/main/java/de/hechler/patrick/utils/cc/file/CCSrcDstPath.java
index 6d08abaac83bb0ace5629ad5f7ae946875cd0290..df05775f2fc070113b5efe94eb1b356f63c60515 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/file/CCSrcDstPath.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/file/CCSrcDstPath.java
@@ -3,23 +3,13 @@ package de.hechler.patrick.utils.cc.file;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Objects;
 
-public class CCSrcDstPath implements CCFile {
+public record CCSrcDstPath(Path src, Path dst) implements CCFile {
 
-	private final Path src;
-	private final Path dst;
-
-	public CCSrcDstPath(Path src, Path dst) {
-		this.src = src;
-		this.dst = dst;
-	}
-
-	public Path src() {
-		return src;
-	}
-
-	public Path dst() {
-		return dst;
+	public CCSrcDstPath {
+		Objects.requireNonNull(src, "source path");
+		Objects.requireNonNull(dst, "destination path");
 	}
 
 	@Override
@@ -33,46 +23,13 @@ public class CCSrcDstPath implements CCFile {
 		Files.write(dst, content);
 	}
 
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((dst == null) ? 0 : dst.hashCode());
-		result = prime * result + ((src == null) ? 0 : src.hashCode());
-		return result;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		CCSrcDstPath other = (CCSrcDstPath) obj;
-		if (dst == null) {
-			if (other.dst != null)
-				return false;
-		} else if (!dst.equals(other.dst))
-			return false;
-		if (src == null) {
-			if (other.src != null)
-				return false;
-		} else if (!src.equals(other.src))
-			return false;
-		return true;
-	}
-
 	@Override
 	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		builder.append("CCSrcDstPath [src=");
-		builder.append(src);
-		builder.append(", dst=");
-		builder.append(dst);
-		builder.append("]");
-		return builder.toString();
+		StringBuilder b = new StringBuilder();
+		b.append(src);
+		b.append(" ==> ");
+		b.append(dst);
+		return b.toString();
 	}
 
 }
diff --git a/src/main/java/de/hechler/patrick/utils/cc/file/CCStream.java b/src/main/java/de/hechler/patrick/utils/cc/file/CCStream.java
index 61a79c851ce59201fdd077f1cd2f2f56b8b37598..0b7d493eeab6fe3ce30fa9f5ba02aacd46b3e432 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/file/CCStream.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/file/CCStream.java
@@ -3,6 +3,7 @@ package de.hechler.patrick.utils.cc.file;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Objects;
 
 public class CCStream implements CCFile {
 
@@ -10,8 +11,8 @@ public class CCStream implements CCFile {
 	private final OutputStream out;
 
 	public CCStream(InputStream in, OutputStream out) {
-		this.in = in;
-		this.out = out;
+		this.in = Objects.requireNonNull(in, "input stream");
+		this.out = Objects.requireNonNull(out, "output stream");
 	}
 
 	@Override
@@ -46,15 +47,9 @@ public class CCStream implements CCFile {
 		if (getClass() != obj.getClass())
 			return false;
 		CCStream other = (CCStream) obj;
-		if (in == null) {
-			if (other.in != null)
-				return false;
-		} else if (!in.equals(other.in))
+		if (!in.equals(other.in))
 			return false;
-		if (out == null) {
-			if (other.out != null)
-				return false;
-		} else if (!out.equals(other.out))
+		if (!out.equals(other.out))
 			return false;
 		return true;
 	}
diff --git a/src/main/java/de/hechler/patrick/utils/cc/ms/DuplicateModifiableString.java b/src/main/java/de/hechler/patrick/utils/cc/ms/DuplicateModifiableString.java
new file mode 100644
index 0000000000000000000000000000000000000000..2733c76f7c58a9e41b829de3c85dd5ebf9b38269
--- /dev/null
+++ b/src/main/java/de/hechler/patrick/utils/cc/ms/DuplicateModifiableString.java
@@ -0,0 +1,162 @@
+package de.hechler.patrick.utils.cc.ms;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import de.hechler.patrick.utils.cc.ModifiableString;
+
+public class DuplicateModifiableString implements ModifiableString {
+
+	private final ModifiableString a;
+	private final ModifiableString b;
+
+	public DuplicateModifiableString() {
+		this(new ModStringBuilder(), new FastStringList());
+	}
+
+	public DuplicateModifiableString(ModifiableString b) {
+		this(new ModStringBuilder(), b);
+	}
+
+	public DuplicateModifiableString(ModifiableString a, ModifiableString b) {
+		checkEquals(a, b);
+		checkEquals(a.hashCode(), b.hashCode());
+		this.a = a;
+		this.b = b;
+	}
+
+	private void checkEquals() {
+		checkEquals(a, b);
+	}
+
+	private static int checkEquals(int a, int b) {
+		if (a != b) {
+			throw new AssertionError();
+		}
+		return a;
+	}
+
+	private static char checkEquals(char a, char b) {
+		if (a != b) {
+			throw new AssertionError();
+		}
+		return a;
+	}
+
+	private static boolean checkEquals(boolean a, boolean b) {
+		if (a != b) {
+			throw new AssertionError();
+		}
+		return a;
+	}
+	
+	private static <T> T checkEquals(T a, T b) {
+		if (!a.equals(b) || a == b) {
+			throw new AssertionError();
+		}
+		return a;
+	}
+
+	private static byte[] checkEquals(byte[] a, byte[] b) {
+		if (!Arrays.equals(a, b) || a == b) {
+			throw new AssertionError();
+		}
+		return a;
+	}
+
+	@Override
+	public int length() {
+		return checkEquals(a.length(), b.length());
+	}
+
+	@Override
+	public char charAt(int index) {
+		char c = a.charAt(index);
+		char c2 = b.charAt(index);
+		return checkEquals(c, c2);
+	}
+
+	@Override
+	public CharSequence subSequence(int start, int end) {
+		CharSequence cs = a.subSequence(start, end);
+		CharSequence cs2 = b.subSequence(start, end);
+		return checkEquals(cs, cs2);
+	}
+
+	@Override
+	public int indexOf(char c, int off) {
+		int i = a.indexOf(c, off);
+		int i2 = b.indexOf(c, off);
+		return checkEquals(i, i2);
+	}
+
+	@Override
+	public int lastIndexOf(char c, int off) {
+		int i = a.lastIndexOf(c, off);
+		int i2 = b.lastIndexOf(c, off);
+		return checkEquals(i, i2);
+	}
+
+	@Override
+	public void replace(int start, int end, String str) {
+		a.replace(start, end, str);
+		b.replace(start, end, str);
+		checkEquals();
+	}
+
+	@Override
+	public void replace(int start, int end, char[] arr, int off, int len) {
+		a.replace(start, end, arr, off, len);
+		b.replace(start, end, arr, off, len);
+		checkEquals();
+	}
+
+	@Override
+	public void insert(int index, String str) {
+		a.insert(index, str);
+		b.insert(index, str);
+		checkEquals();
+	}
+
+	@Override
+	public void insert(int index, char[] arr, int off, int len) {
+		a.insert(index, arr, off, len);
+		b.insert(index, arr, off, len);
+		checkEquals();
+	}
+
+	@Override
+	public void append(String str) {
+		a.append(str);
+		b.append(str);
+		checkEquals();
+	}
+
+	@Override
+	public void append(char[] arr, int off, int len) {
+		a.append(arr, off, len);
+		b.append(arr, off, len);
+		checkEquals();
+	}
+
+	@Override
+	public String toString() {
+		return checkEquals(a.toString(), b.toString());
+	}
+
+	@Override
+	public byte[] getBytes(Charset cs) {
+		return checkEquals(a.getBytes(cs), b.getBytes(cs));
+	}
+	
+	@Override
+	public int hashCode() {
+		return checkEquals(a.hashCode(), b.hashCode());
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		return checkEquals(a.equals(obj), b.equals(obj));
+	}
+
+}
diff --git a/src/main/java/de/hechler/patrick/utils/cc/ms/FastStringList.java b/src/main/java/de/hechler/patrick/utils/cc/ms/FastStringList.java
index 02a2e6f500570e16fb8c596aa668f27a87b32125..267ac8c3d2d7d68bbe9f14682e0f8dce67e4b1dd 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/ms/FastStringList.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/ms/FastStringList.java
@@ -1,9 +1,5 @@
 package de.hechler.patrick.utils.cc.ms;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOError;
-import java.io.IOException;
-import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
@@ -12,21 +8,27 @@ import java.util.Arrays;
 import de.hechler.patrick.utils.cc.ModifiableString;
 
 @SuppressWarnings("unused")
-public final class FastStringList implements ModifiableString {
+public class FastStringList implements ModifiableString {
 
 	private static final int ARR_MAX_SIZE = 2048;
-	private static final int ARR_PREF_SIZE = ARR_MAX_SIZE - (ARR_MAX_SIZE / 8);
 	private static final int ARR_MIN_SIZE = ARR_MAX_SIZE / 2;
+	private static final int ARR_PREF_SIZE = (ARR_MAX_SIZE + ARR_MIN_SIZE) / 2;
 
 	// length/offset is only used/maintained in the user object
-	private int length;
-	private int offset;
-	private FastStringList prev;
-	private int dataSize;
-	private char[] data;
-	private FastStringList next;
+	protected int length;
+	protected int offset;
+	protected FastStringList prev;
+	protected int dataSize;
+	protected char[] data;
+	protected FastStringList next;
 	// nextData is only used/maintained in the userObject
-	private char[] nextData;
+	protected char[] nextData;
+
+	public FastStringList(boolean init) {
+		if (init) {
+			throw new AssertionError();
+		}
+	}
 
 	public FastStringList() {
 		data = new char[ARR_MAX_SIZE];
@@ -107,7 +109,7 @@ public final class FastStringList implements ModifiableString {
 		}
 	}
 
-	private void dataSize(int nml) {
+	protected void dataSize(int nml) {
 		if (nml > ARR_MAX_SIZE) {
 			System.err.println("BREAK!");
 //			throw new AssertionError(nml);
@@ -491,8 +493,8 @@ public final class FastStringList implements ModifiableString {
 				off += ml;
 				sl = sl.next;
 				char[] nd = sl.data;
+				System.arraycopy(d, ml, nd, 0, ei);
 				ml = sl.dataSize;
-				System.arraycopy(d, sl.dataSize, nd, 0, ei);
 				d = nd;
 			}
 			// : insert the new data
@@ -593,8 +595,13 @@ public final class FastStringList implements ModifiableString {
 						System.arraycopy(sl.data, ei, rsd, offset + si, ml - ei);
 						System.arraycopy(pd, npml, rsd, 0, offset);
 					} else {
-						System.arraycopy(sl.data, ei, rsd, si, ml - ei);
-						System.arraycopy(pd, npml, rsd, 0, pml - npml);
+						int cpy = ml - ei;
+						System.arraycopy(sl.data, ei, rsd, si, cpy);
+						int pos = si + cpy;
+						cpy = ARR_MIN_SIZE - cpy - si;
+						System.arraycopy(pd, 0, rsd, pos, cpy);
+						System.arraycopy(pd, cpy, pd, 0, npml);
+						assert pml - cpy == npml;
 					}
 					removeStart.dataSize(ARR_MIN_SIZE);
 					p.dataSize(npml);
@@ -747,6 +754,90 @@ public final class FastStringList implements ModifiableString {
 		}
 	}
 
+	@Override
+	public int hashCode() {
+		FastStringList sl = this;
+		while (sl.prev != null)
+			sl = sl.prev;
+		int result = 0;
+		final int prime = 31;
+		for (; sl != null; sl = sl.next) {
+			int ml = sl.dataSize;
+			char[] d = sl.data;
+			for (int i = 0; i < ml; i++) {
+				result = result * prime + d[i];
+			}
+		}
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (!(obj instanceof ModifiableString ms)) {
+			return false;
+		}
+		if (!(obj instanceof FastStringList osl)) {
+			if (ms.length() != length) {
+				return false;
+			}
+			FastStringList sl = this;
+			while (sl.prev != null)
+				sl = sl.prev;
+			for (int off = 0, noff; sl != null; sl = sl.next, off = noff) {
+				int ml = sl.dataSize;
+				noff = off + ml;
+				CharSequence cs = ms.subSequence(off, noff);
+				if (!cs.toString().equals(new String(sl.data, 0, ml))) {
+					return false;
+				}
+			}
+			return true;
+		}
+		if (length != osl.length) {
+			return false;
+		}
+		FastStringList sl = this;
+		while (sl.prev != null)
+			sl = sl.prev;
+		while (osl.prev != null)
+			osl = osl.prev;
+		int i = 0, oi = 0;
+		int ml = sl.dataSize;
+		int oml = osl.dataSize;
+		while (true) {
+			if (i == ml) {
+				sl = sl.next;
+				if (sl != null) {
+					ml = sl.dataSize;
+					i = 0;
+				}
+			}
+			if (oi == oml) {
+				osl = osl.next;
+				if (osl != null) {
+					oml = osl.dataSize;
+					oi = 0;
+				}
+			}
+			if (sl == osl) {
+				if (sl != null) {
+					throw new AssertionError();
+				}
+				return true;
+			}
+			int len = Math.min(ml - i, oml - oi);
+			int mm = Arrays.mismatch(sl.data, i, i + len, osl.data, oi, oi + len);
+			if (mm != -1) {
+				return false;
+			}
+			i += len;
+			oi += len;
+		}
+	}
+
 	@Override
 	public String toString() {
 		StringBuilder result = new StringBuilder(length);
diff --git a/src/main/java/de/hechler/patrick/utils/cc/ms/ModStringBuilder.java b/src/main/java/de/hechler/patrick/utils/cc/ms/ModStringBuilder.java
index b8f5ff164c7600445af54e2e5beb0fefa8c6b941..08e2b08ad1e32c890dc48d31d060e8aa56b549bb 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/ms/ModStringBuilder.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/ms/ModStringBuilder.java
@@ -27,7 +27,7 @@ public class ModStringBuilder implements ModifiableString {
 	public char charAt(int index) {
 		return this.sb.charAt(index);
 	}
-	
+
 	@Override
 	public CharSequence subSequence(int start, int end) {
 		return this.sb.subSequence(start, end);
@@ -37,12 +37,12 @@ public class ModStringBuilder implements ModifiableString {
 	public int indexOf(char c, int off) {
 		return this.sb.indexOf(String.valueOf(c), off);
 	}
-	
+
 	@Override
 	public int lastIndexOf(char c, int off) {
 		return this.sb.lastIndexOf(String.valueOf(c), off);
 	}
-	
+
 	@Override
 	public void replace(int start, int end, String str) {
 		this.sb.replace(start, end, str);
@@ -88,9 +88,22 @@ public class ModStringBuilder implements ModifiableString {
 		this.sb.append(arr, off, len);
 	}
 
+	@Override
+	public int hashCode() {
+		return sb.toString().hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof ModifiableString)) {
+			return false;
+		}
+		return sb.toString().equals(obj.toString());
+	}
+
 	@Override
 	public String toString() {
 		return this.sb.toString();
 	}
-	
+
 }
diff --git a/src/main/java/de/hechler/patrick/utils/cc/skip/SqlCorrector.java b/src/main/java/de/hechler/patrick/utils/cc/skip/SqlCorrector.java
index e6302beb5340fdbced89f52c34f4abf9202593c5..8a5e5b40bd32e57876e13d36417d0afad2f69157 100644
--- a/src/main/java/de/hechler/patrick/utils/cc/skip/SqlCorrector.java
+++ b/src/main/java/de/hechler/patrick/utils/cc/skip/SqlCorrector.java
@@ -53,7 +53,7 @@ public class SqlCorrector extends MassCarCoder {
 			cc.insert("ì\\n");
 		}
 		if (change || !(p instanceof CCPath)) {
-			cc.save(p);
+//			cc.save(p);
 		}
 		if (!change) {
 			System.out.println("    nothing changed");