PASS: /qc/__sm_chunk_get_position
This commit is contained in:
parent
dfda0017d5
commit
b0280271df
9 changed files with 305 additions and 177 deletions
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CLionExternalBuildManager">
|
||||
<target id="db0ccaeb-4851-470b-83d0-afa663f6ceb9" name="tests/soak" defaultType="MAKE">
|
||||
<configuration id="98973a90-a9d0-431b-9071-9ce6960b0b01" name="tests/soak">
|
||||
<build type="MAKE">
|
||||
<make targetName="tests/soak" />
|
||||
</build>
|
||||
<clean type="MAKE">
|
||||
<make targetName="clean" />
|
||||
</clean>
|
||||
</configuration>
|
||||
</target>
|
||||
</component>
|
||||
</project>
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MakefileBuildTargetsManager">
|
||||
<user-build-targets>
|
||||
<build-target name="tests/soak">
|
||||
<build-configurations>
|
||||
<build-configuration>
|
||||
<make-targets>
|
||||
<make-target>tests/soak</make-target>
|
||||
</make-targets>
|
||||
</build-configuration>
|
||||
</build-configurations>
|
||||
</build-target>
|
||||
<build-target name="clean">
|
||||
<build-configurations>
|
||||
<build-configuration>
|
||||
<make-targets>
|
||||
<make-target>clean</make-target>
|
||||
</make-targets>
|
||||
</build-configuration>
|
||||
</build-configurations>
|
||||
</build-target>
|
||||
</user-build-targets>
|
||||
</component>
|
||||
</project>
|
|
@ -4,29 +4,8 @@
|
|||
<option name="executionMode" value="BINARY" />
|
||||
<option name="pathToExecutable" value="$USER_HOME$/.nix-profile/bin/black" />
|
||||
</component>
|
||||
<component name="CidrRootsConfiguration">
|
||||
<sourceRoots>
|
||||
<file path="$PROJECT_DIR$/examples" />
|
||||
<file path="$PROJECT_DIR$/include" />
|
||||
<file path="$PROJECT_DIR$/lib" />
|
||||
<file path="$PROJECT_DIR$/src" />
|
||||
<file path="$PROJECT_DIR$/test" />
|
||||
<file path="$PROJECT_DIR$/tests" />
|
||||
</sourceRoots>
|
||||
<component name="CMakePythonSetting">
|
||||
<option name="pythonIntegrationState" value="YES" />
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MakefileSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<MakefileProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="version" value="2" />
|
||||
</MakefileProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
|
|
|
@ -15,7 +15,7 @@ set(SOURCE_DIR .)
|
|||
set(HEADER_DIR . test)
|
||||
|
||||
set(COMMON_CMAKE_C_FLAGS "-Wall -Wextra -Wpedantic")
|
||||
set(CMAKE_C_FLAGS_DEBUG "-DSPARSEMAP_DIAGNOSTIC -DDEBUG -g -Og")
|
||||
set(CMAKE_C_FLAGS_DEBUG "-DSPARSEMAP_DIAGNOSTIC -DSPARSEMAP_TESTING -DDEBUG -g -O0")
|
||||
set(CMAKE_C_FLAGS_PROFILE "-DSPARSEMAP_DIAGNOSTIC -DDEBUG -g -Og -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-Ofast")
|
||||
|
||||
|
@ -73,9 +73,10 @@ target_include_directories(ex_4 PRIVATE ${HEADER_DIR})
|
|||
add_custom_target(run_ex_4 COMMAND ex_4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Add test program
|
||||
add_executable(test test/test.c test/munit.c test/common.c)
|
||||
add_executable(test test/test.c test/munit.c test/qc.c test/common.c)
|
||||
target_link_libraries(test PRIVATE sparsemap)
|
||||
target_include_directories(test PRIVATE ${HEADER_DIR})
|
||||
set_source_files_properties(test/test.c PROPERTIES COMPILE_FLAGS "-DDEBUG -DDIAGNOSTIC -DSPARSEMAP_TESTING" )
|
||||
add_custom_target(run_test COMMAND test WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Add soak program
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Gererate a C function that contains a pre-calculated static table where each
|
||||
# 8bit offset into that table encodes the required additional space for what is
|
||||
# described.
|
||||
|
||||
# The 2 bit patters are:
|
||||
# 00 -> 0 additional sm_bitvec_t (ZEROS)
|
||||
# 11 -> 0 additional sm_bitvec_t (ONES)
|
||||
# 10 -> 0 additional sm_bitvec_t (NONE, or RLE if at the start)
|
||||
# 01 -> 1 additional sm_bitvec_t (MIXED, requires additional space)
|
||||
|
||||
# The goal is to output this:
|
||||
|
||||
# /**
|
||||
# * Calculates the number of sm_bitvec_ts required by a single byte with flags
|
||||
# * (in m_data[0]).
|
||||
# */
|
||||
# static size_t
|
||||
# __sm_chunk_calc_vector_size(uint8_t b)
|
||||
# {
|
||||
# // clang-format off
|
||||
# static int lookup[] = {
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||
# 2, 2, 3, 2, 2, 2, 3, 2, 3, 3, 4, 3, 2, 2, 3, 2,
|
||||
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0
|
||||
# };
|
||||
# // clang-format on
|
||||
# return (size_t)lookup[b];
|
||||
# }
|
||||
|
||||
# TODO: fix this... :)
|
||||
|
||||
def create_lookup_table_c_format():
|
||||
"""Creates a lookup table in C-style format."""
|
||||
lookup_table = []
|
||||
for byte in range(256):
|
||||
count = 0
|
||||
for i in range(3):
|
||||
if (byte >> (i * 2)) & 3 == 1:
|
||||
count += 1
|
||||
lookup_table.append(count)
|
||||
|
||||
# Format the output as a C array
|
||||
output = "static int lookup[] = {\n"
|
||||
for i in range(0, 256, 16):
|
||||
line = " " + ", ".join(str(x) for x in lookup_table[i:i+16]) + ",\n"
|
||||
output += line
|
||||
output += "};"
|
||||
|
||||
print(output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_lookup_table_c_format()
|
238
sparsemap.c
238
sparsemap.c
|
@ -68,6 +68,10 @@ void __attribute__((format(printf, 4, 5))) __sm_diag_(const char *file, int line
|
|||
|
||||
typedef uint64_t __sm_bitvec_t;
|
||||
|
||||
typedef struct {
|
||||
__sm_bitvec_t *m_data;
|
||||
} __sm_chunk_t;
|
||||
|
||||
enum __SM_CHUNK_INFO {
|
||||
/* metadata overhead: 4 bytes for __sm_chunk_t count */
|
||||
SM_SIZEOF_OVERHEAD = sizeof(uint32_t),
|
||||
|
@ -85,7 +89,7 @@ enum __SM_CHUNK_INFO {
|
|||
SM_CHUNK_MAX_CAPACITY = (SM_BITS_PER_VECTOR * SM_FLAGS_PER_INDEX),
|
||||
|
||||
/* minimum capacity of a __sm_chunk_t (in bits) */
|
||||
SM_CHUNK_MIN_CAPACITY = SM_BITS_PER_VECTOR,
|
||||
SM_CHUNK_MIN_CAPACITY = (SM_BITS_PER_VECTOR - 2),
|
||||
|
||||
/* __sm_bitvec_t payload is all zeros (2#00) */
|
||||
SM_PAYLOAD_ZEROS = 0,
|
||||
|
@ -113,13 +117,10 @@ enum __SM_CHUNK_INFO {
|
|||
};
|
||||
|
||||
#define SM_CHUNK_GET_FLAGS(from, at) ((((from)) & ((__sm_bitvec_t)SM_FLAG_MASK << ((at)*2))) >> ((at)*2))
|
||||
#define SM_IS_CHUNK_RLE(chunk) ((*((__sm_bitvec_t *)(chunk)->m_data) & (((__sm_bitvec_t)0x3) << (SM_BITS_PER_VECTOR - 2))) == SM_PAYLOAD_NONE)
|
||||
#define SM_IS_CHUNK_RLE(chunk) \
|
||||
(((*((__sm_bitvec_t *)(chunk)->m_data) & (((__sm_bitvec_t)0x3) << (SM_BITS_PER_VECTOR - 2))) >> (SM_BITS_PER_VECTOR - 2)) == SM_PAYLOAD_NONE)
|
||||
#define SM_CHUNK_RLE_LENGTH(chunk) (size_t)(*((__sm_bitvec_t *)(chunk)->m_data) & ~(((__sm_bitvec_t)0x3) << (SM_BITS_PER_VECTOR - 2)))
|
||||
|
||||
typedef struct {
|
||||
__sm_bitvec_t *m_data;
|
||||
} __sm_chunk_t;
|
||||
|
||||
struct __attribute__((aligned(8))) sparsemap {
|
||||
size_t m_capacity; /* The total size of m_data */
|
||||
size_t m_data_used; /* The used size of m_data */
|
||||
|
@ -141,7 +142,6 @@ struct __attribute__((aligned(8))) sparsemap {
|
|||
* @return The calculated number of vectors.
|
||||
* @see bin/gen_chunk_vector_size_table.py
|
||||
*/
|
||||
#if 1
|
||||
static size_t
|
||||
__sm_chunk_calc_vector_size(uint8_t b)
|
||||
{
|
||||
|
@ -167,22 +167,6 @@ __sm_chunk_calc_vector_size(uint8_t b)
|
|||
// clang-format on
|
||||
return (size_t)lookup[b];
|
||||
}
|
||||
#else
|
||||
/* Alternative, non-lookup table, implementation. */
|
||||
static size_t
|
||||
__sm_chunk_calc_vector_size(uint8_t b)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (((b >> (i * 2)) & 0x03) == 0x02) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @brief Calculates the byte offset of a vector within a chunk.
|
||||
*
|
||||
|
@ -192,11 +176,11 @@ __sm_chunk_calc_vector_size(uint8_t b)
|
|||
* additional vectors are stored.
|
||||
*
|
||||
* @param[in] chunk Pointer to the chunk containing the vector.
|
||||
* @param[in] nth Index of the desired vector within the chunk (0-based).
|
||||
* @param[in] bv Index of the desired vector within the chunk (0-based).
|
||||
* @return Byte offset of the vector within the chunk's data.
|
||||
*/
|
||||
static size_t
|
||||
__sm_chunk_get_position(__sm_chunk_t *chunk, size_t nth)
|
||||
__sm_chunk_get_position(__sm_chunk_t *chunk, size_t bv)
|
||||
{
|
||||
/* Handle 4 indices (1 byte) at a time. */
|
||||
size_t num_bytes;
|
||||
|
@ -208,13 +192,13 @@ __sm_chunk_get_position(__sm_chunk_t *chunk, size_t nth)
|
|||
return position;
|
||||
}
|
||||
|
||||
num_bytes = nth / ((size_t)SM_FLAGS_PER_INDEX_BYTE * SM_BITS_PER_VECTOR);
|
||||
num_bytes = bv / ((size_t)SM_FLAGS_PER_INDEX_BYTE * SM_BITS_PER_VECTOR);
|
||||
for (size_t i = 0; i < num_bytes; i++, p++) {
|
||||
position += __sm_chunk_calc_vector_size(*p);
|
||||
}
|
||||
|
||||
nth -= num_bytes * SM_FLAGS_PER_INDEX_BYTE;
|
||||
for (size_t i = 0; i < nth; i++) {
|
||||
bv -= num_bytes * SM_FLAGS_PER_INDEX_BYTE;
|
||||
for (size_t i = 0; i < bv; i++) {
|
||||
size_t flags = SM_CHUNK_GET_FLAGS(*chunk->m_data, i);
|
||||
if (flags == SM_PAYLOAD_MIXED) {
|
||||
position++;
|
||||
|
@ -316,6 +300,7 @@ __sm_chunk_reduce_capacity(__sm_chunk_t *chunk, size_t capacity)
|
|||
}
|
||||
}
|
||||
__sm_assert(__sm_chunk_get_capacity(chunk) == capacity);
|
||||
__sm_assert(SM_IS_CHUNK_RLE(chunk) == false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1281,10 +1266,10 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value)
|
|||
__sm_chunk_t chunk;
|
||||
__sm_chunk_init(&chunk, p + sizeof(sm_idx_t));
|
||||
if (__sm_chunk_reduce_capacity(&chunk, start - aligned_idx)) {
|
||||
/* The __sm_chunk_t is empty then remove it.
|
||||
__sm_remove_data(map, offset, sizeof(sm_idx_t) + sizeof(__sm_bitvec_t) * 2);
|
||||
__sm_set_chunk_count(map, __sm_get_chunk_count(map) - 1);
|
||||
*/
|
||||
/* TODO: The __sm_chunk_t is empty then remove it.
|
||||
__sm_remove_data(map, offset, sizeof(sm_idx_t) + sizeof(__sm_bitvec_t) * 2);
|
||||
__sm_set_chunk_count(map, __sm_get_chunk_count(map) - 1);
|
||||
*/
|
||||
}
|
||||
}
|
||||
*(sm_idx_t *)p = start = aligned_idx;
|
||||
|
@ -1714,7 +1699,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
|
|||
__sm_chunk_t d_chunk;
|
||||
__sm_chunk_init(&d_chunk, dst);
|
||||
if (__sm_chunk_reduce_capacity(&d_chunk, __sm_get_vector_aligned_offset(capacity - (offset % capacity)))) {
|
||||
/* The __sm_chunk_t is empty then remove it.
|
||||
/* TODO: The __sm_chunk_t is empty then remove it.
|
||||
__sm_remove_data(map, offset, sizeof(sm_idx_t) + sizeof(__sm_bitvec_t) * 2);
|
||||
__sm_set_chunk_count(map, __sm_get_chunk_count(map) - 1);
|
||||
*/
|
||||
|
@ -1741,7 +1726,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
|
|||
/* Reduce the capacity of the source-chunk effectively erases bits. */
|
||||
size_t r = __sm_get_vector_aligned_offset(((offset - start) % capacity) + SM_BITS_PER_VECTOR);
|
||||
if (__sm_chunk_reduce_capacity(&s_chunk, r)) {
|
||||
/* The __sm_chunk_t is empty then remove it.
|
||||
/* TODO: The __sm_chunk_t is empty then remove it.
|
||||
__sm_remove_data(map, offset, sizeof(sm_idx_t) + sizeof(__sm_bitvec_t) * 2);
|
||||
__sm_set_chunk_count(map, __sm_get_chunk_count(map) - 1);
|
||||
*/
|
||||
|
@ -1964,3 +1949,188 @@ sparsemap_span(sparsemap_t *map, sparsemap_idx_t idx, size_t len, bool value)
|
|||
|
||||
return offset;
|
||||
}
|
||||
|
||||
#ifdef SPARSEMAP_TESTING
|
||||
|
||||
#include <qc.h>
|
||||
|
||||
static double
|
||||
_tst_pow(double base, int exponent)
|
||||
{
|
||||
if (exponent == 0) {
|
||||
return 1.0; // 0^0 is 1
|
||||
} else if (base == 0.0) {
|
||||
return 0.0; // 0 raised to any positive exponent is 0 (except 0^0)
|
||||
} else if (base < 0.0 && (exponent & 1) != 0) {
|
||||
// negative base with odd exponent, results in a negative
|
||||
return -_tst_pow(-base, exponent);
|
||||
}
|
||||
|
||||
double result = base;
|
||||
for (unsigned int i = 1; i < exponent; i++) {
|
||||
result *= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char *
|
||||
QCC_showChunk(void *value, int len)
|
||||
{
|
||||
static char buffer[(SM_FLAGS_PER_INDEX * 16) + (SM_BITS_PER_VECTOR * 64) + 16];
|
||||
|
||||
__sm_chunk_t *chunk = (__sm_chunk_t *)value;
|
||||
__sm_bitvec_t desc = chunk->m_data[0];
|
||||
|
||||
if (!SM_IS_CHUNK_RLE(chunk)) {
|
||||
char desc_str[SM_FLAGS_PER_INDEX + 1] = { 0 };
|
||||
char *str = desc_str;
|
||||
int mixed = 0;
|
||||
for (int i = 0; i < SM_FLAGS_PER_INDEX; i++) {
|
||||
uint8_t flag = SM_CHUNK_GET_FLAGS(desc, i);
|
||||
switch (flag) {
|
||||
case SM_PAYLOAD_NONE:
|
||||
str += sprintf(str, "Ø");
|
||||
break;
|
||||
case SM_PAYLOAD_ONES:
|
||||
str += sprintf(str, "1");
|
||||
break;
|
||||
case SM_PAYLOAD_ZEROS:
|
||||
str += sprintf(str, "0");
|
||||
break;
|
||||
case SM_PAYLOAD_MIXED:
|
||||
str += sprintf(str, "≡");
|
||||
mixed++;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
str = buffer + snprintf(buffer, sizeof(buffer), "%s :: ", desc_str);
|
||||
for (int i = 0; i < mixed; i++) {
|
||||
str += sprintf(str, "0x%lx%s", chunk->m_data[1 + i], i + 1 < mixed ? " " : "");
|
||||
}
|
||||
} else {
|
||||
sprintf(buffer, "1»%zu", SM_CHUNK_RLE_LENGTH(chunk));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
QCC_freeChunkValue(void *value)
|
||||
{
|
||||
free(value);
|
||||
}
|
||||
|
||||
QCC_GenValue *
|
||||
QCC_genChunk()
|
||||
{
|
||||
bool rle = ((double)random() / (double)RAND_MAX) > 0.5;
|
||||
|
||||
if (rle) {
|
||||
// Generate a chunk with the offset equal to the encoded run length.
|
||||
sparsemap_idx_t from = 1, to = (sparsemap_idx_t)1ULL << (SM_BITS_PER_VECTOR - 2);
|
||||
unsigned int len = ((unsigned int)random() % (to - from)) + from;
|
||||
__sm_chunk_t *chunk = malloc(sizeof(__sm_chunk_t) + (sizeof(__sm_bitvec_t) * 2));
|
||||
__sm_bitvec_t *v = (__sm_bitvec_t *)(chunk + sizeof(__sm_chunk_t));
|
||||
chunk->m_data = v;
|
||||
*v = ((((__sm_bitvec_t)1) << (SM_BITS_PER_VECTOR - 2)) | len);
|
||||
assert(SM_IS_CHUNK_RLE(chunk));
|
||||
assert(SM_CHUNK_RLE_LENGTH(chunk) == (unsigned int)len);
|
||||
QCC_showChunk(chunk, len);
|
||||
return QCC_initGenValue(chunk, 1, QCC_showChunk, QCC_freeChunkValue);
|
||||
} else {
|
||||
// Generate a chunk with the offset equal to the number of additional
|
||||
// vectors (len) and a descriptor that matches that with random data.
|
||||
unsigned int from = 0, to = SM_FLAGS_PER_INDEX;
|
||||
unsigned int len = ((unsigned int)random() % (to - from)) + from;
|
||||
unsigned int cut = ((unsigned int)random() % ((SM_FLAGS_PER_INDEX - len) - from)) + from;
|
||||
__sm_chunk_t *chunk = malloc(sizeof(__sm_chunk_t) + (sizeof(__sm_bitvec_t) * (len + 2)));
|
||||
chunk->m_data = (__sm_bitvec_t *)((uintptr_t)chunk + sizeof(__sm_chunk_t));
|
||||
__sm_bitvec_t *desc = &chunk->m_data[0];
|
||||
*desc = 0;
|
||||
// Run through the flags (2 bits), we know that exactly `len` many flags
|
||||
// must be SM_PAYLOAD_MIXED.
|
||||
for (size_t i = 0, n = len; (i < SM_FLAGS_PER_INDEX - cut) && n; i++) {
|
||||
size_t left = SM_FLAGS_PER_INDEX - cut - i;
|
||||
double prob = (double)n / (double)left;
|
||||
double dice = (double)random() / RAND_MAX;
|
||||
if (dice < prob || left == n) {
|
||||
*desc |= ((__sm_bitvec_t)SM_PAYLOAD_MIXED) << i;
|
||||
size_t pos = (len - n);
|
||||
chunk->m_data[1 + pos] = (uintptr_t)chunk + pos;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < SM_FLAGS_PER_INDEX - cut; i++) {
|
||||
uint8_t flag = SM_CHUNK_GET_FLAGS(*desc, i);
|
||||
if (flag != SM_PAYLOAD_MIXED && ((double)random() / (double)RAND_MAX) > 0.5) {
|
||||
*desc ^= ~((__sm_bitvec_t)SM_PAYLOAD_ONES << (i * 2));
|
||||
}
|
||||
}
|
||||
//TODO: __sm_chunk_reduce_capacity(chunk, SM_CHUNK_MAX_CAPACITY - (cut * SM_BITS_PER_VECTOR));
|
||||
assert(SM_IS_CHUNK_RLE(chunk) == false);
|
||||
return QCC_initGenValue(chunk, 1, QCC_showChunk, QCC_freeChunkValue);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
_tst_sm_chunk_calc_vector_size(uint8_t b)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (((b >> (i * 2)) & 0x03) == 0x02) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
QCC_TestStatus
|
||||
_tst_chunk_calc_vector_size_equality(QCC_GenValue **vals, int len, QCC_Stamp **stamp)
|
||||
{
|
||||
unsigned int a = *QCC_getValue(vals, 0, unsigned int *) % 256;
|
||||
return _tst_sm_chunk_calc_vector_size(a) == __sm_chunk_calc_vector_size(a);
|
||||
}
|
||||
|
||||
QCC_TestStatus
|
||||
_tst_chunk_get_position(QCC_GenValue **vals, int len, QCC_Stamp **stamp)
|
||||
{
|
||||
size_t pos;
|
||||
__sm_chunk_t *chunk = QCC_getValue(vals, 0, __sm_chunk_t *);
|
||||
if (SM_IS_CHUNK_RLE(chunk)) {
|
||||
for (size_t i = 0; i < SM_FLAGS_PER_INDEX; i++) {
|
||||
pos = __sm_chunk_get_position(chunk, i);
|
||||
if (pos != 0) {
|
||||
return QCC_FAIL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t mixed = 0;
|
||||
for (size_t i = 0; i < SM_FLAGS_PER_INDEX; i++) {
|
||||
uint8_t flag = SM_CHUNK_GET_FLAGS(*chunk->m_data, i);
|
||||
switch (flag) {
|
||||
case SM_PAYLOAD_MIXED:
|
||||
pos = __sm_chunk_get_position(chunk, i);
|
||||
if (chunk->m_data[1 + pos] != (uintptr_t)chunk + pos) {
|
||||
return QCC_FAIL;
|
||||
}
|
||||
mixed++;
|
||||
break;
|
||||
case SM_PAYLOAD_ONES:
|
||||
case SM_PAYLOAD_ZEROS:
|
||||
pos = __sm_chunk_get_position(chunk, i);
|
||||
if (pos != mixed) {
|
||||
return QCC_FAIL;
|
||||
}
|
||||
break;
|
||||
case SM_PAYLOAD_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QCC_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
50
test/qc.c
50
test/qc.c
|
@ -37,7 +37,7 @@ typedef struct QCC_Result {
|
|||
int argumentsN;
|
||||
} QCC_Result;
|
||||
|
||||
enum QCC_deref_type { NONE, LONG, INT, FLOAT, DOUBLE, CHAR };
|
||||
enum QCC_deref_type { NONE, LONG, INT, FLOAT, DOUBLE, BYTE, CHAR };
|
||||
|
||||
void
|
||||
QCC_init(int seed)
|
||||
|
@ -73,6 +73,9 @@ QCC_showSimpleValue(void *value, enum QCC_deref_type dt, int maxsize, const char
|
|||
case CHAR:
|
||||
snprintf(vc, maxsize + 1, format, *(char *)value);
|
||||
break;
|
||||
case BYTE:
|
||||
snprintf(vc, maxsize + 1, format, *(uint8_t *)value);
|
||||
break;
|
||||
default:
|
||||
snprintf(vc, maxsize + 1, format, *(int *)value);
|
||||
break;
|
||||
|
@ -380,6 +383,49 @@ QCC_showSimpleArray(void *value, size_t elemSize, QCC_showValue showValue, int l
|
|||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
QCC_showByte(void *value, int len)
|
||||
{
|
||||
return QCC_showSimpleValue(value, BYTE, 3, "'%x'");
|
||||
}
|
||||
|
||||
static char *
|
||||
QCC_showArrayByte(void *value, int n)
|
||||
{
|
||||
return QCC_showSimpleArray(value, sizeof(long), QCC_showByte, n);
|
||||
}
|
||||
|
||||
void
|
||||
QCC_genByteAtR(uint8_t *b, uint8_t *from, uint8_t *to)
|
||||
{
|
||||
uint8_t r = (uint8_t)random() / (uint8_t)RAND_MAX;
|
||||
*b = *from + (*to - *from) * r;
|
||||
}
|
||||
|
||||
void
|
||||
QCC_genByteAt(uint8_t *b)
|
||||
{
|
||||
*b = ((uint8_t)random() / (uint8_t)RAND_MAX) % UINT8_MAX;
|
||||
}
|
||||
|
||||
QCC_GenValue *
|
||||
QCC_genArrayByteLR(int len, long from, long to)
|
||||
{
|
||||
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genByteAtR, &from, &to, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
|
||||
}
|
||||
|
||||
QCC_GenValue *
|
||||
QCC_genArrayByteL(int len)
|
||||
{
|
||||
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genByteAt, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
|
||||
}
|
||||
|
||||
QCC_GenValue *
|
||||
QCC_genArrayByte()
|
||||
{
|
||||
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genByteAt, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
|
||||
}
|
||||
|
||||
static char *
|
||||
QCC_showArrayLong(void *value, int n)
|
||||
{
|
||||
|
@ -760,7 +806,7 @@ QCC_testForAll(int num, int maxFail, QCC_property prop, int genNum, ...)
|
|||
}
|
||||
|
||||
if (succ == num) {
|
||||
printf("%d test passed (%d)!\n", succ, fail);
|
||||
//printf("%d test passed (%d)!\n", succ, fail);
|
||||
QCC_printStamps(stamps, succ);
|
||||
if (stamps)
|
||||
QCC_freeStamp(stamps);
|
||||
|
|
|
@ -253,6 +253,10 @@ QCC_GenValue *QCC_genChar();
|
|||
QCC_GenValue *QCC_genString();
|
||||
QCC_GenValue *QCC_genStringL(int len);
|
||||
|
||||
QCC_GenValue *QCC_genArrayByte();
|
||||
QCC_GenValue *QCC_genArrayByteL(int len);
|
||||
QCC_GenValue *QCC_genArrayByteLR(int len, long from, long to);
|
||||
|
||||
QCC_GenValue *QCC_genArrayLong();
|
||||
QCC_GenValue *QCC_genArrayLongL(int len);
|
||||
QCC_GenValue *QCC_genArrayLongLR(int len, long from, long to);
|
||||
|
|
49
test/test.c
49
test/test.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* smartmap is MIT-licensed, but for this file:
|
||||
* sparsemap is MIT-licensed, but for this file:
|
||||
*
|
||||
* To the extent possible under law, the author(s) of this file have
|
||||
* waived all copyright and related or neighboring rights to this
|
||||
|
@ -10,16 +10,15 @@
|
|||
#define MUNIT_NO_FORK (1)
|
||||
#define MUNIT_ENABLE_ASSERT_ALIASES (1)
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <munit.h>
|
||||
#include <qc.h>
|
||||
#include <sparsemap.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sparsemap.h>
|
||||
#include <common.h>
|
||||
#include <munit.h>
|
||||
#include <qc.h>
|
||||
|
||||
#define munit_free free
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -89,7 +88,7 @@ test_api_new(const MunitParameter params[], void *data)
|
|||
assert_ptr_not_null(map);
|
||||
assert_true(map->m_capacity == 1024);
|
||||
assert_true(map->m_data_used == sizeof(uint32_t));
|
||||
assert_true((((uint8_t)map->m_data[0]) & 0x03) ==0x00);
|
||||
assert_true((((uint8_t)map->m_data[0]) & 0x03) == 0x00);
|
||||
|
||||
munit_free(map);
|
||||
|
||||
|
@ -1268,6 +1267,39 @@ static MunitTest api_test_suite[] = {
|
|||
};
|
||||
// clang-format on
|
||||
|
||||
/* -------------------------- Quickcheck, Property Based Tests */
|
||||
|
||||
extern QCC_TestStatus _tst_chunk_calc_vector_size_equality(QCC_GenValue **vals, int len, QCC_Stamp **stamp);
|
||||
|
||||
static MunitResult
|
||||
qc__sm_chunk_calc_vector_size(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
return QCC_testForAll(1000, 1000, _tst_chunk_calc_vector_size_equality, 1, QCC_genInt);
|
||||
}
|
||||
|
||||
extern QCC_GenValue *QCC_genChunk();
|
||||
extern QCC_TestStatus _tst_chunk_get_position(QCC_GenValue **vals, int len, QCC_Stamp **stamp);
|
||||
|
||||
static MunitResult
|
||||
qc__sm_chunk_get_position(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
return QCC_testForAll(100, 1000, _tst_chunk_get_position, 1, QCC_genChunk);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static MunitTest qc_test_suite[] = {
|
||||
{ (char *)"/__sm_chunk_calc_vector_size", qc__sm_chunk_calc_vector_size, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ (char *)"/__sm_chunk_get_position", qc__sm_chunk_get_position, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
|
||||
};
|
||||
// clang-format off
|
||||
|
||||
/* -------------------------- Scale Tests */
|
||||
|
||||
static void *
|
||||
|
@ -1682,6 +1714,7 @@ static MunitTest perf_test_suite[] = {
|
|||
// clang-format off
|
||||
static MunitSuite other_test_suite[] = {
|
||||
{ "/api", api_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||
{ "/qc", qc_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||
{ "/perf", perf_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||
{ "/scale", scale_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||
{ NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } };
|
||||
|
@ -1704,6 +1737,8 @@ main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
|
|||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
QCC_init(0);
|
||||
|
||||
return munit_suite_main(&main_test_suite, (void *)&info, argc, argv);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue