#include #include #include #include /* 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); }