Add header for handling Byte String as per #123
API designed for streaming bytes and easy integration with other libraries.
This commit is contained in:
1 parent
45b11fb49b
commit
0379f5f1c6
4 files changed
+739
-2
No files matched your search
@@ -37,11 +37,11 @@ PACKAGE=m_lib-$(VERSION)
|
|||||||
VERSION=0.7.4
|
VERSION=0.7.4
|
||||||
|
|
||||||
# Define the contain of the distribution tarball
|
# Define the contain of the distribution tarball
|
||||||
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-thread.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h
|
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-thread.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h m-bstring.h
|
||||||
DOC1=LICENSE README.md
|
DOC1=LICENSE README.md
|
||||||
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png doc/bench-array-log.png doc/bench-array.png doc/bench-list-log.png doc/bench-list.png doc/bench-oset-log.png doc/bench-oset.png doc/bench-umap-log.png doc/bench-umap.png doc/cc.sh
|
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png doc/bench-array-log.png doc/bench-array.png doc/bench-list-log.png doc/bench-list.png doc/bench-oset-log.png doc/bench-oset.png doc/bench-umap-log.png doc/bench-umap.png doc/cc.sh
|
||||||
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c example/ex-curl.c example/ex-dict05.c example/ex-dict06.c example/exm-lib.c example/exm-main.c example/exn-lib.c example/exn-main.c example/exm-header.h example/exn-header.h example/ex-alloc1.c example/ex-alloc2.c example/ex-try01.c example/ex11-generic01.c
|
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c example/ex-curl.c example/ex-dict05.c example/ex-dict06.c example/exm-lib.c example/exm-main.c example/exn-lib.c example/exn-main.c example/exm-header.h example/exn-header.h example/ex-alloc1.c example/ex-alloc2.c example/ex-try01.c example/ex11-generic01.c
|
||||||
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mfuncobj.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/test-mishared.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp tests/test-mtree.c tests/check-bptree-map.cpp tests/check-bptree-set.cpp tests/check-prioqueue.cpp tests/check-umap.cpp tests/test-mtry.c tests/tgen-mmap.c tests/tgen-try.c tests/tgen-tuple.c tests/test-mgeneric.c
|
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mfuncobj.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/test-mishared.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp tests/test-mtree.c tests/check-bptree-map.cpp tests/check-bptree-set.cpp tests/check-prioqueue.cpp tests/check-umap.cpp tests/test-mtry.c tests/tgen-mmap.c tests/tgen-try.c tests/tgen-tuple.c tests/test-mgeneric.c test-mbstring.c
|
||||||
|
|
||||||
.PHONY: all test check doc clean distclean depend install uninstall dist
|
.PHONY: all test check doc clean distclean depend install uninstall dist
|
||||||
|
|
||||||
|
|||||||
+464
@@ -0,0 +1,464 @@
|
|||||||
|
/*
|
||||||
|
* M*LIB - Byte String module
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2024, Patrick Pelissier
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* + Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* + Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (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_BSTRING_H
|
||||||
|
#define MSTARLIB_BSTRING_H
|
||||||
|
|
||||||
|
#include "m-core.h"
|
||||||
|
|
||||||
|
M_BEGIN_PROTECTED_CODE
|
||||||
|
|
||||||
|
// Type definition
|
||||||
|
typedef struct m_bstring_s {
|
||||||
|
size_t size;
|
||||||
|
size_t alloc;
|
||||||
|
size_t offset;
|
||||||
|
uint8_t *ptr;
|
||||||
|
} m_bstring_t[1];
|
||||||
|
|
||||||
|
#define M_BSTRING_CONTRACT(o) do { \
|
||||||
|
M_ASSERT( (o)->size + (o)->offset >= (o)->size ); \
|
||||||
|
M_ASSERT( (o)->alloc >= (o)->size + (o)->offset ); \
|
||||||
|
M_ASSERT( (o)->ptr != NULL || (o)->alloc == 0); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
M_INLINE size_t
|
||||||
|
m_bstring_size(const m_bstring_t s)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT(s);
|
||||||
|
return (size_t) s->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE size_t
|
||||||
|
m_bstring_capacity(const m_bstring_t s)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT(s);
|
||||||
|
return (size_t) s->alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE uint8_t *
|
||||||
|
m_br1nsg_ctr(const m_bstring_t v)
|
||||||
|
{
|
||||||
|
return &v->ptr[v->offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_init(m_bstring_t s)
|
||||||
|
{
|
||||||
|
s->size = 0;
|
||||||
|
s->alloc = 0;
|
||||||
|
s->offset = 0;
|
||||||
|
s->ptr = NULL;
|
||||||
|
M_BSTRING_CONTRACT(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_clear(m_bstring_t s)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT(s);
|
||||||
|
M_MEMORY_FREE( s->ptr);
|
||||||
|
s->offset = -1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_reset(m_bstring_t s)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT(s);
|
||||||
|
s->size = 0;
|
||||||
|
s->offset = 0;
|
||||||
|
M_BSTRING_CONTRACT(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the selected byte-character of the string */
|
||||||
|
M_INLINE uint8_t
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the selected byte-character of the string */
|
||||||
|
M_INLINE void
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test if the string is empty or not */
|
||||||
|
M_INLINE bool
|
||||||
|
m_bstring_empty_p(const m_bstring_t v)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
return v->size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)) {
|
||||||
|
// Insufficient current allocation to store the new string
|
||||||
|
|
||||||
|
// Check if there is a too big offset that can be reduced
|
||||||
|
if (v->offset > v->size / 8 && v->offset > 16) {
|
||||||
|
memmove(&v->ptr[0], &v->ptr[v->offset], v->size);
|
||||||
|
v->offset = 0;
|
||||||
|
if (size_alloc <= v->alloc)
|
||||||
|
return v->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc a new array
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// The string cannot be fully embedded anymore.
|
||||||
|
v->ptr = ptr;
|
||||||
|
v->alloc = alloc;
|
||||||
|
// Size is not set as the function is called in context
|
||||||
|
// where the caller already know the final size of the string,
|
||||||
|
// so it will update it.
|
||||||
|
return &v->ptr[v->offset];
|
||||||
|
}
|
||||||
|
return &v->ptr[v->offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push the byte-character 'c' in the string 'v' */
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_push_back (m_bstring_t v, uint8_t c)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
const size_t size = v->size;
|
||||||
|
uint8_t *ptr = m_bstr1ng_fit2size(v, size+1, false);
|
||||||
|
ptr[size] = c;
|
||||||
|
v->size = size + 1;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_push_back_bytes (m_bstring_t v, size_t n, const void *buffer)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
const size_t size = v->size;
|
||||||
|
M_ASSERT_INDEX (size, size + n);
|
||||||
|
uint8_t *ptr = m_bstr1ng_fit2size(v, size+n, false);
|
||||||
|
memcpy(&ptr[size], buffer, n);
|
||||||
|
v->size = size + n;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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_INLINE void
|
||||||
|
m_bstring_init_move(m_bstring_t v, m_bstring_t org)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (org);
|
||||||
|
memcpy(v, org, sizeof (m_bstring_t));
|
||||||
|
org->offset = -1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_move(m_bstring_t v, m_bstring_t org)
|
||||||
|
{
|
||||||
|
m_bstring_clear(v);
|
||||||
|
m_bstring_init_move(v, org);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE int
|
||||||
|
m_bstring_cmp(const m_bstring_t v1, m_bstring_t v2)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v1);
|
||||||
|
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));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (s1 != s2)
|
||||||
|
return s1 < s2 ? -1 : 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE int
|
||||||
|
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));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (s1 != s2)
|
||||||
|
return s1 < s2 ? -1 : 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE bool
|
||||||
|
m_bstring_equal_p(const m_bstring_t v1, m_bstring_t v2)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v1);
|
||||||
|
M_BSTRING_CONTRACT (v2);
|
||||||
|
const size_t s1 = m_bstring_size(v1);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE bool
|
||||||
|
m_bstring_equal_bytes_p(const m_bstring_t v1, size_t s2, const void *buffer)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v1);
|
||||||
|
const size_t s1 = m_bstring_size(v1);
|
||||||
|
if (s1 != s2)
|
||||||
|
return false;
|
||||||
|
return memcmp(m_br1nsg_ctr(v1), buffer, s1) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE uint8_t
|
||||||
|
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];
|
||||||
|
v->size --;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
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);
|
||||||
|
v->size -= n;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE uint8_t
|
||||||
|
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];
|
||||||
|
v->size --;
|
||||||
|
v->offset++;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
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);
|
||||||
|
v->size -= n;
|
||||||
|
v->offset += n;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_push_bytes_at (m_bstring_t v, size_t pos, size_t n, const void *buffer)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
const size_t size = v->size;
|
||||||
|
M_ASSERT_INDEX (pos, size+1); //pos == size ==> equivalent to _push_back_bytes
|
||||||
|
M_ASSERT_INDEX (size, size + n);
|
||||||
|
uint8_t *ptr = m_bstr1ng_fit2size(v, size+n, false);
|
||||||
|
memmove(&ptr[pos+n], &ptr[pos], size-pos);
|
||||||
|
memcpy(&ptr[pos], buffer, n);
|
||||||
|
v->size = size + n;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_pop_bytes_at(size_t n, void *buffer, m_bstring_t v, size_t pos)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
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);
|
||||||
|
memcpy(buffer, &ptr[pos], n);
|
||||||
|
memmove(&ptr[pos], &ptr[pos+n], size-pos-n);
|
||||||
|
v->size -= n;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_resize (m_bstring_t v, size_t n)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
const size_t size = v->size;
|
||||||
|
if (n > size) {
|
||||||
|
uint8_t *ptr = m_bstr1ng_fit2size(v, n, true);
|
||||||
|
memset(&ptr[size], 0, n-size);
|
||||||
|
}
|
||||||
|
v->size = n;
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_reserve (m_bstring_t v, size_t n)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
const size_t size = v->size;
|
||||||
|
if ( (n != 0 || v->size != 0) && n < v->offset + size) {
|
||||||
|
n = v->offset + size;
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
M_MEMORY_FREE(v->ptr);
|
||||||
|
v->offset = 0;
|
||||||
|
v->size = 0;
|
||||||
|
v->alloc = 0;
|
||||||
|
v->ptr = NULL;
|
||||||
|
} else {
|
||||||
|
uint8_t *ptr = M_MEMORY_REALLOC (uint8_t, v->ptr, n);
|
||||||
|
if (M_UNLIKELY_NOMEM (ptr == NULL)) {
|
||||||
|
M_MEMORY_FULL(n);
|
||||||
|
}
|
||||||
|
// The string cannot be fully embedded anymore.
|
||||||
|
v->ptr = ptr;
|
||||||
|
v->alloc = n;
|
||||||
|
}
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE const uint8_t *
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire / release direct write access: once acquired, user can directly wite at the returned pointer for the requested size
|
||||||
|
// All other operations on the bstring are forbidden during direct access.
|
||||||
|
// This allows passing the bstring buffer to be formatted by different API
|
||||||
|
M_INLINE uint8_t *
|
||||||
|
m_bstring_acquire_access(m_bstring_t v, size_t offset, size_t size_requested)
|
||||||
|
{
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INLINE void
|
||||||
|
m_bstring_release_access(m_bstring_t v)
|
||||||
|
{
|
||||||
|
M_ASSERT( v->size >= v->alloc + v->offset );
|
||||||
|
// Restore canonical representation
|
||||||
|
M_SWAP(size_t, v->size, v->alloc);
|
||||||
|
M_BSTRING_CONTRACT (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Iterator
|
||||||
|
|
||||||
|
// TODO: IO
|
||||||
|
|
||||||
|
// TODO: OPLIST
|
||||||
|
|
||||||
|
// TODO: Hash
|
||||||
|
|
||||||
|
/********************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Define the small name (i.e. without the prefix) of the API provided by this */
|
||||||
|
/* header if it is needed */
|
||||||
|
/* */
|
||||||
|
/********************************************************************************/
|
||||||
|
#if M_USE_SMALL_NAME
|
||||||
|
|
||||||
|
#define bstring_t m_bstring_t
|
||||||
|
#define bstring_size m_bstring_size
|
||||||
|
#define bstring_capacity m_bstring_capacity
|
||||||
|
#define bstring_init m_bstring_init
|
||||||
|
#define bstring_clear m_bstring_clear
|
||||||
|
#define bstring_reset m_bstring_reset
|
||||||
|
#define bstring_get_byte m_bstring_get_byte
|
||||||
|
#define bstring_set_byte m_bstring_set_byte
|
||||||
|
#define bstring_empty_p m_bstring_empty_p
|
||||||
|
#define bstring_push_back m_bstring_push_back
|
||||||
|
#define bstring_push_back_bytes m_bstring_push_back_bytes
|
||||||
|
#define bstring_init_set m_bstring_init_set
|
||||||
|
#define bstring_set m_bstring_set
|
||||||
|
#define bstring_init_move m_bstring_init_move
|
||||||
|
#define bstring_move m_bstring_move
|
||||||
|
#define bstring_cmp m_bstring_cmp
|
||||||
|
#define bstring_cmp_bytes m_bstring_cmp_bytes
|
||||||
|
#define bstring_equal_p m_bstring_equal_p
|
||||||
|
#define bstring_equal_bytes_p m_bstring_equal_bytes_p
|
||||||
|
#define bstring_pop_back m_bstring_pop_back
|
||||||
|
#define bstring_pop_back_bytes m_bstring_pop_back_bytes
|
||||||
|
#define bstring_pop_front m_bstring_pop_front
|
||||||
|
#define bstring_pop_front_bytes m_bstring_pop_front_bytes
|
||||||
|
#define bstring_push_bytes_at m_bstring_push_bytes_at
|
||||||
|
#define bstring_pop_bytes_at m_bstring_pop_bytes_at
|
||||||
|
#define bstring_resize m_bstring_resize
|
||||||
|
#define bstring_reserve m_bstring_reserve
|
||||||
|
#define bstring_view m_bstring_view
|
||||||
|
#define bstring_acquire_access m_bstring_acquire_access
|
||||||
|
#define bstring_release_access m_bstring_release_access
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
M_END_PROTECTED_CODE
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -188,6 +188,7 @@ SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
|
|||||||
M-ARRAY test-marray.c.c test-marray.synt \
|
M-ARRAY test-marray.c.c test-marray.synt \
|
||||||
M-BITSET ../m-bitset.h test-mbitset.synt \
|
M-BITSET ../m-bitset.h test-mbitset.synt \
|
||||||
M-BBPTREE test-mbptree.c test-mbptree.synt \
|
M-BBPTREE test-mbptree.c test-mbptree.synt \
|
||||||
|
M-BSTRING ../m-bstring.h test-mbstring.synt \
|
||||||
M-BUFFER test-mbuffer.c.c test-mbuffer.synt \
|
M-BUFFER test-mbuffer.c.c test-mbuffer.synt \
|
||||||
M-CONCURRENT test-mconcurrent.c.c test-mconcurrent.synt \
|
M-CONCURRENT test-mconcurrent.c.c test-mconcurrent.synt \
|
||||||
M-CORE ../m-core.h test-mcore.synt \
|
M-CORE ../m-core.h test-mcore.synt \
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2024, Patrick Pelissier
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* + Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* + Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include "m-core.h"
|
||||||
|
#include "coverage.h"
|
||||||
|
|
||||||
|
#include "m-bstring.h"
|
||||||
|
|
||||||
|
static void test0(void)
|
||||||
|
{
|
||||||
|
bstring_t b;
|
||||||
|
|
||||||
|
bstring_init(b);
|
||||||
|
assert( bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 0);
|
||||||
|
assert( bstring_capacity(b) == 0);
|
||||||
|
bstring_clear(b);
|
||||||
|
|
||||||
|
bstring_init(b);
|
||||||
|
assert( bstring_empty_p(b) );
|
||||||
|
bstring_push_back(b, 23);
|
||||||
|
assert( !bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 1);
|
||||||
|
assert( bstring_capacity(b) != 0);
|
||||||
|
bstring_reset(b);
|
||||||
|
assert( bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 0);
|
||||||
|
assert( bstring_capacity(b) != 0);
|
||||||
|
bstring_clear(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test1(void)
|
||||||
|
{
|
||||||
|
bstring_t b;
|
||||||
|
uint8_t u;
|
||||||
|
|
||||||
|
bstring_init(b);
|
||||||
|
|
||||||
|
bstring_push_back(b, 23);
|
||||||
|
assert( !bstring_empty_p(b) );
|
||||||
|
u = bstring_get_byte(b, 0);
|
||||||
|
assert(u == 23);
|
||||||
|
bstring_set_byte(b, 0, 24);
|
||||||
|
u = bstring_get_byte(b, 0);
|
||||||
|
assert(u == 24);
|
||||||
|
bstring_set_byte(b, 0, 25);
|
||||||
|
u = bstring_pop_back(b);
|
||||||
|
assert(u == 25);
|
||||||
|
|
||||||
|
for(int i = 0; i < 100; i++)
|
||||||
|
bstring_push_back(b, (uint8_t) i);
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
u = bstring_get_byte(b, (size_t) i);
|
||||||
|
assert(u == i);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
u = bstring_pop_front(b);
|
||||||
|
assert(u == i);
|
||||||
|
}
|
||||||
|
assert( bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 0 );
|
||||||
|
|
||||||
|
for(int i = 0; i < 100; i++)
|
||||||
|
bstring_push_back(b, (uint8_t) i);
|
||||||
|
for(int i = 99; i >= 0 ; i--) {
|
||||||
|
u = bstring_pop_back(b);
|
||||||
|
assert(u == i);
|
||||||
|
}
|
||||||
|
assert( bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 0 );
|
||||||
|
|
||||||
|
bstring_resize(b, 400);
|
||||||
|
assert( !bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 400 );
|
||||||
|
for(int i = 0; i < 400; i++) {
|
||||||
|
u = bstring_get_byte(b, (size_t) i);
|
||||||
|
assert(u == 0);
|
||||||
|
bstring_set_byte(b, (size_t) i, (uint8_t) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bstring_resize(b, 100);
|
||||||
|
assert( !bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 100 );
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
u = bstring_get_byte(b, (size_t) i);
|
||||||
|
assert(u == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bstring_resize(b, 200);
|
||||||
|
assert( !bstring_empty_p(b) );
|
||||||
|
assert( bstring_size(b) == 200 );
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
u = bstring_get_byte(b, (size_t) i);
|
||||||
|
assert(u == i);
|
||||||
|
u = bstring_get_byte(b, (size_t) i+100);
|
||||||
|
assert(u == 0);
|
||||||
|
}
|
||||||
|
bstring_reserve(b, 0);
|
||||||
|
assert( bstring_size(b) == 200 );
|
||||||
|
|
||||||
|
bstring_resize(b, 0);
|
||||||
|
assert( bstring_size(b) == 0 );
|
||||||
|
assert( bstring_capacity(b) != 0 );
|
||||||
|
for(int i = 0; i < 1000; i++) {
|
||||||
|
bstring_push_back(b, (uint8_t) i);
|
||||||
|
u = bstring_pop_front(b);
|
||||||
|
assert(u == (uint8_t) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bstring_reserve(b, 0);
|
||||||
|
assert( bstring_size(b) == 0 );
|
||||||
|
assert( bstring_capacity(b) == 0 );
|
||||||
|
bstring_push_back(b, 23);
|
||||||
|
assert( !bstring_empty_p(b) );
|
||||||
|
|
||||||
|
bstring_reserve(b, 1000);
|
||||||
|
assert( bstring_size(b) == 1);
|
||||||
|
assert( bstring_capacity(b) == 1000 );
|
||||||
|
|
||||||
|
bstring_push_back(b, 25);
|
||||||
|
bstring_push_back(b, 26);
|
||||||
|
const uint8_t *p = bstring_view(b, 0, 3);
|
||||||
|
assert( p[0] == 23);
|
||||||
|
assert( p[1] == 25);
|
||||||
|
assert( p[2] == 26);
|
||||||
|
p = bstring_view(b, 1, 2);
|
||||||
|
assert( p[0] == 25);
|
||||||
|
assert( p[1] == 26);
|
||||||
|
|
||||||
|
uint8_t *w = bstring_acquire_access(b, 0, 3);
|
||||||
|
assert( w[0] == 23);
|
||||||
|
assert( w[1] == 25);
|
||||||
|
assert( w[2] == 26);
|
||||||
|
w[1] = 24;
|
||||||
|
bstring_release_access(b);
|
||||||
|
p = bstring_view(b, 1, 2);
|
||||||
|
assert( p[0] == 24);
|
||||||
|
assert( p[1] == 26);
|
||||||
|
|
||||||
|
bstring_clear(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test2(void)
|
||||||
|
{
|
||||||
|
bstring_t b1, b2;
|
||||||
|
|
||||||
|
bstring_init(b1);
|
||||||
|
for(int i = 0; i < 200; i++)
|
||||||
|
bstring_push_back(b1, (uint8_t) i);
|
||||||
|
bstring_init_set(b2, b1);
|
||||||
|
assert (bstring_equal_p(b1, b2));
|
||||||
|
assert (bstring_cmp(b1, b2) == 0);
|
||||||
|
|
||||||
|
bstring_push_back(b1, 200);;
|
||||||
|
assert (!bstring_equal_p(b1, b2));
|
||||||
|
assert (bstring_cmp(b1, b2) > 0);
|
||||||
|
|
||||||
|
bstring_push_back(b2, 201);;
|
||||||
|
assert (!bstring_equal_p(b1, b2));
|
||||||
|
assert (bstring_cmp(b1, b2) < 0);
|
||||||
|
|
||||||
|
bstring_set_byte(b2, 200, 200);
|
||||||
|
bstring_push_back(b2, 202);;
|
||||||
|
assert (!bstring_equal_p(b1, b2));
|
||||||
|
assert (bstring_cmp(b1, b2) < 0);
|
||||||
|
|
||||||
|
bstring_set(b1, b2);
|
||||||
|
assert (bstring_equal_p(b1, b2));
|
||||||
|
assert (bstring_cmp(b1, b2) == 0);
|
||||||
|
|
||||||
|
bstring_clear(b1);
|
||||||
|
bstring_init_move(b1, b2);
|
||||||
|
assert(bstring_size(b1) == 202);
|
||||||
|
bstring_init(b2);
|
||||||
|
bstring_move(b2,b1);
|
||||||
|
assert(bstring_size(b2) == 202);
|
||||||
|
bstring_clear(b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test3(void)
|
||||||
|
{
|
||||||
|
bstring_t b;
|
||||||
|
uint8_t tab1[] = { 1, 2, 3};
|
||||||
|
uint8_t tab2[] = { 4, 5, 6};
|
||||||
|
uint8_t tab3[] = { 1, 2, 3, 4, 5, 6};
|
||||||
|
uint8_t tab4[] = { 1, 2, 3, 4, 5, 6, 7};
|
||||||
|
uint8_t tmp[3];
|
||||||
|
|
||||||
|
bstring_init(b);
|
||||||
|
|
||||||
|
bstring_push_back_bytes(b, sizeof tab1, tab1);
|
||||||
|
assert( bstring_size(b) == sizeof tab1);
|
||||||
|
assert( bstring_equal_bytes_p(b, sizeof tab1, tab1) );
|
||||||
|
assert( bstring_cmp_bytes(b, sizeof tab1, tab1) == 0);
|
||||||
|
|
||||||
|
bstring_push_back_bytes(b, sizeof tab2, tab2);
|
||||||
|
assert( bstring_size(b) == sizeof tab1 + sizeof tab2);
|
||||||
|
assert( bstring_equal_bytes_p(b, sizeof tab3, tab3) );
|
||||||
|
assert( bstring_equal_bytes_p(b, 1, tab3) == false);
|
||||||
|
assert( bstring_cmp_bytes(b, sizeof tab3, tab3) == 0);
|
||||||
|
assert( bstring_cmp_bytes(b, 1, tab3) > 0);
|
||||||
|
assert( bstring_cmp_bytes(b, sizeof tab4, tab4) < 0);
|
||||||
|
assert( bstring_cmp_bytes(b, sizeof tab2, tab2) < 0);
|
||||||
|
|
||||||
|
bstring_pop_back_bytes(sizeof tmp, tmp, b);
|
||||||
|
assert(tmp[0] == 4 && tmp[1] == 5 && tmp[2] == 6);
|
||||||
|
assert( bstring_size(b) == 3);
|
||||||
|
bstring_pop_back_bytes(sizeof tmp, tmp, b);
|
||||||
|
assert(tmp[0] == 1 && tmp[1] == 2 && tmp[2] == 3);
|
||||||
|
assert( bstring_size(b) == 0);
|
||||||
|
|
||||||
|
bstring_push_back_bytes(b, sizeof tab3, tab3);
|
||||||
|
bstring_pop_front_bytes(sizeof tmp, tmp, b);
|
||||||
|
assert(tmp[0] == 1 && tmp[1] == 2 && tmp[2] == 3);
|
||||||
|
assert( bstring_size(b) == 3);
|
||||||
|
bstring_pop_front_bytes(sizeof tmp, tmp, b);
|
||||||
|
assert(tmp[0] == 4 && tmp[1] == 5 && tmp[2] == 6);
|
||||||
|
assert( bstring_size(b) == 0);
|
||||||
|
|
||||||
|
bstring_clear(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test4(void)
|
||||||
|
{
|
||||||
|
bstring_t b;
|
||||||
|
uint8_t tab1[] = { 1, 2, 6};
|
||||||
|
uint8_t tab2[] = { 3, 4, 5};
|
||||||
|
uint8_t tab3[] = { 1, 2, 3, 4, 5, 6};
|
||||||
|
uint8_t tmp[3];
|
||||||
|
|
||||||
|
bstring_init(b);
|
||||||
|
|
||||||
|
bstring_push_back_bytes(b, sizeof tab1, tab1);
|
||||||
|
bstring_push_bytes_at(b, 2, sizeof tab2, tab2);
|
||||||
|
assert(bstring_size(b) == 6);
|
||||||
|
assert( bstring_equal_bytes_p(b, sizeof tab3, tab3));
|
||||||
|
|
||||||
|
bstring_pop_bytes_at(3, tmp, b, 1);
|
||||||
|
assert(tmp[0] == 2 && tmp[1] == 3 && tmp[2] == 4);
|
||||||
|
assert(bstring_size(b) == 3);
|
||||||
|
|
||||||
|
bstring_clear(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
test0();
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
test4();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user