Implement String opAssign

This commit is contained in:
Eugen Wissner 2017-05-01 12:58:37 +02:00
parent 64e0d666ed
commit e5c7edb72c
1 changed files with 222 additions and 11 deletions

View File

@ -15,6 +15,7 @@ module tanya.container.string;
import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching;
import std.range;
import std.traits;
import tanya.memory;
@ -204,6 +205,127 @@ struct ByCodeUnit(E)
}
}
/**
* Iterates $(D_PSYMBOL String) by UTF-8 code point.
*
* Params:
* E = Element type ($(D_KEYWORD char) or $(D_INLINECODE const(char))).
*/
struct ByCodePoint(E)
if (is(Unqual!E == char))
{
private E* begin, end;
private alias ContainerType = CopyConstness!(E, String);
private ContainerType* container;
invariant
{
assert(this.begin <= this.end);
assert(this.container !is null);
assert(this.begin >= this.container.data);
assert(this.end <= this.container.data + this.container.length);
}
private this(ref ContainerType container, E* begin, E* end) @trusted
in
{
assert(begin <= end);
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
{
this.container = &container;
this.begin = begin;
this.end = end;
}
@disable this();
@property ByCodePoint save()
{
return this;
}
@property bool empty() const
{
return this.begin == this.end;
}
@property dchar front() const
in
{
assert(!empty);
}
out (chr)
{
assert(chr < 0xd800 || chr > 0xdfff);
}
body
{
dchar chr;
ubyte units;
char mask;
const(char)* it = begin;
if (*it & 0x80)
{
mask = 0xe0;
for (units = 1; (*it & mask) != (mask << 1); ++units)
{
mask = (mask >> 1) | 0x80;
}
}
chr = *it++ ^ mask;
for (; units > 0; --units)
{
if (begin >= end)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
}
chr = (chr << 6) | (*it++ & 0x3f);
}
return chr;
}
void popFront()
in
{
assert(!empty);
}
body
{
if (*begin & 0x80)
{
char mask = 0xe0;
for (auto chr = *begin; (chr & mask) != (mask << 1); ++begin)
{
if (begin >= end)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
}
mask = (mask >> 1) | 0x80;
}
}
else
{
++begin;
}
}
ByCodePoint opIndex()
{
return typeof(return)(*this.container, this.begin, this.end);
}
ByCodePoint!(const E) opIndex() const
{
return typeof(return)(*this.container, this.begin, this.end);
}
}
/**
* UTF-8 string.
*/
@ -222,7 +344,7 @@ struct String
* Constructs the string from a stringish range.
*
* Params:
* R = String type.
* S = String type.
* str = Initial string.
* allocator = Allocator.
*
@ -230,10 +352,10 @@ struct String
*
* Precondition: $(D_INLINECODE allocator is null).
*/
this(R)(const R str, shared Allocator allocator = defaultAllocator)
if (!isInfinite!R
&& isInputRange!R
&& isSomeChar!(ElementEncodingType!R))
this(S)(const S str, shared Allocator allocator = defaultAllocator)
if (!isInfinite!S
&& isInputRange!S
&& isSomeChar!(ElementEncodingType!S))
{
this(allocator);
insertBack(str);
@ -265,13 +387,15 @@ struct String
* If $(D_PARAM init) is passed by reference, it will be copied.
*
* Params:
* S = Source string type.
* init = Source string.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator is null).
*/
this(String init, shared Allocator allocator = defaultAllocator)
this(S)(S init, shared Allocator allocator = defaultAllocator)
nothrow @trusted @nogc
if (is(S == String))
{
this(allocator);
if (allocator !is init.allocator)
@ -294,8 +418,9 @@ struct String
}
/// Ditto.
this(ref const String init, shared Allocator allocator = defaultAllocator)
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
nothrow @trusted @nogc
if (is(Unqual!S == String))
{
this(allocator);
reserve(init.length);
@ -322,7 +447,8 @@ struct String
* n = Number of characters to copy.
* chr = Character to fill the string with.
*/
this(C)(const size_t n, const C chr,
this(C)(const size_t n,
const C chr,
shared Allocator allocator = defaultAllocator) @trusted
if (isSomeChar!C)
{
@ -351,7 +477,8 @@ struct String
this.length_ += remaining;
}
private unittest
///
unittest
{
{
auto s = String(1, 'О');
@ -372,7 +499,7 @@ struct String
*/
~this() nothrow @trusted @nogc
{
allocator.deallocate(this.data[0 .. this.capacity_]);
allocator.resize(this.data[0 .. this.capacity_], 0);
}
private void write4Bytes(ref const dchar src)
@ -583,7 +710,7 @@ struct String
while (!range.empty)
{
reserve(length + 4);
reserve(length + wchar.sizeof * 2);
auto ret = insertWideChar(range.front);
if (ret > 0)
@ -829,6 +956,20 @@ struct String
assert(r.length == 8);
}
/**
* Returns: Forward range that iterates over the string by code points.
*/
ByCodePoint!char byCodePoint() pure nothrow @trusted @nogc
{
return typeof(return)(this, this.data, this.data + length);
}
/// Ditto.
ByCodePoint!(const char) byCodePoint() const pure nothrow @trusted @nogc
{
return typeof(return)(this, this.data, this.data + length);
}
/**
* Returns: $(D_KEYWORD true) if the vector is empty.
*/
@ -901,5 +1042,75 @@ struct String
assert(r.empty);
}
/**
* Assigns another string.
*
* If $(D_PARAM that) is passed by value, it won't be copied, but moved.
* This string will take the ownership over $(D_PARAM that)'s storage and
* the allocator.
*
* If $(D_PARAM that) is passed by reference, it will be copied.
*
* Params:
* S = Content type.
* that = The value should be assigned.
*
* Returns: $(D_KEYWORD this).
*/
ref String opAssign(S)(S that) nothrow @safe @nogc
if (is(S == String))
{
swap(this.data, that.data);
swap(this.length_, that.length_);
swap(this.capacity_, that.capacity_);
swap(this.allocator_, that.allocator_);
return this;
}
/// Ditto.
ref String opAssig(S)(ref S that) nothrow @trusted @nogc
if (is(Unqual!S == String))
{
reserve(that.length);
that.data[0 .. that.length].copy(this.data[0 .. that.length]);
this.length_ = that.length;
return this;
}
///
unittest
{
auto s = String("Черная, потом пропахшая выть!");
s = String("Как мне тебя не ласкать, не любить?");
}
/**
* Assigns a stringish range.
*
* Params:
* S = String type.
* that = Initial string.
*
* Returns: $(D_KEYWORD this).
*
* Throws: $(D_PSYMBOL UTFException).
*/
ref String opAssign(S)(S that) nothrow @safe @nogc
if (!isInfinite!S
&& isInputRange!S
&& isSomeChar!(ElementEncodingType!S))
{
this.length_ = 0;
insertBack(that);
return this;
}
///
unittest
{
auto s = String("Оловом светится лужная голь...");
s = "Грустная песня, ты - русская боль.";
}
mixin DefaultAllocator;
}