adaptive.hpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #pragma once
  2. /*****
  3. adaptive allocator
  4. sizeof(string) == SSO + 8
  5. aggressively tries to avoid heap allocations
  6. small strings are stored on the stack
  7. large strings are shared via copy-on-write
  8. SSO alone is very slow on large strings due to copying
  9. SSO alone is very slightly faster than this allocator on small strings
  10. COW alone is very slow on small strings due to heap allocations
  11. COW alone is very slightly faster than this allocator on large strings
  12. adaptive is thus very fast for all string sizes
  13. *****/
  14. namespace nall {
  15. inline string::string() : _data(nullptr), _capacity(SSO - 1), _size(0) {
  16. }
  17. template<typename T>
  18. inline auto string::get() -> T* {
  19. if(_capacity < SSO) return (T*)_text;
  20. if(*_refs > 1) _copy();
  21. return (T*)_data;
  22. }
  23. template<typename T>
  24. inline auto string::data() const -> const T* {
  25. if(_capacity < SSO) return (const T*)_text;
  26. return (const T*)_data;
  27. }
  28. inline auto string::reset() -> type& {
  29. if(_capacity >= SSO && !--*_refs) memory::free(_data);
  30. _data = nullptr;
  31. _capacity = SSO - 1;
  32. _size = 0;
  33. return *this;
  34. }
  35. inline auto string::reserve(uint capacity) -> type& {
  36. if(capacity <= _capacity) return *this;
  37. capacity = bit::round(capacity + 1) - 1;
  38. if(_capacity < SSO) {
  39. _capacity = capacity;
  40. _allocate();
  41. } else if(*_refs > 1) {
  42. _capacity = capacity;
  43. _copy();
  44. } else {
  45. _capacity = capacity;
  46. _resize();
  47. }
  48. return *this;
  49. }
  50. inline auto string::resize(uint size) -> type& {
  51. reserve(size);
  52. get()[_size = size] = 0;
  53. return *this;
  54. }
  55. inline auto string::operator=(const string& source) -> type& {
  56. if(&source == this) return *this;
  57. reset();
  58. if(source._capacity >= SSO) {
  59. _data = source._data;
  60. _refs = source._refs;
  61. _capacity = source._capacity;
  62. _size = source._size;
  63. ++*_refs;
  64. } else {
  65. memory::copy(_text, source._text, SSO);
  66. _capacity = source._capacity;
  67. _size = source._size;
  68. }
  69. return *this;
  70. }
  71. inline auto string::operator=(string&& source) -> type& {
  72. if(&source == this) return *this;
  73. reset();
  74. memory::copy(this, &source, sizeof(string));
  75. source._data = nullptr;
  76. source._capacity = SSO - 1;
  77. source._size = 0;
  78. return *this;
  79. }
  80. //SSO -> COW
  81. inline auto string::_allocate() -> void {
  82. char _temp[SSO];
  83. memory::copy(_temp, _text, SSO);
  84. _data = memory::allocate<char>(_capacity + 1 + sizeof(uint));
  85. memory::copy(_data, _temp, SSO);
  86. _refs = (uint*)(_data + _capacity + 1); //always aligned by 32 via reserve()
  87. *_refs = 1;
  88. }
  89. //COW -> Unique
  90. inline auto string::_copy() -> void {
  91. auto _temp = memory::allocate<char>(_capacity + 1 + sizeof(uint));
  92. memory::copy(_temp, _data, _size = min(_capacity, _size));
  93. _temp[_size] = 0;
  94. --*_refs;
  95. _data = _temp;
  96. _refs = (uint*)(_data + _capacity + 1);
  97. *_refs = 1;
  98. }
  99. //COW -> Resize
  100. inline auto string::_resize() -> void {
  101. _data = memory::resize<char>(_data, _capacity + 1 + sizeof(uint));
  102. _refs = (uint*)(_data + _capacity + 1);
  103. *_refs = 1;
  104. }
  105. }