/* Test of simple and straight-forward malloc implementation.
Copyright (C) 2020-2023 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Written by Bruno Haible , 2020. */
#include
#include
#include
#if defined _WIN32 && !defined __CYGWIN__
/* Declare VirtualAlloc(), GetSystemInfo. */
# define WIN32_LEAN_AND_MEAN
# define WIN32_EXTRA_LEAN
# include
#else
/* Declare getpagesize(). */
# include
/* On HP-UX, getpagesize exists, but it is not declared in even if
the compiler options -D_HPUX_SOURCE -D_XOPEN_SOURCE=600 are used. */
# ifdef __hpux
extern
# ifdef __cplusplus
"C"
# endif
int getpagesize (void);
# endif
/* Declare mmap(). */
# include
# include
/* Some old mmap() implementations require the flag MAP_VARIABLE whenever you
pass an addr == NULL. */
# ifndef MAP_VARIABLE
# define MAP_VARIABLE 0
# endif
#endif
/* ================= Back end of the malloc implementation ================= */
/* The memory page size.
Once it is initialized, a power of 2. Typically 4096 or 8192. */
static uintptr_t pagesize;
/* Initializes pagesize. */
static void
init_pagesize (void)
{
#if defined _WIN32 && !defined __CYGWIN__
/* GetSystemInfo
*/
SYSTEM_INFO info;
GetSystemInfo (&info);
pagesize = info.dwPageSize;
#else
pagesize = getpagesize ();
#endif
}
/* Allocates a contiguous set of pages of memory.
size > 0, must be a multiple of pagesize.
Returns a multiple of PAGESIZE, or 0 upon failure. */
static uintptr_t
alloc_pages (size_t size)
{
#if defined _WIN32 && !defined __CYGWIN__
/* VirtualAlloc
*/
void *mem = VirtualAlloc (NULL, size, MEM_COMMIT, PAGE_READWRITE);
if (mem == NULL)
return 0;
return (uintptr_t) mem;
#else
/* Use mmap with the MAP_ANONYMOUS or MAP_ANON flag. */
void *mem = mmap (NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_VARIABLE, -1, 0);
if (mem == (void *)(-1))
return 0;
return (uintptr_t) mem;
#endif
}
/* Frees a contiguous set of pages of memory, returned by alloc_pages.
size > 0, must be a multiple of pagesize. */
static void
free_pages (uintptr_t pages, size_t size)
{
#if defined _WIN32 && !defined __CYGWIN__
/* VirtualFree
*/
if (!VirtualFree ((void *) pages, 0, MEM_RELEASE))
abort ();
#else
if ((pages & (pagesize - 1)) != 0)
abort ();
if (munmap ((void *) pages, size) < 0)
abort ();
#endif
}
/* Cygwin defines PAGESIZE in . */
#undef PAGESIZE
/* ======================= Instantiate the front end ======================= */
#define PAGESIZE pagesize
/* On Cygwin and Linux/PowerPC, PAGESIZE is 65536. On macOS 11, it is 16384.
On all other platforms, it is either 4096 or 8192. */
#if defined __CYGWIN__ || (defined __linux__ && defined __powerpc__)
# define PAGESIZE_MAX 65536
#else
# define PAGESIZE_MAX 16384
#endif
#define ALLOC_PAGES alloc_pages
#define FREE_PAGES free_pages
#define ALIGNMENT (sizeof (void *)) /* or 8 or 16 or 32 */
#define PAGE_RESERVED_HEADER_SIZE (3 * UINTPTR_WIDTH / 8) /* = 3 * sizeof (void *) */
#include "ssfmalloc.h"
/* ================================= Tests ================================= */
#include
#include
#include "macros.h"
/* Fills a block of a given size with some contents. */
static void
fill_block (uintptr_t block, size_t size)
{
unsigned char code = (size % (UCHAR_MAX - 1)) + 1;
memset ((char *) block, code, size);
}
/* Verifies that the contents of a block is still present
(i.e. has not accidentally been overwritten by other operations). */
static void
verify_block (uintptr_t block, size_t size)
{
unsigned char code = (size % (UCHAR_MAX - 1)) + 1;
char *p = (char *) block;
for (; size > 0; p++, size--)
if ((unsigned char) *p != code)
abort ();
}
static size_t block_sizes[] =
{
/* Small blocks. */
1,
2,
3,
4,
5,
6,
7,
8,
9,
12,
15,
16,
17,
24,
31,
32,
37,
42,
49,
57,
63,
64,
65,
71,
77,
83,
96,
99,
110,
119,
127,
128,
130,
144,
150,
157,
161,
169,
180,
192,
199,
204,
210,
224,
225,
236,
241,
249,
255,
256,
/* Medium blocks. */
257,
281,
284,
294,
301,
308,
341,
447,
525,
659,
771,
842,
729,
999,
1000,
1020,
1023,
1024,
1025,
1280,
1414,
2047,
2048,
2049,
2096,
2401,
2613,
2843,
3010,
3213,
3512,
3678,
3801,
3900,
/* Large blocks. */
4000,
4060,
4080,
4090,
4095,
4096,
4097,
4121,
5381,
7814,
8191,
8192,
8193,
11238,
16383,
16384,
16385,
20184,
51202,
135010
};
#define RANDOM(n) (rand () % (n))
#define RANDOM_BLOCK_SIZE() block_sizes[RANDOM (SIZEOF (block_sizes))]
int
main (int argc, char *argv[])
{
/* Allow the user to provide a non-default random seed on the command line. */
if (argc > 1)
srand (atoi (argv[1]));
init_pagesize ();
/* Randomly allocate and deallocate blocks.
Also verify that there are no unexpected modifications to the contents of
these blocks. */
{
unsigned int repeat;
char *blocks[SIZEOF (block_sizes)];
{
size_t i;
for (i = 0; i < SIZEOF (block_sizes); i++)
blocks[i] = NULL;
}
for (repeat = 0; repeat < 100000; repeat++)
{
unsigned int operation = RANDOM (2);
switch (operation)
{
case 0:
{ /* Allocate a block. */
size_t i = RANDOM (SIZEOF (block_sizes));
size_t size = block_sizes[i];
if (blocks[i] == NULL)
{
uintptr_t block = allocate_block (size);
if (block == 0)
abort ();
fill_block (block, size);
blocks[i] = (char *) block;
}
}
break;
case 1:
{ /* Free a block. */
size_t i = RANDOM (SIZEOF (block_sizes));
size_t size = block_sizes[i];
if (blocks[i] != NULL)
{
uintptr_t block = (uintptr_t) blocks[i];
verify_block (block, size);
free_block (block);
blocks[i] = NULL;
}
}
break;
}
}
/* Free the remaining blocks. */
{
size_t i;
for (i = 0; i < SIZEOF (block_sizes); i++)
if (blocks[i] != NULL)
{
uintptr_t block = (uintptr_t) blocks[i];
size_t size = block_sizes[i];
verify_block (block, size);
free_block (block);
}
}
}
return 0;
}