1 /* 2 * Kiss - A refined core library for D programming language. 3 * 4 * Copyright (C) 2015-2018 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs.cn 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module kiss.container.String; 13 14 import kiss.container.common; 15 import core.stdc.string : memcpy; 16 import std.traits; 17 import std.experimental.allocator; 18 import std.experimental.allocator.mallocator; 19 import Range = std.range.primitives; 20 21 alias IString(Alloc) = StringImpl!(char, Alloc); 22 alias IWString(Alloc) = StringImpl!(wchar, Alloc); 23 alias IDString(Alloc) = StringImpl!(dchar, Alloc); 24 alias String = IString!(Mallocator); 25 alias WString = IWString!(Mallocator); 26 alias DString = IDString!(Mallocator); 27 28 @trusted struct StringImpl(Char, Allocator) 29 if (is(Char == Unqual!Char) && isSomeChar!Char) 30 { 31 alias Data = ArrayCOWData!(Char, Allocator); 32 static if (StaticAlloc!Allocator) 33 { 34 this(const Char[] data) 35 { 36 assign(data); 37 } 38 } 39 else 40 { 41 @disable this(); 42 this(const Char[] data, Allocator alloc) 43 { 44 _alloc = alloc; 45 assign(data); 46 } 47 48 this(Allocator alloc) 49 { 50 _alloc = alloc; 51 } 52 } 53 54 this(this) 55 { 56 Data.inf(_data); 57 } 58 59 ~this() 60 { 61 Data.deInf(_alloc, _data); 62 } 63 64 typeof(this) opSlice() nothrow 65 { 66 return this; 67 } 68 69 typeof(this) opSlice(in size_t low, in size_t high) @trusted 70 in 71 { 72 assert(low <= high); 73 assert(high < _str.length); 74 } 75 body 76 { 77 auto rv = this; 78 rv._str = _str[low .. high]; 79 return rv; 80 } 81 82 Char opIndex(size_t index) const 83 in 84 { 85 assert(index < _str.length); 86 } 87 body 88 { 89 return _str[index]; 90 } 91 92 bool opEquals(S)(S other) const 93 if (is(S == Unqual!(typeof(this))) || is(S : const(Char)[])) 94 { 95 if (_str.length == other.length) 96 { 97 for (size_t i = 0; i < _str.length; ++i) 98 { 99 if (_str[i] != other[i]) 100 return false; 101 } 102 return true; 103 } 104 else 105 return false; 106 } 107 108 int opCmp(S)(S other) const 109 if (is(S == Unqual!(typeof(this))) || is(S : const(Char)[])) 110 { 111 auto a = cast(immutable(Char)[]) _str; 112 auto b = cast(immutable(Char)[]) other; 113 114 if (a < b) 115 { 116 return -1; 117 } 118 else if (a > b) 119 { 120 return 1; 121 } 122 else 123 { 124 return 0; 125 } 126 } 127 128 size_t opDollar() nothrow const 129 { 130 return _str.length; 131 } 132 133 mixin AllocDefine!Allocator; 134 135 void opAssign(S)(auto ref S n) 136 if (is(S == Unqual!(typeof(this))) || is(S : const(Char)[])) 137 { 138 static if (is(S : const Char[])) 139 { 140 assign(n); 141 } 142 else 143 { 144 if (n._data !is _data) 145 { 146 Data.deInf(_alloc, _data); 147 _data = n._data; 148 Data.inf(_data); 149 } 150 _str = n._str; 151 } 152 } 153 154 @property bool empty() const nothrow 155 { 156 return _str.length == 0; 157 } 158 159 @property size_t length() const nothrow 160 { 161 return _str.length; 162 } 163 164 int opApply(scope int delegate(Char) dg) 165 { 166 int result = 0; 167 168 for (size_t i = 0; i < _str.length; i++) 169 { 170 result = dg(_str[i]); 171 if (result) 172 break; 173 } 174 return result; 175 } 176 177 int opApply(scope int delegate(size_t, Char) dg) 178 { 179 int result = 0; 180 181 for (size_t i = 0; i < _str.length; i++) 182 { 183 result = dg(i, _str[i]); 184 if (result) 185 break; 186 } 187 return result; 188 } 189 190 static if (!is(Unqual!Char == dchar)) 191 { 192 int opApply(scope int delegate(dchar) dg) 193 { 194 int result = 0; 195 immutable(Char)[] str = cast(immutable(Char)[]) _str; 196 while (!Range.empty(str)) 197 { 198 result = dg(Range.front(str)); 199 if (result) 200 break; 201 Range.popFront(str); 202 } 203 return result; 204 } 205 } 206 207 @property immutable(Char)[] idup() const 208 { 209 return _str.idup; 210 } 211 212 @property typeof(this) dup() 213 { 214 typeof(this) ret = this; 215 if (this._data !is null) 216 ret.doCOW(0); 217 return ret; 218 } 219 220 @property auto front() const 221 in 222 { 223 assert(!this.empty); 224 } 225 body 226 { 227 return Range.front(_str); 228 } 229 230 @property auto back() const 231 in 232 { 233 assert(!this.empty); 234 } 235 body 236 { 237 return Range.back(_str); 238 } 239 240 @property const(Char)* ptr() const 241 { 242 return _str.ptr; 243 } 244 245 static if (is(Char == char)) 246 { 247 @property const(char)* cstr() 248 { 249 if (_str.length == 0) 250 { 251 return null; 252 } 253 else 254 { 255 doCOW(1); 256 char* ptr = cast(char*) _str.ptr; 257 ptr[_str.length] = '\0'; 258 return ptr; 259 } 260 } 261 } 262 263 immutable(Char)[] opCast(T)() nothrow if (is(T == immutable(Char)[])) 264 { 265 return stdString(); 266 } 267 268 @property immutable(Char)[] stdString() nothrow 269 { 270 return cast(immutable(Char)[]) _str; 271 } 272 273 typeof(this) opBinary(string op, S)(auto ref S other) 274 if ((is(S == Unqual!(typeof(this))) || is(S : const Char[])) && op == "~") 275 { 276 typeof(this) ret = this; 277 ret ~= other; 278 return ret; 279 } 280 281 void opOpAssign(string op, S)(auto ref S other) 282 if ((is(S == Unqual!(typeof(this))) || is(S : const Char[]) || is(Unqual!S == Char)) 283 && op == "~") 284 { 285 static if (is(Unqual!S == Char)) 286 { 287 const size_t tmpLength = 1; 288 } 289 else 290 { 291 if (other.length == 0) 292 return; 293 const size_t tmpLength = other.length; 294 } 295 doCOW(tmpLength); 296 Char* basePtr = _data.data.ptr + baseLength(); 297 Char* tptr = basePtr + _str.length; 298 static if (is(Unqual!S == Char)) 299 { 300 tptr[0] = other; 301 } 302 else 303 { 304 memcpy(tptr, other.ptr, (tmpLength * Char.sizeof)); 305 } 306 size_t len = _str.length + tmpLength; 307 _str = basePtr[0 .. len]; 308 } 309 310 private: 311 void assign(const Char[] input) 312 { 313 if (input.length == 0) 314 { 315 Data.deInf(_alloc, _data); 316 _str = null; 317 _data = null; 318 return; 319 } 320 auto data = buildData(); 321 Data.deInf(_alloc, data); 322 _data.reserve(input.length); 323 size_t len = input.length * Char.sizeof; 324 memcpy(_data.data.ptr, input.ptr, len); 325 _str = _data.data[0 .. input.length]; 326 } 327 328 Data* buildData() 329 { 330 Data* data = null; 331 if (_data !is null && _data.count > 1) 332 { 333 data = _data; 334 _data = null; 335 } 336 if (_data is null) 337 { 338 _data = Data.allocate(_alloc); 339 static if (!StaticAlloc!Allocator) 340 _data._alloc = _alloc; 341 } 342 return data; 343 } 344 345 size_t baseLength() 346 { 347 if ((_str.length == 0) || (_str.ptr is _data.data.ptr)) 348 return 0; 349 else 350 return cast(size_t)(_str.ptr - _data.data.ptr); 351 } 352 353 size_t extenSize(size_t size) 354 { 355 if (size > 0) 356 size = size > 128 ? size + ((size / 3) * 2) : size * 2; 357 else 358 size = 32; 359 return size; 360 } 361 362 void doCOW(size_t tmpLength = 0) 363 { 364 auto data = buildData(); 365 if (data !is null) 366 { 367 _data.reserve(extenSize(_str.length + tmpLength)); 368 if (_str.length > 0) 369 { 370 memcpy(_data.data.ptr, _str.ptr, (_str.length * Char.sizeof)); 371 _str = _data.data[0 .. _str.length]; 372 } 373 Data.deInf(_alloc, data); 374 } 375 else if (tmpLength > 0) 376 { 377 size_t blen = baseLength(); 378 if (_data.reserve(extenSize(blen + _str.length + tmpLength))) 379 _str = _data.data[blen .. (blen + _str.length)]; 380 } 381 } 382 383 private: 384 Data* _data; 385 Char[] _str; 386 } 387 388 version (unittest) : void testFunc(T, size_t Buf)() 389 { 390 import std.conv : to; 391 import std.stdio : writeln; 392 import std.array : empty, popBack, popFront; 393 import std.range.primitives; 394 import std.format : format; 395 396 auto strs = [ 397 "", "ABC", "HellWorld", "", "Foobar", 398 "HellWorldHellWorldHellWorldHellWorldHellWorldHellWorldHellWorldHellWorld", 399 "ABCD", "Hello", "HellWorldHellWorld", "ölleä", 400 "hello\U00010143\u0100\U00010143", "£$€¥", "öhelloöö" 401 ]; 402 403 foreach (strL; strs) 404 { 405 auto str = to!(immutable(T)[])(strL); 406 auto s = String(str); 407 408 assert(s.length == str.length); 409 assert(s.empty == str.empty); 410 assert(s == str); 411 412 auto istr = s.idup(); 413 assert(str == istr); 414 415 foreach (it; strs) 416 { 417 auto cmpS = to!(immutable(T)[])(it); 418 auto itStr = String(cmpS); 419 420 if (cmpS == str) 421 { 422 assert(s == cmpS); 423 assert(s == itStr); 424 assert(s <= itStr); 425 } 426 else 427 { 428 assert(s != cmpS); 429 assert(s != itStr); 430 } 431 } 432 433 if (s.empty) 434 { // if str is empty we do not need to test access 435 continue; //methods 436 } 437 438 assert(s.front == str.front, to!string(s.front)); 439 assert(s.back == str.back); 440 assert(s[0] == str[0], to!string(s[0]) ~ " " ~ to!string(str.front)); 441 for (size_t i = 0; i < str.length; ++i) 442 { 443 assert(str[i] == s[i]); 444 } 445 446 for (size_t it = 0; it < str.length; ++it) 447 { 448 for (size_t jt = it; jt < str.length; ++jt) 449 { 450 auto ss = s[it .. jt]; 451 auto strc = str[it .. jt]; 452 453 assert(ss.length == strc.length); 454 assert(ss.empty == strc.empty); 455 456 for (size_t k = 0; k < ss.length; ++k) 457 { 458 assert(ss[k] == strc[k], format("it %s jt %s k %s ss[k] %s strc[k] %s str %s", 459 it, jt, k, ss[k], strc[k], str)); 460 } 461 } 462 } 463 464 String t; 465 assert(t.empty); 466 467 t = str; 468 assert(s == t); 469 assert(!t.empty); 470 assert(t.front == str.front, to!string(t.front)); 471 assert(t.back == str.back); 472 assert(t[0] == str[0]); 473 assert(t.length == str.length); 474 475 auto tdup = t.dup; 476 assert(!tdup.empty); 477 assert(tdup.front == str.front, to!string(tdup.front)); 478 assert(tdup.back == str.back); 479 assert(tdup[0] == str[0]); 480 assert(tdup.length == str.length); 481 482 istr = t.idup(); 483 assert(str == istr); 484 485 foreach (it; strs) 486 { 487 auto joinStr = to!(immutable(T)[])(it); 488 auto itStr = String(joinStr); 489 auto compareStr = str ~ joinStr; 490 String tdup22 = tdup; 491 String tdup23 = tdup; 492 tdup22 ~= (joinStr); 493 tdup23 ~= itStr; 494 495 auto t2dup = tdup ~ joinStr; 496 auto t2dup2 = tdup ~ itStr; 497 498 assert(t2dup.length == compareStr.length); 499 assert(t2dup2.length == compareStr.length); 500 assert(tdup22.length == compareStr.length); 501 assert(tdup23.length == compareStr.length); 502 503 assert(t2dup == compareStr); 504 assert(t2dup2 == compareStr); 505 assert(tdup22 == compareStr); 506 assert(tdup23 == compareStr); 507 tdup22 ~= 's'; 508 tdup23 ~= 's'; 509 assert(tdup22 == tdup23); 510 assert(tdup22 != compareStr); 511 assert(tdup23 != compareStr); 512 } 513 } 514 } 515 516 unittest 517 { 518 testFunc!(char, 3)(); 519 }