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