195 lines
4.9 KiB
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);
|
|
}
|