├── .gitignore ├── CString ├── ArrayPool.cs ├── CString.cs ├── ExtensionLibs.cs ├── FreeList.cs ├── IStringBlock.cs ├── NumberFormatercs.cs ├── Properties │ └── AssemblyInfo.cs ├── StringPool.cs └── bin │ ├── Debug │ ├── CString.dll │ ├── CString.pdb │ ├── UnityEngine.dll │ └── UnityEngine.xml │ └── Release │ ├── CString.dll │ ├── CString.pdb │ ├── UnityEngine.dll │ └── UnityEngine.xml ├── LICENSE ├── README.md └── UnitTest ├── Assets ├── CStringTest.cs ├── CStringTest.cs.meta ├── Plugins.meta ├── Plugins │ ├── CString.dll │ └── CString.dll.meta ├── UnitTest.unity └── UnitTest.unity.meta └── ProjectSettings ├── AudioManager.asset ├── ClusterInputManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── ProjectVersion.txt ├── QualitySettings.asset ├── TagManager.asset ├── TimeManager.asset ├── UnityAdsSettings.asset └── UnityConnectSettings.asset /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Autogenerated VS/MD solution and project files 9 | ExportedObj/ 10 | *.csproj 11 | *.unityproj 12 | *.sln 13 | *.suo 14 | *.tmp 15 | *.user 16 | *.userprefs 17 | *.pidb 18 | *.booproj 19 | *.svd 20 | 21 | 22 | # Unity3D generated meta files 23 | *.pidb.meta 24 | 25 | # Unity3D Generated File On Crash Reports 26 | sysinfo.txt 27 | 28 | # Builds 29 | *.apk 30 | *.unitypackage 31 | /UnitTest/Library 32 | /CString/obj 33 | -------------------------------------------------------------------------------- /CString/ArrayPool.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2017 topameng(topameng@qq.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | public class ArrayPool 26 | { 27 | public const int MAX_COUNT = 16; 28 | Queue[] pool = new Queue[MAX_COUNT]; 29 | 30 | public ArrayPool() 31 | { 32 | for (int i = 0; i < MAX_COUNT; i++) 33 | { 34 | pool[i] = new Queue(); 35 | } 36 | } 37 | 38 | public int NextPowerOfTwo(int v) 39 | { 40 | v -= 1; 41 | v |= v >> 16; 42 | v |= v >> 8; 43 | v |= v >> 4; 44 | v |= v >> 2; 45 | v |= v >> 1; 46 | return v + 1; 47 | } 48 | 49 | public T[] Alloc(int n) 50 | { 51 | int size = NextPowerOfTwo(n); 52 | int pos = GetSlot(size); 53 | 54 | if (pos >= 0 && pos < MAX_COUNT) 55 | { 56 | Queue queue = pool[pos]; 57 | int count = queue.Count; 58 | 59 | if (count > 0) 60 | { 61 | return queue.Dequeue(); 62 | } 63 | 64 | return new T[size]; 65 | } 66 | 67 | return new T[n]; 68 | } 69 | 70 | public void Collect(T[] buffer) 71 | { 72 | if (buffer == null) return; 73 | int pos = GetSlot(buffer.Length); 74 | 75 | if (pos >= 0 && pos < MAX_COUNT) 76 | { 77 | Queue queue = pool[pos]; 78 | queue.Enqueue(buffer); 79 | } 80 | } 81 | 82 | int GetSlot(int value) 83 | { 84 | int len = 0; 85 | 86 | while (value > 0) 87 | { 88 | ++len; 89 | value >>= 1; 90 | } 91 | 92 | return len; 93 | } 94 | } -------------------------------------------------------------------------------- /CString/CString.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2017 topameng(topameng@qq.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | using System; 23 | using System.Text; 24 | using System.Collections.Generic; 25 | using System.Runtime.CompilerServices; 26 | using System.Runtime.InteropServices; 27 | using System.Globalization; 28 | using System.IO; 29 | 30 | [Serializable] 31 | public class CString : IDisposable 32 | { 33 | const char DEFAULT_ALLOC_CHAR = (char)0xCCCC; 34 | const int DEFAULT_CAPACITY = 256; 35 | 36 | static ArrayPool pool = new ArrayPool(); 37 | static Queue queue = new Queue(); 38 | static Queue blocks = new Queue(); 39 | static Stack stack = new Stack(); 40 | 41 | static CStringBlock currentBlock = null; 42 | static string NewLine = Environment.NewLine; 43 | 44 | [NonSerialized] 45 | char[] _buffer; 46 | [NonSerialized] 47 | int length = 0; 48 | 49 | bool beDisposed = false; 50 | 51 | internal class CStringBlock : IStringBlock 52 | { 53 | List list; 54 | bool beDisposed = false; 55 | 56 | public CStringBlock() 57 | { 58 | list = new List(); 59 | } 60 | 61 | public void Init() 62 | { 63 | beDisposed = false; 64 | } 65 | 66 | public void Push(CString str) 67 | { 68 | list.Add(str); 69 | } 70 | 71 | public bool Remove(CString str) 72 | { 73 | return list.Remove(str); 74 | } 75 | 76 | public void Dispose() 77 | { 78 | if (beDisposed) 79 | { 80 | return; 81 | } 82 | 83 | if (this != currentBlock) 84 | { 85 | throw new Exception("dispose in it's own block"); 86 | } 87 | 88 | for (int i = 0; i < list.Count; i++) 89 | { 90 | list[i].Dispose(); 91 | } 92 | 93 | list.Clear(); 94 | blocks.Enqueue(this); 95 | stack.Pop(); 96 | currentBlock = stack.Count > 0 ? stack.Peek() : null; 97 | beDisposed = true; 98 | } 99 | } 100 | 101 | private static readonly char[] WhiteChars = 102 | { 103 | (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD, 104 | (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029, 105 | (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004, 106 | (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B, 107 | (char) 0x3000, (char) 0xFEFF, 108 | }; 109 | 110 | public int Capacity 111 | { 112 | get 113 | { 114 | return _buffer.Length; 115 | } 116 | } 117 | 118 | public CString(int count) 119 | { 120 | if (currentBlock != null) 121 | { 122 | _buffer = pool.Alloc(count); 123 | currentBlock.Push(this); 124 | } 125 | else 126 | { 127 | _buffer = new char[pool.NextPowerOfTwo(count)]; 128 | } 129 | 130 | ClearBuffer(_buffer); 131 | } 132 | 133 | public CString(string str) 134 | { 135 | if (currentBlock != null) 136 | { 137 | _buffer = pool.Alloc(str.Length); 138 | currentBlock.Push(this); 139 | } 140 | else 141 | { 142 | _buffer = new char[pool.NextPowerOfTwo(str.Length)]; 143 | } 144 | 145 | CopyFromString(str); 146 | } 147 | 148 | public int EnsureCapacity(int capacity) 149 | { 150 | if (capacity < 0) 151 | { 152 | throw new ArgumentOutOfRangeException("Capacity must be greater than 0."); 153 | } 154 | 155 | if (capacity <= _buffer.Length) 156 | { 157 | return _buffer.Length; 158 | } 159 | 160 | char[] tmp = pool.Alloc(capacity); 161 | CharCopy(tmp, _buffer, length); 162 | pool.Collect(_buffer); 163 | _buffer = tmp; 164 | return capacity; 165 | } 166 | 167 | CString() 168 | { 169 | _buffer = null; 170 | length = 0; 171 | } 172 | 173 | static public CString Alloc(int size) 174 | { 175 | CString str = null; 176 | 177 | if (queue.Count > 0 && currentBlock != null) 178 | { 179 | str = queue.Dequeue(); 180 | } 181 | else 182 | { 183 | str = new CString(); 184 | } 185 | 186 | if (currentBlock != null) 187 | { 188 | str._buffer = pool.Alloc(size); 189 | currentBlock.Push(str); 190 | } 191 | else 192 | { 193 | str._buffer = new char[pool.NextPowerOfTwo(size)]; 194 | } 195 | 196 | str.beDisposed = false; 197 | str.length = 0; 198 | return str; 199 | } 200 | 201 | public void Dispose() 202 | { 203 | if (beDisposed) 204 | { 205 | return; 206 | } 207 | 208 | beDisposed = true; 209 | 210 | if (currentBlock != null) 211 | { 212 | pool.Collect(_buffer); 213 | queue.Enqueue(this); 214 | } 215 | 216 | _buffer = null; 217 | length = 0; 218 | } 219 | 220 | static public IStringBlock Block() 221 | { 222 | CStringBlock cb = null; 223 | 224 | if (blocks.Count != 0) 225 | { 226 | cb = blocks.Dequeue(); 227 | } 228 | else 229 | { 230 | cb = new CStringBlock(); 231 | } 232 | 233 | cb.Init(); 234 | stack.Push(cb); 235 | currentBlock = cb; 236 | return cb; 237 | } 238 | 239 | static public bool IsBlocking() 240 | { 241 | return currentBlock != null; 242 | } 243 | 244 | unsafe void CopyFromString(string str) 245 | { 246 | if (string.IsNullOrEmpty(str)) 247 | { 248 | return; 249 | } 250 | 251 | fixed (char* s1 = _buffer, s2 = str) 252 | { 253 | CharCopy(s1, s2, str.Length); 254 | length = str.Length; 255 | } 256 | } 257 | 258 | unsafe void ClearBuffer(char[] buffer) 259 | { 260 | fixed (char* p = buffer) 261 | { 262 | memset((byte*)p, 0xcc, sizeof(char) * buffer.Length); 263 | } 264 | } 265 | 266 | public void Clear() 267 | { 268 | length = 0; 269 | } 270 | 271 | public static unsafe bool Equals(CString a, CString b) 272 | { 273 | object l = a as object; 274 | object r = b as object; 275 | 276 | if (l == r) 277 | { 278 | return true; 279 | } 280 | 281 | if (l == null || r == null) 282 | { 283 | return false; 284 | } 285 | 286 | int len = a.length; 287 | 288 | if (len != b.length) 289 | { 290 | return false; 291 | } 292 | 293 | fixed (char* s1 = a._buffer, s2 = b._buffer) 294 | { 295 | char* s1_ptr = s1; 296 | char* s2_ptr = s2; 297 | 298 | while (len >= 8) 299 | { 300 | if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] || ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] || 301 | ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] || ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3]) 302 | { 303 | return false; 304 | } 305 | 306 | s1_ptr += 8; 307 | s2_ptr += 8; 308 | len -= 8; 309 | } 310 | 311 | if (len >= 4) 312 | { 313 | if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] || ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1]) 314 | { 315 | return false; 316 | } 317 | 318 | s1_ptr += 4; 319 | s2_ptr += 4; 320 | len -= 4; 321 | } 322 | 323 | if (len > 1) 324 | { 325 | if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0]) 326 | { 327 | return false; 328 | } 329 | 330 | s1_ptr += 2; 331 | s2_ptr += 2; 332 | len -= 2; 333 | } 334 | 335 | return len == 0 || *s1_ptr == *s2_ptr; 336 | } 337 | } 338 | 339 | public static bool operator ==(CString a, CString b) 340 | { 341 | return Equals(a, b); 342 | } 343 | 344 | public static bool operator !=(CString a, CString b) 345 | { 346 | return !Equals(a, b); 347 | } 348 | 349 | public override bool Equals(Object obj) 350 | { 351 | return Equals(this, obj as CString); 352 | } 353 | 354 | public bool Equals(CString value) 355 | { 356 | return Equals(this, value); 357 | } 358 | 359 | public static unsafe bool Equals(CString a, string b) 360 | { 361 | object l = a as object; 362 | object r = b as object; 363 | 364 | if (l == r) 365 | { 366 | return true; 367 | } 368 | 369 | if (l == null || r == null) 370 | { 371 | return false; 372 | } 373 | 374 | int len = a.length; 375 | 376 | if (len != b.Length) 377 | { 378 | return false; 379 | } 380 | 381 | fixed (char* s1 = a._buffer, s2 = b) 382 | { 383 | char* s1_ptr = s1; 384 | char* s2_ptr = s2; 385 | 386 | while (len >= 8) 387 | { 388 | if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] || ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] || 389 | ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] || ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3]) 390 | { 391 | return false; 392 | } 393 | 394 | s1_ptr += 8; 395 | s2_ptr += 8; 396 | len -= 8; 397 | } 398 | 399 | if (len >= 4) 400 | { 401 | if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] || ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1]) 402 | { 403 | return false; 404 | } 405 | 406 | s1_ptr += 4; 407 | s2_ptr += 4; 408 | len -= 4; 409 | } 410 | 411 | if (len > 1) 412 | { 413 | if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0]) 414 | { 415 | return false; 416 | } 417 | 418 | s1_ptr += 2; 419 | s2_ptr += 2; 420 | len -= 2; 421 | } 422 | 423 | return len == 0 || *s1_ptr == *s2_ptr; 424 | } 425 | } 426 | 427 | public static bool operator ==(CString a, string b) 428 | { 429 | return Equals(a, b); 430 | } 431 | 432 | public static bool operator !=(CString a, string b) 433 | { 434 | return !Equals(a, b); 435 | } 436 | 437 | public bool Equals(string value) 438 | { 439 | return Equals(this, value); 440 | } 441 | 442 | public static bool operator ==(string a, CString b) 443 | { 444 | return Equals(b, a); 445 | } 446 | 447 | public static bool operator !=(string a, CString b) 448 | { 449 | return !Equals(b, a); 450 | } 451 | 452 | public unsafe override int GetHashCode() 453 | { 454 | fixed (char* c = _buffer) 455 | { 456 | char* cc = c; 457 | char* end = cc + length - 1; 458 | int h = 0; 459 | 460 | for (; cc < end; cc += 2) 461 | { 462 | h = (h << 5) - h + *cc; 463 | h = (h << 5) - h + cc[1]; 464 | } 465 | 466 | ++end; 467 | 468 | if (cc < end) 469 | { 470 | h = (h << 5) - h + *cc; 471 | } 472 | 473 | return h; 474 | } 475 | } 476 | 477 | /*unsafe internal void NumberToString(string format, int value, IFormatProvider provider) 478 | { 479 | fixed (char* p = _buffer) 480 | { 481 | length = NumberFormatter.NumberToString(p, format, value, provider); 482 | } 483 | } 484 | 485 | unsafe internal void NumberToString(string format, uint value, IFormatProvider provider) 486 | { 487 | fixed (char* p = _buffer) 488 | { 489 | length = NumberFormatter.NumberToString(p, format, value, provider); 490 | } 491 | } 492 | 493 | unsafe internal void NumberToString(string format, long value, IFormatProvider provider) 494 | { 495 | fixed (char* p = _buffer) 496 | { 497 | length = NumberFormatter.NumberToString(p, format, value, provider); 498 | } 499 | } 500 | 501 | unsafe internal void NumberToString(string format, ulong value, IFormatProvider provider) 502 | { 503 | fixed (char* p = _buffer) 504 | { 505 | length = NumberFormatter.NumberToString(p, format, value, provider); 506 | } 507 | } 508 | 509 | unsafe internal void NumberToString(string format, float value, IFormatProvider provider) 510 | { 511 | fixed (char* p = _buffer) 512 | { 513 | length = NumberFormatter.NumberToString(p, format, value, provider); 514 | } 515 | } 516 | 517 | unsafe internal void NumberToString(string format, double value, IFormatProvider provider) 518 | { 519 | fixed (char* p = _buffer) 520 | { 521 | length = NumberFormatter.NumberToString(p, format, value, provider); 522 | } 523 | }*/ 524 | 525 | public static implicit operator CString(string str) 526 | { 527 | CString temp = Alloc(str.Length); 528 | temp.CopyFromString(str); 529 | return temp; 530 | } 531 | 532 | public unsafe static CString operator +(CString left, CString right) 533 | { 534 | int count = left.length + right.length; 535 | CString str = Alloc(count); 536 | 537 | fixed (char* c1 = left._buffer, c2 = right._buffer, dest = str._buffer) 538 | { 539 | CharCopy(dest, c1, left.length); 540 | CharCopy(dest + left.length, c2, right.length); 541 | str.length = count; 542 | } 543 | 544 | return str; 545 | } 546 | 547 | public unsafe static CString operator +(CString left, int rhl) 548 | { 549 | CString right = rhl.ToCString(); 550 | int count = left.length + right.length; 551 | CString str = Alloc(count); 552 | 553 | fixed (char* c1 = left._buffer, c2 = right._buffer, dest = str._buffer) 554 | { 555 | CharCopy(dest, c1, left.length); 556 | CharCopy(dest + left.length, c2, right.length); 557 | str.length = count; 558 | } 559 | 560 | right.Dispose(); 561 | return str; 562 | } 563 | 564 | public unsafe static CString operator +(CString left, uint rhl) 565 | { 566 | CString right = rhl.ToCString(); 567 | int count = left.length + right.length; 568 | CString str = Alloc(count); 569 | 570 | fixed (char* c1 = left._buffer, c2 = right._buffer, dest = str._buffer) 571 | { 572 | CharCopy(dest, c1, left.length); 573 | CharCopy(dest + left.length, c2, right.length); 574 | str.length = count; 575 | } 576 | 577 | right.Dispose(); 578 | return str; 579 | } 580 | 581 | public unsafe static CString operator +(CString left, float rhl) 582 | { 583 | CString right = rhl.ToCString(); 584 | int count = left.length + right.length; 585 | CString str = Alloc(count); 586 | 587 | fixed (char* c1 = left._buffer, c2 = right._buffer, dest = str._buffer) 588 | { 589 | CharCopy(dest, c1, left.length); 590 | CharCopy(dest + left.length, c2, right.length); 591 | str.length = count; 592 | } 593 | 594 | right.Dispose(); 595 | return str; 596 | } 597 | 598 | public unsafe static CString operator +(CString left, double rhl) 599 | { 600 | CString right = rhl.ToCString(); 601 | int count = left.length + right.length; 602 | CString str = Alloc(count); 603 | 604 | fixed (char* c1 = left._buffer, c2 = right._buffer, dest = str._buffer) 605 | { 606 | CharCopy(dest, c1, left.length); 607 | CharCopy(dest + left.length, c2, right.length); 608 | str.length = count; 609 | } 610 | 611 | right.Dispose(); 612 | return str; 613 | } 614 | 615 | unsafe void SetIndex(int index, char src) 616 | { 617 | fixed (char* p = _buffer) 618 | { 619 | p[index] = src; 620 | } 621 | } 622 | 623 | public char this[int index] 624 | { 625 | get 626 | { 627 | return _buffer[index]; 628 | } 629 | 630 | set 631 | { 632 | SetIndex(index, value); 633 | } 634 | } 635 | 636 | public int Length 637 | { 638 | get 639 | { 640 | return length; 641 | } 642 | } 643 | 644 | public int Count 645 | { 646 | get 647 | { 648 | return _buffer.Length; 649 | } 650 | } 651 | 652 | public CString Clone() 653 | { 654 | return SubstringUnchecked(0, length); 655 | } 656 | 657 | public char[] ToCharArray() 658 | { 659 | return ToCharArray(0, length); 660 | } 661 | 662 | public unsafe char[] ToCharArray(int startIndex, int len) 663 | { 664 | if (len < 0) 665 | { 666 | throw new ArgumentOutOfRangeException("length", "len < 0"); 667 | } 668 | 669 | if (startIndex < 0 || startIndex > this.length - len) 670 | { 671 | throw new ArgumentOutOfRangeException("startIndex", "startIndex out of range"); 672 | } 673 | 674 | char[] tmp = new char[len]; 675 | 676 | fixed (char* dest = tmp, src = _buffer) 677 | { 678 | CharCopy(dest, src + startIndex, len); 679 | } 680 | 681 | return tmp; 682 | } 683 | 684 | public CString[] Split(params char[] separator) 685 | { 686 | return Split(separator, Int32.MaxValue); 687 | } 688 | 689 | public CString[] Split(char[] separator, int count) 690 | { 691 | if (separator == null || separator.Length == 0) 692 | { 693 | separator = WhiteChars; 694 | } 695 | 696 | if (count < 0) 697 | { 698 | throw new ArgumentOutOfRangeException("count"); 699 | } 700 | 701 | if (count == 0) 702 | { 703 | return new CString[0]; 704 | } 705 | 706 | if (count == 1) 707 | { 708 | return new CString[1] { this }; 709 | } 710 | 711 | return Split(separator, count, StringSplitOptions.None); 712 | } 713 | 714 | static List splitList = new List(); 715 | 716 | public CString[] Split(char[] separator, int count, StringSplitOptions options) 717 | { 718 | if (separator == null || separator.Length == 0) 719 | { 720 | return Split(WhiteChars, count, options); 721 | } 722 | 723 | if (count < 0) 724 | { 725 | throw new ArgumentOutOfRangeException("count", "Count cannot be less than zero."); 726 | } 727 | 728 | if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries)) 729 | { 730 | throw new ArgumentException("Illegal enum value: " + options + "."); 731 | } 732 | 733 | bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries; 734 | 735 | if (count == 0 || (CString.IsNullOrEmpty(this) && removeEmpty)) 736 | { 737 | return new CString[0]; 738 | } 739 | 740 | int pos = 0; 741 | int matchCount = 0; 742 | splitList.Clear(); 743 | 744 | while (pos < this.Length) 745 | { 746 | int matchIndex = -1; 747 | int matchPos = Int32.MaxValue; 748 | 749 | // Find the first position where any of the separators matches 750 | for (int i = 0; i < separator.Length; ++i) 751 | { 752 | char sep = separator[i]; 753 | int match = IndexOf(sep, pos); 754 | 755 | if (match > -1 && match < matchPos) 756 | { 757 | matchIndex = i; 758 | matchPos = match; 759 | } 760 | } 761 | 762 | if (matchIndex == -1) 763 | { 764 | break; 765 | } 766 | 767 | if (!(matchPos == pos && removeEmpty)) 768 | { 769 | if (splitList.Count == count - 1) 770 | { 771 | break; 772 | } 773 | 774 | splitList.Add(Substring(pos, matchPos - pos)); 775 | } 776 | 777 | pos = matchPos + 1; 778 | matchCount++; 779 | } 780 | 781 | if (matchCount == 0) 782 | { 783 | return new CString[] { this }; 784 | } 785 | 786 | // string contained only separators 787 | if (removeEmpty && matchCount != 0 && pos == this.Length && splitList.Count == 0) 788 | { 789 | return new CString[0]; 790 | } 791 | 792 | if (!(removeEmpty && pos == this.Length)) 793 | { 794 | splitList.Add(this.Substring(pos)); 795 | } 796 | 797 | return splitList.ToArray(); 798 | } 799 | 800 | public CString[] Split(string[] separator, int count, StringSplitOptions options) 801 | { 802 | if (separator == null || separator.Length == 0) 803 | { 804 | return Split(WhiteChars, count, options); 805 | } 806 | 807 | if (count < 0) 808 | { 809 | throw new ArgumentOutOfRangeException("count", "Count cannot be less than zero."); 810 | } 811 | 812 | if (options != StringSplitOptions.None && options != StringSplitOptions.RemoveEmptyEntries) 813 | { 814 | throw new ArgumentException("Illegal enum value: " + options + "."); 815 | } 816 | 817 | if (count == 1) 818 | { 819 | return new CString[] { this }; 820 | } 821 | 822 | bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries; 823 | 824 | if (count == 0 || (this == String.Empty && removeEmpty)) 825 | { 826 | return new CString[0]; 827 | } 828 | 829 | int pos = 0; 830 | int matchCount = 0; 831 | splitList.Clear(); 832 | 833 | while (pos < this.Length) 834 | { 835 | int matchIndex = -1; 836 | int matchPos = Int32.MaxValue; 837 | 838 | // Find the first position where any of the separators matches 839 | for (int i = 0; i < separator.Length; ++i) 840 | { 841 | string sep = separator[i]; 842 | 843 | if (sep == null || sep == String.Empty) 844 | { 845 | continue; 846 | } 847 | 848 | int match = IndexOf(sep, pos); 849 | 850 | if (match > -1 && match < matchPos) 851 | { 852 | matchIndex = i; 853 | matchPos = match; 854 | } 855 | } 856 | 857 | if (matchIndex == -1) 858 | { 859 | break; 860 | } 861 | 862 | if (!(matchPos == pos && removeEmpty)) 863 | { 864 | if (splitList.Count == count - 1) 865 | { 866 | break; 867 | } 868 | 869 | splitList.Add(Substring(pos, matchPos - pos)); 870 | } 871 | 872 | pos = matchPos + separator[matchIndex].Length; 873 | matchCount++; 874 | } 875 | 876 | if (matchCount == 0) 877 | { 878 | return new CString[] { this }; 879 | } 880 | 881 | // string contained only separators 882 | if (removeEmpty && matchCount != 0 && pos == this.Length && splitList.Count == 0) 883 | { 884 | return new CString[0]; 885 | } 886 | 887 | if (!(removeEmpty && pos == this.Length)) 888 | { 889 | splitList.Add(this.Substring(pos)); 890 | } 891 | 892 | return splitList.ToArray(); 893 | } 894 | 895 | public CString[] Split(char[] separator, StringSplitOptions options) 896 | { 897 | return Split(separator, Int32.MaxValue, options); 898 | } 899 | 900 | public CString[] Split(String[] separator, StringSplitOptions options) 901 | { 902 | return Split(separator, Int32.MaxValue, options); 903 | } 904 | 905 | /// 906 | /// 分配新的CString为当前字符串从startIndex开始的一部分 907 | /// 908 | public CString Substring(int startIndex) 909 | { 910 | if (startIndex == 0) 911 | { 912 | return this; 913 | } 914 | 915 | if (startIndex < 0 || startIndex > this.length) 916 | { 917 | throw new ArgumentOutOfRangeException("startIndex"); 918 | } 919 | 920 | return SubstringUnchecked(startIndex, length - startIndex); 921 | } 922 | 923 | public CString Substring(int startIndex, int len) 924 | { 925 | if (len < 0) 926 | { 927 | throw new ArgumentOutOfRangeException("length", "Cannot be negative."); 928 | } 929 | 930 | if (startIndex < 0) 931 | { 932 | throw new ArgumentOutOfRangeException("startIndex", "Cannot be negative."); 933 | } 934 | 935 | if (startIndex > this.length) 936 | { 937 | throw new ArgumentOutOfRangeException("startIndex", "Cannot exceed length of string."); 938 | } 939 | 940 | if (startIndex > this.length - len) 941 | { 942 | throw new ArgumentOutOfRangeException("length", "startIndex + length > this.length"); 943 | } 944 | 945 | if (startIndex == 0 && len == this.length) 946 | { 947 | return this; 948 | } 949 | 950 | return SubstringUnchecked(startIndex, len); 951 | } 952 | 953 | internal unsafe CString SubstringUnchecked(int startIndex, int len) 954 | { 955 | if (len == 0) 956 | { 957 | return Alloc(0); 958 | } 959 | 960 | CString tmp = Alloc(len); 961 | 962 | fixed (char* dest = tmp._buffer, src = _buffer) 963 | { 964 | CharCopy(dest, src + startIndex, len); 965 | tmp.length = len; 966 | } 967 | 968 | return tmp; 969 | } 970 | 971 | internal unsafe CString TrimStringUnchecked(int startIndex, int len) 972 | { 973 | if (len == 0) 974 | { 975 | Clear(); 976 | return this; 977 | } 978 | 979 | fixed (char* dest = _buffer) 980 | { 981 | CharCopy(dest, dest + startIndex, len); 982 | this.length = len; 983 | } 984 | 985 | return this; 986 | } 987 | 988 | /// 989 | /// 去除字符串前后的空格, 注意不同于string, 这个函数修改的是字符串自身. 990 | /// 991 | /// 返回当前字符串 992 | public CString Trim() 993 | { 994 | if (length == 0) 995 | { 996 | return this; 997 | } 998 | 999 | int start = FindNotWhiteSpace(0, length, 1); 1000 | 1001 | if (start == length) 1002 | { 1003 | Clear(); 1004 | return this; 1005 | } 1006 | 1007 | int end = FindNotWhiteSpace(length - 1, start, -1); 1008 | int newLength = end - start + 1; 1009 | 1010 | if (newLength == length) 1011 | { 1012 | return this; 1013 | } 1014 | 1015 | return TrimStringUnchecked(start, newLength); 1016 | } 1017 | 1018 | /// 1019 | /// 从字符串两端去除trimChars包含的单个字符, 注意不同于string, 这个函数修改的是字符串自身. 1020 | /// 1021 | /// 返回当前字符串 1022 | public CString Trim(params char[] trimChars) 1023 | { 1024 | if (trimChars == null || trimChars.Length == 0) 1025 | { 1026 | return Trim(); 1027 | } 1028 | 1029 | if (length == 0) 1030 | { 1031 | return this; 1032 | } 1033 | 1034 | int start = FindNotInTable(0, length, 1, trimChars); 1035 | 1036 | if (start == length) 1037 | { 1038 | Clear(); 1039 | return this; 1040 | } 1041 | 1042 | int end = FindNotInTable(length - 1, start, -1, trimChars); 1043 | int newLength = end - start + 1; 1044 | 1045 | if (newLength == length) 1046 | { 1047 | return this; 1048 | } 1049 | 1050 | return TrimStringUnchecked(start, newLength); 1051 | } 1052 | 1053 | public CString TrimStart(params char[] trimChars) 1054 | { 1055 | if (length == 0) 1056 | { 1057 | return this; 1058 | } 1059 | 1060 | int start; 1061 | 1062 | if (trimChars == null || trimChars.Length == 0) 1063 | { 1064 | start = FindNotWhiteSpace(0, length, 1); 1065 | } 1066 | else 1067 | { 1068 | start = FindNotInTable(0, length, 1, trimChars); 1069 | } 1070 | 1071 | if (start == 0) 1072 | { 1073 | return this; 1074 | } 1075 | 1076 | return TrimStringUnchecked(start, length - start); 1077 | } 1078 | 1079 | public CString TrimEnd(params char[] trimChars) 1080 | { 1081 | if (length == 0) 1082 | { 1083 | return this; 1084 | } 1085 | 1086 | int end; 1087 | 1088 | if (trimChars == null || trimChars.Length == 0) 1089 | { 1090 | end = FindNotWhiteSpace(length - 1, -1, -1); 1091 | } 1092 | else 1093 | { 1094 | end = FindNotInTable(length - 1, -1, -1, trimChars); 1095 | } 1096 | 1097 | end++; 1098 | 1099 | if (end == length) 1100 | { 1101 | return this; 1102 | } 1103 | 1104 | return SubstringUnchecked(0, end); 1105 | } 1106 | 1107 | 1108 | private int FindNotWhiteSpace(int pos, int target, int change) 1109 | { 1110 | while (pos != target) 1111 | { 1112 | char c = this[pos]; 1113 | 1114 | if (Char.IsWhiteSpace(c)) 1115 | { 1116 | pos += change; 1117 | } 1118 | else 1119 | { 1120 | return pos; 1121 | } 1122 | } 1123 | return pos; 1124 | } 1125 | 1126 | private unsafe int FindNotInTable(int pos, int target, int change, char[] table) 1127 | { 1128 | fixed (char* tablePtr = table, thisPtr = _buffer) 1129 | { 1130 | while (pos != target) 1131 | { 1132 | char c = thisPtr[pos]; 1133 | int x = 0; 1134 | 1135 | while (x < table.Length) 1136 | { 1137 | if (c == tablePtr[x]) 1138 | { 1139 | break; 1140 | } 1141 | 1142 | x++; 1143 | } 1144 | 1145 | if (x == table.Length) 1146 | { 1147 | return pos; 1148 | } 1149 | 1150 | pos += change; 1151 | } 1152 | } 1153 | 1154 | return pos; 1155 | } 1156 | 1157 | public static int Compare(CString strA, CString strB) 1158 | { 1159 | return CompareOrdinal(strA, strB); 1160 | } 1161 | 1162 | public static int Compare(CString strA, CString strB, bool ignoreCase) 1163 | { 1164 | if (ignoreCase) 1165 | { 1166 | return CompareOrdinalCaseInsensitiveUnchecked(strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue); 1167 | } 1168 | else 1169 | { 1170 | return CompareOrdinalUnchecked(strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue); 1171 | } 1172 | } 1173 | 1174 | 1175 | /*public unsafe static int Compare(CString strA, CString strB, StringComparison comparisonType) 1176 | { 1177 | switch (comparisonType) 1178 | { 1179 | case StringComparison.Ordinal: 1180 | return CompareOrdinalUnchecked(strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue); 1181 | case StringComparison.OrdinalIgnoreCase: 1182 | return CompareOrdinalCaseInsensitiveUnchecked(strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue); 1183 | default: 1184 | throw new ArgumentException("Invalid value for StringComparison", "comparisonType"); 1185 | } 1186 | } 1187 | 1188 | public static int Compare(CString strA, int indexA, CString strB, int indexB, int length, StringComparison comparisonType) 1189 | { 1190 | switch (comparisonType) 1191 | { 1192 | case StringComparison.Ordinal: 1193 | return CompareOrdinal(strA, indexA, strB, indexB, length); 1194 | case StringComparison.OrdinalIgnoreCase: 1195 | return CompareOrdinalCaseInsensitive(strA, indexA, strB, indexB, length); 1196 | default: 1197 | throw new ArgumentException("Invalid value for StringComparison", "comparisonType"); 1198 | } 1199 | } 1200 | 1201 | public static bool Equals(CString a, CString b, StringComparison comparisonType) 1202 | { 1203 | return CString.Compare(a, b, comparisonType) == 0; 1204 | } 1205 | 1206 | public bool Equals(CString value, StringComparison comparisonType) 1207 | { 1208 | return CString.Compare(value, this, comparisonType) == 0; 1209 | }*/ 1210 | 1211 | public static bool Equals(CString a, CString b, bool ignoreCase) 1212 | { 1213 | return CString.Compare(a, b, ignoreCase) == 0; 1214 | } 1215 | 1216 | public bool Equals(CString value, bool ignoreCase) 1217 | { 1218 | return CString.Compare(value, this, ignoreCase) == 0; 1219 | } 1220 | 1221 | public static int CompareOrdinal(CString strA, CString strB) 1222 | { 1223 | return CompareOrdinalUnchecked(strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue); 1224 | } 1225 | 1226 | 1227 | unsafe public static int CompareOrdinal(CString strA, int indexA, CString strB, int indexB, int len) 1228 | { 1229 | if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (len < 0)) 1230 | { 1231 | throw new ArgumentOutOfRangeException(); 1232 | } 1233 | 1234 | return CompareOrdinalUnchecked(strA, indexA, len, strB, indexB, len); 1235 | } 1236 | 1237 | internal static unsafe int CompareOrdinalUnchecked(CString strA, int indexA, int lenA, CString strB, int indexB, int lenB) 1238 | { 1239 | if (strA == null) 1240 | { 1241 | return strB == null ? 0 : -1; 1242 | } 1243 | else if (strB == null) 1244 | { 1245 | return 1; 1246 | } 1247 | 1248 | int lengthA = Math.Min(lenA, strA.Length - indexA); 1249 | int lengthB = Math.Min(lenB, strB.Length - indexB); 1250 | 1251 | if (lengthA == lengthB && Object.ReferenceEquals(strA, strB)) 1252 | { 1253 | return 0; 1254 | } 1255 | 1256 | fixed (char* aptr = strA._buffer, bptr = strB._buffer) 1257 | { 1258 | char* ap = aptr + indexA; 1259 | char* end = ap + Math.Min(lengthA, lengthB); 1260 | char* bp = bptr + indexB; 1261 | while (ap < end) 1262 | { 1263 | if (*ap != *bp) 1264 | { 1265 | return *ap - *bp; 1266 | } 1267 | 1268 | ap++; 1269 | bp++; 1270 | } 1271 | return lengthA - lengthB; 1272 | } 1273 | } 1274 | 1275 | internal static int CompareOrdinalCaseInsensitive(CString strA, int indexA, CString strB, int indexB, int length) 1276 | { 1277 | if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0)) 1278 | { 1279 | throw new ArgumentOutOfRangeException(); 1280 | } 1281 | 1282 | return CompareOrdinalCaseInsensitiveUnchecked(strA, indexA, length, strB, indexB, length); 1283 | } 1284 | 1285 | internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked(CString strA, int indexA, int lenA, CString strB, int indexB, int lenB) 1286 | { 1287 | if (strA == null) 1288 | { 1289 | return strB == null ? 0 : -1; 1290 | } 1291 | else if (strB == null) 1292 | { 1293 | return 1; 1294 | } 1295 | 1296 | int lengthA = Math.Min(lenA, strA.Length - indexA); 1297 | int lengthB = Math.Min(lenB, strB.Length - indexB); 1298 | 1299 | if (lengthA == lengthB && Object.ReferenceEquals(strA, strB)) 1300 | { 1301 | return 0; 1302 | } 1303 | 1304 | fixed (char* aptr = strA._buffer, bptr = strB._buffer) 1305 | { 1306 | char* ap = aptr + indexA; 1307 | char* end = ap + Math.Min(lengthA, lengthB); 1308 | char* bp = bptr + indexB; 1309 | while (ap < end) 1310 | { 1311 | if (*ap != *bp) 1312 | { 1313 | char c1 = Char.ToUpperInvariant(*ap); 1314 | char c2 = Char.ToUpperInvariant(*bp); 1315 | 1316 | if (c1 != c2) 1317 | { 1318 | return c1 - c2; 1319 | } 1320 | } 1321 | ap++; 1322 | bp++; 1323 | } 1324 | return lengthA - lengthB; 1325 | } 1326 | } 1327 | 1328 | 1329 | internal unsafe static void memcpy(CString dest, CString src, int size, int src_offset) 1330 | { 1331 | fixed (char* _dest = dest._buffer) 1332 | { 1333 | fixed (char* _src = src._buffer) 1334 | { 1335 | src += src_offset; 1336 | CharCopy(_dest, _src, size); 1337 | } 1338 | } 1339 | } 1340 | 1341 | public bool EndsWith(CString value) 1342 | { 1343 | if (value == null) 1344 | { 1345 | throw new ArgumentNullException("value"); 1346 | } 1347 | 1348 | if (length < value.Length) 1349 | { 1350 | return false; 1351 | } 1352 | 1353 | for (int i = _buffer.Length - 1, j = value.Length - 1; j >= 0; i--, j--) 1354 | { 1355 | if (_buffer[i] != value._buffer[j]) 1356 | { 1357 | return false; 1358 | } 1359 | } 1360 | 1361 | return true; 1362 | } 1363 | 1364 | public int IndexOfAny(char[] anyOf) 1365 | { 1366 | if (anyOf == null) 1367 | { 1368 | throw new ArgumentNullException(); 1369 | } 1370 | 1371 | if (this.length == 0) 1372 | { 1373 | return -1; 1374 | } 1375 | 1376 | return IndexOfAnyUnchecked(anyOf, 0, this.length); 1377 | } 1378 | 1379 | public int IndexOfAny(char[] anyOf, int startIndex) 1380 | { 1381 | if (anyOf == null) 1382 | { 1383 | throw new ArgumentNullException(); 1384 | } 1385 | 1386 | if (startIndex < 0 || startIndex > this.length) 1387 | { 1388 | throw new ArgumentOutOfRangeException(); 1389 | } 1390 | 1391 | return IndexOfAnyUnchecked(anyOf, startIndex, this.length - startIndex); 1392 | } 1393 | 1394 | public int IndexOfAny(char[] anyOf, int startIndex, int count) 1395 | { 1396 | if (anyOf == null) 1397 | { 1398 | throw new ArgumentNullException(); 1399 | } 1400 | 1401 | if (startIndex < 0 || startIndex > this.length) 1402 | { 1403 | throw new ArgumentOutOfRangeException(); 1404 | } 1405 | 1406 | if (count < 0 || startIndex > this.length - count) 1407 | { 1408 | throw new ArgumentOutOfRangeException("count", "Count cannot be negative, and startIndex + count must be less than length of the string."); 1409 | } 1410 | 1411 | return IndexOfAnyUnchecked(anyOf, startIndex, count); 1412 | } 1413 | 1414 | private unsafe int IndexOfAnyUnchecked(char[] anyOf, int startIndex, int count) 1415 | { 1416 | if (anyOf.Length == 0) 1417 | { 1418 | return -1; 1419 | } 1420 | 1421 | if (anyOf.Length == 1) 1422 | { 1423 | return IndexOfUnchecked(anyOf[0], startIndex, count); 1424 | } 1425 | 1426 | fixed (char* any = anyOf) 1427 | { 1428 | int highest = *any; 1429 | int lowest = *any; 1430 | 1431 | char* end_any_ptr = any + anyOf.Length; 1432 | char* any_ptr = any; 1433 | 1434 | while (++any_ptr != end_any_ptr) 1435 | { 1436 | if (*any_ptr > highest) 1437 | { 1438 | highest = *any_ptr; 1439 | continue; 1440 | } 1441 | 1442 | if (*any_ptr < lowest) 1443 | { 1444 | lowest = *any_ptr; 1445 | } 1446 | } 1447 | 1448 | fixed (char* start = _buffer) 1449 | { 1450 | char* ptr = start + startIndex; 1451 | char* end_ptr = ptr + count; 1452 | 1453 | while (ptr != end_ptr) 1454 | { 1455 | if (*ptr > highest || *ptr < lowest) 1456 | { 1457 | ptr++; 1458 | continue; 1459 | } 1460 | 1461 | if (*ptr == *any) 1462 | { 1463 | return (int)(ptr - start); 1464 | } 1465 | 1466 | any_ptr = any; 1467 | 1468 | while (++any_ptr != end_any_ptr) 1469 | { 1470 | if (*ptr == *any_ptr) 1471 | { 1472 | return (int)(ptr - start); 1473 | } 1474 | } 1475 | 1476 | ptr++; 1477 | } 1478 | } 1479 | } 1480 | 1481 | return -1; 1482 | } 1483 | 1484 | public int IndexOf(char value) 1485 | { 1486 | if (length == 0) 1487 | { 1488 | return -1; 1489 | } 1490 | 1491 | return IndexOfUnchecked(value, 0, length); 1492 | } 1493 | 1494 | public int IndexOf(char value, int startIndex) 1495 | { 1496 | if (startIndex < 0) 1497 | { 1498 | throw new ArgumentOutOfRangeException("startIndex", "< 0"); 1499 | } 1500 | 1501 | if (startIndex > length) 1502 | { 1503 | throw new ArgumentOutOfRangeException("startIndex", "startIndex > this.length"); 1504 | } 1505 | 1506 | if ((startIndex == 0 && length == 0) || (startIndex == length)) 1507 | { 1508 | return -1; 1509 | } 1510 | 1511 | return IndexOfUnchecked(value, startIndex, length - startIndex); 1512 | } 1513 | 1514 | public int IndexOf(char value, int startIndex, int count) 1515 | { 1516 | if (startIndex < 0 || startIndex > length) 1517 | { 1518 | throw new ArgumentOutOfRangeException("startIndex", "Cannot be negative and must be< 0"); 1519 | } 1520 | 1521 | if (count < 0) 1522 | { 1523 | throw new ArgumentOutOfRangeException("count", "< 0"); 1524 | } 1525 | 1526 | if (startIndex > length - count) 1527 | { 1528 | throw new ArgumentOutOfRangeException("count", "startIndex + count > this.length"); 1529 | } 1530 | 1531 | if ((startIndex == 0 && length == 0) || (startIndex == length) || (count == 0)) 1532 | { 1533 | return -1; 1534 | } 1535 | 1536 | return IndexOfUnchecked(value, startIndex, count); 1537 | } 1538 | 1539 | internal unsafe int IndexOfUnchecked(char value, int startIndex, int count) 1540 | { 1541 | // It helps JIT compiler to optimize comparison 1542 | int value_32 = (int)value; 1543 | 1544 | fixed (char* start = _buffer) 1545 | { 1546 | char* ptr = start + startIndex; 1547 | char* end_ptr = ptr + (count >> 3 << 3); 1548 | 1549 | while (ptr != end_ptr) 1550 | { 1551 | if (*ptr == value_32) 1552 | return (int)(ptr - start); 1553 | if (ptr[1] == value_32) 1554 | return (int)(ptr - start + 1); 1555 | if (ptr[2] == value_32) 1556 | return (int)(ptr - start + 2); 1557 | if (ptr[3] == value_32) 1558 | return (int)(ptr - start + 3); 1559 | if (ptr[4] == value_32) 1560 | return (int)(ptr - start + 4); 1561 | if (ptr[5] == value_32) 1562 | return (int)(ptr - start + 5); 1563 | if (ptr[6] == value_32) 1564 | return (int)(ptr - start + 6); 1565 | if (ptr[7] == value_32) 1566 | return (int)(ptr - start + 7); 1567 | 1568 | ptr += 8; 1569 | } 1570 | 1571 | end_ptr += count & 0x07; 1572 | 1573 | while (ptr != end_ptr) 1574 | { 1575 | if (*ptr == value_32) 1576 | { 1577 | return (int)(ptr - start); 1578 | } 1579 | 1580 | ptr++; 1581 | } 1582 | return -1; 1583 | } 1584 | } 1585 | 1586 | public int IndexOf(string value) 1587 | { 1588 | return IndexOf(value, 0, length); 1589 | } 1590 | 1591 | public int IndexOf(string value, int startIndex) 1592 | { 1593 | return IndexOf(value, startIndex, length - startIndex); 1594 | } 1595 | 1596 | public unsafe int IndexOf(string value, int startIndex, int count) 1597 | { 1598 | if (value == null) 1599 | { 1600 | throw new ArgumentNullException("value"); 1601 | } 1602 | 1603 | if (startIndex < 0 || startIndex > Length) 1604 | { 1605 | throw new ArgumentOutOfRangeException("startIndex", "Cannot be negative, and should not exceed length of string."); 1606 | } 1607 | 1608 | if (count < 0 || startIndex > length - count) 1609 | { 1610 | throw new ArgumentOutOfRangeException("count", "Cannot be negative, and should point to location in string."); 1611 | } 1612 | 1613 | if (value.Length == 0) 1614 | { 1615 | return startIndex; 1616 | } 1617 | 1618 | if (startIndex == 0 && Length == 0) 1619 | { 1620 | return -1; 1621 | } 1622 | 1623 | if (count == 0) 1624 | { 1625 | return -1; 1626 | } 1627 | 1628 | int valueLen = count; 1629 | 1630 | fixed (char* thisptr = _buffer, valueptr = value) 1631 | { 1632 | char* ap = thisptr + startIndex; 1633 | char* thisEnd = ap + count - valueLen + 1; 1634 | 1635 | while (ap != thisEnd) 1636 | { 1637 | if (*ap == *valueptr) 1638 | { 1639 | for (int i = 1; i < valueLen; i++) 1640 | { 1641 | if (ap[i] != valueptr[i]) 1642 | { 1643 | goto NextVal; 1644 | } 1645 | } 1646 | return (int)(ap - thisptr); 1647 | } 1648 | NextVal: 1649 | ap++; 1650 | } 1651 | } 1652 | 1653 | return -1; 1654 | } 1655 | 1656 | internal unsafe int IndexOfOrdinalUnchecked(string value, int startIndex, int count) 1657 | { 1658 | int valueLen = value.Length; 1659 | 1660 | if (count < valueLen) 1661 | { 1662 | return -1; 1663 | } 1664 | 1665 | if (valueLen <= 1) 1666 | { 1667 | if (valueLen == 1) 1668 | { 1669 | return IndexOfUnchecked(value[0], startIndex, count); 1670 | } 1671 | 1672 | return startIndex; 1673 | } 1674 | 1675 | fixed (char* thisptr = _buffer, valueptr = value) 1676 | { 1677 | char* ap = thisptr + startIndex; 1678 | char* thisEnd = ap + count - valueLen + 1; 1679 | 1680 | while (ap != thisEnd) 1681 | { 1682 | if (*ap == *valueptr) 1683 | { 1684 | for (int i = 1; i < valueLen; i++) 1685 | { 1686 | if (ap[i] != valueptr[i]) 1687 | { 1688 | goto NextVal; 1689 | } 1690 | } 1691 | return (int)(ap - thisptr); 1692 | } 1693 | 1694 | NextVal: 1695 | ap++; 1696 | } 1697 | } 1698 | 1699 | return -1; 1700 | } 1701 | 1702 | public int LastIndexOfAny(char[] anyOf) 1703 | { 1704 | if (anyOf == null) 1705 | { 1706 | throw new ArgumentNullException(); 1707 | } 1708 | 1709 | return LastIndexOfAnyUnchecked(anyOf, this.length - 1, this.length); 1710 | } 1711 | 1712 | public int LastIndexOfAny(char[] anyOf, int startIndex) 1713 | { 1714 | if (anyOf == null) 1715 | { 1716 | throw new ArgumentNullException(); 1717 | } 1718 | 1719 | if (startIndex < 0 || startIndex >= this.length) 1720 | { 1721 | throw new ArgumentOutOfRangeException("startIndex", "Cannot be negative, and should be less than length of string."); 1722 | } 1723 | 1724 | if (this.length == 0) 1725 | { 1726 | return -1; 1727 | } 1728 | 1729 | return LastIndexOfAnyUnchecked(anyOf, startIndex, startIndex + 1); 1730 | } 1731 | 1732 | public int LastIndexOfAny(char[] anyOf, int startIndex, int count) 1733 | { 1734 | if (anyOf == null) 1735 | { 1736 | throw new ArgumentNullException(); 1737 | } 1738 | 1739 | if ((startIndex < 0) || (startIndex >= this.Length)) 1740 | { 1741 | throw new ArgumentOutOfRangeException("startIndex", "< 0 || > this.Length"); 1742 | } 1743 | 1744 | if ((count < 0) || (count > this.Length)) 1745 | { 1746 | throw new ArgumentOutOfRangeException("count", "< 0 || > this.Length"); 1747 | } 1748 | 1749 | if (startIndex - count + 1 < 0) 1750 | { 1751 | throw new ArgumentOutOfRangeException("startIndex - count + 1 < 0"); 1752 | } 1753 | 1754 | if (this.length == 0) 1755 | { 1756 | return -1; 1757 | } 1758 | 1759 | return LastIndexOfAnyUnchecked(anyOf, startIndex, count); 1760 | } 1761 | 1762 | private unsafe int LastIndexOfAnyUnchecked(char[] anyOf, int startIndex, int count) 1763 | { 1764 | if (anyOf.Length == 1) 1765 | { 1766 | return LastIndexOfUnchecked(anyOf[0], startIndex, count); 1767 | } 1768 | 1769 | fixed (char* start = _buffer, testStart = anyOf) 1770 | { 1771 | char* ptr = start + startIndex; 1772 | char* ptrEnd = ptr - count; 1773 | char* test; 1774 | char* testEnd = testStart + anyOf.Length; 1775 | 1776 | while (ptr != ptrEnd) 1777 | { 1778 | test = testStart; 1779 | while (test != testEnd) 1780 | { 1781 | if (*test == *ptr) 1782 | { 1783 | return (int)(ptr - start); 1784 | } 1785 | test++; 1786 | } 1787 | ptr--; 1788 | } 1789 | return -1; 1790 | } 1791 | } 1792 | 1793 | 1794 | public int LastIndexOf(char value) 1795 | { 1796 | if (length == 0) 1797 | { 1798 | return -1; 1799 | } 1800 | 1801 | return LastIndexOfUnchecked(value, this.length - 1, this.length); 1802 | } 1803 | 1804 | public int LastIndexOf(char value, int startIndex) 1805 | { 1806 | return LastIndexOf(value, startIndex, startIndex + 1); 1807 | } 1808 | 1809 | public int LastIndexOf(char value, int startIndex, int count) 1810 | { 1811 | if (startIndex == 0 && length == 0) 1812 | { 1813 | return -1; 1814 | } 1815 | 1816 | if ((startIndex < 0) || (startIndex >= this.Length)) 1817 | { 1818 | throw new ArgumentOutOfRangeException("startIndex", "< 0 || >= this.Length"); 1819 | } 1820 | 1821 | if ((count < 0) || (count > this.Length)) 1822 | { 1823 | throw new ArgumentOutOfRangeException("count", "< 0 || > this.Length"); 1824 | } 1825 | 1826 | if (startIndex - count + 1 < 0) 1827 | { 1828 | throw new ArgumentOutOfRangeException("startIndex - count + 1 < 0"); 1829 | } 1830 | 1831 | return LastIndexOfUnchecked(value, startIndex, count); 1832 | } 1833 | 1834 | internal unsafe int LastIndexOfUnchecked(char value, int startIndex, int count) 1835 | { 1836 | // It helps JIT compiler to optimize comparison 1837 | int value_32 = (int)value; 1838 | 1839 | fixed (char* start = _buffer) 1840 | { 1841 | char* ptr = start + startIndex; 1842 | char* end_ptr = ptr - (count >> 3 << 3); 1843 | 1844 | while (ptr != end_ptr) 1845 | { 1846 | if (*ptr == value_32) 1847 | return (int)(ptr - start); 1848 | if (ptr[-1] == value_32) 1849 | return (int)(ptr - start) - 1; 1850 | if (ptr[-2] == value_32) 1851 | return (int)(ptr - start) - 2; 1852 | if (ptr[-3] == value_32) 1853 | return (int)(ptr - start) - 3; 1854 | if (ptr[-4] == value_32) 1855 | return (int)(ptr - start) - 4; 1856 | if (ptr[-5] == value_32) 1857 | return (int)(ptr - start) - 5; 1858 | if (ptr[-6] == value_32) 1859 | return (int)(ptr - start) - 6; 1860 | if (ptr[-7] == value_32) 1861 | return (int)(ptr - start) - 7; 1862 | 1863 | ptr -= 8; 1864 | } 1865 | 1866 | end_ptr -= count & 0x07; 1867 | 1868 | while (ptr != end_ptr) 1869 | { 1870 | if (*ptr == value_32) 1871 | return (int)(ptr - start); 1872 | 1873 | ptr--; 1874 | } 1875 | return -1; 1876 | } 1877 | } 1878 | 1879 | // Following methods are culture-sensitive 1880 | public int LastIndexOf(string value) 1881 | { 1882 | if (this.length == 0) 1883 | { 1884 | // This overload does additional checking 1885 | return LastIndexOf(value, 0, 0); 1886 | } 1887 | else 1888 | { 1889 | return LastIndexOf(value, length - 1, length); 1890 | } 1891 | } 1892 | 1893 | public int LastIndexOf(string value, int startIndex) 1894 | { 1895 | int max = startIndex; 1896 | 1897 | if (max < length) 1898 | { 1899 | max++; 1900 | } 1901 | return LastIndexOf(value, startIndex, max); 1902 | } 1903 | 1904 | public unsafe int LastIndexOf(string value, int startIndex, int count) 1905 | { 1906 | if (value == null) 1907 | { 1908 | throw new ArgumentNullException("value"); 1909 | } 1910 | 1911 | // -1 > startIndex > for string (0 > startIndex >= for char) 1912 | if ((startIndex < -1) || (startIndex > length)) 1913 | { 1914 | throw new ArgumentOutOfRangeException("startIndex", "< 0 || > this.Length"); 1915 | } 1916 | 1917 | if ((count < 0) || (count > length)) 1918 | { 1919 | throw new ArgumentOutOfRangeException("count", "< 0 || > this.Length"); 1920 | } 1921 | 1922 | if (startIndex - count + 1 < 0) 1923 | { 1924 | throw new ArgumentOutOfRangeException("startIndex - count + 1 < 0"); 1925 | } 1926 | 1927 | int valueLen = value.Length; 1928 | 1929 | if (valueLen == 0) 1930 | { 1931 | return startIndex; 1932 | } 1933 | 1934 | if (startIndex == 0 && length == 0) 1935 | { 1936 | return -1; 1937 | } 1938 | 1939 | // This check is needed to match undocumented MS behaviour 1940 | if (length == 0 && valueLen > 0) 1941 | { 1942 | return -1; 1943 | } 1944 | 1945 | if (count == 0) 1946 | { 1947 | return -1; 1948 | } 1949 | 1950 | if (startIndex == length) 1951 | { 1952 | startIndex--; 1953 | } 1954 | 1955 | fixed (char* thisptr = _buffer, valueptr = value) 1956 | { 1957 | char* ap = thisptr + startIndex - valueLen + 1; 1958 | char* thisEnd = ap - count + valueLen - 1; 1959 | 1960 | while (ap != thisEnd) 1961 | { 1962 | if (*ap == *valueptr) 1963 | { 1964 | for (int i = 1; i < valueLen; i++) 1965 | { 1966 | if (ap[i] != valueptr[i]) 1967 | goto NextVal; 1968 | } 1969 | return (int)(ap - thisptr); 1970 | } 1971 | NextVal: 1972 | ap--; 1973 | } 1974 | } 1975 | 1976 | return -1; 1977 | } 1978 | 1979 | public bool Contains(String value) 1980 | { 1981 | return IndexOf(value) != -1; 1982 | } 1983 | 1984 | public static bool IsNullOrEmpty(CString value) 1985 | { 1986 | object obj = value; 1987 | if (obj == null) return true; 1988 | 1989 | return value.length == 0; 1990 | } 1991 | 1992 | public static bool IsNullOrWhiteSpace(CString value) 1993 | { 1994 | object obj = value; 1995 | 1996 | if (obj == null) 1997 | { 1998 | return true; 1999 | } 2000 | 2001 | for (int i = 0; i < value.Length; i++) 2002 | { 2003 | if (!Char.IsWhiteSpace(value[i])) 2004 | { 2005 | return false; 2006 | } 2007 | } 2008 | 2009 | return true; 2010 | } 2011 | 2012 | public CString Remove(int startIndex) 2013 | { 2014 | if (startIndex < 0) 2015 | { 2016 | throw new ArgumentOutOfRangeException("startIndex", "StartIndex can not be less than zero"); 2017 | } 2018 | 2019 | if (startIndex >= this.length) 2020 | { 2021 | throw new ArgumentOutOfRangeException("startIndex", "StartIndex must be less than the length of the string"); 2022 | } 2023 | 2024 | length = startIndex; 2025 | return this; 2026 | } 2027 | 2028 | public unsafe CString Remove(int startIndex, int len) 2029 | { 2030 | if (startIndex < 0 || len < 0 || startIndex > length - len) 2031 | { 2032 | throw new ArgumentOutOfRangeException(); 2033 | } 2034 | 2035 | fixed (char* dest = _buffer) 2036 | { 2037 | CharCopy(dest + startIndex, dest + startIndex + len, length - startIndex - len); 2038 | } 2039 | 2040 | length -= len; 2041 | return this; 2042 | } 2043 | 2044 | public CString PadLeft(int totalWidth) 2045 | { 2046 | return PadLeft(totalWidth, ' '); 2047 | } 2048 | 2049 | /// 2050 | /// 在当前字符串前插入totalWidth个paddingChar 2051 | /// 2052 | /// 2053 | /// 2054 | /// 2055 | public unsafe CString PadLeft(int totalWidth, char paddingChar) 2056 | { 2057 | //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue 2058 | if (totalWidth < 0) 2059 | { 2060 | throw new ArgumentOutOfRangeException("totalWidth", "< 0"); 2061 | } 2062 | 2063 | if (totalWidth < length) 2064 | { 2065 | return this; 2066 | } 2067 | 2068 | EnsureCapacity(totalWidth); 2069 | 2070 | fixed (char* dest = _buffer) 2071 | { 2072 | for (int i = length - 1; i >= 0; i--) 2073 | { 2074 | _buffer[i + totalWidth - length] = _buffer[i]; 2075 | } 2076 | 2077 | for (int i = 0; i < totalWidth - length; i++) 2078 | { 2079 | dest[i] = paddingChar; 2080 | } 2081 | } 2082 | 2083 | length = totalWidth; 2084 | return this; 2085 | } 2086 | 2087 | public CString PadRight(int totalWidth) 2088 | { 2089 | return PadRight(totalWidth, ' '); 2090 | } 2091 | 2092 | public unsafe CString PadRight(int totalWidth, char paddingChar) 2093 | { 2094 | //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue 2095 | if (totalWidth < 0) 2096 | { 2097 | throw new ArgumentOutOfRangeException("totalWidth", "< 0"); 2098 | } 2099 | 2100 | if (totalWidth < length) 2101 | { 2102 | return this; 2103 | } 2104 | 2105 | if (totalWidth == 0) 2106 | { 2107 | length = 0; 2108 | return this; 2109 | } 2110 | 2111 | EnsureCapacity(totalWidth); 2112 | 2113 | for (int i = length; i < totalWidth; i++) 2114 | { 2115 | _buffer[i] = paddingChar; 2116 | } 2117 | 2118 | length = totalWidth; 2119 | return this; 2120 | } 2121 | 2122 | public bool StartsWith(string value) 2123 | { 2124 | if (value == null) 2125 | { 2126 | throw new ArgumentNullException("value"); 2127 | } 2128 | 2129 | if (length < value.Length) 2130 | { 2131 | return false; 2132 | } 2133 | 2134 | for (int i = 0; i < value.Length; i++) 2135 | { 2136 | if (_buffer[i] != value[i]) 2137 | { 2138 | return false; 2139 | } 2140 | } 2141 | 2142 | return true; 2143 | } 2144 | 2145 | 2146 | public CString Replace(char oldChar, char newChar) 2147 | { 2148 | if (length == 0 || oldChar == newChar) 2149 | { 2150 | return this; 2151 | } 2152 | 2153 | int start_pos = IndexOfUnchecked(oldChar, 0, length); 2154 | 2155 | if (start_pos == -1) 2156 | { 2157 | return this; 2158 | } 2159 | 2160 | for (int i = start_pos; i < length; i++) 2161 | { 2162 | if (_buffer[i] == oldChar) 2163 | { 2164 | _buffer[i] = newChar; 2165 | } 2166 | } 2167 | 2168 | return this; 2169 | } 2170 | 2171 | public CString Replace(char oldChar, char newChar, int startIndex, int count) 2172 | { 2173 | if (startIndex < 0 || count < 0 || startIndex > length - count) 2174 | { 2175 | throw new ArgumentOutOfRangeException(); 2176 | } 2177 | 2178 | if (length == 0 || oldChar == newChar) 2179 | { 2180 | return this; 2181 | } 2182 | 2183 | int start_pos = IndexOfUnchecked(oldChar, startIndex, count); 2184 | 2185 | if (start_pos == -1) 2186 | { 2187 | return this; 2188 | } 2189 | 2190 | for (int i = start_pos; i < startIndex + count; i++) 2191 | { 2192 | if (_buffer[i] == oldChar) 2193 | { 2194 | _buffer[i] = newChar; 2195 | } 2196 | } 2197 | 2198 | return this; 2199 | } 2200 | 2201 | public unsafe CString Replace(string oldValue, string newValue, int startIndex, int count) 2202 | { 2203 | if (oldValue == null) 2204 | { 2205 | throw new ArgumentNullException("The old value cannot be null."); 2206 | } 2207 | 2208 | if (startIndex < 0 || count < 0 || startIndex > length - count) 2209 | { 2210 | throw new ArgumentOutOfRangeException(); 2211 | } 2212 | 2213 | if (oldValue.Length == 0) 2214 | { 2215 | throw new ArgumentException("The old value cannot be zero length."); 2216 | } 2217 | 2218 | CString substr = Substring(startIndex, count); 2219 | CString replace = substr.Replace(oldValue, newValue); 2220 | // return early if no oldValue was found 2221 | if ((object)replace == (object)substr) 2222 | { 2223 | return this; 2224 | } 2225 | 2226 | EnsureCapacity(replace.Length + length - count); 2227 | 2228 | fixed (char* dest = _buffer, src = replace._buffer) 2229 | { 2230 | if (replace.Length < count) 2231 | { 2232 | CharCopy(dest + startIndex + replace.Length, dest + startIndex + count, length - startIndex - count); 2233 | } 2234 | else if (replace.Length > count) 2235 | { 2236 | CharCopyReverse(dest + startIndex + replace.Length, dest + startIndex + count, length - startIndex - count); 2237 | } 2238 | 2239 | CharCopy(dest + startIndex, src, replace.Length); 2240 | } 2241 | 2242 | length = replace.Length + (length - count); 2243 | return this; 2244 | } 2245 | 2246 | public CString Replace(String oldValue, String newValue) 2247 | { 2248 | if (oldValue == null) 2249 | { 2250 | throw new ArgumentNullException("oldValue"); 2251 | } 2252 | 2253 | if (oldValue.Length == 0) 2254 | { 2255 | throw new ArgumentException("oldValue is the empty string."); 2256 | } 2257 | 2258 | if (length == 0) 2259 | { 2260 | return this; 2261 | } 2262 | 2263 | if (newValue == null) 2264 | { 2265 | newValue = string.Empty; 2266 | } 2267 | 2268 | return ReplaceUnchecked(oldValue, newValue); 2269 | } 2270 | 2271 | //好像有问题 2272 | private unsafe CString ReplaceUnchecked(string oldValue, string newValue) 2273 | { 2274 | if (oldValue.Length > length) 2275 | { 2276 | return this; 2277 | } 2278 | 2279 | if (oldValue.Length == 1 && newValue.Length == 1) 2280 | { 2281 | return Replace(oldValue[0], newValue[0]); 2282 | } 2283 | 2284 | const int maxValue = 200; // Allocate 800 byte maximum 2285 | int* dat = stackalloc int[maxValue]; 2286 | 2287 | fixed (char* source = _buffer, replace = newValue) 2288 | { 2289 | int i = 0, count = 0; 2290 | 2291 | while (i < length) 2292 | { 2293 | int found = IndexOfOrdinalUnchecked(oldValue, i, length - i); 2294 | 2295 | if (found < 0) 2296 | { 2297 | break; 2298 | } 2299 | else 2300 | { 2301 | if (count < maxValue) 2302 | { 2303 | dat[count++] = found; 2304 | } 2305 | else 2306 | { 2307 | return ReplaceFallback(oldValue, newValue, maxValue); 2308 | } 2309 | } 2310 | 2311 | i = found + oldValue.Length; 2312 | } 2313 | 2314 | if (count == 0) 2315 | { 2316 | return this; 2317 | } 2318 | 2319 | int nlen = this.length + (newValue.Length - oldValue.Length) * count; 2320 | CString temp = Alloc(nlen); 2321 | int curPos = 0, lastReadPos = 0; 2322 | 2323 | fixed (char* dest = temp._buffer) 2324 | { 2325 | for (int j = 0; j < count; j++) 2326 | { 2327 | int precopy = dat[j] - lastReadPos; 2328 | CharCopy(dest + curPos, source + lastReadPos, precopy); 2329 | curPos += precopy; 2330 | lastReadPos = dat[j] + oldValue.Length; 2331 | CharCopy(dest + curPos, replace, newValue.Length); 2332 | curPos += newValue.Length; 2333 | } 2334 | 2335 | CharCopy(dest + curPos, source + lastReadPos, length - lastReadPos); 2336 | } 2337 | 2338 | temp.length = nlen; 2339 | return temp; 2340 | } 2341 | } 2342 | 2343 | private CString ReplaceFallback(string oldValue, string newValue, int testedCount) 2344 | { 2345 | int lengthEstimate = length + ((newValue.Length - oldValue.Length) * testedCount); 2346 | CString sb = Alloc(lengthEstimate); 2347 | 2348 | for (int i = 0; i < length;) 2349 | { 2350 | int found = IndexOfOrdinalUnchecked(oldValue, i, length - i); 2351 | 2352 | if (found < 0) 2353 | { 2354 | sb.Append(SubstringUnchecked(i, length - i)); 2355 | break; 2356 | } 2357 | 2358 | sb.Append(SubstringUnchecked(i, found - i)); 2359 | sb.Append(newValue); 2360 | i = found + oldValue.Length; 2361 | } 2362 | 2363 | return sb; 2364 | } 2365 | 2366 | public CString ToLower() 2367 | { 2368 | return ToLower(CultureInfo.CurrentCulture); 2369 | } 2370 | 2371 | public CString ToLower(CultureInfo culture) 2372 | { 2373 | if (culture == null) 2374 | { 2375 | throw new ArgumentNullException("culture"); 2376 | } 2377 | 2378 | if (culture.LCID == 0x007F) 2379 | { 2380 | return ToLowerInvariant(); 2381 | } 2382 | 2383 | return ToLower(culture.TextInfo); 2384 | } 2385 | 2386 | internal CString ToLowerInvariant() 2387 | { 2388 | if (length == 0) 2389 | { 2390 | return this; 2391 | } 2392 | 2393 | for (int i = 0; i < length; i++) 2394 | { 2395 | _buffer[i] = Char.ToLowerInvariant(_buffer[i]); 2396 | } 2397 | 2398 | return this; 2399 | } 2400 | 2401 | internal CString ToLower(TextInfo text) 2402 | { 2403 | if (length == 0) 2404 | { 2405 | return this; 2406 | } 2407 | 2408 | for (int i = 0; i < length; i++) 2409 | { 2410 | _buffer[i] = text.ToLower(_buffer[i]); 2411 | } 2412 | 2413 | return this; 2414 | } 2415 | 2416 | public CString ToUpper() 2417 | { 2418 | return ToUpper(CultureInfo.CurrentCulture); 2419 | } 2420 | 2421 | public CString ToUpper(CultureInfo culture) 2422 | { 2423 | if (culture == null) 2424 | { 2425 | throw new ArgumentNullException("culture"); 2426 | } 2427 | 2428 | if (culture.LCID == 0x007F) 2429 | { 2430 | return ToUpperInvariant(); 2431 | } 2432 | 2433 | return ToUpper(culture.TextInfo); 2434 | } 2435 | 2436 | internal CString ToUpperInvariant() 2437 | { 2438 | if (length == 0) 2439 | { 2440 | return this; 2441 | } 2442 | 2443 | for (int i = 0; i < length; i++) 2444 | { 2445 | _buffer[i] = Char.ToUpperInvariant(_buffer[i]); 2446 | } 2447 | 2448 | return this; 2449 | } 2450 | 2451 | internal unsafe CString ToUpper(TextInfo text) 2452 | { 2453 | if (length == 0) 2454 | { 2455 | return this; 2456 | } 2457 | 2458 | for (int i = 0; i < length; i++) 2459 | { 2460 | _buffer[i] = text.ToUpper(_buffer[i]); 2461 | } 2462 | 2463 | return this; 2464 | } 2465 | 2466 | public override string ToString() 2467 | { 2468 | if (length == 0) 2469 | { 2470 | return string.Empty; 2471 | } 2472 | 2473 | return new string(_buffer, 0, length); 2474 | } 2475 | 2476 | public string ToString(int startIndex, int len) 2477 | { 2478 | if (startIndex < 0 || len < 0 || startIndex > this.length - len) 2479 | { 2480 | throw new ArgumentOutOfRangeException(); 2481 | } 2482 | 2483 | if (length == 0) 2484 | { 2485 | return string.Empty; 2486 | } 2487 | 2488 | return new string(_buffer, startIndex, len); 2489 | } 2490 | 2491 | public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) 2492 | { 2493 | if (destination == null) 2494 | { 2495 | throw new ArgumentNullException("destination"); 2496 | } 2497 | 2498 | if (Length - count < sourceIndex || destination.Length - count < destinationIndex || sourceIndex < 0 || destinationIndex < 0 || count < 0) 2499 | { 2500 | throw new ArgumentOutOfRangeException(); 2501 | } 2502 | 2503 | for (int i = 0; i < count; i++) 2504 | { 2505 | destination[destinationIndex + i] = _buffer[sourceIndex + i]; 2506 | } 2507 | } 2508 | 2509 | public unsafe string CopyToString(string str) 2510 | { 2511 | if (str.Length == length) 2512 | { 2513 | fixed (char* src = _buffer, dest = str) 2514 | { 2515 | CharCopy(dest, src, length); 2516 | } 2517 | 2518 | return str; 2519 | } 2520 | else 2521 | { 2522 | char[] buffer = new char[length]; 2523 | Buffer.BlockCopy(_buffer, 0, buffer, 0, length * sizeof(char)); 2524 | return new string(buffer, 0, length); 2525 | } 2526 | } 2527 | 2528 | public unsafe string CopyToString(string str, int len) 2529 | { 2530 | fixed (char* src = _buffer, dest = str) 2531 | { 2532 | CharCopy(dest, src, len); 2533 | } 2534 | 2535 | return str; 2536 | } 2537 | 2538 | public unsafe string CopyToString(int srcOffset, string dst, int destOffset, int count) 2539 | { 2540 | fixed (char* src = _buffer, dest = dst) 2541 | { 2542 | CharCopy(dest + destOffset, src + srcOffset, count); 2543 | } 2544 | 2545 | return dst; 2546 | } 2547 | 2548 | //internal static StringBuilder FormatHelper(StringBuilder result, IFormatProvider provider, string format, params object[] args) 2549 | //{ 2550 | // if (format == null) 2551 | // { 2552 | // throw new ArgumentNullException("format"); 2553 | // } 2554 | 2555 | // if (args == null) 2556 | // { 2557 | // throw new ArgumentNullException("args"); 2558 | // } 2559 | 2560 | // if (result == null) 2561 | // { 2562 | // /* Try to approximate the size of result to avoid reallocations */ 2563 | // int i, len; 2564 | // len = 0; 2565 | 2566 | // for (i = 0; i < args.Length; ++i) 2567 | // { 2568 | // string s = args[i] as string; 2569 | 2570 | // if (s != null) 2571 | // { 2572 | // len += s.Length; 2573 | // } 2574 | // else 2575 | // { 2576 | // break; 2577 | // } 2578 | // } 2579 | 2580 | // if (i == args.Length) 2581 | // { 2582 | // result = new StringBuilder(len + format.Length); 2583 | // } 2584 | // else 2585 | // { 2586 | // result = new StringBuilder(); 2587 | // } 2588 | // } 2589 | 2590 | // int ptr = 0; 2591 | // int start = ptr; 2592 | 2593 | // while (ptr < format.Length) 2594 | // { 2595 | // char c = format[ptr++]; 2596 | 2597 | // if (c == '{') 2598 | // { 2599 | // result.Append(format, start, ptr - start - 1); 2600 | 2601 | // // check for escaped open bracket 2602 | 2603 | // if (format[ptr] == '{') 2604 | // { 2605 | // start = ptr++; 2606 | // continue; 2607 | // } 2608 | 2609 | // // parse specifier 2610 | 2611 | // int n, width; 2612 | // bool left_align; 2613 | // CString arg_format; 2614 | // ParseFormatSpecifier(format, ref ptr, out n, out width, out left_align, out arg_format); 2615 | 2616 | // if (n >= args.Length) 2617 | // { 2618 | // throw new FormatException("Index (zero based) must be greater than or equal to zero and less than the size of the argument list."); 2619 | // } 2620 | 2621 | // // format argument 2622 | 2623 | // object arg = args[n]; 2624 | // string str; 2625 | // ICustomFormatter formatter = null; 2626 | 2627 | // if (provider != null) 2628 | // { 2629 | // formatter = provider.GetFormat(typeof(ICustomFormatter)) as ICustomFormatter; 2630 | // } 2631 | // if (arg == null) 2632 | // { 2633 | // str = String.Empty; 2634 | // } 2635 | 2636 | // else if (formatter != null) 2637 | // { 2638 | // str = formatter.Format(arg_format.ToString(), arg, provider); //todo:fixed tostring 2639 | // } 2640 | // else if (arg is IFormattable) 2641 | // { 2642 | // str = ((IFormattable)arg).ToString(arg_format == , provider); 2643 | // } 2644 | // else 2645 | // { 2646 | // str = arg.ToString(); 2647 | // } 2648 | 2649 | // // pad formatted string and append to result 2650 | 2651 | // if (width > str.Length) 2652 | // { 2653 | // const char padchar = ' '; 2654 | // int padlen = width - str.Length; 2655 | 2656 | // if (left_align) 2657 | // { 2658 | // result.Append(str); 2659 | // result.Append(padchar, padlen); 2660 | // } 2661 | // else 2662 | // { 2663 | // result.Append(padchar, padlen); 2664 | // result.Append(str); 2665 | // } 2666 | // } 2667 | // else 2668 | // { 2669 | // result.Append(str); 2670 | // } 2671 | 2672 | // start = ptr; 2673 | // } 2674 | // else if (c == '}' && ptr < format.Length && format[ptr] == '}') 2675 | // { 2676 | // result.Append(format, start, ptr - start - 1); 2677 | // start = ptr++; 2678 | // } 2679 | // else if (c == '}') 2680 | // { 2681 | // throw new FormatException("Input string was not in a correct format."); 2682 | // } 2683 | // } 2684 | 2685 | // if (start < format.Length) 2686 | // { 2687 | // result.Append(format, start, format.Length - start); 2688 | // } 2689 | 2690 | // return result; 2691 | //} 2692 | 2693 | //public static CString Copy(CString str) 2694 | //{ 2695 | // if (str == null) 2696 | // { 2697 | // throw new ArgumentNullException("str"); 2698 | // } 2699 | 2700 | // return str.SubstringUnchecked(0, str.length); 2701 | //} 2702 | 2703 | public unsafe static CString Concat(CString str0, CString str1) 2704 | { 2705 | if (str0 == null || str0.Length == 0) 2706 | { 2707 | if (str1 == null || str1.Length == 0) 2708 | { 2709 | return Alloc(0); 2710 | } 2711 | 2712 | return str1; 2713 | } 2714 | 2715 | if (str1 == null || str1.Length == 0) 2716 | { 2717 | return str0; 2718 | } 2719 | 2720 | int count = str0.length + str1.length; 2721 | CString tmp = Alloc(count); 2722 | 2723 | fixed (char* dest = tmp._buffer, src = str0._buffer) 2724 | { 2725 | CharCopy(dest, src, str0.length); 2726 | } 2727 | 2728 | fixed (char* dest = tmp._buffer, src = str1._buffer) 2729 | { 2730 | CharCopy(dest + str0.Length, src, str1.length); 2731 | } 2732 | 2733 | tmp.length = count; 2734 | return tmp; 2735 | } 2736 | 2737 | public unsafe static CString Concat(CString str0, CString str1, CString str2) 2738 | { 2739 | if (str0 == null || str0.Length == 0) 2740 | { 2741 | if (str1 == null || str1.Length == 0) 2742 | { 2743 | if (str2 == null || str2.Length == 0) 2744 | return Alloc(0); 2745 | return str2; 2746 | } 2747 | else 2748 | { 2749 | if (str2 == null || str2.Length == 0) 2750 | return str1; 2751 | } 2752 | str0 = Alloc(0); 2753 | } 2754 | else 2755 | { 2756 | if (str1 == null || str1.Length == 0) 2757 | { 2758 | if (str2 == null || str2.Length == 0) 2759 | return str0; 2760 | else 2761 | str1 = Alloc(0); 2762 | } 2763 | else 2764 | { 2765 | if (str2 == null || str2.Length == 0) 2766 | str2 = Alloc(0); 2767 | } 2768 | } 2769 | 2770 | CString tmp = Alloc(str0.length + str1.length + str2.length); 2771 | 2772 | if (str0.Length != 0) 2773 | { 2774 | fixed (char* dest = tmp._buffer, src = str0._buffer) 2775 | { 2776 | CharCopy(dest, src, str0.length); 2777 | } 2778 | } 2779 | if (str1.Length != 0) 2780 | { 2781 | fixed (char* dest = tmp._buffer, src = str1._buffer) 2782 | { 2783 | CharCopy(dest + str0.Length, src, str1.length); 2784 | } 2785 | } 2786 | if (str2.Length != 0) 2787 | { 2788 | fixed (char* dest = tmp._buffer, src = str2._buffer) 2789 | { 2790 | CharCopy(dest + str0.Length + str1.Length, src, str2.length); 2791 | } 2792 | } 2793 | 2794 | tmp.length = str0.length + str1.length + str2.length; 2795 | return tmp; 2796 | } 2797 | 2798 | public unsafe static CString Concat(CString str0, CString str1, CString str2, CString str3) 2799 | { 2800 | if (str0 == null && str1 == null && str2 == null && str3 == null) 2801 | return String.Empty; 2802 | 2803 | if (str0 == null) 2804 | str0 = Alloc(0); 2805 | if (str1 == null) 2806 | str1 = Alloc(0); 2807 | if (str2 == null) 2808 | str2 = Alloc(0); 2809 | if (str3 == null) 2810 | str3 = Alloc(0); 2811 | 2812 | CString tmp = Alloc(str0.length + str1.length + str2.length + str3.length); 2813 | 2814 | if (str0.Length != 0) 2815 | { 2816 | fixed (char* dest = tmp._buffer, src = str0._buffer) 2817 | { 2818 | CharCopy(dest, src, str0.length); 2819 | } 2820 | } 2821 | if (str1.Length != 0) 2822 | { 2823 | fixed (char* dest = tmp._buffer, src = str1._buffer) 2824 | { 2825 | CharCopy(dest + str0.Length, src, str1.length); 2826 | } 2827 | } 2828 | if (str2.Length != 0) 2829 | { 2830 | fixed (char* dest = tmp._buffer, src = str2._buffer) 2831 | { 2832 | CharCopy(dest + str0.Length + str1.Length, src, str2.length); 2833 | } 2834 | } 2835 | if (str3.Length != 0) 2836 | { 2837 | fixed (char* dest = tmp._buffer, src = str3._buffer) 2838 | { 2839 | CharCopy(dest + str0.Length + str1.Length + str2.Length, src, str3.length); 2840 | } 2841 | } 2842 | 2843 | tmp.length = str0.length + str1.length + str2.length + str3.length; 2844 | return tmp; 2845 | } 2846 | 2847 | public unsafe CString Append(CString right) 2848 | { 2849 | int count = length + right.length; 2850 | EnsureCapacity(count); 2851 | 2852 | fixed (char* dest = _buffer, src = right._buffer) 2853 | { 2854 | CharCopy(dest + length, src, right.length); 2855 | } 2856 | 2857 | length = count; 2858 | return this; 2859 | } 2860 | 2861 | public unsafe CString Append(char value) 2862 | { 2863 | EnsureCapacity(length + 1); 2864 | _buffer[length++] = value; 2865 | return this; 2866 | } 2867 | 2868 | public unsafe CString Append(short value) 2869 | { 2870 | EnsureCapacity(length + 8); 2871 | 2872 | fixed (char* p = _buffer) 2873 | { 2874 | length += NumberFormatter.NumberToString(p + length, (int)value, null); 2875 | } 2876 | 2877 | return this; 2878 | } 2879 | 2880 | public unsafe CString Append(ushort value) 2881 | { 2882 | EnsureCapacity(length + 8); 2883 | 2884 | fixed (char* p = _buffer) 2885 | { 2886 | length += NumberFormatter.NumberToString(p + length, (int)value, null); 2887 | } 2888 | 2889 | return this; 2890 | } 2891 | 2892 | public unsafe CString Append(byte value) 2893 | { 2894 | EnsureCapacity(length + 8); 2895 | 2896 | fixed (char* p = _buffer) 2897 | { 2898 | length += NumberFormatter.NumberToString(p + length, (int)value, null); 2899 | } 2900 | 2901 | return this; 2902 | } 2903 | 2904 | public unsafe CString Append(sbyte value) 2905 | { 2906 | EnsureCapacity(length + 8); 2907 | 2908 | fixed (char* p = _buffer) 2909 | { 2910 | length += NumberFormatter.NumberToString(p + length, (int)value, null); 2911 | } 2912 | 2913 | return this; 2914 | } 2915 | 2916 | public unsafe CString Append(int value) 2917 | { 2918 | EnsureCapacity(length + 16); 2919 | 2920 | fixed (char* p = _buffer) 2921 | { 2922 | length += NumberFormatter.NumberToString(p + length, value, null); 2923 | } 2924 | 2925 | return this; 2926 | } 2927 | 2928 | public unsafe CString Append(uint value) 2929 | { 2930 | EnsureCapacity(length + 16); 2931 | 2932 | fixed (char* p = _buffer) 2933 | { 2934 | length += NumberFormatter.NumberToString(p + length, value, null); 2935 | } 2936 | 2937 | return this; 2938 | } 2939 | 2940 | public unsafe CString Append(long value) 2941 | { 2942 | EnsureCapacity(length + 32); 2943 | 2944 | fixed (char* p = _buffer) 2945 | { 2946 | length += NumberFormatter.NumberToString(p + length, value, null); 2947 | } 2948 | 2949 | return this; 2950 | } 2951 | 2952 | public unsafe CString Append(ulong value) 2953 | { 2954 | EnsureCapacity(length + 32); 2955 | 2956 | fixed (char* p = _buffer) 2957 | { 2958 | length += NumberFormatter.NumberToString(p + length, value, null); 2959 | } 2960 | 2961 | return this; 2962 | } 2963 | 2964 | public unsafe CString Append(float value) 2965 | { 2966 | EnsureCapacity(length + 16); 2967 | 2968 | fixed (char* p = _buffer) 2969 | { 2970 | length += NumberFormatter.NumberToString(p + length, value, null); 2971 | } 2972 | 2973 | return this; 2974 | } 2975 | 2976 | public unsafe CString Append(double value) 2977 | { 2978 | EnsureCapacity(length + 32); 2979 | 2980 | fixed (char* p = _buffer) 2981 | { 2982 | length += NumberFormatter.NumberToString(p + length, value, null); 2983 | } 2984 | 2985 | return this; 2986 | } 2987 | 2988 | public unsafe CString Append(bool value) 2989 | { 2990 | EnsureCapacity(length + 5); 2991 | 2992 | if (value) 2993 | { 2994 | _buffer[length++] = 'T'; 2995 | _buffer[length++] = 'r'; 2996 | _buffer[length++] = 'u'; 2997 | _buffer[length++] = 'e'; 2998 | } 2999 | else 3000 | { 3001 | _buffer[length++] = 'F'; 3002 | _buffer[length++] = 'a'; 3003 | _buffer[length++] = 'l'; 3004 | _buffer[length++] = 's'; 3005 | _buffer[length++] = 'e'; 3006 | } 3007 | 3008 | return this; 3009 | } 3010 | 3011 | public unsafe CString Append(string value) 3012 | { 3013 | int count = length + value.Length; 3014 | EnsureCapacity(count); 3015 | 3016 | fixed(char* dest = _buffer, src = value) 3017 | { 3018 | CharCopy(dest + length, src, value.Length); 3019 | } 3020 | 3021 | length = count; 3022 | return this; 3023 | } 3024 | 3025 | public unsafe CString Append(char[] value) 3026 | { 3027 | if (value == null) 3028 | { 3029 | return this; 3030 | } 3031 | 3032 | int size = length + value.Length; 3033 | EnsureCapacity(size); 3034 | 3035 | fixed (char* dest = _buffer, src = value) 3036 | { 3037 | CharCopy(dest + length, src, value.Length); 3038 | } 3039 | 3040 | length = size; 3041 | return this; 3042 | } 3043 | 3044 | public CString Append(char c, int repeatCount) 3045 | { 3046 | if (repeatCount < 0) 3047 | { 3048 | throw new ArgumentOutOfRangeException("count", "Cannot be negative."); 3049 | } 3050 | 3051 | EnsureCapacity(length + repeatCount); 3052 | 3053 | for (int i = 0; i < repeatCount; i++) 3054 | { 3055 | _buffer[length + i] = c; 3056 | } 3057 | 3058 | length += repeatCount; 3059 | return this; 3060 | } 3061 | 3062 | public CString Append(char[] value, int startIndex, int charCount) 3063 | { 3064 | int count = length + charCount; 3065 | EnsureCapacity(count); 3066 | 3067 | for (int i = 0; i < charCount; i++) 3068 | { 3069 | _buffer[length + i] = value[startIndex + i]; 3070 | } 3071 | 3072 | length = count; 3073 | return this; 3074 | } 3075 | 3076 | public unsafe CString Append(string value, int startIndex, int count) 3077 | { 3078 | if (value == null) 3079 | { 3080 | if (startIndex != 0 && count != 0) 3081 | { 3082 | throw new ArgumentNullException("value"); 3083 | } 3084 | 3085 | return this; 3086 | } 3087 | 3088 | if (count < 0 || startIndex < 0 || startIndex > value.Length - count) 3089 | { 3090 | throw new ArgumentOutOfRangeException(); 3091 | } 3092 | 3093 | int size = count + length; 3094 | EnsureCapacity(size); 3095 | 3096 | fixed (char* dest = _buffer, src = value) 3097 | { 3098 | CharCopy(dest + length, src + startIndex, count); 3099 | } 3100 | 3101 | length = size; 3102 | return this; 3103 | } 3104 | 3105 | public unsafe CString AppendFormat(string format, short value) 3106 | { 3107 | EnsureCapacity(length + 8); 3108 | 3109 | fixed (char* p = _buffer) 3110 | { 3111 | length += NumberFormatter.NumberToString(p + length, format, (int)value, null); 3112 | } 3113 | 3114 | return this; 3115 | } 3116 | 3117 | public unsafe CString AppendFormat(string format, ushort value) 3118 | { 3119 | EnsureCapacity(length + 8); 3120 | 3121 | fixed (char* p = _buffer) 3122 | { 3123 | length += NumberFormatter.NumberToString(p + length, format, (int)value, null); 3124 | } 3125 | 3126 | return this; 3127 | } 3128 | 3129 | public unsafe CString AppendFormat(string format, byte value) 3130 | { 3131 | EnsureCapacity(length + 8); 3132 | 3133 | fixed (char* p = _buffer) 3134 | { 3135 | length += NumberFormatter.NumberToString(p + length, format, (int)value, null); 3136 | } 3137 | 3138 | return this; 3139 | } 3140 | 3141 | public unsafe CString AppendFormat(string format, sbyte value) 3142 | { 3143 | EnsureCapacity(length + 8); 3144 | 3145 | fixed (char* p = _buffer) 3146 | { 3147 | length += NumberFormatter.NumberToString(p + length, format, (int)value, null); 3148 | } 3149 | 3150 | return this; 3151 | } 3152 | 3153 | public unsafe CString AppendFormat(string format, int value) 3154 | { 3155 | EnsureCapacity(length + 32); 3156 | 3157 | fixed (char* p = _buffer) 3158 | { 3159 | length += NumberFormatter.NumberToString(p + length, format, value, null); 3160 | } 3161 | 3162 | return this; 3163 | } 3164 | 3165 | public unsafe CString AppendFormat(string format, uint value) 3166 | { 3167 | EnsureCapacity(length + 32); 3168 | 3169 | fixed (char* p = _buffer) 3170 | { 3171 | length += NumberFormatter.NumberToString(p + length, format, value, null); 3172 | } 3173 | 3174 | return this; 3175 | } 3176 | 3177 | public unsafe CString AppendFormat(string format, long value) 3178 | { 3179 | EnsureCapacity(length + 64); 3180 | 3181 | fixed (char* p = _buffer) 3182 | { 3183 | length += NumberFormatter.NumberToString(p + length, format, value, null); 3184 | } 3185 | 3186 | return this; 3187 | } 3188 | 3189 | public unsafe CString AppendFormat(string format, ulong value) 3190 | { 3191 | EnsureCapacity(length + 64); 3192 | 3193 | fixed (char* p = _buffer) 3194 | { 3195 | length += NumberFormatter.NumberToString(p + length, format, value, null); 3196 | } 3197 | 3198 | return this; 3199 | } 3200 | 3201 | public unsafe CString AppendFormat(string format, float value) 3202 | { 3203 | EnsureCapacity(length + 32); 3204 | 3205 | fixed (char* p = _buffer) 3206 | { 3207 | length += NumberFormatter.NumberToString(p + length, format, value, null); 3208 | } 3209 | 3210 | return this; 3211 | } 3212 | 3213 | public unsafe CString AppendFormat(string format, double value) 3214 | { 3215 | EnsureCapacity(length + 64); 3216 | 3217 | fixed (char* p = _buffer) 3218 | { 3219 | length += NumberFormatter.NumberToString(p + length, format, value, null); 3220 | } 3221 | 3222 | return this; 3223 | } 3224 | 3225 | 3226 | public CString AppendLine() 3227 | { 3228 | return Append(NewLine); 3229 | } 3230 | 3231 | public CString AppendLine(string value) 3232 | { 3233 | return Append(value).Append(NewLine); 3234 | } 3235 | 3236 | public CString Insert(int index, char[] value) 3237 | { 3238 | return Insert(index, value, 0, value.Length); 3239 | } 3240 | 3241 | public unsafe CString Insert(int index, char[] value, int startIndex, int count) 3242 | { 3243 | if (value == null) 3244 | { 3245 | if (startIndex == 0 && count == 0) 3246 | { 3247 | return this; 3248 | } 3249 | 3250 | throw new ArgumentNullException("value"); 3251 | } 3252 | 3253 | if (index > length || index < 0 || count < 0 || startIndex < 0) 3254 | { 3255 | throw new ArgumentOutOfRangeException(); 3256 | } 3257 | 3258 | EnsureCapacity(length + count); 3259 | 3260 | fixed (char* dest = _buffer, src = value) 3261 | { 3262 | CharCopyReverse(dest + index + count, dest + index, length - index); 3263 | CharCopy(dest + index, src + startIndex, count); 3264 | } 3265 | 3266 | length += count; 3267 | return this; 3268 | } 3269 | 3270 | public unsafe CString Insert(int index, string value) 3271 | { 3272 | if (index > length || index < 0) 3273 | { 3274 | throw new ArgumentOutOfRangeException(); 3275 | } 3276 | 3277 | if (value == null || value.Length == 0) 3278 | { 3279 | return this; 3280 | } 3281 | 3282 | EnsureCapacity(length + value.Length); 3283 | 3284 | fixed(char* dest = _buffer, src = value) 3285 | { 3286 | CharCopyReverse(dest + index + value.Length, dest + index, length - index); 3287 | CharCopy(dest + index, src, value.Length); 3288 | } 3289 | 3290 | length += value.Length; 3291 | return this; 3292 | } 3293 | 3294 | public CString Insert(int index, string value, int count) 3295 | { 3296 | if (count < 0) 3297 | { 3298 | throw new ArgumentOutOfRangeException(); 3299 | } 3300 | 3301 | if (value != null && value != String.Empty) 3302 | { 3303 | for (int i = 0; i < count; i++) 3304 | { 3305 | Insert(index, value); 3306 | } 3307 | } 3308 | 3309 | return this; 3310 | } 3311 | 3312 | public unsafe CString Insert(int index, CString value) 3313 | { 3314 | Insert(index, value._buffer, 0, value.length); 3315 | return this; 3316 | } 3317 | 3318 | static char[] numbuffer = new char[64]; 3319 | 3320 | public CString Insert(int index, bool value) 3321 | { 3322 | return Insert(index, value ? "True" : "False"); 3323 | } 3324 | 3325 | public CString Insert(int index, byte value) 3326 | { 3327 | return Insert(index, (int)value); 3328 | } 3329 | 3330 | public CString Insert(int index, short value) 3331 | { 3332 | return Insert(index, (int)value); 3333 | } 3334 | 3335 | public CString Insert(int index, sbyte value) 3336 | { 3337 | return Insert(index, (int)value); 3338 | } 3339 | 3340 | public CString Insert(int index, ushort value) 3341 | { 3342 | return Insert(index, (int)value); 3343 | } 3344 | 3345 | public unsafe CString Insert(int index, char value) 3346 | { 3347 | if (index > length || index < 0) 3348 | { 3349 | throw new ArgumentOutOfRangeException("index"); 3350 | } 3351 | 3352 | EnsureCapacity(length + 1); 3353 | 3354 | fixed (char* dest = _buffer) 3355 | { 3356 | CharCopyReverse(dest + index + 1, dest + index, length - index); 3357 | } 3358 | 3359 | _buffer[index] = value; 3360 | ++length; 3361 | return this; 3362 | } 3363 | 3364 | public unsafe CString Insert(int index, float value) 3365 | { 3366 | int len = -1; 3367 | 3368 | fixed (char* p = numbuffer) 3369 | { 3370 | len = NumberFormatter.NumberToString(p, value, null); 3371 | } 3372 | 3373 | return Insert(index, numbuffer, 0, len); 3374 | } 3375 | 3376 | public unsafe CString Insert(int index, double value) 3377 | { 3378 | int len = -1; 3379 | 3380 | fixed (char* p = numbuffer) 3381 | { 3382 | len = NumberFormatter.NumberToString(p, value, null); 3383 | } 3384 | 3385 | return Insert(index, numbuffer, 0, len); 3386 | } 3387 | 3388 | public unsafe CString Insert(int index, int value) 3389 | { 3390 | int len = -1; 3391 | 3392 | fixed (char* p = numbuffer) 3393 | { 3394 | len = NumberFormatter.NumberToString(p, value, null); 3395 | } 3396 | 3397 | return Insert(index, numbuffer, 0, len); 3398 | } 3399 | 3400 | public unsafe CString Insert(int index, long value) 3401 | { 3402 | int len = -1; 3403 | 3404 | fixed (char* p = numbuffer) 3405 | { 3406 | len = NumberFormatter.NumberToString(p, value, null); 3407 | } 3408 | 3409 | return Insert(index, numbuffer, 0, len); 3410 | } 3411 | 3412 | public unsafe CString Insert(int index, uint value) 3413 | { 3414 | int len = -1; 3415 | 3416 | fixed (char* p = numbuffer) 3417 | { 3418 | len = NumberFormatter.NumberToString(p, value, null); 3419 | } 3420 | 3421 | return Insert(index, numbuffer, 0, len); 3422 | } 3423 | 3424 | public unsafe CString Insert(int index, ulong value) 3425 | { 3426 | int len = -1; 3427 | 3428 | fixed (char* p = numbuffer) 3429 | { 3430 | len = NumberFormatter.NumberToString(p, value, null); 3431 | } 3432 | 3433 | return Insert(index, numbuffer, 0, len); 3434 | } 3435 | 3436 | public CString Insert(int index, object value) 3437 | { 3438 | return Insert(index, value.ToString()); 3439 | } 3440 | 3441 | public static CString Join(string separator, CString[] value) 3442 | { 3443 | if (value == null) 3444 | { 3445 | throw new ArgumentNullException("value"); 3446 | } 3447 | 3448 | if (separator == null) 3449 | { 3450 | separator = String.Empty; 3451 | } 3452 | 3453 | return JoinUnchecked(separator, value, 0, value.Length); 3454 | } 3455 | 3456 | public static CString Join(string separator, CString[] value, int startIndex, int count) 3457 | { 3458 | if (value == null) 3459 | { 3460 | throw new ArgumentNullException("value"); 3461 | } 3462 | 3463 | if (startIndex < 0) 3464 | { 3465 | throw new ArgumentOutOfRangeException("startIndex", "< 0"); 3466 | } 3467 | 3468 | if (count < 0) 3469 | { 3470 | throw new ArgumentOutOfRangeException("count", "< 0"); 3471 | } 3472 | 3473 | if (startIndex > value.Length - count) 3474 | { 3475 | throw new ArgumentOutOfRangeException("startIndex", "startIndex + count > value.length"); 3476 | } 3477 | 3478 | if (startIndex == value.Length) 3479 | { 3480 | return String.Empty; 3481 | } 3482 | 3483 | if (separator == null) 3484 | { 3485 | separator = String.Empty; 3486 | } 3487 | 3488 | return JoinUnchecked(separator, value, startIndex, count); 3489 | } 3490 | 3491 | private static unsafe CString JoinUnchecked(string separator, CString[] value, int startIndex, int count) 3492 | { 3493 | int length = 0; 3494 | int maxIndex = startIndex + count; 3495 | // Precount the number of characters that the resulting string will have 3496 | for (int i = startIndex; i < maxIndex; i++) 3497 | { 3498 | CString s = value[i]; 3499 | 3500 | if (s != null) 3501 | { 3502 | length += s.length; 3503 | } 3504 | } 3505 | 3506 | length += separator.Length * (count - 1); 3507 | 3508 | if (length <= 0) 3509 | { 3510 | return String.Empty; 3511 | } 3512 | 3513 | CString tmp = Alloc(length); 3514 | maxIndex--; 3515 | 3516 | fixed (char* dest = tmp._buffer, sepsrc = separator) 3517 | { 3518 | // Copy each string from value except the last one and add a separator for each 3519 | int pos = 0; 3520 | for (int i = startIndex; i < maxIndex; i++) 3521 | { 3522 | CString source = value[i]; 3523 | 3524 | if (source != null) 3525 | { 3526 | if (source.Length > 0) 3527 | { 3528 | fixed (char* src = source._buffer) 3529 | CharCopy(dest + pos, src, source.Length); 3530 | pos += source.Length; 3531 | } 3532 | } 3533 | if (separator.Length > 0) 3534 | { 3535 | CharCopy(dest + pos, sepsrc, separator.Length); 3536 | pos += separator.Length; 3537 | } 3538 | } 3539 | // Append last string that does not get an additional separator 3540 | CString sourceLast = value[maxIndex]; 3541 | 3542 | if (sourceLast != null) 3543 | { 3544 | if (sourceLast.Length > 0) 3545 | { 3546 | fixed (char* src = sourceLast._buffer) 3547 | CharCopy(dest + pos, src, sourceLast.Length); 3548 | } 3549 | } 3550 | } 3551 | 3552 | return tmp; 3553 | } 3554 | 3555 | 3556 | //private static void ParseFormatSpecifier(string str, ref int ptr, out int n, out int width, out bool left_align, out CString format) 3557 | //{ 3558 | // try 3559 | // { 3560 | // // N = argument number (non-negative integer) 3561 | // n = ParseDecimal(str, ref ptr); 3562 | 3563 | // if (n < 0) 3564 | // { 3565 | // throw new FormatException("Input string was not in a correct format."); 3566 | // } 3567 | 3568 | // // M = width (non-negative integer) 3569 | 3570 | // if (str[ptr] == ',') 3571 | // { 3572 | // // White space between ',' and number or sign. 3573 | // ++ptr; 3574 | // while (Char.IsWhiteSpace(str[ptr])) 3575 | // { 3576 | // ++ptr; 3577 | // } 3578 | 3579 | // int start = ptr; 3580 | // format = new CString(str.Substring(start, ptr - start)); 3581 | // left_align = (str[ptr] == '-'); 3582 | 3583 | // if (left_align) 3584 | // { 3585 | // ++ptr; 3586 | // } 3587 | 3588 | // width = ParseDecimal(str, ref ptr); 3589 | 3590 | // if (width < 0) 3591 | // { 3592 | // throw new FormatException("Input string was not in a correct format."); 3593 | // } 3594 | // } 3595 | // else 3596 | // { 3597 | // width = 0; 3598 | // left_align = false; 3599 | // format = new CString(); 3600 | // } 3601 | 3602 | // if (str[ptr] == ':') 3603 | // { 3604 | // int start = ++ptr; 3605 | 3606 | // while (str[ptr] != '}') 3607 | // { 3608 | // ++ptr; 3609 | // } 3610 | 3611 | // format.Append(str.Substring(start, ptr - start)); 3612 | // } 3613 | // else 3614 | // { 3615 | // format = null; 3616 | // } 3617 | 3618 | // if (str[ptr++] != '}') 3619 | // { 3620 | // throw new FormatException("Input string was not in a correct format."); 3621 | // } 3622 | // } 3623 | // catch (IndexOutOfRangeException) 3624 | // { 3625 | // throw new FormatException("Input string was not in a correct format."); 3626 | // } 3627 | //} 3628 | 3629 | //private static void ParseFormatSpecifier(string str, ref int ptr, out int n, out int width, out bool left_align, out string format) 3630 | //{ 3631 | // try 3632 | // { 3633 | // // N = argument number (non-negative integer) 3634 | // n = ParseDecimal(str, ref ptr); 3635 | 3636 | // if (n < 0) 3637 | // { 3638 | // throw new FormatException("Input string was not in a correct format."); 3639 | // } 3640 | 3641 | // // M = width (non-negative integer) 3642 | 3643 | // if (str[ptr] == ',') 3644 | // { 3645 | // // White space between ',' and number or sign. 3646 | // ++ptr; 3647 | // while (Char.IsWhiteSpace(str[ptr])) 3648 | // { 3649 | // ++ptr; 3650 | // } 3651 | 3652 | // int start = ptr; 3653 | // format = str.Substring(start, ptr - start); 3654 | // left_align = (str[ptr] == '-'); 3655 | 3656 | // if (left_align) 3657 | // { 3658 | // ++ptr; 3659 | // } 3660 | 3661 | // width = ParseDecimal(str, ref ptr); 3662 | 3663 | // if (width < 0) 3664 | // { 3665 | // throw new FormatException("Input string was not in a correct format."); 3666 | // } 3667 | // } 3668 | // else 3669 | // { 3670 | // width = 0; 3671 | // left_align = false; 3672 | // format = string.Empty; 3673 | // } 3674 | 3675 | // if (str[ptr] == ':') 3676 | // { 3677 | // int start = ++ptr; 3678 | 3679 | // while (str[ptr] != '}') 3680 | // { 3681 | // ++ptr; 3682 | // } 3683 | 3684 | // format += str.Substring(start, ptr - start); 3685 | // } 3686 | // else 3687 | // { 3688 | // format = null; 3689 | // } 3690 | 3691 | // if (str[ptr++] != '}') 3692 | // { 3693 | // throw new FormatException("Input string was not in a correct format."); 3694 | // } 3695 | // } 3696 | // catch (IndexOutOfRangeException) 3697 | // { 3698 | // throw new FormatException("Input string was not in a correct format."); 3699 | // } 3700 | //} 3701 | 3702 | 3703 | //private static int ParseDecimal(string str, ref int ptr) 3704 | //{ 3705 | // int p = ptr; 3706 | // int n = 0; 3707 | // while (true) 3708 | // { 3709 | // char c = str[p]; 3710 | // if (c < '0' || '9' < c) break; 3711 | // n = n * 10 + c - '0'; 3712 | // ++p; 3713 | // } 3714 | 3715 | // if (p == ptr) 3716 | // { 3717 | // return -1; 3718 | // } 3719 | 3720 | // ptr = p; 3721 | // return n; 3722 | //} 3723 | 3724 | internal static unsafe void memset(byte* dest, int val, int len) 3725 | { 3726 | if (len < 8) 3727 | { 3728 | while (len != 0) 3729 | { 3730 | *dest = (byte)val; 3731 | ++dest; 3732 | --len; 3733 | } 3734 | return; 3735 | } 3736 | 3737 | if (val != 0) 3738 | { 3739 | val = val | (val << 8); 3740 | val = val | (val << 16); 3741 | } 3742 | // align to 4 3743 | int rest = (int)dest & 3; 3744 | 3745 | if (rest != 0) 3746 | { 3747 | rest = 4 - rest; 3748 | len -= rest; 3749 | do 3750 | { 3751 | *dest = (byte)val; 3752 | ++dest; 3753 | --rest; 3754 | } while (rest != 0); 3755 | } 3756 | 3757 | while (len >= 16) 3758 | { 3759 | ((int*)dest)[0] = val; 3760 | ((int*)dest)[1] = val; 3761 | ((int*)dest)[2] = val; 3762 | ((int*)dest)[3] = val; 3763 | dest += 16; 3764 | len -= 16; 3765 | } 3766 | 3767 | while (len >= 4) 3768 | { 3769 | ((int*)dest)[0] = val; 3770 | dest += 4; 3771 | len -= 4; 3772 | } 3773 | // tail bytes 3774 | while (len > 0) 3775 | { 3776 | *dest = (byte)val; 3777 | dest++; 3778 | len--; 3779 | } 3780 | } 3781 | 3782 | static unsafe void memcpy4(byte* dest, byte* src, int size) 3783 | { 3784 | /*while (size >= 32) { 3785 | // using long is better than int and slower than double 3786 | // FIXME: enable this only on correct alignment or on platforms 3787 | // that can tolerate unaligned reads/writes of doubles 3788 | ((double*)dest) [0] = ((double*)src) [0]; 3789 | ((double*)dest) [1] = ((double*)src) [1]; 3790 | ((double*)dest) [2] = ((double*)src) [2]; 3791 | ((double*)dest) [3] = ((double*)src) [3]; 3792 | dest += 32; 3793 | src += 32; 3794 | size -= 32; 3795 | }*/ 3796 | while (size >= 16) 3797 | { 3798 | ((int*)dest)[0] = ((int*)src)[0]; 3799 | ((int*)dest)[1] = ((int*)src)[1]; 3800 | ((int*)dest)[2] = ((int*)src)[2]; 3801 | ((int*)dest)[3] = ((int*)src)[3]; 3802 | dest += 16; 3803 | src += 16; 3804 | size -= 16; 3805 | } 3806 | while (size >= 4) 3807 | { 3808 | ((int*)dest)[0] = ((int*)src)[0]; 3809 | dest += 4; 3810 | src += 4; 3811 | size -= 4; 3812 | } 3813 | while (size > 0) 3814 | { 3815 | ((byte*)dest)[0] = ((byte*)src)[0]; 3816 | dest += 1; 3817 | src += 1; 3818 | --size; 3819 | } 3820 | } 3821 | static unsafe void memcpy2(byte* dest, byte* src, int size) 3822 | { 3823 | while (size >= 8) 3824 | { 3825 | ((short*)dest)[0] = ((short*)src)[0]; 3826 | ((short*)dest)[1] = ((short*)src)[1]; 3827 | ((short*)dest)[2] = ((short*)src)[2]; 3828 | ((short*)dest)[3] = ((short*)src)[3]; 3829 | dest += 8; 3830 | src += 8; 3831 | size -= 8; 3832 | } 3833 | 3834 | while (size >= 2) 3835 | { 3836 | ((short*)dest)[0] = ((short*)src)[0]; 3837 | dest += 2; 3838 | src += 2; 3839 | size -= 2; 3840 | } 3841 | 3842 | if (size > 0) 3843 | { 3844 | ((byte*)dest)[0] = ((byte*)src)[0]; 3845 | } 3846 | } 3847 | static unsafe void memcpy1(byte* dest, byte* src, int size) 3848 | { 3849 | while (size >= 8) 3850 | { 3851 | ((byte*)dest)[0] = ((byte*)src)[0]; 3852 | ((byte*)dest)[1] = ((byte*)src)[1]; 3853 | ((byte*)dest)[2] = ((byte*)src)[2]; 3854 | ((byte*)dest)[3] = ((byte*)src)[3]; 3855 | ((byte*)dest)[4] = ((byte*)src)[4]; 3856 | ((byte*)dest)[5] = ((byte*)src)[5]; 3857 | ((byte*)dest)[6] = ((byte*)src)[6]; 3858 | ((byte*)dest)[7] = ((byte*)src)[7]; 3859 | dest += 8; 3860 | src += 8; 3861 | size -= 8; 3862 | } 3863 | while (size >= 2) 3864 | { 3865 | ((byte*)dest)[0] = ((byte*)src)[0]; 3866 | ((byte*)dest)[1] = ((byte*)src)[1]; 3867 | dest += 2; 3868 | src += 2; 3869 | size -= 2; 3870 | } 3871 | 3872 | if (size > 0) 3873 | { 3874 | ((byte*)dest)[0] = ((byte*)src)[0]; 3875 | } 3876 | } 3877 | 3878 | internal static unsafe void memcpy(byte* dest, byte* src, int size) 3879 | { 3880 | // FIXME: if pointers are not aligned, try to align them 3881 | // so a faster routine can be used. Handle the case where 3882 | // the pointers can't be reduced to have the same alignment 3883 | // (just ignore the issue on x86?) 3884 | if ((((int)dest | (int)src) & 3) != 0) 3885 | { 3886 | if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) 3887 | { 3888 | dest[0] = src[0]; 3889 | ++dest; 3890 | ++src; 3891 | --size; 3892 | } 3893 | if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) 3894 | { 3895 | ((short*)dest)[0] = ((short*)src)[0]; 3896 | dest += 2; 3897 | src += 2; 3898 | size -= 2; 3899 | } 3900 | if ((((int)dest | (int)src) & 1) != 0) 3901 | { 3902 | memcpy1(dest, src, size); 3903 | return; 3904 | } 3905 | if ((((int)dest | (int)src) & 2) != 0) 3906 | { 3907 | memcpy2(dest, src, size); 3908 | return; 3909 | } 3910 | } 3911 | memcpy4(dest, src, size); 3912 | } 3913 | 3914 | internal static unsafe void CharCopy(char* dest, char* src, int count) 3915 | { 3916 | // Same rules as for memcpy, but with the premise that 3917 | // chars can only be aligned to even addresses if their 3918 | // enclosing types are correctly aligned 3919 | if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) 3920 | { 3921 | if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) 3922 | { 3923 | ((short*)dest)[0] = ((short*)src)[0]; 3924 | dest++; 3925 | src++; 3926 | count--; 3927 | } 3928 | if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) 3929 | { 3930 | memcpy2((byte*)dest, (byte*)src, count * 2); 3931 | return; 3932 | } 3933 | } 3934 | memcpy4((byte*)dest, (byte*)src, count * 2); 3935 | } 3936 | 3937 | internal static unsafe void CharCopy(char[] target, char[] source, int count) 3938 | { 3939 | fixed(char* dest = target, src = source) 3940 | { 3941 | CharCopy(dest, src, count); 3942 | } 3943 | } 3944 | 3945 | internal static unsafe void CharCopyReverse(char* dest, char* src, int count) 3946 | { 3947 | dest += count; 3948 | src += count; 3949 | 3950 | for (int i = count; i > 0; i--) 3951 | { 3952 | dest--; 3953 | src--; 3954 | *dest = *src; 3955 | } 3956 | } 3957 | 3958 | public bool IsRootedPath() 3959 | { 3960 | if (length == 0) 3961 | { 3962 | return false; 3963 | } 3964 | 3965 | if (IndexOfAny(Path.GetInvalidPathChars()) != -1) 3966 | { 3967 | throw new ArgumentException("Illegal characters in path."); 3968 | } 3969 | 3970 | char c = _buffer[0]; 3971 | bool dirEqualsVolume = (Path.DirectorySeparatorChar == Path.VolumeSeparatorChar); 3972 | 3973 | return (c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar || (!dirEqualsVolume && length > 1 && _buffer[1] == Path.VolumeSeparatorChar)); 3974 | } 3975 | } 3976 | 3977 | 3978 | 3979 | -------------------------------------------------------------------------------- /CString/ExtensionLibs.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2017 topameng(topameng@qq.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | using System; 23 | using System.Globalization; 24 | using System.IO; 25 | using System.Runtime.CompilerServices; 26 | using System.Runtime.ConstrainedExecution; 27 | using System.Text; 28 | 29 | public static partial class ExtensionLibs 30 | { 31 | public static CString ToCString(this System.Boolean boolean) 32 | { 33 | return boolean ? "True" : "False"; 34 | } 35 | 36 | public static CString ToCString(this System.SByte number) 37 | { 38 | CString str = CString.Alloc(8); 39 | str.Append(number); 40 | return str; 41 | } 42 | 43 | public static CString ToCString(this System.SByte number, string format) 44 | { 45 | CString str = CString.Alloc(8); 46 | str.AppendFormat(format, number); 47 | return str; 48 | } 49 | 50 | //public static CString ToCString(this System.SByte number, string format, IFormatProvider provider) 51 | //{ 52 | // CString str = CString.Alloc(8); 53 | // str.NumberToString(format, (int)number, provider); 54 | // return str; 55 | //} 56 | 57 | public static CString ToCString(this System.Byte number) 58 | { 59 | CString str = CString.Alloc(8); 60 | str.Append(number); 61 | return str; 62 | } 63 | 64 | public static CString ToCString(this System.Byte number, string format) 65 | { 66 | CString str = CString.Alloc(8); 67 | str.AppendFormat(format, number); 68 | return str; 69 | } 70 | 71 | //public static CString ToCString(this System.Byte number, string format, IFormatProvider provider) 72 | //{ 73 | // CString str = CString.Alloc(8); 74 | // str.NumberToString(format, (int)number, provider); 75 | // return str; 76 | //} 77 | 78 | public static CString ToCString(this System.Int16 number) 79 | { 80 | CString str = CString.Alloc(8); 81 | str.Append(number); 82 | return str; 83 | } 84 | 85 | public static CString ToCString(this System.Int16 number, string format) 86 | { 87 | CString str = CString.Alloc(16); 88 | str.AppendFormat(format, number); 89 | return str; 90 | } 91 | 92 | //public static CString ToCString(this System.Int16 number, string format, IFormatProvider provider) 93 | //{ 94 | // CString str = CString.Alloc(16); 95 | // str.NumberToString(format, (int)number, provider); 96 | // return str; 97 | //} 98 | 99 | public static CString ToCString(this System.UInt16 number) 100 | { 101 | CString str = CString.Alloc(8); 102 | str.Append(number); 103 | return str; 104 | } 105 | 106 | public static CString ToCString(this System.UInt16 number, string format) 107 | { 108 | CString str = CString.Alloc(16); 109 | str.AppendFormat(format, number); 110 | return str; 111 | } 112 | 113 | //public static CString ToCString(this System.UInt16 number, string format, IFormatProvider provider) 114 | //{ 115 | // CString str = CString.Alloc(16); 116 | // str.NumberToString(format, (int)number, provider); 117 | // return str; 118 | //} 119 | 120 | public static CString ToCString(this System.Int32 number) 121 | { 122 | CString str = CString.Alloc(16); 123 | str.Append(number); 124 | return str; 125 | } 126 | 127 | public static CString ToCString(this System.Int32 number, string format) 128 | { 129 | CString str = CString.Alloc(32); 130 | str.AppendFormat(format, number); 131 | return str; 132 | } 133 | 134 | //public static CString ToCString(this System.Int32 number, string format, IFormatProvider provider) 135 | //{ 136 | // CString str = CString.Alloc(32); 137 | // str.NumberToString(format, number, provider); 138 | // return str; 139 | //} 140 | 141 | public static CString ToCString(this System.UInt32 number) 142 | { 143 | CString str = CString.Alloc(16); 144 | str.Append(number); 145 | return str; 146 | } 147 | 148 | public static CString ToCString(this System.UInt32 number, string format) 149 | { 150 | CString str = CString.Alloc(32); 151 | str.AppendFormat(format, number); 152 | return str; 153 | } 154 | 155 | //public static CString ToCString(this System.UInt32 number, string format, IFormatProvider provider) 156 | //{ 157 | // CString str = CString.Alloc(32); 158 | // str.NumberToString(format, number, provider); 159 | // return str; 160 | //} 161 | 162 | public static CString ToCString(this System.Int64 number) 163 | { 164 | CString str = CString.Alloc(32); 165 | str.Append(number); 166 | return str; 167 | } 168 | 169 | public static CString ToCString(this System.Int64 number, string format) 170 | { 171 | CString str = CString.Alloc(64); 172 | str.AppendFormat(format, number); 173 | return str; 174 | } 175 | 176 | //public static CString ToCString(this System.Int64 number, string format, IFormatProvider provider) 177 | //{ 178 | // CString str = CString.Alloc(64); 179 | // str.NumberToString(format, number, provider); 180 | // return str; 181 | //} 182 | 183 | public static CString ToCString(this System.UInt64 number) 184 | { 185 | CString str = CString.Alloc(32); 186 | str.Append(number); 187 | return str; 188 | } 189 | 190 | public static CString ToCString(this System.UInt64 number, string format) 191 | { 192 | CString str = CString.Alloc(64); 193 | str.AppendFormat(format, number); 194 | return str; 195 | } 196 | 197 | //public static CString ToCString(this System.UInt64 number, string format, IFormatProvider provider) 198 | //{ 199 | // CString str = CString.Alloc(64); 200 | // str.NumberToString(format, number, provider); 201 | // return str; 202 | //} 203 | 204 | public static CString ToCString(this System.Single number) 205 | { 206 | CString str = CString.Alloc(32); 207 | str.Append(number); 208 | return str; 209 | } 210 | 211 | public static CString ToCString(this System.Single number, string format) 212 | { 213 | CString str = CString.Alloc(64); 214 | str.AppendFormat(format, number); 215 | return str; 216 | } 217 | 218 | //public static CString ToCString(this System.Single number, string format, IFormatProvider provider) 219 | //{ 220 | // CString str = CString.Alloc(64); 221 | // str.NumberToString(format, number, provider); 222 | // return str; 223 | //} 224 | 225 | public static CString ToCString(this System.Double number) 226 | { 227 | CString str = CString.Alloc(64); 228 | str.Append(number); 229 | return str; 230 | } 231 | 232 | public static CString ToCString(this System.Double number, string format) 233 | { 234 | CString str = CString.Alloc(64); 235 | str.AppendFormat(format, number); 236 | return str; 237 | } 238 | 239 | //public static CString ToCString(this System.Double number, string format, IFormatProvider provider) 240 | //{ 241 | // CString str = CString.Alloc(64); 242 | // str.NumberToString(format, number, provider); 243 | // return str; 244 | //} 245 | 246 | /// 247 | /// 使用CString src覆盖dest字符串len长度内容 248 | /// 249 | /// 250 | public static string ReplaceEx(this string dest, CString src, int len = -1) 251 | { 252 | if (len <= -1) 253 | { 254 | len = src.Length; 255 | } 256 | else if (len > src.Length) 257 | { 258 | throw new ArgumentOutOfRangeException("len > src.Length"); 259 | } 260 | 261 | if (len > dest.Length) 262 | { 263 | throw new ArgumentOutOfRangeException("len > dest.Length"); 264 | } 265 | 266 | return src.CopyToString(dest, len); 267 | } 268 | 269 | public static unsafe string ReplaceEx(this string dest, int offset, string src) 270 | { 271 | if (offset < 0) 272 | { 273 | throw new ArgumentOutOfRangeException("offset", "Cannot be negative."); 274 | } 275 | 276 | if (offset >= dest.Length) 277 | { 278 | throw new ArgumentOutOfRangeException("offset >= dest.Length"); 279 | } 280 | 281 | if (offset + src.Length > dest.Length) 282 | { 283 | throw new ArgumentOutOfRangeException("offset + src.Length > dest.Length"); 284 | } 285 | 286 | fixed (char* dst = dest, s = src) 287 | { 288 | CString.CharCopy(dst + offset, s, src.Length); 289 | } 290 | 291 | return dest; 292 | } 293 | 294 | public static unsafe string ReplaceEx(this string str, char oldChar, char newChar) 295 | { 296 | if (str.Length == 0 || oldChar == newChar) 297 | { 298 | return str; 299 | } 300 | 301 | fixed(char* p = str) 302 | { 303 | for (int i = 0; i < str.Length; i++) 304 | { 305 | if (p[i] == oldChar) 306 | { 307 | p[i] = newChar; 308 | } 309 | } 310 | } 311 | 312 | return str; 313 | } 314 | 315 | public static unsafe string ReplaceEx(this string str, string oldStr, string newStr) 316 | { 317 | if (oldStr.Length != newStr.Length) 318 | { 319 | throw new ArgumentOutOfRangeException("oldStr.Length != newStr.Length"); 320 | } 321 | 322 | if (oldStr == null) 323 | { 324 | throw new ArgumentNullException("oldStr"); 325 | } 326 | 327 | if (oldStr.Length == 0) 328 | { 329 | throw new ArgumentException("oldStr is the empty string."); 330 | } 331 | 332 | if (str.Length == 0 || oldStr == newStr || oldStr.Length > str.Length) 333 | { 334 | return str; 335 | } 336 | 337 | if (oldStr.Length == 1 && newStr.Length == 1) 338 | { 339 | return ReplaceEx(str, oldStr[0], newStr[0]); 340 | } 341 | 342 | int length = str.Length; 343 | int step = oldStr.Length; 344 | 345 | fixed (char* dst = str, s = newStr) 346 | { 347 | for (int i = 0; i < length;) 348 | { 349 | int found = str.IndexOf(oldStr, i, length - i); 350 | 351 | if (found < 0) 352 | { 353 | break; 354 | } 355 | 356 | CString.CharCopy(dst + i, s, newStr.Length); 357 | i = found + step; 358 | } 359 | } 360 | 361 | return str; 362 | } 363 | 364 | public static string ToLowerEx(this string str) 365 | { 366 | return ToLowerEx(str, CultureInfo.CurrentCulture); 367 | } 368 | 369 | public static string ToLowerEx(this string str, CultureInfo culture) 370 | { 371 | if (culture == null) 372 | { 373 | throw new ArgumentNullException("culture"); 374 | } 375 | 376 | if (culture.LCID == 0x007F) 377 | { 378 | return ToLowerInvariant(str); 379 | } 380 | 381 | return ToLower(str, culture.TextInfo); 382 | } 383 | 384 | internal static unsafe string ToLowerInvariant(string str) 385 | { 386 | if (str.Length == 0) 387 | { 388 | return str; 389 | } 390 | 391 | fixed (char* dest = str) 392 | { 393 | for (int i = 0; i < str.Length; i++) 394 | { 395 | dest[i] = Char.ToLowerInvariant(dest[i]); 396 | } 397 | } 398 | 399 | return str; 400 | } 401 | 402 | internal static unsafe string ToLower(string str, TextInfo text) 403 | { 404 | if (str.Length == 0) 405 | { 406 | return str; 407 | } 408 | 409 | fixed (char* dest = str) 410 | { 411 | for (int i = 0; i < str.Length; i++) 412 | { 413 | dest[i] = text.ToLower(dest[i]); 414 | } 415 | } 416 | 417 | return str; 418 | } 419 | 420 | public static string ToUpperEx(this string str) 421 | { 422 | return ToUpperEx(str, CultureInfo.CurrentCulture); 423 | } 424 | 425 | public static string ToUpperEx(this string str, CultureInfo culture) 426 | { 427 | if (culture == null) 428 | { 429 | throw new ArgumentNullException("culture"); 430 | } 431 | 432 | if (culture.LCID == 0x007F) 433 | { 434 | return ToUpperExInvariant(str); 435 | } 436 | 437 | return ToUpper(str, culture.TextInfo); 438 | } 439 | 440 | internal static unsafe string ToUpperExInvariant(string str) 441 | { 442 | if (str.Length == 0) 443 | { 444 | return str; 445 | } 446 | 447 | fixed (char* dest = str) 448 | { 449 | for (int i = 0; i < str.Length; i++) 450 | { 451 | dest[i] = Char.ToUpperInvariant(dest[i]); 452 | } 453 | } 454 | 455 | return str; 456 | } 457 | 458 | internal static unsafe string ToUpper(string str, TextInfo text) 459 | { 460 | if (str.Length == 0) 461 | { 462 | return str; 463 | } 464 | 465 | fixed (char* dest = str) 466 | { 467 | for (int i = 0; i < str.Length; i++) 468 | { 469 | dest[i] = text.ToUpper(dest[i]); 470 | } 471 | } 472 | 473 | return str; 474 | } 475 | 476 | public static unsafe CString SubStringEx(this string str, int startIndex) 477 | { 478 | if (startIndex < 0) 479 | { 480 | throw new ArgumentOutOfRangeException("startIndex", "Cannot be negative."); 481 | } 482 | 483 | if (startIndex > str.Length) 484 | { 485 | throw new ArgumentOutOfRangeException("startIndex", "Cannot exceed length of string."); 486 | } 487 | 488 | int len = str.Length - startIndex; 489 | 490 | if (len == 0) 491 | { 492 | return CString.Alloc(0); 493 | } 494 | 495 | if (startIndex == 0 && len == str.Length) 496 | { 497 | return str; 498 | } 499 | 500 | CString cstr = CString.Alloc(len); 501 | cstr.Append(str, startIndex, len); 502 | return cstr; 503 | } 504 | 505 | public static unsafe void CopyToEx(this string str, int startIndex, string outStr) 506 | { 507 | if (startIndex < 0) 508 | { 509 | throw new ArgumentOutOfRangeException("startIndex", "Cannot be negative."); 510 | } 511 | 512 | if (startIndex + outStr.Length > str.Length) 513 | { 514 | throw new ArgumentOutOfRangeException("startIndex", "Cannot exceed length of string."); 515 | } 516 | 517 | fixed (char* src = str, dest = outStr) 518 | { 519 | CString.CharCopy(dest, src + startIndex, outStr.Length); 520 | } 521 | } 522 | } 523 | -------------------------------------------------------------------------------- /CString/FreeList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | 6 | public class FreeList : IList, IList, ICollection 7 | { 8 | T[] _items; 9 | int _size; 10 | int _version; 11 | 12 | static readonly T[] EmptyArray = new T[0]; 13 | static ArrayPool pool = new ArrayPool(); 14 | 15 | public FreeList() 16 | { 17 | _items = EmptyArray; 18 | } 19 | 20 | public FreeList(IEnumerable collection) 21 | { 22 | CheckCollection(collection); 23 | ICollection c = collection as ICollection; 24 | 25 | if (c == null) 26 | { 27 | _items = EmptyArray; 28 | AddEnumerable(collection); 29 | } 30 | else 31 | { 32 | _items = pool.Alloc(c.Count); 33 | AddCollection(c); 34 | } 35 | } 36 | 37 | public FreeList(int capacity) 38 | { 39 | if (capacity < 0) 40 | { 41 | throw new ArgumentOutOfRangeException("capacity"); 42 | } 43 | 44 | _items = pool.Alloc(capacity); 45 | } 46 | 47 | internal FreeList(T[] data, int size) 48 | { 49 | _items = data; 50 | _size = size; 51 | } 52 | 53 | public void Dispose() 54 | { 55 | if (_items != EmptyArray) 56 | { 57 | pool.Collect(_items); 58 | } 59 | 60 | _items = EmptyArray; 61 | _size = 0; 62 | } 63 | 64 | public void Add(T item) 65 | { 66 | if (_size == _items.Length) 67 | { 68 | GrowIfNeeded(1); 69 | } 70 | 71 | _items[_size++] = item; 72 | _version++; 73 | } 74 | 75 | void GrowIfNeeded(int newCount) 76 | { 77 | int newSize = _size + newCount; 78 | 79 | if (newSize > _items.Length) 80 | { 81 | T[] a = pool.Alloc(newSize); 82 | Array.Copy(_items, a, _size); 83 | pool.Collect(_items); 84 | _items = a; 85 | } 86 | } 87 | 88 | void CheckRange(int idx, int count) 89 | { 90 | if (idx < 0) 91 | { 92 | throw new ArgumentOutOfRangeException("index"); 93 | } 94 | 95 | if (count < 0) 96 | { 97 | throw new ArgumentOutOfRangeException("count"); 98 | } 99 | 100 | if ((uint)idx + (uint)count > (uint)_size) 101 | { 102 | throw new ArgumentException("index and count exceed length of list"); 103 | } 104 | } 105 | 106 | void AddCollection(ICollection collection) 107 | { 108 | int collectionCount = collection.Count; 109 | 110 | if (collectionCount == 0) 111 | { 112 | return; 113 | } 114 | 115 | GrowIfNeeded(collectionCount); 116 | collection.CopyTo(_items, _size); 117 | _size += collectionCount; 118 | } 119 | 120 | void AddEnumerable(IEnumerable enumerable) 121 | { 122 | foreach (T t in enumerable) 123 | { 124 | Add(t); 125 | } 126 | } 127 | 128 | public void AddRange(IEnumerable collection) 129 | { 130 | CheckCollection(collection); 131 | ICollection c = collection as ICollection; 132 | 133 | if (c != null) 134 | { 135 | AddCollection(c); 136 | } 137 | else 138 | { 139 | AddEnumerable(collection); 140 | } 141 | 142 | _version++; 143 | } 144 | 145 | public void AddRange(FreeList list, int length) 146 | { 147 | if (length < 0) 148 | { 149 | throw new ArgumentOutOfRangeException("length"); 150 | } 151 | 152 | GrowIfNeeded(length); 153 | Array.Copy(list._items, 0, _items, _size, length); 154 | _size += length; 155 | } 156 | 157 | public void AddRange(T[] array, int length) 158 | { 159 | if (length < 0) 160 | { 161 | throw new ArgumentOutOfRangeException("length"); 162 | } 163 | 164 | GrowIfNeeded(length); 165 | Array.Copy(array, 0, _items, _size, length); 166 | _size += length; 167 | } 168 | 169 | public ReadOnlyCollection AsReadOnly() 170 | { 171 | return new ReadOnlyCollection(this); 172 | } 173 | 174 | public int BinarySearch(T item) 175 | { 176 | return Array.BinarySearch(_items, 0, _size, item); 177 | } 178 | 179 | public int BinarySearch(T item, IComparer comparer) 180 | { 181 | return Array.BinarySearch(_items, 0, _size, item, comparer); 182 | } 183 | 184 | public int BinarySearch(int index, int count, T item, IComparer comparer) 185 | { 186 | CheckRange(index, count); 187 | return Array.BinarySearch(_items, index, count, item, comparer); 188 | } 189 | 190 | public void Clear() 191 | { 192 | //Array.Clear(_items, 0, _items.Length); 193 | _size = 0; 194 | _version++; 195 | } 196 | 197 | public bool Contains(T item) 198 | { 199 | return Array.IndexOf(_items, item, 0, _size) != -1; 200 | } 201 | 202 | public FreeList ConvertAll(Converter converter) 203 | { 204 | if (converter == null) 205 | { 206 | throw new ArgumentNullException("converter"); 207 | } 208 | 209 | FreeList u = new FreeList(_size); 210 | 211 | for (int i = 0; i < _size; i++) 212 | { 213 | u._items[i] = converter(_items[i]); 214 | } 215 | 216 | u._size = _size; 217 | return u; 218 | } 219 | 220 | public void CopyTo(T[] array) 221 | { 222 | Array.Copy(_items, 0, array, 0, _size); 223 | } 224 | 225 | public void CopyTo(T[] array, int arrayIndex) 226 | { 227 | Array.Copy(_items, 0, array, arrayIndex, _size); 228 | } 229 | 230 | public void CopyTo(int index, T[] array, int arrayIndex, int count) 231 | { 232 | CheckRange(index, count); 233 | Array.Copy(_items, index, array, arrayIndex, count); 234 | } 235 | 236 | public bool Exists(Predicate match) 237 | { 238 | CheckMatch(match); 239 | return GetIndex(0, _size, match) != -1; 240 | } 241 | 242 | public T Find(Predicate match) 243 | { 244 | CheckMatch(match); 245 | int i = GetIndex(0, _size, match); 246 | return (i != -1) ? _items[i] : default(T); 247 | } 248 | 249 | static void CheckMatch(Predicate match) 250 | { 251 | if (match == null) 252 | { 253 | throw new ArgumentNullException("match"); 254 | } 255 | } 256 | 257 | public FreeList FindAll(Predicate match) 258 | { 259 | CheckMatch(match); 260 | 261 | if (this._size <= 0x10000) // <= 8 * 1024 * 8 (8k in stack) 262 | { 263 | return FindAllStackBits(match); 264 | } 265 | else 266 | { 267 | return FindAllList(match); 268 | } 269 | } 270 | 271 | protected FreeList FindAllStackBits(Predicate match) 272 | { 273 | unsafe 274 | { 275 | uint* bits = stackalloc uint[(this._size / 32) + 1]; 276 | uint* ptr = bits; 277 | int found = 0; 278 | uint bitmask = 0x80000000; 279 | 280 | for (int i = 0; i < this._size; i++) 281 | { 282 | if (match(this._items[i])) 283 | { 284 | (*ptr) = (*ptr) | bitmask; 285 | found++; 286 | } 287 | 288 | bitmask = bitmask >> 1; 289 | 290 | if (bitmask == 0) 291 | { 292 | ptr++; 293 | bitmask = 0x80000000; 294 | } 295 | } 296 | 297 | T[] results = pool.Alloc(found); 298 | bitmask = 0x80000000; 299 | ptr = bits; 300 | int j = 0; 301 | 302 | for (int i = 0; i < this._size && j < found; i++) 303 | { 304 | if (((*ptr) & bitmask) == bitmask) 305 | { 306 | results[j++] = this._items[i]; 307 | } 308 | 309 | bitmask = bitmask >> 1; 310 | 311 | if (bitmask == 0) 312 | { 313 | ptr++; 314 | bitmask = 0x80000000; 315 | } 316 | } 317 | 318 | return new FreeList(results, found); 319 | } 320 | } 321 | 322 | protected FreeList FindAllList(Predicate match) 323 | { 324 | FreeList results = new FreeList(); 325 | 326 | for (int i = 0; i < this._size; i++) 327 | { 328 | if (match(_items[i])) 329 | { 330 | results.Add(_items[i]); 331 | } 332 | } 333 | 334 | return results; 335 | } 336 | 337 | public int FindIndex(Predicate match) 338 | { 339 | CheckMatch(match); 340 | return GetIndex(0, _size, match); 341 | } 342 | 343 | public int FindIndex(int startIndex, Predicate match) 344 | { 345 | CheckMatch(match); 346 | CheckIndex(startIndex); 347 | return GetIndex(startIndex, _size - startIndex, match); 348 | } 349 | 350 | public int FindIndex(int startIndex, int count, Predicate match) 351 | { 352 | CheckMatch(match); 353 | CheckRange(startIndex, count); 354 | return GetIndex(startIndex, count, match); 355 | } 356 | 357 | int GetIndex(int startIndex, int count, Predicate match) 358 | { 359 | int end = startIndex + count; 360 | 361 | for (int i = startIndex; i < end; i++) 362 | { 363 | if (match(_items[i])) 364 | { 365 | return i; 366 | } 367 | } 368 | 369 | return -1; 370 | } 371 | 372 | public T FindLast(Predicate match) 373 | { 374 | CheckMatch(match); 375 | int i = GetLastIndex(0, _size, match); 376 | return i == -1 ? default(T) : this[i]; 377 | } 378 | 379 | public int FindLastIndex(Predicate match) 380 | { 381 | CheckMatch(match); 382 | return GetLastIndex(0, _size, match); 383 | } 384 | 385 | public int FindLastIndex(int startIndex, Predicate match) 386 | { 387 | CheckMatch(match); 388 | CheckIndex(startIndex); 389 | return GetLastIndex(0, startIndex + 1, match); 390 | } 391 | 392 | public int FindLastIndex(int startIndex, int count, Predicate match) 393 | { 394 | CheckMatch(match); 395 | int start = startIndex - count + 1; 396 | CheckRange(start, count); 397 | return GetLastIndex(start, count, match); 398 | } 399 | 400 | int GetLastIndex(int startIndex, int count, Predicate match) 401 | { 402 | // unlike FindLastIndex, takes regular params for search range 403 | for (int i = startIndex + count; i != startIndex;) 404 | { 405 | if (match(_items[--i])) 406 | { 407 | return i; 408 | } 409 | } 410 | 411 | return -1; 412 | } 413 | 414 | public void ForEach(Action action) 415 | { 416 | if (action == null) 417 | { 418 | throw new ArgumentNullException("action"); 419 | } 420 | 421 | for (int i = 0; i < _size; i++) 422 | { 423 | action(_items[i]); 424 | } 425 | } 426 | 427 | public Enumerator GetEnumerator() 428 | { 429 | return new Enumerator(this); 430 | } 431 | 432 | public FreeList GetRange(int index, int count) 433 | { 434 | CheckRange(index, count); 435 | T[] tmpArray = pool.Alloc(count); 436 | Array.Copy(_items, index, tmpArray, 0, count); 437 | return new FreeList(tmpArray, count); 438 | } 439 | 440 | public int IndexOf(T item) 441 | { 442 | return Array.IndexOf(_items, item, 0, _size); 443 | } 444 | 445 | public int IndexOf(T item, int index) 446 | { 447 | CheckIndex(index); 448 | return Array.IndexOf(_items, item, index, _size - index); 449 | } 450 | 451 | public int IndexOf(T item, int index, int count) 452 | { 453 | if (index < 0) 454 | { 455 | throw new ArgumentOutOfRangeException("index"); 456 | } 457 | 458 | if (count < 0) 459 | { 460 | throw new ArgumentOutOfRangeException("count"); 461 | } 462 | 463 | if ((uint)index + (uint)count > (uint)_size) 464 | { 465 | throw new ArgumentOutOfRangeException("index and count exceed length of list"); 466 | } 467 | 468 | return Array.IndexOf(_items, item, index, count); 469 | } 470 | 471 | void Shift(int start, int delta) 472 | { 473 | if (delta < 0) 474 | { 475 | start -= delta; 476 | } 477 | 478 | if (start < _size) 479 | { 480 | Array.Copy(_items, start, _items, start + delta, _size - start); 481 | } 482 | 483 | _size += delta; 484 | 485 | if (delta < 0) 486 | { 487 | Array.Clear(_items, _size, -delta); 488 | } 489 | } 490 | 491 | void CheckIndex(int index) 492 | { 493 | if (index < 0 || (uint)index > (uint)_size) 494 | { 495 | throw new ArgumentOutOfRangeException("index"); 496 | } 497 | } 498 | 499 | public void Insert(int index, T item) 500 | { 501 | CheckIndex(index); 502 | 503 | if (_size == _items.Length) 504 | { 505 | GrowIfNeeded(1); 506 | } 507 | 508 | Shift(index, 1); 509 | _items[index] = item; 510 | _version++; 511 | } 512 | 513 | void CheckCollection(IEnumerable collection) 514 | { 515 | if (collection == null) 516 | { 517 | throw new ArgumentNullException("collection"); 518 | } 519 | } 520 | 521 | public void InsertRange(int index, IEnumerable collection) 522 | { 523 | CheckCollection(collection); 524 | CheckIndex(index); 525 | 526 | if (collection == this) 527 | { 528 | T[] buffer = pool.Alloc(_size); 529 | CopyTo(buffer, 0); 530 | GrowIfNeeded(_size); 531 | Shift(index, buffer.Length); 532 | Array.Copy(buffer, 0, _items, index, buffer.Length); 533 | pool.Collect(buffer); 534 | } 535 | else 536 | { 537 | ICollection c = collection as ICollection; 538 | 539 | if (c != null) 540 | { 541 | InsertCollection(index, c); 542 | } 543 | else 544 | { 545 | InsertEnumeration(index, collection); 546 | } 547 | } 548 | 549 | _version++; 550 | } 551 | 552 | void InsertCollection(int index, ICollection collection) 553 | { 554 | int collectionCount = collection.Count; 555 | GrowIfNeeded(collectionCount); 556 | 557 | Shift(index, collectionCount); 558 | collection.CopyTo(_items, index); 559 | } 560 | 561 | void InsertEnumeration(int index, IEnumerable enumerable) 562 | { 563 | foreach (T t in enumerable) 564 | { 565 | Insert(index++, t); 566 | } 567 | } 568 | 569 | public int LastIndexOf(T item) 570 | { 571 | return Array.LastIndexOf(_items, item, _size - 1, _size); 572 | } 573 | 574 | public int LastIndexOf(T item, int index) 575 | { 576 | CheckIndex(index); 577 | return Array.LastIndexOf(_items, item, index, index + 1); 578 | } 579 | 580 | public int LastIndexOf(T item, int index, int count) 581 | { 582 | if (index < 0) 583 | { 584 | throw new ArgumentOutOfRangeException("index", index, "index is negative"); 585 | } 586 | 587 | if (count < 0) 588 | { 589 | throw new ArgumentOutOfRangeException("count", count, "count is negative"); 590 | } 591 | 592 | if (index - count + 1 < 0) 593 | { 594 | throw new ArgumentOutOfRangeException("cound", count, "count is too large"); 595 | } 596 | 597 | return Array.LastIndexOf(_items, item, index, count); 598 | } 599 | 600 | public bool Remove(T item) 601 | { 602 | int loc = IndexOf(item); 603 | 604 | if (loc != -1) 605 | { 606 | RemoveAt(loc); 607 | } 608 | 609 | return loc != -1; 610 | } 611 | 612 | public int RemoveAll(Predicate match) 613 | { 614 | CheckMatch(match); 615 | int i = 0; 616 | int j = 0; 617 | 618 | // Find the first item to remove 619 | for (i = 0; i < _size; i++) 620 | { 621 | if (match(_items[i])) 622 | { 623 | break; 624 | } 625 | } 626 | 627 | if (i == _size) 628 | { 629 | return 0; 630 | } 631 | 632 | _version++; 633 | 634 | // Remove any additional items 635 | for (j = i + 1; j < _size; j++) 636 | { 637 | if (!match(_items[j])) 638 | { 639 | _items[i++] = _items[j]; 640 | } 641 | } 642 | 643 | if (j - i > 0) 644 | { 645 | Array.Clear(_items, i, j - i); 646 | } 647 | 648 | _size = i; 649 | return (j - i); 650 | } 651 | 652 | public void RemoveAt(int index) 653 | { 654 | if (index < 0 || (uint)index >= (uint)_size) 655 | { 656 | throw new ArgumentOutOfRangeException("index"); 657 | } 658 | 659 | Shift(index, -1); 660 | _version++; 661 | } 662 | 663 | public T Pop() 664 | { 665 | if (_size <= 0) 666 | { 667 | throw new InvalidOperationException(); 668 | } 669 | 670 | T poped = _items[--_size]; 671 | _items[_size] = default(T); 672 | _version++; 673 | return poped; 674 | } 675 | 676 | public void RemoveRange(int index, int count) 677 | { 678 | CheckRange(index, count); 679 | 680 | if (count > 0) 681 | { 682 | Shift(index, -count); 683 | _version++; 684 | } 685 | } 686 | 687 | public void Reverse() 688 | { 689 | Array.Reverse(_items, 0, _size); 690 | _version++; 691 | } 692 | 693 | public void Reverse(int index, int count) 694 | { 695 | CheckRange(index, count); 696 | Array.Reverse(_items, index, count); 697 | _version++; 698 | } 699 | 700 | public void Sort() 701 | { 702 | Array.Sort(_items, 0, _size, Comparer.Default); 703 | _version++; 704 | } 705 | 706 | public void Sort(IComparer comparer) 707 | { 708 | Array.Sort(_items, 0, _size, comparer); 709 | _version++; 710 | } 711 | 712 | public void Sort(Comparison comparison) 713 | { 714 | if (comparison == null) 715 | { 716 | throw new ArgumentNullException("comparison"); 717 | } 718 | 719 | if (_size <= 1 || _items.Length <= 1) 720 | { 721 | return; 722 | } 723 | 724 | try 725 | { 726 | int low0 = 0; 727 | int high0 = _size - 1; 728 | qsort(_items, low0, high0, comparison); 729 | } 730 | catch (Exception e) 731 | { 732 | throw new InvalidOperationException("Comparison threw an exception.", e); 733 | } 734 | 735 | _version++; 736 | } 737 | 738 | private static void qsort(T[] array, int low0, int high0, Comparison comparison) 739 | { 740 | if (low0 >= high0) 741 | { 742 | return; 743 | } 744 | 745 | int low = low0; 746 | int high = high0; 747 | 748 | // Be careful with overflows 749 | int mid = low + ((high - low) / 2); 750 | T keyPivot = array[mid]; 751 | 752 | while (true) 753 | { 754 | // Move the walls in 755 | while (low < high0 && comparison(array[low], keyPivot) < 0) 756 | ++low; 757 | while (high > low0 && comparison(keyPivot, array[high]) < 0) 758 | --high; 759 | 760 | if (low <= high) 761 | { 762 | swap(array, low, high); 763 | ++low; 764 | --high; 765 | } 766 | else 767 | break; 768 | } 769 | 770 | if (low0 < high) 771 | qsort(array, low0, high, comparison); 772 | if (low < high0) 773 | qsort(array, low, high0, comparison); 774 | } 775 | 776 | private static void swap(T[] array, int i, int j) 777 | { 778 | T tmp = array[i]; 779 | array[i] = array[j]; 780 | array[j] = tmp; 781 | } 782 | 783 | public void Sort(int index, int count, IComparer comparer) 784 | { 785 | CheckRange(index, count); 786 | Array.Sort(_items, index, count, comparer); 787 | _version++; 788 | } 789 | 790 | public T[] ToArray() 791 | { 792 | T[] t = new T[_size]; 793 | Array.Copy(_items, t, _size); 794 | 795 | return t; 796 | } 797 | 798 | public T[] ToArray2() 799 | { 800 | if (_size < _items.Length / 2) 801 | { 802 | T[] newList = pool.Alloc(_size); 803 | Array.Copy(_items, newList, _size); 804 | pool.Collect(_items); 805 | _items = newList; 806 | } 807 | 808 | return _items; 809 | } 810 | 811 | public bool TrueForAll(Predicate match) 812 | { 813 | CheckMatch(match); 814 | 815 | for (int i = 0; i < _size; i++) 816 | { 817 | if (!match(_items[i])) 818 | { 819 | return false; 820 | } 821 | } 822 | 823 | return true; 824 | } 825 | 826 | public int Count 827 | { 828 | get { return _size; } 829 | } 830 | 831 | public T this[int index] 832 | { 833 | get 834 | { 835 | if ((uint)index >= (uint)_size) 836 | { 837 | throw new ArgumentOutOfRangeException("index"); 838 | } 839 | 840 | return _items[index]; 841 | } 842 | set 843 | { 844 | CheckIndex(index); 845 | 846 | if ((uint)index == (uint)_size) 847 | { 848 | throw new ArgumentOutOfRangeException("index"); 849 | } 850 | 851 | _items[index] = value; 852 | } 853 | } 854 | 855 | IEnumerator IEnumerable.GetEnumerator() 856 | { 857 | return GetEnumerator(); 858 | } 859 | 860 | void ICollection.CopyTo(Array array, int arrayIndex) 861 | { 862 | Array.Copy(_items, 0, array, arrayIndex, _size); 863 | } 864 | 865 | IEnumerator IEnumerable.GetEnumerator() 866 | { 867 | return GetEnumerator(); 868 | } 869 | 870 | int IList.Add(object item) 871 | { 872 | try 873 | { 874 | Add((T)item); 875 | return _size - 1; 876 | } 877 | catch (NullReferenceException) 878 | { 879 | } 880 | catch (InvalidCastException) 881 | { 882 | } 883 | throw new ArgumentException("item"); 884 | } 885 | 886 | bool IList.Contains(object item) 887 | { 888 | try 889 | { 890 | return Contains((T)item); 891 | } 892 | catch (NullReferenceException) 893 | { 894 | } 895 | catch (InvalidCastException) 896 | { 897 | } 898 | return false; 899 | } 900 | 901 | int IList.IndexOf(object item) 902 | { 903 | try 904 | { 905 | return IndexOf((T)item); 906 | } 907 | catch (NullReferenceException) 908 | { 909 | } 910 | catch (InvalidCastException) 911 | { 912 | } 913 | return -1; 914 | } 915 | 916 | void IList.Insert(int index, object item) 917 | { 918 | CheckIndex(index); 919 | 920 | try 921 | { 922 | Insert(index, (T)item); 923 | return; 924 | } 925 | catch (NullReferenceException) 926 | { 927 | } 928 | catch (InvalidCastException) 929 | { 930 | } 931 | throw new ArgumentException("item"); 932 | } 933 | 934 | void IList.Remove(object item) 935 | { 936 | try 937 | { 938 | Remove((T)item); 939 | return; 940 | } 941 | catch (NullReferenceException) 942 | { 943 | } 944 | catch (InvalidCastException) 945 | { 946 | } 947 | } 948 | 949 | bool ICollection.IsReadOnly 950 | { 951 | get { return false; } 952 | } 953 | 954 | bool ICollection.IsSynchronized 955 | { 956 | get { return false; } 957 | } 958 | 959 | object ICollection.SyncRoot 960 | { 961 | get { return this; } 962 | } 963 | 964 | bool IList.IsFixedSize 965 | { 966 | get { return false; } 967 | } 968 | 969 | 970 | bool IList.IsReadOnly 971 | { 972 | get { return false; } 973 | } 974 | 975 | object IList.this[int index] 976 | { 977 | get { return this[index]; } 978 | set 979 | { 980 | try 981 | { 982 | this[index] = (T)value; 983 | return; 984 | } 985 | catch (NullReferenceException) 986 | { 987 | // can happen when 'value' is null and T is a valuetype 988 | } 989 | catch (InvalidCastException) 990 | { 991 | } 992 | throw new ArgumentException("value"); 993 | } 994 | } 995 | 996 | [Serializable] 997 | public struct Enumerator : IEnumerator, IDisposable 998 | { 999 | FreeList l; 1000 | int next; 1001 | int ver; 1002 | 1003 | T current; 1004 | 1005 | internal Enumerator(FreeList l) 1006 | : this() 1007 | { 1008 | this.l = l; 1009 | ver = l._version; 1010 | } 1011 | 1012 | public void Dispose() 1013 | { 1014 | l = null; 1015 | } 1016 | 1017 | void VerifyState() 1018 | { 1019 | if (l == null) 1020 | { 1021 | throw new ObjectDisposedException(GetType().FullName); 1022 | } 1023 | 1024 | if (ver != l._version) 1025 | { 1026 | throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); 1027 | } 1028 | } 1029 | 1030 | public bool MoveNext() 1031 | { 1032 | VerifyState(); 1033 | 1034 | if (next < 0) 1035 | { 1036 | return false; 1037 | } 1038 | 1039 | if (next < l._size) 1040 | { 1041 | current = l._items[next++]; 1042 | return true; 1043 | } 1044 | 1045 | next = -1; 1046 | return false; 1047 | } 1048 | 1049 | public T Current 1050 | { 1051 | get 1052 | { 1053 | return current; 1054 | } 1055 | } 1056 | 1057 | void IEnumerator.Reset() 1058 | { 1059 | VerifyState(); 1060 | next = 0; 1061 | } 1062 | 1063 | object IEnumerator.Current 1064 | { 1065 | get 1066 | { 1067 | VerifyState(); 1068 | 1069 | if (next <= 0) 1070 | { 1071 | throw new InvalidOperationException(); 1072 | } 1073 | 1074 | return current; 1075 | } 1076 | } 1077 | } 1078 | } 1079 | -------------------------------------------------------------------------------- /CString/IStringBlock.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2017 topameng(topameng@qq.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | using System; 23 | 24 | public interface IStringBlock : IDisposable 25 | { 26 | bool Remove(CString str); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /CString/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("CString")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("ToLua")] 12 | [assembly: AssemblyProduct("CString")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("0b9713cb-157e-4110-89b3-950cbe99e2b8")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CString/StringPool.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2017 topameng(topameng@qq.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | public static class StringPool 26 | { 27 | const int MaxSize = 1024; 28 | const int MaxQueueSize = 8; 29 | public static Dictionary> map = new Dictionary>(); 30 | 31 | static public void PreAlloc(int size, int count) 32 | { 33 | if (size > MaxSize || size <= 0) 34 | { 35 | return; 36 | } 37 | 38 | count = Math.Max(MaxQueueSize, count); 39 | Queue queue = null; 40 | 41 | if (map.TryGetValue(size, out queue)) 42 | { 43 | for (int i = queue.Count; i < count; i++) 44 | { 45 | queue.Enqueue(new string((char)0xCC, size)); 46 | } 47 | } 48 | else 49 | { 50 | queue = new Queue(); 51 | map[size] = queue; 52 | 53 | for (int i = 0; i < count; i++) 54 | { 55 | queue.Enqueue(new string((char)0xCC, size)); 56 | } 57 | } 58 | } 59 | 60 | static public string Alloc(int size) 61 | { 62 | if (size == 0) 63 | { 64 | return string.Empty; 65 | } 66 | 67 | if (size >= MaxSize) 68 | { 69 | return new string((char)0xCC, size); 70 | } 71 | 72 | Queue queue = null; 73 | 74 | if (map.TryGetValue(size, out queue)) 75 | { 76 | if (queue.Count > 0) 77 | { 78 | return queue.Dequeue(); 79 | } 80 | } 81 | else 82 | { 83 | queue = new Queue(); 84 | map[size] = queue; 85 | } 86 | 87 | return new string((char)0xCC, size); 88 | } 89 | 90 | static public void Collect(string str) 91 | { 92 | if (string.IsNullOrEmpty(str)) 93 | { 94 | return; 95 | } 96 | 97 | int size = str.Length; 98 | 99 | if (size < MaxSize && size > 0) 100 | { 101 | Queue queue = null; 102 | 103 | if (!map.TryGetValue(str.Length, out queue)) 104 | { 105 | queue = new Queue(); 106 | map[size] = queue; 107 | } 108 | 109 | if (queue.Count <= MaxQueueSize) 110 | { 111 | queue.Enqueue(str); 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /CString/bin/Debug/CString.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/CString/bin/Debug/CString.dll -------------------------------------------------------------------------------- /CString/bin/Debug/CString.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/CString/bin/Debug/CString.pdb -------------------------------------------------------------------------------- /CString/bin/Debug/UnityEngine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/CString/bin/Debug/UnityEngine.dll -------------------------------------------------------------------------------- /CString/bin/Release/CString.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/CString/bin/Release/CString.dll -------------------------------------------------------------------------------- /CString/bin/Release/CString.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/CString/bin/Release/CString.pdb -------------------------------------------------------------------------------- /CString/bin/Release/UnityEngine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/CString/bin/Release/UnityEngine.dll -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 topameng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CString 2 | C# 动态字符串,主要取代StringBuilder功能,但也包含大部分的string功能,可以再某些情况下取代字符串。 3 | 达到减少内存分配的目的 4 | 5 | 比如替代ngui UILable 成员mProcessedText: 6 | https://github.com/topameng/mGUI/blob/ad1c27c8be15a5ab334cc9809e7a91baf0ab0fc5/Assets/NGUI/Scripts/UI/UILabel.cs#L94 7 | -------------------------------------------------------------------------------- /UnitTest/Assets/CStringTest.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | public class CStringTest : MonoBehaviour 6 | { 7 | void Start() 8 | { 9 | System.Text.StringBuilder sb = new System.Text.StringBuilder(); 10 | sb.Append('\''); 11 | string str = sb.ToString(); 12 | Debug.Log(str); 13 | //char sep = (char)0x85; 14 | //str = str.Replace(',', sep); 15 | //string[] ss = str.Split(); 16 | 17 | //for(int i = 0; i < ss.Length; i++) 18 | //{ 19 | // Debug.Log(ss[i]); 20 | //} 21 | 22 | TestEquals(); 23 | TestEmptyOrNull(); 24 | 25 | CString cs = "test/?.lua"; 26 | cs = cs.Replace("?.lua", "TestLoader.lua"); 27 | Debug.Log(cs.ToString()); 28 | } 29 | 30 | void TestEquals() 31 | { 32 | CString cs = "hello unity"; 33 | string str = "hello unity"; 34 | string str1 = "hello"; 35 | 36 | if (cs == str) 37 | { 38 | Debug.Log("operator==(CString a, string b) ok"); 39 | } 40 | else 41 | { 42 | Debug.LogError("operator==(CString a, string b) failed"); 43 | } 44 | 45 | if (cs != str1) 46 | { 47 | Debug.Log("operator!=(CString a, string b) ok"); 48 | } 49 | else 50 | { 51 | Debug.LogError("operator!=(CString a, string b) failed"); 52 | } 53 | 54 | if (str == cs) 55 | { 56 | Debug.Log("operator==(string a, CString b) ok"); 57 | } 58 | else 59 | { 60 | Debug.LogError("operator==(string a, CString b) failed"); 61 | } 62 | 63 | if (str1 != cs) 64 | { 65 | Debug.Log("operator!=(string a, CString b) ok"); 66 | } 67 | else 68 | { 69 | Debug.LogError("operator!=(string a, CString b) failed"); 70 | } 71 | 72 | if (cs.Equals(str) && !cs.Equals(str1)) 73 | { 74 | Debug.Log("CString.Equals(string str) ok"); 75 | } 76 | else 77 | { 78 | Debug.LogError("CString.Equals(string str) failed"); 79 | } 80 | 81 | if (str.Equals(cs)) //无法扩展替换,因为被 Equals(object obj) 先匹配掉了 82 | { 83 | Debug.Log("String.Equals(CString cs) ok"); 84 | } 85 | else 86 | { 87 | Debug.LogError("String.Equals(CString cs) failed: sorry can't override this operator, don't use as this"); 88 | } 89 | } 90 | 91 | void TestEmptyOrNull() 92 | { 93 | CString str = new CString(0); 94 | 95 | if (CString.IsNullOrEmpty(null) && CString.IsNullOrEmpty(str)) 96 | { 97 | Debug.Log("CString.IsNullOrEmpty(CString cs) ok"); 98 | } 99 | else 100 | { 101 | Debug.LogError("CString.IsNullOrEmpty(CString cs) failed"); 102 | } 103 | 104 | if (CString.IsNullOrWhiteSpace(null) && CString.IsNullOrWhiteSpace(" ")) 105 | { 106 | Debug.Log("CString.IsNullOrWhiteSpace(CString cs) ok"); 107 | } 108 | else 109 | { 110 | Debug.LogError("CString.IsNullOrWhiteSpace(CString cs) failed"); 111 | } 112 | 113 | str = null; 114 | CString str1 = null; 115 | 116 | if (str == null && str == str1 && CString.Equals(str, null)) 117 | { 118 | Debug.Log("CString compare to null ok"); 119 | } 120 | else 121 | { 122 | Debug.LogError("CString compare to null failed"); 123 | } 124 | } 125 | 126 | void TestToLower() 127 | { 128 | 129 | } 130 | 131 | void TestToUpper() 132 | { 133 | 134 | } 135 | 136 | void TestReplace() 137 | { 138 | 139 | } 140 | 141 | private void Update() 142 | { 143 | //Profiler.BeginSample("CString"); 144 | //using (CString.Block()) 145 | //{ 146 | //} 147 | 148 | //Profiler.EndSample(); 149 | string hello = StringPool.Alloc(5); 150 | TestBlock(hello); 151 | StringPool.Collect(hello); 152 | } 153 | 154 | string TestBlock(string str) 155 | { 156 | using (CString.Block()) 157 | { 158 | CString sb = CString.Alloc(256); 159 | sb.Append("hello"); 160 | sb.CopyToString(str); 161 | return str; 162 | } 163 | } 164 | 165 | } -------------------------------------------------------------------------------- /UnitTest/Assets/CStringTest.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a5ccb98f1f91624198802c2bd74fa51 3 | timeCreated: 1486524871 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /UnitTest/Assets/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1554bb95cf7954d4f801027f0d2e6768 3 | folderAsset: yes 4 | timeCreated: 1486524764 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /UnitTest/Assets/Plugins/CString.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/Assets/Plugins/CString.dll -------------------------------------------------------------------------------- /UnitTest/Assets/Plugins/CString.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6c6670772a1e40947932b23d982fd6b8 3 | timeCreated: 1486524765 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | platformData: 11 | Any: 12 | enabled: 1 13 | settings: {} 14 | Editor: 15 | enabled: 0 16 | settings: 17 | DefaultValueInitialized: true 18 | WindowsStoreApps: 19 | enabled: 0 20 | settings: 21 | CPU: AnyCPU 22 | userData: 23 | assetBundleName: 24 | assetBundleVariant: 25 | -------------------------------------------------------------------------------- /UnitTest/Assets/UnitTest.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/Assets/UnitTest.unity -------------------------------------------------------------------------------- /UnitTest/Assets/UnitTest.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65f071b95def05a4cb963539d3165295 3 | timeCreated: 1486535589 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.3.5p7 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /UnitTest/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topameng/CString/4eb15b843577fd45c219ec2297ec26bfc98e955f/UnitTest/ProjectSettings/UnityConnectSettings.asset --------------------------------------------------------------------------------