An embeddable, thread-safe implementation of the cubescript language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
4.8 KiB

  1. #include <cassert>
  2. #include <cstring>
  3. #include <cubescript/cubescript.hh>
  4. #include "cs_strman.hh"
  5. #include "cs_thread.hh"
  6. namespace cubescript {
  7. struct string_ref_state {
  8. internal_state *state;
  9. std::size_t length;
  10. std::size_t refcount;
  11. };
  12. inline string_ref_state *get_ref_state(char const *ptr) {
  13. string_ref_state *r;
  14. std::memcpy(&r, &ptr, sizeof(r));
  15. return r - 1;
  16. }
  17. char const *string_pool::add(std::string_view str) {
  18. auto it = counts.find(str);
  19. /* already present: just increment ref */
  20. if (it != counts.end()) {
  21. auto *st = it->second;
  22. /* having a null pointer is the same as non-existence */
  23. if (st) {
  24. ++st->refcount;
  25. st += 1;
  26. char const *r;
  27. std::memcpy(&r, &st, sizeof(r));
  28. return r;
  29. }
  30. }
  31. /* not present: allocate brand new data */
  32. auto ss = str.size();
  33. auto strp = alloc_buf(ss);
  34. /* write string data, it's already pre-terminated */
  35. memcpy(strp, str.data(), ss);
  36. /* store it */
  37. counts.emplace(std::string_view{strp, ss}, get_ref_state(strp));
  38. return strp;
  39. }
  40. char const *string_pool::ref(char const *ptr) {
  41. auto *ss = get_ref_state(ptr);
  42. ++ss->refcount;
  43. return ptr;
  44. }
  45. string_ref string_pool::steal(char *ptr) {
  46. auto *ss = get_ref_state(ptr);
  47. auto sr = std::string_view{ptr, ss->length};
  48. /* much like add(), but we already have memory */
  49. auto it = counts.find(sr);
  50. if (it != counts.end()) {
  51. auto *st = it->second;
  52. if (st) {
  53. /* the buffer is superfluous now */
  54. cstate->alloc(ss, ss->length + sizeof(string_ref_state) + 1, 0);
  55. st += 1;
  56. char const *rp;
  57. std::memcpy(&rp, &st, sizeof(rp));
  58. return string_ref{rp};
  59. }
  60. }
  61. ss->refcount = 0; /* string_ref will increment it */
  62. counts.emplace(sr, ss);
  63. return string_ref{ptr};
  64. }
  65. void string_pool::unref(char const *ptr) {
  66. auto *ss = get_ref_state(ptr);
  67. if (!--ss->refcount) {
  68. /* refcount zero, so ditch it
  69. * this path is a little slow...
  70. */
  71. auto sr = std::string_view{ptr, ss->length};
  72. auto it = counts.find(sr);
  73. /* this should *never* happen unless we have a bug */
  74. #ifndef NDEBUG
  75. assert(it != counts.end());
  76. #else
  77. if (it == counts.end()) {
  78. abort();
  79. }
  80. #endif
  81. /* we're freeing the key */
  82. counts.erase(it);
  83. /* dealloc */
  84. cstate->alloc(ss, ss->length + sizeof(string_ref_state) + 1, 0);
  85. }
  86. }
  87. char const *string_pool::find(std::string_view str) const {
  88. auto it = counts.find(str);
  89. if (it == counts.end()) {
  90. return nullptr;
  91. }
  92. auto *sp = it->second + 1;
  93. char const *rp;
  94. std::memcpy(&rp, &sp, sizeof(rp));
  95. return rp;
  96. }
  97. std::string_view string_pool::get(char const *ptr) const {
  98. auto *ss = get_ref_state(ptr);
  99. return std::string_view{ptr, ss->length};
  100. }
  101. char *string_pool::alloc_buf(std::size_t len) const {
  102. auto mem = cstate->alloc(nullptr, 0, len + sizeof(string_ref_state) + 1);
  103. /* write length and initial refcount */
  104. auto *sst = static_cast<string_ref_state *>(mem);
  105. sst->state = cstate;
  106. sst->length = len;
  107. sst->refcount = 1;
  108. /* pre-terminate */
  109. char *strp;
  110. sst += 1;
  111. std::memcpy(&strp, &sst, sizeof(strp));
  112. strp[len] = '\0';
  113. /* now the user can fill it */
  114. return strp;
  115. }
  116. char const *str_managed_ref(char const *str) {
  117. return get_ref_state(str)->state->strman->ref(str);
  118. }
  119. void str_managed_unref(char const *str) {
  120. get_ref_state(str)->state->strman->unref(str);
  121. }
  122. std::string_view str_managed_view(char const *str) {
  123. return get_ref_state(str)->state->strman->get(str);
  124. }
  125. /* strref implementation */
  126. LIBCUBESCRIPT_EXPORT string_ref::string_ref(state &cs, std::string_view str) {
  127. p_str = state_p{cs}.ts().istate->strman->add(str);
  128. }
  129. LIBCUBESCRIPT_EXPORT string_ref::string_ref(string_ref const &ref):
  130. p_str{ref.p_str}
  131. {
  132. get_ref_state(p_str)->state->strman->ref(p_str);
  133. }
  134. /* this can be used by friends to do quick string_ref creation */
  135. LIBCUBESCRIPT_EXPORT string_ref::string_ref(char const *p) {
  136. p_str = str_managed_ref(p);
  137. }
  138. LIBCUBESCRIPT_EXPORT string_ref::~string_ref() {
  139. str_managed_unref(p_str);
  140. }
  141. LIBCUBESCRIPT_EXPORT string_ref &string_ref::operator=(string_ref const &ref) {
  142. p_str = str_managed_ref(ref.p_str);
  143. return *this;
  144. }
  145. LIBCUBESCRIPT_EXPORT char const *string_ref::data() const {
  146. return p_str;
  147. }
  148. LIBCUBESCRIPT_EXPORT string_ref::operator std::string_view() const {
  149. return str_managed_view(p_str);
  150. }
  151. LIBCUBESCRIPT_EXPORT bool string_ref::operator==(string_ref const &s) const {
  152. return p_str == s.p_str;
  153. }
  154. LIBCUBESCRIPT_EXPORT bool string_ref::operator!=(string_ref const &s) const {
  155. return p_str != s.p_str;
  156. }
  157. } /* namespace cubescript */