├── GC Free String ├── Test.unity ├── Test.unity.meta ├── gmcs.rsp ├── gmcs.rsp.meta ├── gstring.cs ├── gstring.cs.meta ├── gstringTest.cs ├── gstringTest.cs.meta ├── smcs.rsp └── smcs.rsp.meta ├── LICENSE └── README.md /GC Free String/Test.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vexe/gstring/2389437d3622b650b8fb36e137606f20d55f6496/GC Free String/Test.unity -------------------------------------------------------------------------------- /GC Free String/Test.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7be11b763ecd665439366c9158e8e8a3 3 | timeCreated: 1436112244 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GC Free String/gmcs.rsp: -------------------------------------------------------------------------------- 1 | -unsafe -------------------------------------------------------------------------------- /GC Free String/gmcs.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ee76bb0c4cb4c0640801855d3d96c4a1 3 | timeCreated: 1436111654 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GC Free String/gstring.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.CompilerServices; 3 | using UnityEngine; 4 | 5 | //#define DBG 6 | 7 | // gstring (gcfreestring) is a string wrapper that uses pointers to mutate the string when performing misc string operations 8 | // the purpose is to be able to perform most the common operations we do no strings (concat, format, replace, etc) 9 | // without any allocation. 10 | // gstring is not meant to be stored as member variables, but to quickly declare them in a 'gstring block', use them 11 | // for whatever string operation you want, then dispose of them. 12 | // The nice thing is that you don't have to manually dispose of gstrings, once you're in a block all assignments are 13 | // registered so that when the block/scope ends all used gstrings are disposed. 14 | // 15 | // But what if you wanted to keep/store the result you calculated and not dispose of them? 16 | // Well this is where 'intern' comes in - basically there's a runtime intern (cache) table 17 | // of strings (similar to .NET's string const intern table). 18 | // string str = result.Intern(); 19 | // Which basically says, if the string is in the intern (cache) table, return it 20 | // otherwise allocate new memory for it and store it in the table, next time we ask for it, it's there. 21 | // The nice thing about interning is that you could pre-intern your strings via the static method gstring.Intern 22 | // 23 | // NOTES: 24 | // 1- The class is not designed with concurrency/threading in mind, it's meant to be used in Unity 25 | // 2- Cultural stuff I did not consider as well 26 | // 3- Again, you shouldn't have gstring members in your class. All gstring instances are meant to be disposed. 27 | // You just quickly open up a gstring.Block() and use gstrings in it, if you want to store a result you get 28 | // back from a gstring operation use Intern 29 | 30 | namespace System 31 | { 32 | public class gstring 33 | { 34 | static Dictionary> g_cache; 35 | static Stack g_blocks; 36 | static List g_intern_table; 37 | static gstring_block g_current_block; 38 | static List g_finds; 39 | static gstring[] g_format_args; 40 | 41 | const int INITIAL_BLOCK_CAPACITY = 32; 42 | const int INITIAL_CACHE_CAPACITY = 128; 43 | const int INITIAL_STACK_CAPACITY = 48; 44 | const int INITIAL_INTERN_CAPACITY = 256; 45 | const char NEW_ALLOC_CHAR = 'X'; 46 | 47 | [NonSerialized] string _value; 48 | [NonSerialized] bool _disposed; 49 | 50 | internal gstring() 51 | { 52 | throw new NotSupportedException(); 53 | } 54 | 55 | internal gstring(int length) 56 | { 57 | _value = new string(NEW_ALLOC_CHAR, length); 58 | } 59 | 60 | static gstring() 61 | { 62 | Initialize(INITIAL_CACHE_CAPACITY, 63 | INITIAL_STACK_CAPACITY, 64 | INITIAL_BLOCK_CAPACITY, 65 | INITIAL_INTERN_CAPACITY); 66 | 67 | g_finds = new List(10); 68 | g_format_args = new gstring[10]; 69 | } 70 | 71 | internal void dispose() 72 | { 73 | if (_disposed) 74 | throw new ObjectDisposedException(this); 75 | 76 | // At this point there *must* be a stack whose length is equal to ours 77 | // otherwise we wouldn't exist 78 | var stack = g_cache[Length]; 79 | stack.Push(this); 80 | #if DBG 81 | if (log != null) 82 | log("Disposed: " + _value + " Length=" + Length + " Stack=" + stack.Count); 83 | #endif 84 | memcpy(_value, NEW_ALLOC_CHAR); 85 | 86 | _disposed = true; 87 | } 88 | 89 | internal static gstring get(string value) 90 | { 91 | if (value == null) 92 | return null; 93 | #if DBG 94 | if (log != null) 95 | log("Getting: " + value); 96 | #endif 97 | var result = get(value.Length); 98 | memcpy(dst: result, src: value); 99 | return result; 100 | } 101 | 102 | internal static string __intern(string value) 103 | { 104 | int idx = g_intern_table.IndexOf(value); 105 | if (idx != -1) 106 | return g_intern_table[idx]; 107 | 108 | string interned = new string(NEW_ALLOC_CHAR, value.Length); 109 | memcpy(interned, value); 110 | g_intern_table.Add(interned); 111 | #if DBG 112 | if (log != null) 113 | log("Interned: " + value); 114 | #endif 115 | return interned; 116 | } 117 | 118 | internal static gstring get(int length) 119 | { 120 | if (g_current_block == null) 121 | throw new InvalidOperationException("Getting gstrings must be done in a gstring_block. Make sure you do a using(gstring.block())"); 122 | 123 | if (length <= 0) 124 | throw new InvalidOperationException("Invalid length: " + length); 125 | 126 | gstring result; 127 | Stack stack; 128 | if (!g_cache.TryGetValue(length, out stack)) 129 | { 130 | stack = new Stack(INITIAL_STACK_CAPACITY); 131 | for (int i = 0; i < INITIAL_STACK_CAPACITY; i++) 132 | stack.Push(new gstring(length)); 133 | g_cache[length] = stack; 134 | result = stack.Pop(); 135 | } 136 | else 137 | { 138 | if (stack.Count == 0) 139 | { 140 | if (Log != null) 141 | Log("Stack=0 Allocating new gstring Length=" + length); 142 | result = new gstring(length); 143 | } 144 | else 145 | { 146 | result = stack.Pop(); 147 | #if DBG 148 | if (log != null) 149 | log("Popped Length=" + length + " Stack=" + stack.Count); 150 | #endif 151 | } 152 | } 153 | 154 | result._disposed = false; 155 | 156 | g_current_block.push(result); 157 | 158 | return result; 159 | } 160 | 161 | internal static int get_digit_count(int value) 162 | { 163 | int cnt; 164 | for (cnt = 1; (value /= 10) > 0; cnt++); 165 | return cnt; 166 | } 167 | 168 | internal static int internal_index_of(string input, char value, int start) 169 | { 170 | return internal_index_of(input, value, start, input.Length - start); 171 | } 172 | 173 | internal static int internal_index_of(string input, string value) 174 | { 175 | return internal_index_of(input, value, 0, input.Length); 176 | } 177 | 178 | internal static int internal_index_of(string input, string value, int start) 179 | { 180 | return internal_index_of(input, value, start, input.Length - start); 181 | } 182 | 183 | internal unsafe static gstring internal_format(string input, int num_args) 184 | { 185 | // "{0} {1}", "Hello", "World" -> 186 | // "xxxxxxxxxxx" 187 | // "Helloxxxxxx" 188 | // "Hello xxxxx" 189 | // "Hello World" 190 | 191 | // "Player={0} Id={1}", "Jon", 10 -> 192 | // "xxxxxxxxxxxxxxxx" 193 | // "Player=xxxxxxxxx" 194 | // "Player=Jonxxxxxx" 195 | // "Player=Jon Id=xx" 196 | // "Player=Jon Id=10" 197 | 198 | if (input == null) 199 | throw new ArgumentNullException("value"); 200 | 201 | int new_len = input.Length - 3 * num_args; 202 | 203 | for (int i = 0; i < num_args; i++) 204 | { 205 | gstring arg = g_format_args[i]; 206 | new_len += arg.Length; 207 | } 208 | 209 | gstring result = get(new_len); 210 | string res_value = result._value; 211 | 212 | int brace_idx = -3; 213 | for(int i = 0, j = 0, x = 0; x < num_args; x++) 214 | { 215 | string arg = g_format_args[x]._value; 216 | brace_idx = internal_index_of(input, '{', brace_idx + 3); 217 | if (brace_idx == -1) 218 | throw new InvalidOperationException("Couldn't find open brace for argument " + arg); 219 | if (brace_idx + 2 >= input.Length || input[brace_idx + 2] != '}') 220 | throw new InvalidOperationException("Couldn't find close brace for argument " + arg); 221 | 222 | fixed(char* ptr_input = input) 223 | { 224 | fixed(char* ptr_result = res_value) 225 | { 226 | for(int k = 0; i < new_len; ) 227 | { 228 | if (j < brace_idx) 229 | ptr_result[i++] = ptr_input[j++]; 230 | else 231 | { 232 | ptr_result[i++] = arg[k++]; 233 | if (k == arg.Length) 234 | { 235 | j += 3; 236 | break; 237 | } 238 | } 239 | } 240 | } 241 | } 242 | } 243 | 244 | return result; 245 | } 246 | 247 | internal unsafe static int internal_index_of(string input, char value, int start, int count) 248 | { 249 | if (start < 0 || start >= input.Length) 250 | throw new ArgumentOutOfRangeException("start"); 251 | 252 | if (start + count > input.Length) 253 | throw new ArgumentOutOfRangeException("count=" + count + " start+count=" + start + count); 254 | 255 | fixed (char* ptr_this = input) 256 | { 257 | int end = start + count; 258 | for(int i = start; i < end; i++) 259 | if (ptr_this[i] == value) 260 | return i; 261 | return -1; 262 | } 263 | } 264 | 265 | internal unsafe static int internal_index_of(string input, string value, int start, int count) 266 | { 267 | int input_len = input.Length; 268 | 269 | if (start < 0 || start >= input_len) 270 | throw new ArgumentOutOfRangeException("start"); 271 | 272 | if (count < 0 || start + count > input_len) 273 | throw new ArgumentOutOfRangeException("count=" + count + " start+count=" + (start + count)); 274 | 275 | if (count == 0) 276 | return -1; 277 | 278 | fixed (char* ptr_input = input) 279 | { 280 | fixed (char* ptr_value = value) 281 | { 282 | int found = 0; 283 | int end = start + count; 284 | for(int i = start; i < end; i++) 285 | { 286 | for(int j = 0; j < value.Length && i + j < input_len; j++) 287 | { 288 | if (ptr_input[i + j] == ptr_value[j]) 289 | { 290 | found++; 291 | if (found == value.Length) 292 | return i; 293 | continue; 294 | } 295 | if (found > 0) 296 | break; 297 | } 298 | } 299 | return -1; 300 | } 301 | } 302 | } 303 | 304 | internal unsafe static gstring internal_remove(string input, int start, int count) 305 | { 306 | if (start < 0 || start >= input.Length) 307 | throw new ArgumentOutOfRangeException("start=" + start + " Length=" + input.Length); 308 | 309 | if (count < 0 || start + count > input.Length) 310 | throw new ArgumentOutOfRangeException("count=" + count + " start+count=" + (start + count) + " Length=" + input.Length); 311 | 312 | if (count == 0) 313 | return input; 314 | 315 | gstring result = get(input.Length - count); 316 | internal_remove(result, input, start, count); 317 | return result; 318 | } 319 | 320 | internal unsafe static void internal_remove(string dst, string src, int start, int count) 321 | { 322 | fixed(char* src_ptr = src) 323 | { 324 | fixed(char* dst_ptr = dst) 325 | { 326 | for(int i = 0, j = 0; i < dst.Length; i++) 327 | { 328 | if (i >= start && i < start + count) // within removal range 329 | continue; 330 | dst_ptr[j++] = src_ptr[i]; 331 | } 332 | } 333 | } 334 | } 335 | 336 | internal unsafe static gstring internal_replace(string value, string old_value, string new_value) 337 | { 338 | // "Hello, World. There World" | World->Jon = 339 | // "000000000000000000000" (len = orig - 2 * (world-jon) = orig - 4 340 | // "Hello, 00000000000000" 341 | // "Hello, Jon00000000000" 342 | // "Hello, Jon. There 000" 343 | // "Hello, Jon. There Jon" 344 | 345 | // "Hello, World. There World" | World->Alexander = 346 | // "000000000000000000000000000000000" (len = orig + 2 * (alexander-world) = orig + 8 347 | // "Hello, 00000000000000000000000000" 348 | // "Hello, Alexander00000000000000000" 349 | // "Hello, Alexander. There 000000000" 350 | // "Hello, Alexander. There Alexander" 351 | 352 | if (old_value == null) 353 | throw new ArgumentNullException("old_value"); 354 | 355 | if (new_value == null) 356 | throw new ArgumentNullException("new_value"); 357 | 358 | int idx = internal_index_of(value, old_value); 359 | if (idx == -1) 360 | return value; 361 | 362 | g_finds.Clear(); 363 | g_finds.Add(idx); 364 | 365 | // find all the indicies beforehand 366 | while(idx + old_value.Length < value.Length) 367 | { 368 | idx = internal_index_of(value, old_value, idx + old_value.Length); 369 | if (idx == -1) 370 | break; 371 | g_finds.Add(idx); 372 | } 373 | 374 | // calc the right new total length 375 | int new_len; 376 | int dif = old_value.Length - new_value.Length; 377 | if (dif > 0) 378 | new_len = value.Length - (g_finds.Count * dif); 379 | else 380 | new_len = value.Length + (g_finds.Count * -dif); 381 | 382 | gstring result = get(new_len); 383 | fixed(char* ptr_this = value) 384 | { 385 | fixed(char* ptr_result = result._value) 386 | { 387 | for (int i = 0, x = 0, j = 0; i < new_len;) 388 | { 389 | if (x == g_finds.Count || g_finds[x] != j) 390 | { 391 | ptr_result[i++] = ptr_this[j++]; 392 | } 393 | else 394 | { 395 | for (int n = 0; n < new_value.Length; n++) 396 | ptr_result[i + n] = new_value[n]; 397 | 398 | x++; 399 | i += new_value.Length; 400 | j += old_value.Length; 401 | } 402 | } 403 | } 404 | } 405 | return result; 406 | } 407 | 408 | internal unsafe static gstring internal_insert(string value, char to_insert, int start, int count) 409 | { 410 | // "HelloWorld" (to_insert=x, start=5, count=3) -> "HelloxxxWorld" 411 | 412 | if (start < 0 || start >= value.Length) 413 | throw new ArgumentOutOfRangeException("start=" + start + " Length=" + value.Length); 414 | 415 | if (count < 0) 416 | throw new ArgumentOutOfRangeException("count=" + count); 417 | 418 | if (count == 0) 419 | return get(value); 420 | 421 | int new_len = value.Length + count; 422 | gstring result = get(new_len); 423 | fixed(char* ptr_value = value) 424 | { 425 | fixed(char* ptr_result = result._value) 426 | { 427 | for(int i = 0, j = 0; i < new_len; i++) 428 | { 429 | if (i >= start && i < start + count) 430 | ptr_result[i] = to_insert; 431 | else 432 | ptr_result[i] = ptr_value[j++]; 433 | } 434 | } 435 | } 436 | return result; 437 | } 438 | 439 | internal unsafe static gstring internal_insert(string input, string to_insert, int start) 440 | { 441 | if (input == null) 442 | throw new ArgumentNullException("input"); 443 | 444 | if (to_insert == null) 445 | throw new ArgumentNullException("to_insert"); 446 | 447 | if (start < 0 || start >= input.Length) 448 | throw new ArgumentOutOfRangeException("start=" + start + " Length=" + input.Length); 449 | 450 | if (to_insert.Length == 0) 451 | return get(input); 452 | 453 | int new_len = input.Length + to_insert.Length; 454 | gstring result = get(new_len); 455 | internal_insert(result, input, to_insert, start); 456 | return result; 457 | } 458 | 459 | internal unsafe static gstring internal_concat(string s1, string s2) 460 | { 461 | int total_length = s1.Length + s2.Length; 462 | gstring result = get(total_length); 463 | fixed(char* ptr_result = result._value) 464 | { 465 | fixed(char* ptr_s1 = s1) 466 | { 467 | fixed(char* ptr_s2 = s2) 468 | { 469 | memcpy(dst: ptr_result, src: ptr_s1, length: s1.Length, src_offset: 0); 470 | memcpy(dst: ptr_result, src: ptr_s2, length: s2.Length, src_offset: s1.Length); 471 | } 472 | } 473 | } 474 | return result; 475 | } 476 | 477 | internal unsafe static void internal_insert(string dst, string src, string to_insert, int start) 478 | { 479 | fixed(char* ptr_src = src) 480 | { 481 | fixed(char* ptr_dst = dst) 482 | { 483 | fixed(char* ptr_to_insert = to_insert) 484 | { 485 | for(int i = 0, j = 0, k = 0; i < dst.Length; i++) 486 | { 487 | if (i >= start && i < start + to_insert.Length) 488 | ptr_dst[i] = ptr_to_insert[k++]; 489 | else 490 | ptr_dst[i] = ptr_src[j++]; 491 | } 492 | } 493 | } 494 | } 495 | } 496 | 497 | internal unsafe static void intcpy(char* dst, int value, int start, int count) 498 | { 499 | int end = start + count; 500 | for (int i = end - 1; i >= start; i--, value /= 10) 501 | *(dst + i) = (char)(value % 10 + 48); 502 | } 503 | 504 | internal unsafe static void memcpy(char* dst, char* src, int count) 505 | { 506 | for(int i = 0; i < count; i++) 507 | *(dst++) = *(src++); 508 | } 509 | 510 | internal unsafe static void memcpy(string dst, char src) 511 | { 512 | fixed (char* ptr_dst = dst) 513 | { 514 | int len = dst.Length; 515 | for (int i = 0; i < len; i++) 516 | ptr_dst[i] = src; 517 | } 518 | } 519 | 520 | internal unsafe static void memcpy(string dst, char src, int index) 521 | { 522 | fixed (char* ptr = dst) 523 | ptr[index] = src; 524 | } 525 | 526 | internal unsafe static void memcpy(string dst, string src) 527 | { 528 | if (dst.Length != src.Length) 529 | throw new InvalidOperationException("Length mismatch"); 530 | 531 | memcpy(dst, src, dst.Length, 0); 532 | } 533 | 534 | internal unsafe static void memcpy(char* dst, char* src, int length, int src_offset) 535 | { 536 | for (int i = 0; i < length; i++) 537 | *(dst + i + src_offset) = *(src + i); 538 | } 539 | 540 | internal unsafe static void memcpy(string dst, string src, int length, int src_offset) 541 | { 542 | fixed (char* ptr_dst = dst) 543 | { 544 | fixed (char* ptr_src = src) 545 | { 546 | for (int i = 0; i < length; i++) 547 | ptr_dst[i + src_offset] = ptr_src[i]; 548 | } 549 | } 550 | } 551 | 552 | internal class gstring_block : IDisposable 553 | { 554 | readonly Stack stack; 555 | 556 | internal gstring_block(int capacity) 557 | { 558 | stack = new Stack(capacity); 559 | } 560 | 561 | internal void push(gstring str) 562 | { 563 | stack.Push(str); 564 | } 565 | 566 | internal IDisposable begin() 567 | { 568 | #if DBG 569 | if (log != null) 570 | log("Began block"); 571 | #endif 572 | return this; 573 | } 574 | 575 | void IDisposable.Dispose() 576 | { 577 | #if DBG 578 | if (log != null) 579 | log("Disposing block"); 580 | #endif 581 | while (stack.Count > 0) 582 | { 583 | var str = stack.Pop(); 584 | str.dispose(); 585 | } 586 | 587 | gstring.g_blocks.Push(this); 588 | } 589 | } 590 | 591 | // Public API 592 | #region 593 | 594 | public static Action Log = null; 595 | 596 | public static int DecimalAccuracy = 3; // digits after the decimal point 597 | 598 | public int Length 599 | { 600 | get { return _value.Length; } 601 | } 602 | 603 | public static void Initialize(int cache_capacity, int stack_capacity, int block_capacity, int intern_capacity) 604 | { 605 | g_cache = new Dictionary>(cache_capacity); 606 | g_blocks = new Stack(block_capacity); 607 | g_intern_table = new List(intern_capacity); 608 | 609 | for (int c = 1; c < cache_capacity; c++) 610 | { 611 | var stack = new Stack(stack_capacity); 612 | for (int j = 0; j < stack_capacity; j++) 613 | stack.Push(new gstring(c)); 614 | g_cache[c] = stack; 615 | } 616 | 617 | for (int i = 0; i < block_capacity; i++) 618 | { 619 | var block = new gstring_block(block_capacity * 2); 620 | g_blocks.Push(block); 621 | } 622 | } 623 | 624 | public static IDisposable Block() 625 | { 626 | if (g_blocks.Count == 0) 627 | g_current_block = new gstring_block(INITIAL_BLOCK_CAPACITY * 2); 628 | else 629 | g_current_block = g_blocks.Pop(); 630 | return g_current_block.begin(); 631 | } 632 | 633 | public string Intern() 634 | { 635 | return __intern(_value); 636 | } 637 | 638 | public static string Intern(string value) 639 | { 640 | return __intern(value); 641 | } 642 | 643 | public static void Intern(string[] values) 644 | { 645 | for(int i = 0; i < values.Length; i++) 646 | __intern(values[i]); 647 | } 648 | 649 | public char this[int i] 650 | { 651 | get { return _value[i]; } 652 | set { memcpy(this, value, i); } 653 | } 654 | 655 | public override int GetHashCode() 656 | { 657 | return RuntimeHelpers.GetHashCode(_value); 658 | } 659 | 660 | public override bool Equals(object obj) 661 | { 662 | if (obj == null) 663 | return ReferenceEquals(this, null); 664 | 665 | var gstr = obj as gstring; 666 | if (gstr != null) 667 | return gstr._value == this._value; 668 | 669 | var str = obj as string; 670 | if (str != null) 671 | return str == this._value; 672 | 673 | return false; 674 | } 675 | 676 | public override string ToString() 677 | { 678 | return _value; 679 | } 680 | 681 | public static implicit operator gstring(bool value) 682 | { 683 | return get(value ? "True" : "False"); 684 | } 685 | 686 | public unsafe static implicit operator gstring(int value) 687 | { 688 | // e.g. 125 689 | // first pass: count the number of digits 690 | // then: get a gstring with length = num digits 691 | // finally: iterate again, get the char of each digit, memcpy char to result 692 | bool negative = value < 0; 693 | value = Math.Abs(value); 694 | int num_digits = get_digit_count(value); 695 | gstring result; 696 | if (negative) 697 | { 698 | result = get(num_digits + 1); 699 | fixed(char* ptr = result._value) 700 | { 701 | *ptr = '-'; 702 | intcpy(ptr, value, 1, num_digits); 703 | } 704 | } 705 | else 706 | { 707 | result = get(num_digits); 708 | fixed(char* ptr = result._value) 709 | intcpy(ptr, value, 0, num_digits); 710 | } 711 | return result; 712 | } 713 | 714 | public unsafe static implicit operator gstring(float value) 715 | { 716 | // e.g. 3.148 717 | bool negative = value < 0; 718 | if (negative) value = -value; 719 | int mul = (int)Math.Pow(10, DecimalAccuracy); 720 | int number = (int)(value * mul); // gets the number as a whole, e.g. 3148 721 | int left_num = number / mul; // left part of the decimal point, e.g. 3 722 | int right_num = number % mul; // right part of the decimal pnt, e.g. 148 723 | int left_digit_count = get_digit_count(left_num); // e.g. 1 724 | int right_digit_count = get_digit_count(right_num); // e.g. 3 725 | int total = left_digit_count + right_digit_count + 1; // +1 for '.' 726 | 727 | gstring result; 728 | if (negative) 729 | { 730 | result = get(total + 1); // +1 for '-' 731 | fixed(char* ptr = result._value) 732 | { 733 | *ptr = '-'; 734 | intcpy(ptr, left_num, 1, left_digit_count); 735 | *(ptr + left_digit_count + 1) = '.'; 736 | intcpy(ptr, right_num, left_digit_count + 2, right_digit_count); 737 | } 738 | } 739 | else 740 | { 741 | result = get(total); 742 | fixed(char* ptr = result._value) 743 | { 744 | intcpy(ptr, left_num, 0, left_digit_count); 745 | *(ptr + left_digit_count) = '.'; 746 | intcpy(ptr, right_num, left_digit_count + 1, right_digit_count); 747 | } 748 | } 749 | return result; 750 | } 751 | 752 | public static implicit operator gstring(string value) 753 | { 754 | return get(value); 755 | } 756 | 757 | public static implicit operator string(gstring value) 758 | { 759 | return value._value; 760 | } 761 | 762 | public static gstring operator +(gstring left, gstring right) 763 | { 764 | return internal_concat(left, right); 765 | } 766 | 767 | public static bool operator ==(gstring left, gstring right) 768 | { 769 | if (ReferenceEquals(left, null)) 770 | return ReferenceEquals(right, null); 771 | if (ReferenceEquals(right, null)) 772 | return false; 773 | return left._value == right._value; 774 | } 775 | 776 | public static bool operator !=(gstring left, gstring right) 777 | { 778 | return !(left._value == right._value); 779 | } 780 | 781 | public unsafe gstring ToUpper() 782 | { 783 | var result = get(Length); 784 | fixed(char* ptr_this = this._value) 785 | { 786 | fixed(char* ptr_result = result._value) 787 | { 788 | for(int i = 0; i < _value.Length; i++) 789 | { 790 | var ch = ptr_this[i]; 791 | if (char.IsLower(ch)) 792 | ptr_result[i] = char.ToUpper(ch); 793 | else 794 | ptr_result[i] = ptr_this[i]; 795 | } 796 | } 797 | } 798 | return result; 799 | } 800 | 801 | public unsafe gstring ToLower() 802 | { 803 | var result = get(Length); 804 | fixed(char* ptr_this = this._value) 805 | { 806 | fixed(char* ptr_result = result._value) 807 | { 808 | for(int i = 0;i < _value.Length; i++) 809 | { 810 | var ch = ptr_this[i]; 811 | if (char.IsUpper(ch)) 812 | ptr_result[i] = char.ToLower(ch); 813 | else 814 | ptr_result[i] = ptr_this[i]; 815 | } 816 | } 817 | } 818 | return result; 819 | } 820 | 821 | public gstring Remove(int start) 822 | { 823 | return Remove(start, Length - start); 824 | } 825 | 826 | public gstring Remove(int start, int count) 827 | { 828 | return internal_remove(this._value, start, count); 829 | } 830 | 831 | public gstring Insert(char value, int start, int count) 832 | { 833 | return internal_insert(this._value, value, start, count); 834 | } 835 | 836 | public gstring Insert(string value, int start) 837 | { 838 | return internal_insert(this._value, value, start); 839 | } 840 | 841 | public unsafe gstring Replace(char old_value, char new_value) 842 | { 843 | gstring result = get(Length); 844 | fixed(char* ptr_this = this._value) 845 | { 846 | fixed(char* ptr_result = result._value) 847 | { 848 | for(int i = 0; i < Length; i++) 849 | { 850 | if (ptr_this[i] == old_value) 851 | ptr_result[i] = new_value; 852 | else 853 | ptr_result[i] = ptr_this[i]; 854 | } 855 | } 856 | } 857 | return result; 858 | } 859 | 860 | public gstring Replace(string old_value, string new_value) 861 | { 862 | return internal_replace(this._value, old_value, new_value); 863 | } 864 | 865 | public gstring Substring(int start) 866 | { 867 | return Substring(start, Length - start); 868 | } 869 | 870 | public unsafe gstring Substring(int start, int count) 871 | { 872 | if (start < 0 || start >= Length) 873 | throw new ArgumentOutOfRangeException("start"); 874 | 875 | if (count > Length) 876 | throw new ArgumentOutOfRangeException("count"); 877 | 878 | gstring result = get(count); 879 | fixed(char* src = this._value) 880 | fixed(char* dst = result._value) 881 | memcpy(dst, src + start, count); 882 | 883 | return result; 884 | } 885 | 886 | public bool Contains(string value) 887 | { 888 | return IndexOf(value) != -1; 889 | } 890 | 891 | public bool Contains(char value) 892 | { 893 | return IndexOf(value) != -1; 894 | } 895 | 896 | public int LastIndexOf(string value) 897 | { 898 | int idx = -1; 899 | int last_find = -1; 900 | while(true) 901 | { 902 | idx = internal_index_of(this._value, value, idx + value.Length); 903 | last_find = idx; 904 | if (idx == -1 || idx + value.Length >= this._value.Length) 905 | break; 906 | } 907 | return last_find; 908 | } 909 | 910 | public int LastIndexOf(char value) 911 | { 912 | int idx = -1; 913 | int last_find = -1; 914 | while(true) 915 | { 916 | idx = internal_index_of(this._value, value, idx + 1); 917 | last_find = idx; 918 | if (idx == -1 || idx + 1 >= this._value.Length) 919 | break; 920 | } 921 | return last_find; 922 | } 923 | 924 | public int IndexOf(char value) 925 | { 926 | return IndexOf(value, 0, Length); 927 | } 928 | 929 | public int IndexOf(char value, int start) 930 | { 931 | return internal_index_of(this._value, value, start); 932 | } 933 | 934 | public int IndexOf(char value, int start, int count) 935 | { 936 | return internal_index_of(this._value, value, start, count); 937 | } 938 | 939 | public int IndexOf(string value) 940 | { 941 | return IndexOf(value, 0, Length); 942 | } 943 | 944 | public int IndexOf(string value, int start) 945 | { 946 | return IndexOf(value, start, Length - start); 947 | } 948 | 949 | public int IndexOf(string value, int start, int count) 950 | { 951 | return internal_index_of(this._value, value, start, count); 952 | } 953 | 954 | public unsafe bool EndsWith(string postfix) 955 | { 956 | if (postfix == null) 957 | throw new ArgumentNullException("postfix"); 958 | 959 | if (this.Length < postfix.Length) 960 | return false; 961 | 962 | fixed(char* ptr_this = this._value) 963 | { 964 | fixed(char* ptr_postfix = postfix) 965 | { 966 | for (int i = this._value.Length - 1, j = postfix.Length - 1; j >= 0; i--, j--) 967 | if (ptr_this[i] != ptr_postfix[j]) 968 | return false; 969 | } 970 | } 971 | 972 | return true; 973 | } 974 | 975 | public unsafe bool StartsWith(string prefix) 976 | { 977 | if (prefix == null) 978 | throw new ArgumentNullException("prefix"); 979 | 980 | if (this.Length < prefix.Length) 981 | return false; 982 | 983 | fixed(char* ptr_this = this._value) 984 | { 985 | fixed(char* ptr_prefix = prefix) 986 | { 987 | for (int i = 0; i < prefix.Length; i++) 988 | if (ptr_this[i] != ptr_prefix[i]) 989 | return false; 990 | } 991 | } 992 | 993 | return true; 994 | } 995 | 996 | public static int GetCacheCount(int length) 997 | { 998 | Stack stack; 999 | if (!g_cache.TryGetValue(length, out stack)) 1000 | return -1; 1001 | return stack.Count; 1002 | } 1003 | 1004 | public gstring Concat(gstring value) 1005 | { 1006 | return internal_concat(this, value); 1007 | } 1008 | 1009 | public static gstring Concat(gstring s0, gstring s1) { return s0 + s1; } 1010 | 1011 | public static gstring Concat(gstring s0, gstring s1, gstring s2) { return s0 + s1 + s2; } 1012 | 1013 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3) { return s0 + s1 + s2 + s3; } 1014 | 1015 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3, gstring s4) { return s0 + s1 + s2 + s3 + s4; } 1016 | 1017 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3, gstring s4, gstring s5) { return s0 + s1 + s2 + s3 + s4 + s5; } 1018 | 1019 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3, gstring s4, gstring s5, gstring s6) { return s0 + s1 + s2 + s3 + s4 + s5 + s6; } 1020 | 1021 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3, gstring s4, gstring s5, gstring s6, gstring s7) { return s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7; } 1022 | 1023 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3, gstring s4, gstring s5, gstring s6, gstring s7, gstring s8) { return s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8; } 1024 | 1025 | public static gstring Concat(gstring s0, gstring s1, gstring s2, gstring s3, gstring s4, gstring s5, gstring s6, gstring s7, gstring s8, gstring s9) { return s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9; } 1026 | 1027 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3, gstring arg4, gstring arg5, gstring arg6, gstring arg7, gstring arg8, gstring arg9) 1028 | { 1029 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1030 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1031 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1032 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1033 | if (arg4 == null) throw new ArgumentNullException("arg4"); 1034 | if (arg5 == null) throw new ArgumentNullException("arg5"); 1035 | if (arg6 == null) throw new ArgumentNullException("arg6"); 1036 | if (arg7 == null) throw new ArgumentNullException("arg7"); 1037 | if (arg8 == null) throw new ArgumentNullException("arg8"); 1038 | if (arg9 == null) throw new ArgumentNullException("arg9"); 1039 | 1040 | g_format_args[0] = arg0; 1041 | g_format_args[1] = arg1; 1042 | g_format_args[2] = arg2; 1043 | g_format_args[3] = arg3; 1044 | g_format_args[4] = arg4; 1045 | g_format_args[5] = arg5; 1046 | g_format_args[6] = arg6; 1047 | g_format_args[7] = arg7; 1048 | g_format_args[8] = arg8; 1049 | g_format_args[9] = arg9; 1050 | return internal_format(input, 10); 1051 | } 1052 | 1053 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3, gstring arg4, gstring arg5, gstring arg6, gstring arg7, gstring arg8) 1054 | { 1055 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1056 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1057 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1058 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1059 | if (arg4 == null) throw new ArgumentNullException("arg4"); 1060 | if (arg5 == null) throw new ArgumentNullException("arg5"); 1061 | if (arg6 == null) throw new ArgumentNullException("arg6"); 1062 | if (arg7 == null) throw new ArgumentNullException("arg7"); 1063 | if (arg8 == null) throw new ArgumentNullException("arg8"); 1064 | 1065 | g_format_args[0] = arg0; 1066 | g_format_args[1] = arg1; 1067 | g_format_args[2] = arg2; 1068 | g_format_args[3] = arg3; 1069 | g_format_args[4] = arg4; 1070 | g_format_args[5] = arg5; 1071 | g_format_args[6] = arg6; 1072 | g_format_args[7] = arg7; 1073 | g_format_args[8] = arg8; 1074 | return internal_format(input, 9); 1075 | } 1076 | 1077 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3, gstring arg4, gstring arg5, gstring arg6, gstring arg7) 1078 | { 1079 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1080 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1081 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1082 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1083 | if (arg4 == null) throw new ArgumentNullException("arg4"); 1084 | if (arg5 == null) throw new ArgumentNullException("arg5"); 1085 | if (arg6 == null) throw new ArgumentNullException("arg6"); 1086 | if (arg7 == null) throw new ArgumentNullException("arg7"); 1087 | 1088 | g_format_args[0] = arg0; 1089 | g_format_args[1] = arg1; 1090 | g_format_args[2] = arg2; 1091 | g_format_args[3] = arg3; 1092 | g_format_args[4] = arg4; 1093 | g_format_args[5] = arg5; 1094 | g_format_args[6] = arg6; 1095 | g_format_args[7] = arg7; 1096 | return internal_format(input, 8); 1097 | } 1098 | 1099 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3, gstring arg4, gstring arg5, gstring arg6) 1100 | { 1101 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1102 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1103 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1104 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1105 | if (arg4 == null) throw new ArgumentNullException("arg4"); 1106 | if (arg5 == null) throw new ArgumentNullException("arg5"); 1107 | if (arg6 == null) throw new ArgumentNullException("arg6"); 1108 | 1109 | g_format_args[0] = arg0; 1110 | g_format_args[1] = arg1; 1111 | g_format_args[2] = arg2; 1112 | g_format_args[3] = arg3; 1113 | g_format_args[4] = arg4; 1114 | g_format_args[5] = arg5; 1115 | g_format_args[6] = arg6; 1116 | return internal_format(input, 7); 1117 | } 1118 | 1119 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3, gstring arg4, gstring arg5) 1120 | { 1121 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1122 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1123 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1124 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1125 | if (arg4 == null) throw new ArgumentNullException("arg4"); 1126 | if (arg5 == null) throw new ArgumentNullException("arg5"); 1127 | 1128 | g_format_args[0] = arg0; 1129 | g_format_args[1] = arg1; 1130 | g_format_args[2] = arg2; 1131 | g_format_args[3] = arg3; 1132 | g_format_args[4] = arg4; 1133 | g_format_args[5] = arg5; 1134 | return internal_format(input, 6); 1135 | } 1136 | 1137 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3, gstring arg4) 1138 | { 1139 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1140 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1141 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1142 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1143 | if (arg4 == null) throw new ArgumentNullException("arg4"); 1144 | 1145 | g_format_args[0] = arg0; 1146 | g_format_args[1] = arg1; 1147 | g_format_args[2] = arg2; 1148 | g_format_args[3] = arg3; 1149 | g_format_args[4] = arg4; 1150 | return internal_format(input, 5); 1151 | } 1152 | 1153 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2, gstring arg3) 1154 | { 1155 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1156 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1157 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1158 | if (arg3 == null) throw new ArgumentNullException("arg3"); 1159 | 1160 | g_format_args[0] = arg0; 1161 | g_format_args[1] = arg1; 1162 | g_format_args[2] = arg2; 1163 | g_format_args[3] = arg3; 1164 | return internal_format(input, 4); 1165 | } 1166 | 1167 | public static gstring Format(string input, gstring arg0, gstring arg1, gstring arg2) 1168 | { 1169 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1170 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1171 | if (arg2 == null) throw new ArgumentNullException("arg2"); 1172 | 1173 | g_format_args[0] = arg0; 1174 | g_format_args[1] = arg1; 1175 | g_format_args[2] = arg2; 1176 | return internal_format(input, 3); 1177 | } 1178 | 1179 | public static gstring Format(string input, gstring arg0, gstring arg1) 1180 | { 1181 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1182 | if (arg1 == null) throw new ArgumentNullException("arg1"); 1183 | 1184 | g_format_args[0] = arg0; 1185 | g_format_args[1] = arg1; 1186 | return internal_format(input, 2); 1187 | } 1188 | 1189 | public static gstring Format(string input, gstring arg0) 1190 | { 1191 | if (arg0 == null) throw new ArgumentNullException("arg0"); 1192 | 1193 | g_format_args[0] = arg0; 1194 | return internal_format(input, 1); 1195 | } 1196 | 1197 | #endregion 1198 | 1199 | } 1200 | 1201 | public static class GStringExtensions 1202 | { 1203 | public static bool IsNullOrEmpty(this gstring str) 1204 | { 1205 | return str == null || str.Length == 0; 1206 | } 1207 | 1208 | public static bool IsPrefix(this gstring str, string value) 1209 | { 1210 | return str.StartsWith(value); 1211 | } 1212 | 1213 | public static bool ispostfix(this gstring str, string postfix) 1214 | { 1215 | return str.EndsWith(postfix); 1216 | } 1217 | } 1218 | } 1219 | -------------------------------------------------------------------------------- /GC Free String/gstring.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 50ffe16c4f0c9794389d166dbec8f6db 3 | timeCreated: 1436111498 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /GC Free String/gstringTest.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public class gstringTest : MonoBehaviour 7 | { 8 | Dictionary dict = new Dictionary(); 9 | 10 | ProfilerBlock profiler = ProfilerBlock.Instance; 11 | 12 | public string outsideString; 13 | public string outsideString1; 14 | 15 | void Update() 16 | { 17 | using (profiler.Sample("gstring")) 18 | { 19 | using (gstring.Block()) 20 | { 21 | using (profiler.Sample("Format")) 22 | { 23 | gstring gf = gstring.Format("Number = {0}, Float = {1} String = {2}", 123, 3.148f, "Text"); 24 | int x = 10; // declare an int, just so we can step over to it and inspect values when debugging 25 | } 26 | 27 | using (profiler.Sample("Concat")) 28 | { 29 | gstring it = gstring.Concat("That's ", "a lot", " of", " strings", " to ", "concat"); 30 | int x = 10; 31 | } 32 | 33 | using (profiler.Sample("Substring + IndexOf + LastIndexOf")) 34 | { 35 | gstring path = "Path/To/Some/File.txt"; 36 | int period = path.IndexOf('.'); 37 | var ext = path.Substring(period + 1); 38 | var file = path.Substring(path.LastIndexOf('/') + 1, 4); 39 | int x = 10; 40 | } 41 | 42 | using (profiler.Sample("Replace (char)")) 43 | { 44 | gstring input = "This is some sort of text"; 45 | gstring replacement = input.Replace('o', '0').Replace('i', '1'); 46 | int x = 10; 47 | } 48 | 49 | using (profiler.Sample("Replace (string)")) 50 | { 51 | gstring input = "m_This is the is is form of text"; 52 | gstring replacement = input.Replace("m_", "").Replace("is", "si"); 53 | int x = 10; 54 | } 55 | 56 | using (profiler.Sample("Concat + Intern")) 57 | { 58 | for (int i = 0; i < 4; i++) 59 | dict[gstring.Concat("Item", i).Intern()] = i; 60 | outsideString1 = gstring.Concat("I'm ", "long ", "gone ", "by ", "the ", "end ", "of ", "this ", "gstring block"); 61 | outsideString = gstring.Concat("I'll ", "be ", "still ", "around ", "here").Intern(); 62 | int x = 10; 63 | } 64 | 65 | using (profiler.Sample("ToUpper + ToLower")) 66 | { 67 | gstring s1 = "Hello"; 68 | gstring s2 = s1.ToUpper(); 69 | gstring s3 = s2 + s1.ToLower(); 70 | int x = 10; 71 | } 72 | } 73 | } 74 | 75 | using (profiler.Sample("string")) 76 | { 77 | using (profiler.Sample("Format")) 78 | { 79 | string gf = string.Format("Number = {0}, Float = {1} String = {2}", 123, 3.148f, "Text"); 80 | int x = 10; 81 | } 82 | 83 | using (profiler.Sample("Concat")) 84 | { 85 | string it = string.Concat("That's ", "a lot ", " of", " strings", " to ", "concat"); 86 | int x = 10; 87 | } 88 | 89 | using (profiler.Sample("Substring + IndexOf + LastIndexOf")) 90 | { 91 | string path = "Path/To/Some/File.txt"; 92 | int period = path.IndexOf('.'); 93 | var ext = path.Substring(period + 1); 94 | var file = path.Substring(path.LastIndexOf('/') + 1, 4); 95 | int x = 10; 96 | } 97 | 98 | using (profiler.Sample("Replace (char)")) 99 | { 100 | string input = "This is some sort of text"; 101 | string replacement = input.Replace('o', 'O').Replace('i', 'I'); 102 | int x = 10; 103 | } 104 | 105 | using (profiler.Sample("Replace (string)")) 106 | { 107 | string input = "m_This is the is is form of text"; 108 | string replacement = input.Replace("m_", "").Replace("is", "si"); 109 | int x = 10; 110 | } 111 | 112 | using (profiler.Sample("ToUpper + ToLower")) 113 | { 114 | string s1 = "Hello"; 115 | string s2 = s1.ToUpper(); 116 | string s3 = s2 + s1.ToLower(); 117 | int x = 10; 118 | } 119 | 120 | } 121 | } 122 | 123 | public class ProfilerBlock : IDisposable 124 | { 125 | public static readonly ProfilerBlock Instance = new ProfilerBlock(); 126 | 127 | public IDisposable Sample(string sample) 128 | { 129 | Profiler.BeginSample(sample); 130 | return this; 131 | } 132 | 133 | public void Dispose() 134 | { 135 | Profiler.EndSample(); 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /GC Free String/gstringTest.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82fc0e4e200d5c14ab9c5a2fcb411ad1 3 | timeCreated: 1436111621 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /GC Free String/smcs.rsp: -------------------------------------------------------------------------------- 1 | -unsafe -------------------------------------------------------------------------------- /GC Free String/smcs.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c5c4f9f8c1963934682c118b74edf65d 3 | timeCreated: 1436111654 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gstring 2 | GC Free string for C# 3 | 4 | Lets you do pretty much most of the common string operations without any alloactions. 5 | 6 | See the video in this thread to see how I accomplished it http://forum.unity3d.com/threads/gstring-gc-free-string-for-unity.338588/ 7 | 8 | See gstringTest for sample usage code. 9 | 10 | Note that the implementaiton uses pointers and "unsafe" code so make sure if you're on Unity to have the smcs and gmcs files with the -unsafe flag, you might have to restart Unity if it's the first time you add them. 11 | --------------------------------------------------------------------------------