1 module awebview.wrapper.webstring; 2 3 import std.algorithm; 4 import std.traits; 5 import std.array; 6 7 import awebview.wrapper.cpp; 8 import awebview.wrapper.weakref; 9 10 import carbon.nonametype; 11 import carbon.memory; 12 import carbon.templates; 13 14 15 enum bool hasCppWebString(T) = is(typeof((T obj){ 16 awebview.wrapper.cpp.WebString cpp = obj.cppObj; 17 })); 18 19 20 21 /** 22 This type manages $(D WebStringCpp) by reference-counting. 23 */ 24 struct WebString 25 { 26 alias PayloadType(This) = typeof(This.init.payload); 27 28 /** 29 Return reference to $(D WebStringCpp) that is managed by reference-counting in $(D WebString). 30 31 If $(D this) is not initialized yet, 32 $(D WebStringCpp) is initialized by $(awebview.wrapper.cpp.WebString.init). 33 34 If $(D this) is $(D const) or $(D immutable) and $(D this) is not initialized yet, 35 this method returns a reference to global empty instance of immutable(WebStringCpp). 36 */ 37 //@property 38 //ref WebStringCpp payload() nothrow @nogc 39 //{ 40 // if(!_instance.refCountedStore.isInitialized) 41 // __ctor(cast(awebview.wrapper.cpp.WebString)null); 42 43 // return _instance.refCountedPayload; 44 //} 45 46 ///// ditto 47 //@property 48 //ref inout(WebStringCpp) payload(this T)() inout nothrow @nogc 49 //if(is(T == const) || is(T == immutable)) 50 //{ 51 // if(this._instance.refCountedStore.isInitialized) 52 // return *cast(typeof(return)*)&(this._instance.refCountedPayload()); 53 // else 54 // return *cast(typeof(return)*)WebStringCpp._emptyInstance; 55 //} 56 @property 57 auto ref payload(this T)() nothrow @nogc @trusted 58 { 59 static if(is(T == WebString)) 60 { 61 if(!_instance.refCountedStore.isInitialized) 62 __ctor(cast(awebview.wrapper.cpp.WebString)null); 63 64 return _instance.refCountedPayload; 65 } 66 else static if(is(T == const) || is(T == immutable) || is(T == inout)) 67 { 68 alias R = typeof(this._instance.refCountedPayload()); 69 70 if(this._instance.refCountedStore.isInitialized) 71 return this._instance.refCountedPayload; 72 else 73 return *cast(R*)WebStringCpp._emptyInstance; 74 } 75 } 76 77 unittest { 78 WebString ws; 79 assert(!ws._instance.refCountedStore.isInitialized); 80 81 // call this.payload 82 assert(ws.payload.cppField.instance_ !is null); 83 assert(ws._instance.refCountedStore.isInitialized); 84 } 85 86 unittest { 87 const WebString cws; 88 assert(!cws._instance.refCountedStore.isInitialized); 89 90 assert(&(cws.payload()) == WebStringCpp._emptyInstance); 91 92 WebString ws = "foo"; 93 const cws2 = ws; 94 assert(cws2._instance.refCountedStore.isInitialized); 95 assert(&(cws2.payload()) == &(ws.payload())); 96 } 97 98 99 /** 100 Construct $(D WebString) by copying. 101 */ 102 this(ref WebStringCpp ws) nothrow @nogc 103 { 104 _instance = Instance(RefCountedNoGC!WebStringCpp(ws.cppObj)); 105 } 106 107 108 /// ditto 109 this(in awebview.wrapper.cpp.WebString cppObj) nothrow @nogc 110 { 111 _instance = Instance(RefCountedNoGC!WebStringCpp(cppObj)); 112 } 113 114 115 /// ditto 116 this(Char)(in Char[] str) nothrow @nogc 117 { 118 _instance = Instance(RefCountedNoGC!WebStringCpp(str)); 119 } 120 121 unittest { 122 WebStringCpp wcpp = "foobar"; 123 WebString ws1 = wcpp; // ref WebStringCpp 124 assert(ws1.data == "foobar"w); 125 assert(ws1.cppObj !is wcpp.cppObj); // constructed by copying. 126 127 WebString ws2 = wcpp.cppObj; // CppObj 128 assert(ws2.data == "foobar"w); 129 assert(ws2.cppObj !is wcpp.cppObj); // constructed by copying. 130 131 WebString ws3 = wcpp.data; // const(wchar)[] 132 assert(ws3.data == "foobar"w); 133 assert(ws3.cppObj !is wcpp.cppObj); // constructed by copying. 134 } 135 136 137 /** 138 Return a reference to an instance of the C++'s class $(D Awesomium::WebString). 139 */ 140 @property 141 awebview.wrapper.cpp.WebString 142 cppObj() nothrow @nogc 143 { return payload.cppObj; } 144 145 146 /// ditto 147 @property 148 inout(awebview.wrapper.cpp.WebString) 149 cppObj(this T)() inout pure nothrow @nogc 150 if(is(T == const) || is(T == immutable)) 151 { return (cast(T*)&this).payload.cppObj; } 152 153 unittest { 154 WebString ws = "foobar"; 155 assert(ws.cppObj is ws.payload.cppObj); 156 157 const cws = ws; 158 assert(ws.cppObj is cws.cppObj); 159 assert(cws.cppObj is cws.payload.cppObj); 160 161 WebString e; 162 const WebString ce = e; 163 assert(ce.cppObj is WebStringCpp._emptyInstance.cppObj); 164 } 165 166 167 /** 168 Return reference to $(D WebStringCpp) that is managed by reference-counting in $(D WebString). 169 */ 170 @property 171 WeakRef!(PayloadType!This) weakRef(this This)() inout nothrow @nogc 172 { 173 alias WSCpp = PayloadType!This; 174 return refP!WSCpp(cast(WSCpp*)&(cast(This*)&this).payload()); 175 } 176 177 unittest { 178 179 } 180 181 182 @property 183 WebString dup() const @nogc 184 { 185 auto cppObj = this.payload.cppObj; 186 return WebString(cppObj); 187 } 188 189 190 void opAssign(WebString ws) @nogc 191 { _instance = ws._instance; } 192 193 void opAssign(Char)(const(Char)[] str) @nogc 194 { this = WebString(str); } 195 196 void opAssign(ref const WebStringCpp wrefstr) @nogc 197 { this = WebString(wrefstr.cppObj); } 198 199 void opAssign(in awebview.wrapper.cpp.WebString cppObj) @nogc 200 { this = WebString(cppObj); } 201 202 203 204 const(wchar)* ptr() const nothrow @property @nogc 205 { return payload.ptr; } 206 207 208 alias opDollar = length; 209 210 size_t length() const nothrow @property @nogc 211 { return payload.length; } 212 213 214 bool empty() const nothrow @property @nogc 215 { return payload.empty; } 216 217 218 const(wchar)[] data() const nothrow @property @nogc 219 { return this.ptr[0 .. this.length]; } 220 221 222 const(wchar)[] opSlice() const nothrow @property @nogc 223 { return this.data; } 224 225 226 void opOpAssign(string op : "~")(const WebString ws) nothrow @nogc 227 { 228 this ~= ws.cppObj; 229 } 230 231 232 void opOpAssign(string op : "~")(ref const WebStringCpp ws) nothrow @nogc 233 { 234 this ~= ws.cppObj; 235 } 236 237 238 void opOpAssign(string op : "~", Char)(const(Char)[] str) nothrow @nogc 239 { 240 if(!this._instance.refCountedStore.isInitialized){ 241 __ctor(ws.cppObj); 242 return; 243 } 244 245 if(this._instance.refCountedStore.refCount > 1) 246 this = this.dup; 247 248 this.payload ~= str; 249 } 250 251 252 void opOpAssign(string op : "~")(in awebview.wrapper.cpp.WebString cppObj) 253 { 254 if(!this._instance.refCountedStore.isInitialized){ 255 __ctor(ws.cppObj); 256 return; 257 } 258 259 if(this._instance.refCountedStore.refCount > 1) 260 this = this.dup; 261 262 this.payload ~= cppObj; 263 } 264 265 266 void clear() 267 { 268 _instance.__xdtor(); 269 assert(!_instance.refCountedStore.isInitialized); 270 } 271 272 273 bool opEquals(const WebString rhs) const nothrow @nogc 274 { return this.payload.opEquals(rhs.payload); } 275 276 bool opEquals(ref const WebString rhs) const nothrow @nogc 277 { return this.payload.opEquals(rhs.payload); } 278 279 bool opEquals(ref const WebStringCpp rhs) const nothrow @nogc 280 { return this.payload.opEquals(rhs); } 281 282 bool opEquals(Char)(const Char[] rhs) const 283 { return this.payload.opEquals(rhs); } 284 285 int opCmp(const WebString rhs) const nothrow @nogc 286 { return this.payload.opCmp(rhs.payload); } 287 288 int opCmp(ref const WebString rhs) const nothrow @nogc 289 { return this.payload.opCmp(rhs.payload); } 290 291 int opCmp(ref const WebStringCpp rhs) const nothrow @nogc 292 { return this.payload.opCmp(rhs); } 293 294 int opCmp(Char)(const Char[] rhs) const 295 if(isSomeChar!Char) 296 { return this.payload.opCmp(rhs); } 297 298 299 const(wchar)[] toString() const nothrow @nogc 300 { return this.data; } 301 302 private: 303 /// RefCountedNoGC!WebStringCpp _instance; 304 Instance _instance; 305 306 alias Instance = typeof(_dummyTypeCreate()); 307 308 static auto _dummyTypeCreate() 309 { 310 static struct R 311 { 312 RefCountedNoGC!WebStringCpp obj; 313 alias obj this; 314 } 315 316 return R(); 317 } 318 } 319 320 321 322 struct WebStringCpp 323 { 324 alias data this; // const(wchar)[] data() const @property; 325 326 this(const(char)[] str) nothrow @nogc 327 in{ 328 assert(str.length <= uint.max); 329 } 330 body{ 331 WebStringMember.ctor(this.cppObj!false); 332 WebStringMember.CreateFromUTF8(str.ptr, cast(uint)str.length, this.cppObj!false); 333 } 334 335 unittest 336 { 337 WebStringCpp ws = "foobar"; 338 assert(ws.data == "foobar"); 339 } 340 341 342 this(const(wchar)[] str) nothrow @nogc 343 in{ 344 assert(str.length <= uint.max); 345 } 346 body{ 347 WebStringMember.ctor(this.cppObj!false, cast(const(ushort)*)str.ptr, cast(uint)str.length); 348 } 349 350 351 this(const awebview.wrapper.cpp.WebString ws) nothrow @nogc 352 { 353 if(ws !is null) 354 WebStringMember.ctor(this.cppObj!false, ws); 355 else 356 WebStringMember.ctor(this.cppObj!false); 357 } 358 359 360 this(this) nothrow @nogc 361 { 362 if(this.cppField.instance_ !is null){ 363 typeof(_field) f; 364 WebStringCpp* vp = cast(WebStringCpp*)cast(void*)&f; 365 WebStringMember.ctor(vp.cppObj!false, this.cppObj); 366 this._field = vp._field; 367 } 368 } 369 370 371 ~this() nothrow @nogc 372 { 373 if(this.cppField.instance_ !is null) 374 WebStringMember.dtor(this.cppObj!false); 375 } 376 377 378 private 379 ref inout(awebview.wrapper.cpp.WebString.Field) 380 cppField() inout pure nothrow @trusted @property @nogc 381 { return *cast(typeof(return)*)_field.ptr; } 382 383 384 awebview.wrapper.cpp.WebString 385 cppObj(bool withInitialize = true)() nothrow @trusted @property @nogc 386 { 387 typeof(return) o = cast(typeof(return))cast(void*)_field.ptr; 388 389 static if(withInitialize) 390 if(cppField.instance_ is null) 391 WebStringMember.ctor(o); 392 393 return o; 394 } 395 396 397 inout(awebview.wrapper.cpp.WebString) 398 cppObj() inout pure nothrow @trusted @property @nogc 399 { 400 if(cppField.instance_ is null) 401 return cast(typeof(return))cast(inout(void)*)(_emptyInstance._field.ptr); 402 else 403 return cast(typeof(return))cast(inout(void)*)_field.ptr; 404 } 405 406 407 WeakRef!WebStringCpp weakRef(this T)() inout pure nothrow @trusted @property @nogc 408 { 409 return refP!T(cast(T*)&this); 410 } 411 412 413 void opAssign()(auto ref const WebStringCpp str) nothrow @nogc 414 { 415 WebStringMember.opAssign(this.cppObj, str.cppObj); 416 } 417 418 419 void opAssign(Char)(const(Char)[] c) nothrow @nogc 420 if(is(Char == char) || is(Char == wchar)) 421 { 422 this.clear(); 423 this ~= c; 424 } 425 426 427 const(wchar)* ptr() const nothrow @property @nogc 428 { 429 return cast(const(wchar)*)WebStringMember.data(this.cppObj); 430 } 431 432 433 alias opDollar = length; 434 435 size_t length() const nothrow @property @nogc 436 { 437 return WebStringMember.length(this.cppObj); 438 } 439 440 441 bool empty() const nothrow @property @nogc 442 { 443 return WebStringMember.IsEmpty(this.cppObj); 444 } 445 446 447 const(wchar)[] data() const nothrow @property @nogc 448 { 449 return this.ptr[0 .. this.length]; 450 } 451 452 453 const(wchar)[] opSlice() const nothrow @property @nogc 454 { 455 return this.data; 456 } 457 458 459 const(wchar)[] opSlice(size_t a, size_t b) const nothrow @property @nogc 460 { 461 return this.data[a .. b]; 462 } 463 464 465 void opOpAssign(string op : "~")(auto ref const WebStringCpp s) nothrow @nogc 466 { 467 WebStringMember.Append(this.cppObj, s.cppObj); 468 } 469 470 471 void opOpAssign(string op : "~", Char)(const(Char)[] str) nothrow @nogc 472 if(is(Char == char) || is(Char == wchar)) 473 { 474 auto ws = WebStringCpp(str); 475 this.opOpAssign!"~"(ws); 476 } 477 478 479 void opOpAssign(string op : "~")(awebview.wrapper.cpp.WebString cppObj) nothrow @nogc 480 { 481 WebStringMember.Append(this.cppObj, cppObj); 482 } 483 484 485 void clear() nothrow @nogc 486 { 487 WebStringMember.Clear(this.cppObj); 488 } 489 490 491 bool opEquals()(auto ref const WebStringCpp rhs) const nothrow @nogc 492 { 493 return WebStringMember.opEquals(this.cppObj, rhs.cppObj); 494 } 495 496 497 bool opEquals(Char)(const Char[] rhs) const 498 if(isSomeChar!Char) 499 { 500 static if(is(Char == wchar)) 501 return this.data == rhs; 502 else 503 return opCmp(rhs) == 0; 504 } 505 506 507 int opCmp()(auto ref const WebStringCpp rhs) const 508 { 509 return WebStringMember.opCmp(this.cppObj, rhs.cppObj); 510 } 511 512 513 int opCmp(Char)(const Char[] rhs) const 514 if(isSomeChar!Char) 515 { 516 return cmp(this.data, rhs); 517 } 518 519 520 const(wchar)[] toString() const nothrow @nogc 521 { 522 return this.data; 523 } 524 525 526 static 527 auto weakRef(HandleWS)(HandleWS ws) @trusted 528 if(is(HandleWS : const(awebview.wrapper.cpp.WebString))) 529 { 530 static if(is(HandleWS == awebview.wrapper.cpp.WebString)) 531 WebStringCpp* wsp = cast(WebStringCpp*)cast(void*)ws; 532 else static if(is(HandleWS == const(awebview.wrapper.cpp.WebString))) 533 const(WebStringCpp)* wsp = cast(const(WebStringCpp)*)cast(const(void)*)ws; 534 else 535 immutable(WebStringCpp)* wsp = cast(immutable(WebStringCpp)*)cast(immutable(void)*)ws; 536 537 return refP(wsp); 538 } 539 540 541 private: 542 ubyte[awebview.wrapper.cpp.WebString.Field.sizeof] _field; 543 544 static shared immutable(WebStringCpp*) _emptyInstance; 545 546 shared static this() 547 { 548 WebStringCpp* s = new WebStringCpp; 549 WebStringMember.ctor(s.cppObj!false); 550 551 _emptyInstance = cast(immutable)s; // s is unique 552 } 553 } 554 555 556 unittest { 557 WebStringCpp empty_; 558 assert(empty_.length == 0); 559 assert(empty_.empty); 560 //assert(empty_.ptr is null); 561 assert(empty_ == empty_); 562 assert(empty_ == ""); 563 assert(empty_ == ""w); 564 assert(empty_ == ""d); 565 empty_ ~= "foobar"; 566 assert(empty_ == "foobar"); 567 empty_ ~= "ああああ"; 568 assert(empty_ == "foobarああああ"); 569 } 570 571 unittest { 572 import std.conv : to; 573 574 wstring src = "オーサミウムオーサミウムおーさみうむ"; 575 WebStringCpp str = src; 576 assert(str.length == src.length); 577 assert(!str.empty); 578 assert(str.ptr != src.ptr); // copy 579 assert(str == src); 580 assert(str.data == src); 581 assert(str == to!string(src)); 582 assert(str[0 .. 3] == src[0 .. 3]); 583 assert(str[0 .. $] == src[0 .. $]); 584 585 WebStringCpp str2 = "foobar"w; 586 assert(str2.length == 6); 587 assert(!str2.empty); 588 assert(str2[0 .. 3] == "foo"); 589 assert(str2[3 .. $] == "bar"); 590 assert(str2.front == 'f'); 591 592 str2 = str; 593 assert(str2 == str); 594 595 WebStringCpp str3 = str; 596 assert(str3.ptr != str.ptr); 597 assert(str3 == str); 598 } 599 600 unittest { 601 WebStringCpp ws; 602 auto r1 = ws.weakRef; 603 auto r2 = ws.cppObj.weakRef!WebStringCpp; 604 605 assert(ws.cppObj is r1.cppObj); 606 assert(r1.cppObj is r2.cppObj); 607 608 ws = "foobar"; 609 assert(ws.cppObj is r1.cppObj); 610 assert(ws.cppObj is r2.cppObj); 611 612 r1 ~= "foobar"; 613 assert(ws == "foobarfoobar"); 614 615 ws = WebStringCpp("f"); 616 assert(ws.cppObj is r1.cppObj); 617 assert(ws.cppObj is r2.cppObj); 618 619 ws.clear(); 620 assert(ws.cppObj is r1.cppObj); 621 assert(ws.cppObj is r2.cppObj); 622 }