From 8a1f30eeff137fd03bd1787bb2ad80682998335f Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 5 Oct 2024 10:23:47 +0200 Subject: [PATCH] Finish implementation of Byte String --- m-bstring.h | 206 +++++++++++++++++++++++++++++++++--------- tests/test-mbstring.c | 49 ++++++++++ 2 files changed, 214 insertions(+), 41 deletions(-) diff --git a/m-bstring.h b/m-bstring.h index 7350c295..9a493ba8 100644 --- a/m-bstring.h +++ b/m-bstring.h @@ -29,14 +29,15 @@ M_BEGIN_PROTECTED_CODE -// Type definition +/* Byte String Type definition*/ typedef struct m_bstring_s { - size_t size; - size_t alloc; - size_t offset; - uint8_t *ptr; + size_t size; // Number of effective BYTES + size_t alloc; // Size of the allocated buffer + size_t offset; // Offset where the effective BYTES are stored in the allocated buffer + uint8_t *ptr; // Pointer to the allocated buffer } m_bstring_t[1]; +// Contract of a bstring #define M_BSTRING_CONTRACT(o) do { \ M_ASSERT( (o)->size + (o)->offset >= (o)->size ); \ M_ASSERT( (o)->alloc >= (o)->size + (o)->offset ); \ @@ -47,18 +48,18 @@ M_INLINE size_t m_bstring_size(const m_bstring_t s) { M_BSTRING_CONTRACT(s); - return (size_t) s->size; + return s->size; } M_INLINE size_t m_bstring_capacity(const m_bstring_t s) { M_BSTRING_CONTRACT(s); - return (size_t) s->alloc; + return s->alloc; } M_INLINE uint8_t * -m_br1nsg_ctr(const m_bstring_t v) +m_bstr1ng_cstr(const m_bstring_t v) { return &v->ptr[v->offset]; } @@ -77,7 +78,7 @@ M_INLINE void m_bstring_clear(m_bstring_t s) { M_BSTRING_CONTRACT(s); - M_MEMORY_FREE( s->ptr); + M_MEMORY_FREE(s->ptr); s->offset = (size_t) -1; } @@ -90,6 +91,18 @@ m_bstring_reset(m_bstring_t s) M_BSTRING_CONTRACT(s); } +M_INLINE void +m_bstring_swap(m_bstring_t s1, m_bstring_t s2) +{ + M_BSTRING_CONTRACT(s1); + M_BSTRING_CONTRACT(s2); + m_bstring_t tmp; + memcpy(tmp, s1, sizeof (m_bstring_t)); + memcpy(s1, s2, sizeof (m_bstring_t)); + memcpy(s2, tmp, sizeof (m_bstring_t)); + M_BSTRING_CONTRACT(s1); + M_BSTRING_CONTRACT(s2); +} /* Return the selected byte-character of the string */ M_INLINE uint8_t @@ -97,7 +110,7 @@ m_bstring_get_byte(const m_bstring_t v, size_t index) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX(index, v->size); - return m_br1nsg_ctr(v)[index]; + return m_bstr1ng_cstr(v)[index]; } /* Set the selected byte-character of the string */ @@ -106,7 +119,7 @@ m_bstring_set_byte(m_bstring_t v, size_t index, const uint8_t c) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX(index, v->size); - m_br1nsg_ctr(v)[index] = c; + m_bstr1ng_cstr(v)[index] = c; } /* Test if the string is empty or not */ @@ -118,13 +131,20 @@ m_bstring_empty_p(const m_bstring_t v) } /* Internal method to fit the Byte string to the given size - Ensures that the string capacity is greater than size_alloc+offset - The string 'v' no longer has a working size field. + Ensures that the Byte String capacity is greater than size_alloc+offset + The returned Byte String 'v' no longer has a **correct** size field. + If exact_alloc is true, it will request exactly the request size for the buffer + on reallocation otherwise it will augment the requested size to amortize the future + allocations. Return a pointer to the writable string. */ M_INLINE uint8_t * m_bstr1ng_fit2size (m_bstring_t v, size_t size_alloc, bool exact_alloc) { + // Very unlikely overflow case + if (M_UNLIKELY_NOMEM (v->offset > v->offset + size_alloc)) { + goto allocation_error; + } M_ASSERT_INDEX (v->offset, v->offset + size_alloc); // Note: this function may be called in context where the contract is not fulfilled. if (M_UNLIKELY (v->offset + size_alloc > v->alloc)) { @@ -132,28 +152,23 @@ m_bstr1ng_fit2size (m_bstring_t v, size_t size_alloc, bool exact_alloc) // Check if there is a too big offset that can be reduced if (v->offset > v->size / 8 && v->offset > 16) { + // Move back the effective bytes to the beginning of the buffer memmove(&v->ptr[0], &v->ptr[v->offset], v->size); v->offset = 0; + // If it is now sufficient, we can return. if (size_alloc <= v->alloc) return v->ptr; } - // Alloc a new array + // Alloc a new array. Compute the requested allocation size size_t alloc = (v->offset + size_alloc + (exact_alloc ? 0 : size_alloc / 2)); alloc = M_MAX(alloc, 16); if (M_UNLIKELY_NOMEM (alloc < size_alloc)) { - /* Overflow in alloc computation */ - M_MEMORY_FULL(alloc); - // NOTE: Return is currently broken. - abort(); - return NULL; + goto allocation_error; } uint8_t *ptr = M_MEMORY_REALLOC (uint8_t, v->ptr, alloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { - M_MEMORY_FULL(alloc); - // NOTE: Return is currently broken. - abort(); - return NULL; + goto allocation_error; } // The string cannot be fully embedded anymore. v->ptr = ptr; @@ -164,9 +179,14 @@ m_bstr1ng_fit2size (m_bstring_t v, size_t size_alloc, bool exact_alloc) return &v->ptr[v->offset]; } return &v->ptr[v->offset]; + allocation_error: + M_MEMORY_FULL(size_alloc); + // NOTE: Return is currently broken. + abort(); + return NULL; } -/* Push the byte-character 'c' in the string 'v' */ +/* Push the byte 'c' in the bstring 'v' */ M_INLINE void m_bstring_push_back (m_bstring_t v, uint8_t c) { @@ -178,6 +198,7 @@ m_bstring_push_back (m_bstring_t v, uint8_t c) M_BSTRING_CONTRACT (v); } +/* Push the array of bytes from 'buffer' in the bstring 'v' */ M_INLINE void m_bstring_push_back_bytes (m_bstring_t v, size_t n, const void *buffer) { @@ -194,14 +215,14 @@ M_INLINE void m_bstring_init_set(m_bstring_t v, const m_bstring_t org) { m_bstring_init(v); - m_bstring_push_back_bytes(v, m_bstring_size(org), m_br1nsg_ctr(org)); + m_bstring_push_back_bytes(v, m_bstring_size(org), m_bstr1ng_cstr(org)); } M_INLINE void m_bstring_set(m_bstring_t v, const m_bstring_t org) { m_bstring_reset(v); - m_bstring_push_back_bytes(v, m_bstring_size(org), m_br1nsg_ctr(org)); + m_bstring_push_back_bytes(v, m_bstring_size(org), m_bstr1ng_cstr(org)); } M_INLINE void @@ -219,6 +240,13 @@ m_bstring_move(m_bstring_t v, m_bstring_t org) m_bstring_init_move(v, org); } +M_INLINE size_t +m_bstring_hash(const m_bstring_t v) +{ + M_BSTRING_CONTRACT (v); + return m_core_hash(m_bstr1ng_cstr(v), m_bstring_size(v)); +} + M_INLINE int m_bstring_cmp(const m_bstring_t v1, m_bstring_t v2) { @@ -226,7 +254,7 @@ m_bstring_cmp(const m_bstring_t v1, m_bstring_t v2) M_BSTRING_CONTRACT (v2); const size_t s1 = m_bstring_size(v1); const size_t s2 = m_bstring_size(v2); - int ret = memcmp(m_br1nsg_ctr(v1), m_br1nsg_ctr(v2), M_MIN(s1, s2)); + int ret = memcmp(m_bstr1ng_cstr(v1), m_bstr1ng_cstr(v2), M_MIN(s1, s2)); if (ret) return ret; if (s1 != s2) @@ -239,7 +267,7 @@ m_bstring_cmp_bytes(const m_bstring_t v1, size_t s2, const void *p2) { M_BSTRING_CONTRACT (v1); const size_t s1 = m_bstring_size(v1); - int ret = memcmp(m_br1nsg_ctr(v1), p2, M_MIN(s1, s2)); + int ret = memcmp(m_bstr1ng_cstr(v1), p2, M_MIN(s1, s2)); if (ret) return ret; if (s1 != s2) @@ -256,7 +284,7 @@ m_bstring_equal_p(const m_bstring_t v1, m_bstring_t v2) const size_t s2 = m_bstring_size(v2); if (s1 != s2) return false; - return memcmp(m_br1nsg_ctr(v1), m_br1nsg_ctr(v2), s1) == 0; + return memcmp(m_bstr1ng_cstr(v1), m_bstr1ng_cstr(v2), s1) == 0; } M_INLINE bool @@ -266,7 +294,7 @@ m_bstring_equal_bytes_p(const m_bstring_t v1, size_t s2, const void *buffer) const size_t s1 = m_bstring_size(v1); if (s1 != s2) return false; - return memcmp(m_br1nsg_ctr(v1), buffer, s1) == 0; + return memcmp(m_bstr1ng_cstr(v1), buffer, s1) == 0; } M_INLINE uint8_t @@ -274,7 +302,7 @@ m_bstring_pop_back(m_bstring_t v) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX(0, v->size); - uint8_t c = m_br1nsg_ctr(v)[v->size-1]; + uint8_t c = m_bstr1ng_cstr(v)[v->size-1]; v->size --; M_BSTRING_CONTRACT (v); return c; @@ -285,7 +313,7 @@ m_bstring_pop_back_bytes(size_t n, void *buffer, m_bstring_t v) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX(n-1, v->size); - memcpy(buffer, &m_br1nsg_ctr(v)[v->size-n], n); + memcpy(buffer, &m_bstr1ng_cstr(v)[v->size-n], n); v->size -= n; M_BSTRING_CONTRACT (v); } @@ -295,7 +323,7 @@ m_bstring_pop_front(m_bstring_t v) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX(0, v->size); - uint8_t c = m_br1nsg_ctr(v)[0]; + uint8_t c = m_bstr1ng_cstr(v)[0]; v->size --; v->offset++; M_BSTRING_CONTRACT (v); @@ -307,7 +335,7 @@ m_bstring_pop_front_bytes(size_t n, void *buffer, m_bstring_t v) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX(n-1, v->size); - memcpy(buffer, &m_br1nsg_ctr(v)[0], n); + memcpy(buffer, &m_bstr1ng_cstr(v)[0], n); v->size -= n; v->offset += n; M_BSTRING_CONTRACT (v); @@ -334,7 +362,7 @@ m_bstring_pop_bytes_at(size_t n, void *buffer, m_bstring_t v, size_t pos) const size_t size = v->size; M_ASSERT_INDEX (pos, size); M_ASSERT_INDEX(n-1, size-pos); - uint8_t *ptr = m_br1nsg_ctr(v); + uint8_t *ptr = m_bstr1ng_cstr(v); memcpy(buffer, &ptr[pos], n); memmove(&ptr[pos], &ptr[pos+n], size-pos-n); v->size -= n; @@ -385,7 +413,7 @@ m_bstring_view(const m_bstring_t v, size_t offset, size_t size_requested) { M_BSTRING_CONTRACT (v); M_ASSERT_INDEX (offset+size_requested, v->size+1); - return &m_br1nsg_ctr(v)[offset]; + return &m_bstr1ng_cstr(v)[offset]; } // Acquire / release direct write access: once acquired, user can directly wite at the returned pointer for the requested size @@ -398,7 +426,7 @@ m_bstring_acquire_access(m_bstring_t v, size_t offset, size_t size_requested) M_ASSERT_INDEX (offset+size_requested, v->size+1); // Break canonical representation (this will generate assertion if a normal API is called now) M_SWAP(size_t, v->size, v->alloc); - return &m_br1nsg_ctr(v)[offset]; + return &m_bstr1ng_cstr(v)[offset]; } M_INLINE void @@ -410,13 +438,103 @@ m_bstring_release_access(m_bstring_t v) M_BSTRING_CONTRACT (v); } -// TODO: Iterator +// No Iterator planned for this container (No added value) -// TODO: IO +#if M_USE_STDIO -// TODO: OPLIST +M_INLINE size_t +m_bstring_fwrite(FILE *f, const m_bstring_t v) +{ + M_BSTRING_CONTRACT (v); + M_ASSERT (f != NULL); + return fwrite(m_bstr1ng_cstr(v), 1, m_bstring_size(v), f); +} -// TODO: Hash +M_INLINE bool +m_bstring_fread(m_bstring_t v, FILE *f, size_t num) +{ + M_BSTRING_CONTRACT (v); + M_ASSERT (f != NULL); + m_bstring_reset(v); + if (M_UNLIKELY(num == 0)) { + return true; + } + uint8_t *ptr = m_bstr1ng_fit2size(v, num, true); + size_t n = fread(ptr, 1, num, f); + M_ASSERT (n <= num); + v->size = n; + return n == num; +} + +#endif // Have stdio + +// No formatted string input/output. Only serializer + +/* Transform the Byte String 'v2' + and output it in the given serializer + See serialization for return code. +*/ +M_INLINE m_serial_return_code_t +m_bstring_out_serial(m_serial_write_t serial, const m_bstring_t v) +{ + M_ASSERT (serial != NULL && serial->m_interface != NULL); + M_BSTRING_CONTRACT (v); + m_serial_local_t local; + m_serial_return_code_t ret; + size_t size = m_bstring_size(v); + uint8_t *ptr = m_bstr1ng_cstr(v); + ret = serial->m_interface->write_array_start(local, serial, size); + if (size > 0) { + ret |= serial->m_interface->write_integer(serial, ptr[0], 1); + for(size_t i = 1; i < size; i++) { + ret |= serial->m_interface->write_array_next(local, serial); + ret |= serial->m_interface->write_integer(serial, ptr[i], 1); + } + } + ret |= serial->m_interface->write_array_end(local, serial); + return ret; +} + +/* Read from the serializer + and set the converted value in the byte string 'v'. + See serialization for return code. +*/ +M_INLINE m_serial_return_code_t +m_bstring_in_serial(m_bstring_t v, m_serial_read_t f) +{ + M_ASSERT (f != NULL && f->m_interface != NULL); + M_BSTRING_CONTRACT (v); + m_serial_return_code_t ret; + m_serial_local_t local; + size_t estimated_size = 0, real_size = 0; + m_bstring_reset(v); + ret = f->m_interface->read_array_start(local, f, &estimated_size); + if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) { + return ret; + } + uint8_t *ptr = m_bstr1ng_fit2size(v, estimated_size, true); + do { + long long val; + ret = f->m_interface->read_integer(f, &val, 1); + if (ret != M_SERIAL_OK_DONE) { break; } + ptr[real_size++] = (uint8_t) val; + ret = f->m_interface->read_array_next(local, f); + } while (ret == M_SERIAL_OK_CONTINUE); + v->size = real_size; + M_BSTRING_CONTRACT (v); + return ret; +} + +/* Define the OPLIST of a BYTE STRING */ +#define M_BSTRING_OPLIST \ + (INIT(m_bstring_init),INIT_SET(m_bstring_init_set), SET(m_bstring_set), \ + INIT_MOVE(m_bstring_init_move), MOVE(m_bstring_move), \ + SWAP(m_bstring_swap), RESET(m_bstring_reset), \ + EMPTY_P(m_bstring_empty_p), \ + CLEAR(m_bstring_clear), HASH(m_bstring_hash), EQUAL(m_bstring_equal_p), \ + CMP(m_bstring_cmp), TYPE(m_bstring_t), GENTYPE(struct m_bstring_s*), \ + OUT_SERIAL(m_bstring_out_serial), IN_SERIAL(m_bstring_in_serial), \ + ) /********************************************************************************/ /* */ @@ -456,6 +574,12 @@ m_bstring_release_access(m_bstring_t v) #define bstring_view m_bstring_view #define bstring_acquire_access m_bstring_acquire_access #define bstring_release_access m_bstring_release_access +#define bstring_hash m_bstring_hash +#define bstring_fread m_bstring_fread +#define bstring_fwrite m_bstring_fwrite +#define bstring_out_serial m_bstring_out_serial +#define bstring_in_serial m_bstring_in_serial +#define bstring_swap m_bstring_swap #endif diff --git a/tests/test-mbstring.c b/tests/test-mbstring.c index 641468c2..34418c01 100644 --- a/tests/test-mbstring.c +++ b/tests/test-mbstring.c @@ -33,6 +33,7 @@ static void test0(void) assert( bstring_empty_p(b) ); assert( bstring_size(b) == 0); assert( bstring_capacity(b) == 0); + assert( bstring_hash(b) != 0); bstring_clear(b); bstring_init(b); @@ -41,10 +42,12 @@ static void test0(void) assert( !bstring_empty_p(b) ); assert( bstring_size(b) == 1); assert( bstring_capacity(b) != 0); + assert( bstring_hash(b) != 0); bstring_reset(b); assert( bstring_empty_p(b) ); assert( bstring_size(b) == 0); assert( bstring_capacity(b) != 0); + assert( bstring_hash(b) != 0); bstring_clear(b); } @@ -183,6 +186,10 @@ static void test2(void) assert (!bstring_equal_p(b1, b2)); assert (bstring_cmp(b1, b2) < 0); + bstring_swap(b1, b2); + assert (bstring_cmp(b1, b2) > 0); + bstring_swap(b1, b2); + bstring_set(b1, b2); assert (bstring_equal_p(b1, b2)); assert (bstring_cmp(b1, b2) == 0); @@ -261,6 +268,47 @@ static void test4(void) bstring_clear(b); } +static void test_io(void) +{ + uint8_t tab1[] = { 1, 2, 3}; + bstring_t b; + + bstring_init(b); + + FILE *f = fopen("a-mbstring.dat", "wb"); + if (!f) abort(); + size_t n = bstring_fwrite(f, b); + assert(n == 0); + fclose (f); + + bstring_push_back_bytes(b, sizeof tab1, tab1); + + f = fopen("a-mbstring.dat", "rb"); + if (!f) abort(); + bool success = bstring_fread(b, f, 0); + assert(success); + assert(bstring_size(b) == 0); + fclose (f); + + bstring_push_back_bytes(b, sizeof tab1, tab1); + + f = fopen("a-mbstring.dat", "wb"); + if (!f) abort(); + n = bstring_fwrite(f, b); + assert(n == sizeof tab1); + fclose (f); + + f = fopen("a-mbstring.dat", "rb"); + if (!f) abort(); + success = bstring_fread(b, f, sizeof tab1); + assert(success); + assert(bstring_size(b) == sizeof tab1); + assert(m_bstring_equal_bytes_p(b, sizeof tab1, tab1)); + fclose (f); + + bstring_clear(b); +} + int main(void) { test0(); @@ -268,5 +316,6 @@ int main(void) test2(); test3(); test4(); + test_io(); exit(0); }