test_libc_fxlib/src/memfunctest.c

195 lines
4.9 KiB
C

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
/* 4-abyte buffer alignement */
#define ALIGN(n) __attribute__((aligned(n)))
/* Unused parameters */
#define UNUSED __attribute__((unused))
//---
// Basic setup
//---
/* Source buffer, used as a data source when copying */
ALIGN(4) static uint8_t src[256];
/* Destination buffer, used as destination when copying or clearing */
ALIGN(4) static uint8_t dst[256];
/* System buffer, used to reproduce the behavior on the system and compare */
ALIGN(4) static uint8_t sys[256];
/* Temporary buffer, used by the naive memmove() */
ALIGN(4) static uint8_t tmp[256];
/* fill() - fill a buffer with non-zero data */
static void fill(uint8_t *buf)
{
for(int i = 0; i < 256; i++) buf[i] = i;
}
/* clear() - clear a buffer with zeros */
static void clear(uint8_t *buf)
{
for(int i = 0; i < 256; i++) buf[i] = 0;
}
/* cmp() - check that some two buffers are equal
Returns non-zero if the buffers differ. */
static int cmp(uint8_t *lft, uint8_t *rgt)
{
for(int i = 0; i < 256; i++) if(lft[i] != rgt[i]) return 1;
return 0;
}
//---
// Naive functions (reference behaviour)
//---
/* naive_memcpy() - bytewise copy */
static void *naive_memcpy(void *_dst, const void *_src, size_t len)
{
uint8_t *dst = _dst;
uint8_t const *src = _src;
while(len--) *dst++ = *src++;
return _dst;
}
/* naive_memset() - bytewise set */
static void *naive_memset(void *_dst, int byte, size_t len)
{
uint8_t *dst = _dst;
while(len--) *dst++ = byte;
return _dst;
}
/* naive_memmove() - bytewise copy with buffer */
static void *naive_memmove(void *_dst, const void *_src, size_t len)
{
naive_memcpy(tmp, _src, len);
naive_memcpy(_dst, tmp, len);
return _dst;
}
//---
// Testing functions
//---
static int test_memcpy(int off_dst, int off_src, size_t len)
{
clear(dst);
clear(sys);
memcpy(dst + off_dst, src + off_src, len);
naive_memcpy(sys + off_dst, src + off_src, len);
return cmp(dst, sys);
}
static int test_memset(int off_dst, UNUSED int off_src, size_t len)
{
fill(dst);
fill(sys);
memset(dst + off_dst, 0, len);
naive_memset(sys + off_dst, 0, len);
return cmp(dst, sys);
}
static int test_memmove(int off_dst, int off_src, size_t len)
{
fill(dst);
fill(sys);
memmove(dst + off_dst, dst + off_src, len);
naive_memmove(sys + off_dst, sys + off_src, len);
return cmp(dst, sys);
}
//---
// Automated tests
//
// These tests are meant to check all size/alignment scenarios for the
// core memory functions. Two intervals of sizes are tested:
// - 12..15 bytes, expected to be handled naively
// - 192..195 bytes, expected to trigger alignment-related optimizations
// For each of these intervals, all alignments possibilities are tested:
// - 4n + { 0,1,2,3 } for the source address
// - 4n + { 0,1,2,3 } for destination address
// Also, the source and destination regions are made to overlap to allow
// non-trivial memmove() cases to be checked.
//
// The testing function non-zero if one or more tests fail.
//---
/* test() - check core memory functions in various size/alignment scenarios
The function to test takes three arguments: two buffer offsets and the size
of the operation. Bounds need no be checked. It must return 0 in case of
success and non-zero in case of failure.
@func Function to test, will be called with various sizes and alignments
@count If non-null, set to number of tests performed
Returns the number of failed tests; thus, non-zero indicates failure. */
static int test(int (*func)(int off_dst, int off_src, size_t size), int *count)
{
/* Number of failed tests */
int failed = 0;
/* Number of tests */
int tests = 0;
/* For each source and destination alignment... */
for(int dst_al = 0; dst_al < 4; dst_al++)
for(int src_al = 0; src_al < 4; src_al++)
/* For each "alignment" of operation size... */
for(int len_al = 0; len_al < 4; len_al++)
{
/* Try a small size first */
failed += !!func(96 + dst_al, 96 + src_al, 12 + len_al);
/* Then a medium region without overlapping */
failed += !!func(4 + dst_al, 128 + src_al, 92 + len_al);
/* A large region with left-right overlapping */
failed += !!func(64 + dst_al, 96 + src_al, 128 + len_al);
/* A large region with right-left overlapping */
failed += !!func(96 + dst_al, 64 + src_al, 128 + len_al);
tests += 4;
}
if(count) *count = tests;
return failed;
}
//---
// Main function
//---
/* memfunctest() - check the bejavior of the core memory functions
Returns non-zero if any test fails. */
int memfunctest(void)
{
int count = 0, failed = 0, count_one, failed_one;
fill(src);
failed_one = test(test_memcpy, &count_one);
failed += failed_one;
count += count_one;
failed_one = test(test_memset, &count_one);
failed += failed_one;
count += count_one;
failed_one = test(test_memmove, &count_one);
failed += failed_one;
count += count_one;
char str[21];
sprintf(str, "Score: %d/%d.", count - failed, count);
Print(str);
return (failed != 0);
}