Modify string_t so that it can still be trivially movable once again.
This commit is contained in:
1 parent
c89d6ef0ce
commit
547c67b676
1 file changed
+38
-42
+38
-42
@@ -22,6 +22,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MSTARLIB_STRING_H
|
||||
#define MSTARLIB_STRING_H
|
||||
|
||||
@@ -46,11 +47,10 @@
|
||||
// Note: use of strlen can slow down a lot the program in some cases.
|
||||
#define STRINGI_CONTRACT(v) do { \
|
||||
M_ASSUME (v != NULL); \
|
||||
M_ASSUME (v->ptr != NULL); \
|
||||
STRINGI_ASSUME (string_size(v) == strlen(stringi_get_str(v))); \
|
||||
M_ASSUME (string_get_str(v)[string_size(v)] == 0); \
|
||||
STRINGI_ASSUME (string_size(v) == strlen(string_get_cstr(v))); \
|
||||
M_ASSUME (string_get_cstr(v)[string_size(v)] == 0); \
|
||||
M_ASSUME (string_size(v) < string_capacity(v)); \
|
||||
M_ASSUME (string_capacity(v) < sizeof (str_heap_t) || !stringi_stack_p(v)); \
|
||||
M_ASSUME (string_capacity(v) < sizeof (string_heap_t) || !stringi_stack_p(v)); \
|
||||
} while(0)
|
||||
|
||||
|
||||
@@ -74,16 +74,16 @@ typedef struct {
|
||||
} string_heap_t;
|
||||
// string if it is stack allocated
|
||||
typedef struct {
|
||||
char buffer[sizeof (str_heap_t)];
|
||||
char buffer[sizeof (string_heap_t)];
|
||||
} string_stack_t;
|
||||
// both cases of string are possible
|
||||
typedef union {
|
||||
str_heap_t heap;
|
||||
str_stack_t stack;
|
||||
string_heap_t heap;
|
||||
string_stack_t stack;
|
||||
} string_union_t;
|
||||
// main structure
|
||||
typedef struct {
|
||||
str_union_t u;
|
||||
string_union_t u;
|
||||
char *ptr;
|
||||
} string_t[1];
|
||||
|
||||
@@ -100,7 +100,7 @@ static inline bool
|
||||
stringi_stack_p(const string_t s)
|
||||
{
|
||||
// Function can be called when contract is not fullfilled
|
||||
return (s->ptr == &s->u.stack.buffer[0]);
|
||||
return (s->ptr == NULL);
|
||||
}
|
||||
|
||||
/* Set the size of the string */
|
||||
@@ -109,8 +109,8 @@ stringi_set_size(string_t s, size_t size)
|
||||
{
|
||||
// Function can be called when contract is not fullfilled
|
||||
if (stringi_stack_p(s)) {
|
||||
assert (size < sizeof (str_heap_t) - 1);
|
||||
s->u.stack.buffer[sizeof (str_heap_t) - 1] = size;
|
||||
assert (size < sizeof (string_heap_t) - 1);
|
||||
s->u.stack.buffer[sizeof (string_heap_t) - 1] = size;
|
||||
} else
|
||||
s->u.heap.size = size;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ string_size(const string_t s)
|
||||
{
|
||||
// Function can be called when contract is not fullfilled
|
||||
// Reading both values before calling the '?' operator allows compiler to generate branchless code
|
||||
const size_t s_stack = s->u.stack.buffer[sizeof (str_heap_t) - 1];
|
||||
const size_t s_stack = s->u.stack.buffer[sizeof (string_heap_t) - 1];
|
||||
const size_t s_heap = s->u.heap.size;
|
||||
return stringi_stack_p(s) ? s_stack : s_heap;
|
||||
}
|
||||
@@ -130,31 +130,36 @@ string_capacity(const string_t s)
|
||||
{
|
||||
// Function can be called when contract is not fullfilled
|
||||
// Reading both values before calling the '?' operator allows compiler to generate branchless code
|
||||
const size_t c_stack = sizeof (str_heap_t) - 1;
|
||||
const size_t c_stack = sizeof (string_heap_t) - 1;
|
||||
const size_t c_heap = s->u.heap.alloc;
|
||||
return stringi_stack_p(s) ? c_stack : c_heap;
|
||||
}
|
||||
|
||||
/* Return a writable pointer to the array of char of the string */
|
||||
static inline char*
|
||||
stringi_get_str(const string_t v)
|
||||
stringi_get_str(string_t v)
|
||||
{
|
||||
// Function can be called when contract is not fullfilled
|
||||
return v->ptr;
|
||||
char *const ptr_stack = &v->u.stack.buffer[0];
|
||||
char *const ptr_heap = v->ptr;
|
||||
return stringi_stack_p(v) ? ptr_stack : ptr_heap;
|
||||
}
|
||||
|
||||
static inline const char*
|
||||
string_get_cstr(const string_t v)
|
||||
{
|
||||
STRINGI_CONTRACT (v);
|
||||
return v->ptr;
|
||||
// Function cannot be called when contract is not fullfilled
|
||||
// but it is called by contract (so no contract check to avoid infinite recursion).
|
||||
const char *const ptr_stack = &v->u.stack.buffer[0];
|
||||
const char *const ptr_heap = v->ptr;
|
||||
return stringi_stack_p(v) ? ptr_stack : ptr_heap;
|
||||
}
|
||||
|
||||
static inline void
|
||||
string_init(string_t s)
|
||||
{
|
||||
s->ptr = &s->u.stack.buffer[0];
|
||||
s->ptr[0] = 0;
|
||||
s->ptr = NULL;
|
||||
s->u.stack.buffer[0] = 0;
|
||||
stringi_set_size(s, 0);
|
||||
STRINGI_CONTRACT(s);
|
||||
}
|
||||
@@ -169,6 +174,7 @@ string_clear(string_t v)
|
||||
/* This is not needed but is safer to make
|
||||
the string invalid so that it can be detected. */
|
||||
v->ptr = NULL;
|
||||
v->u.stack.buffer[sizeof (string_heap_t) - 1] = CHAR_MAX;
|
||||
}
|
||||
|
||||
/* NOTE: Internaly used by STRING_DECL_INIT */
|
||||
@@ -194,6 +200,7 @@ string_clear_get_str(string_t v)
|
||||
p = ptr;
|
||||
}
|
||||
v->ptr = NULL;
|
||||
v->u.stack.buffer[sizeof (string_heap_t) - 1] = CHAR_MAX;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -218,7 +225,7 @@ static inline bool
|
||||
string_empty_p(const string_t v)
|
||||
{
|
||||
STRINGI_CONTRACT (v);
|
||||
return v->ptr[0] == 0;
|
||||
return string_size(v) == 0;
|
||||
}
|
||||
|
||||
/* Ensures that the string capacity is greater than size_alloc
|
||||
@@ -242,9 +249,7 @@ stringi_fit2size (string_t v, size_t size_alloc)
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
char *ptr = v->ptr;
|
||||
ptr = stringi_stack_p(v) ? NULL : ptr;
|
||||
ptr = M_MEMORY_REALLOC (char, ptr, alloc);
|
||||
char *ptr = M_MEMORY_REALLOC (char, v->ptr, alloc);
|
||||
if (M_UNLIKELY (ptr == NULL)) {
|
||||
M_MEMORY_FULL(sizeof (char) * alloc);
|
||||
// NOTE: Return is broken...
|
||||
@@ -254,14 +259,13 @@ stringi_fit2size (string_t v, size_t size_alloc)
|
||||
M_ASSUME(ptr != &v->u.stack.buffer[0]);
|
||||
if (stringi_stack_p(v)) {
|
||||
/* Copy the stack allocation into the heap allocation */
|
||||
memcpy(ptr, &v->u.stack.buffer[0], v->u.stack.buffer[sizeof (str_heap_t) - 1]+1);
|
||||
memcpy(ptr, &v->u.stack.buffer[0], v->u.stack.buffer[sizeof (string_heap_t) - 1]+1);
|
||||
}
|
||||
v->ptr = ptr;
|
||||
v->u.heap.alloc = alloc;
|
||||
return ptr;
|
||||
}
|
||||
assert(v->ptr != NULL);
|
||||
return v->ptr;
|
||||
return stringi_get_str(v);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -274,13 +278,13 @@ string_reserve(string_t v, size_t alloc)
|
||||
alloc = size+1;
|
||||
}
|
||||
assert (alloc > 0);
|
||||
if (alloc < sizeof (str_heap_t)) {
|
||||
if (alloc < sizeof (string_heap_t)) {
|
||||
if (!stringi_stack_p(v)) {
|
||||
/* Transform Heap Allocate to Stack Allocate */
|
||||
char *ptr = &v->u.stack.buffer[0];
|
||||
memcpy(ptr, v->ptr, size+1);
|
||||
M_MEMORY_FREE(v->ptr);
|
||||
v->ptr = ptr;
|
||||
v->ptr = NULL;
|
||||
stringi_set_size(v, size);
|
||||
} else {
|
||||
/* Already a stack based alloc: nothing to do */
|
||||
@@ -292,7 +296,7 @@ string_reserve(string_t v, size_t alloc)
|
||||
M_MEMORY_FULL(sizeof (char) * alloc);
|
||||
return;
|
||||
}
|
||||
v->ptr = ptr; // Can be != ptr if v->ptr was NULL before.
|
||||
v->ptr = ptr;
|
||||
v->u.heap.alloc = alloc;
|
||||
}
|
||||
STRINGI_CONTRACT (v);
|
||||
@@ -371,8 +375,6 @@ string_init_move(string_t v1, string_t v2)
|
||||
{
|
||||
STRINGI_CONTRACT (v2);
|
||||
memcpy(v1, v2, sizeof (string_t));
|
||||
if (stringi_stack_p(v2))
|
||||
v1->ptr = &v1->u.stack.buffer[0];
|
||||
// Note: nullify v2 to be safer
|
||||
v2->ptr = NULL;
|
||||
STRINGI_CONTRACT (v1);
|
||||
@@ -383,15 +385,9 @@ string_swap(string_t v1, string_t v2)
|
||||
{
|
||||
STRINGI_CONTRACT (v1);
|
||||
STRINGI_CONTRACT (v2);
|
||||
bool s1 = stringi_stack_p(v1);
|
||||
bool s2 = stringi_stack_p(v2);
|
||||
M_SWAP (size_t, v1->u.heap.size, v2->u.heap.size);
|
||||
M_SWAP (size_t, v1->u.heap.alloc, v2->u.heap.alloc);
|
||||
M_SWAP (char *, v1->ptr, v2->ptr);
|
||||
if (s2)
|
||||
v1->ptr = &v1->u.stack.buffer[0];
|
||||
if (s1)
|
||||
v2->ptr = &v2->u.stack.buffer[0];
|
||||
STRINGI_CONTRACT (v1);
|
||||
STRINGI_CONTRACT (v2);
|
||||
}
|
||||
@@ -437,7 +433,7 @@ string_cat(string_t v, const string_t v2)
|
||||
if (M_LIKELY (size > 0)) {
|
||||
const size_t old_size = string_size(v);
|
||||
char *ptr = stringi_fit2size(v, old_size + size + 1);
|
||||
memcpy(&ptr[old_size], v2->ptr, size);
|
||||
memcpy(&ptr[old_size], string_get_cstr(v2), size);
|
||||
ptr[old_size + size] = 0;
|
||||
stringi_set_size(v, old_size + size);
|
||||
}
|
||||
@@ -1301,7 +1297,7 @@ string_utf8_p(string_t str)
|
||||
for(size_t i = 0 ; i < size; i++) { \
|
||||
char c = string_get_char(str, i); \
|
||||
if (c == sep) { \
|
||||
string_set_strn(tmp, &str->ptr[begin], i - begin); \
|
||||
string_set_strn(tmp, &string_get_cstr(str)[begin], i - begin); \
|
||||
/* If push move method is available, use it */ \
|
||||
M_IF_METHOD(PUSH_MOVE,oplist)( \
|
||||
M_CALL_PUSH_MOVE(oplist, cont, &tmp); \
|
||||
@@ -1312,7 +1308,7 @@ string_utf8_p(string_t str)
|
||||
begin = i + 1; \
|
||||
} \
|
||||
} \
|
||||
string_set_strn(tmp, &str->ptr[begin], size - begin); \
|
||||
string_set_strn(tmp, &string_get_cstr(str)[begin], size - begin); \
|
||||
M_CALL_PUSH(oplist, cont, tmp); \
|
||||
/* HACK: if method reverse is defined, it is likely that we have */ \
|
||||
/* inserted the items in the wrong order (aka for a list) */ \
|
||||
@@ -1763,7 +1759,7 @@ namespace m_string {
|
||||
string_t v2; \
|
||||
string_init(v2); \
|
||||
bool ret = string_in_str(v2, f); \
|
||||
strncpy(v->s, v2->ptr, max_size); \
|
||||
strncpy(v->s, string_get_cstr(v2), max_size); \
|
||||
string_clear(v2); \
|
||||
return ret; \
|
||||
} \
|
||||
@@ -1776,7 +1772,7 @@ namespace m_string {
|
||||
string_t v2; \
|
||||
string_init(v2); \
|
||||
bool ret = string_parse_str(v2, str, endptr); \
|
||||
strncpy(v->s, v2->ptr, max_size); \
|
||||
strncpy(v->s, string_get_cstr(v2), max_size); \
|
||||
string_clear(v2); \
|
||||
return ret; \
|
||||
} \
|
||||
|
||||
Reference in new issue
Block a user