2016-03-11 02:06:06 +00:00
|
|
|
import std.math;
|
|
|
|
|
import std.stdio;
|
2016-03-11 20:37:50 +00:00
|
|
|
import std.utf: count;
|
2016-03-11 02:06:06 +00:00
|
|
|
import std.string;
|
|
|
|
|
import std.conv: to;
|
2016-03-11 20:37:50 +00:00
|
|
|
import std.algorithm: min, max;
|
2016-03-11 02:06:06 +00:00
|
|
|
|
|
|
|
|
class Buffer {
|
|
|
|
|
private string value = null;
|
|
|
|
|
private Buffer left;
|
|
|
|
|
private Buffer right;
|
|
|
|
|
|
|
|
|
|
string name = "";
|
|
|
|
|
string savedText;
|
|
|
|
|
|
2016-03-11 20:37:50 +00:00
|
|
|
ulong count;
|
2016-03-11 02:06:06 +00:00
|
|
|
|
|
|
|
|
int splitLength = 1000;
|
|
|
|
|
int joinLength = 500;
|
|
|
|
|
double rebalanceRatio = 1.2;
|
|
|
|
|
|
|
|
|
|
this() { }
|
|
|
|
|
|
|
|
|
|
this(string str, string name = "") {
|
|
|
|
|
this.value = str;
|
2016-03-11 20:37:50 +00:00
|
|
|
this.count = str.count;
|
2016-03-11 02:06:06 +00:00
|
|
|
this.name = name;
|
|
|
|
|
this.savedText = str;
|
|
|
|
|
|
|
|
|
|
left = new Buffer();
|
|
|
|
|
right = new Buffer();
|
|
|
|
|
left.value = "";
|
|
|
|
|
right.value = "";
|
|
|
|
|
|
|
|
|
|
adjust();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void save(string filename = null) {
|
|
|
|
|
if (filename is null) {
|
|
|
|
|
filename = name;
|
|
|
|
|
}
|
|
|
|
|
if (filename != "") {
|
|
|
|
|
string bufSrc = this.toString();
|
|
|
|
|
File f = File(filename, "w");
|
|
|
|
|
f.write(bufSrc);
|
|
|
|
|
f.close();
|
|
|
|
|
savedText = bufSrc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@property string[] lines() {
|
|
|
|
|
string str = this.toString();
|
|
|
|
|
if (str == "") {
|
|
|
|
|
return [""];
|
|
|
|
|
} else {
|
|
|
|
|
return str.split("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void adjust() {
|
|
|
|
|
if (value !is null) {
|
2016-03-11 20:37:50 +00:00
|
|
|
if (count > splitLength) {
|
|
|
|
|
auto divide = cast(int) floor(count / 2.0);
|
2016-03-11 02:06:06 +00:00
|
|
|
left = new Buffer(value[0 .. divide]);
|
|
|
|
|
right = new Buffer(value[divide .. $]);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2016-03-11 20:37:50 +00:00
|
|
|
if (count < joinLength) {
|
2016-03-11 02:06:06 +00:00
|
|
|
value = left.toString() ~ right.toString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override
|
|
|
|
|
string toString() {
|
|
|
|
|
if (value !is null) {
|
|
|
|
|
return value;
|
|
|
|
|
} else {
|
|
|
|
|
return left.toString ~ right.toString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void remove(ulong start, ulong end) {
|
|
|
|
|
if (value !is null) {
|
2016-03-11 20:37:50 +00:00
|
|
|
value = to!string(value.to!dstring[0 .. start] ~ value.to!dstring[end .. $]);
|
|
|
|
|
count = value.count;
|
2016-03-11 02:06:06 +00:00
|
|
|
} else {
|
2016-03-11 20:37:50 +00:00
|
|
|
auto leftStart = min(start, left.count);
|
|
|
|
|
auto leftEnd = min(end, left.count);
|
|
|
|
|
auto rightStart = max(0, min(start - left.count, right.count));
|
|
|
|
|
auto rightEnd = max(0, min(end - left.count, right.count));
|
|
|
|
|
if (leftStart < left.count) {
|
2016-03-11 02:06:06 +00:00
|
|
|
left.remove(leftStart, leftEnd);
|
|
|
|
|
}
|
|
|
|
|
if (rightEnd > 0) {
|
|
|
|
|
right.remove(rightStart, rightEnd);
|
|
|
|
|
}
|
2016-03-11 20:37:50 +00:00
|
|
|
count = left.count + right.count;
|
2016-03-11 02:06:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
adjust();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void insert(ulong position, string value) {
|
|
|
|
|
if (this.value !is null) {
|
2016-03-11 20:37:50 +00:00
|
|
|
this.value = to!string(this.value.to!dstring[0 .. position] ~ value.to!dstring ~ this.value.to!dstring[position .. $]);
|
|
|
|
|
count = this.value.count;
|
2016-03-11 02:06:06 +00:00
|
|
|
} else {
|
2016-03-11 20:37:50 +00:00
|
|
|
if (position < left.count) {
|
2016-03-11 02:06:06 +00:00
|
|
|
left.insert(position, value);
|
2016-03-11 20:37:50 +00:00
|
|
|
count = left.count + right.count;
|
2016-03-11 02:06:06 +00:00
|
|
|
} else {
|
2016-03-11 20:37:50 +00:00
|
|
|
right.insert(position - left.count, value);
|
2016-03-11 02:06:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
adjust();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rebuild() {
|
|
|
|
|
if (value is null) {
|
|
|
|
|
value = left.toString() ~ right.toString();
|
|
|
|
|
adjust();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rebalance() {
|
|
|
|
|
if (value is null) {
|
2016-03-11 20:37:50 +00:00
|
|
|
if (left.count / right.count > rebalanceRatio ||
|
|
|
|
|
right.count / left.count > rebalanceRatio) {
|
2016-03-11 02:06:06 +00:00
|
|
|
rebuild();
|
|
|
|
|
} else {
|
|
|
|
|
left.rebalance();
|
|
|
|
|
right.rebalance();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-11 20:37:50 +00:00
|
|
|
string substring(ulong start, ulong end = count) {
|
2016-03-11 02:06:06 +00:00
|
|
|
if (value !is null) {
|
|
|
|
|
return value[start .. end];
|
|
|
|
|
} else {
|
2016-03-11 20:37:50 +00:00
|
|
|
auto leftStart = min(start, left.count);
|
|
|
|
|
auto leftEnd = min(end, left.count);
|
|
|
|
|
auto rightStart = max(0, min(start - left.count, right.count));
|
|
|
|
|
auto rightEnd = max(0, min(end - left.count, right.count));
|
2016-03-11 02:06:06 +00:00
|
|
|
|
|
|
|
|
if (leftStart != leftEnd) {
|
|
|
|
|
if (rightStart != rightEnd) {
|
|
|
|
|
return left.substring(leftStart, leftEnd) ~ right.substring(rightStart, rightEnd);
|
|
|
|
|
} else {
|
|
|
|
|
return left.substring(leftStart, leftEnd);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (rightStart != rightEnd) {
|
|
|
|
|
return right.substring(rightStart, rightEnd);
|
|
|
|
|
} else {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char charAt(ulong pos) {
|
|
|
|
|
return to!char(substring(pos, pos + 1));
|
|
|
|
|
}
|
|
|
|
|
}
|