blob: 58bf5aa1bb140c0d3926d2a74e2ec82a8ad7f8a0 [file]
// Copyright 2018 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <emscripten.h>
#include <emscripten/emmalloc.h>
#ifndef RANDOM_ITERS
#define RANDOM_ITERS 12345
#endif
void emmalloc_blank_slate_from_orbit();
void stage(const char* name) {
// Using printf here over out, at least until we can fix
// https://github.com/emscripten-core/emscripten/issues/14804
printf(">> %s\n", name);
/*
EM_ASM({
out('>> ' + UTF8ToString($0) + '\n');
}, name);
*/
}
void basics() {
stage("basics");
stage("allocate 0");
void* ptr = malloc(0);
assert(ptr != 0);
free(ptr);
stage("allocate 100");
void* first = malloc(100);
stage("free 100");
free(first);
stage("allocate another 100");
void* second = malloc(100);
stage("allocate 10");
#ifndef WASMFS
// There is no strict guarantee that the second allocation be equal to the
// first, but in practice on a fresh heap that tends to be the case. With
// WasmFS, however, the heap has already seen a bunch of use by the time we
// get here, and due to fragmentation etc. we cannot predict getting the exact
// same result the second time.
assert(second == first);
#endif
void* third = malloc(10);
assert(!emmalloc_validate_memory_regions());
stage("allocate 10 more");
void* four = malloc(10);
assert(!emmalloc_validate_memory_regions());
stage("free the first");
free(second);
stage("free all");
free(third);
free(four);
assert(!emmalloc_validate_memory_regions());
}
void previous_sbrk() {
stage("previous_sbrk");
emmalloc_blank_slate_from_orbit();
void* old = sbrk(0);
assert((size_t)old % 4 == 0);
sbrk(3); // unalign things
void* other = malloc(10);
free(other);
assert(other != old);
}
void test_calloc() {
stage("calloc");
emmalloc_blank_slate_from_orbit();
char* ptr = (char*)malloc(10);
ptr[0] = 77;
free(ptr);
char* cptr = (char*)calloc(10, 1);
assert(cptr == ptr);
assert(ptr[0] == 0);
}
void test_realloc() {
stage("realloc0");
emmalloc_blank_slate_from_orbit();
for (int i = 0; i < 2; i++) {
char* ptr = (char*)malloc(100);
stage("realloc0.1");
char* raptr = (char*)realloc(ptr, 1);
assert(raptr == ptr);
stage("realloc0.2");
char* raptr2 = (char*)realloc(raptr, 100);
assert(raptr2 == ptr);
char* last = (char*)malloc(1);
assert(last >= ptr + 100);
// slightly more still fits
stage("realloc0.3");
char* raptr3 = (char*)realloc(raptr2, 11);
assert(raptr3 == ptr);
// finally, realloc a size we must reallocate for
stage("realloc0.4");
char* raptr4 = (char*)realloc(raptr3, 1000);
assert(raptr4);
assert(raptr4 != ptr);
// leaving those in place, do another iteration
}
stage("realloc1");
emmalloc_blank_slate_from_orbit();
// realloc of NULL is like malloc
assert(realloc(NULL, 10) != 0);
stage("realloc2");
emmalloc_blank_slate_from_orbit();
{
// realloc to 0 is like free
void* ptr = malloc(10);
assert(realloc(ptr, 0) == NULL);
}
stage("realloc3");
emmalloc_blank_slate_from_orbit();
{
// realloc copies
char* ptr = (char*)malloc(10);
*ptr = 123;
for (int i = 5; i <= 16; i++) {
char* temp = (char*)realloc(ptr, i);
assert(*temp == 123);
assert(temp == ptr);
}
stage("realloc3.5");
malloc(1);
malloc(100);
{
char* temp = (char*)realloc(ptr, 17);
assert(*temp == 123);
assert(temp != ptr);
ptr = temp;
}
}
}
void check_aligned(size_t align, size_t ptr) {
if (align != 0 && ((align & (align - 1)) != 0)) {
assert(ptr == 0);
} else {
assert(ptr);
assert(align == 0 || ptr % align == 0);
}
}
void aligned() {
stage("aligned");
for (int i = 0; i < 35; i++) {
for (int j = 0; j < 35; j++) {
emmalloc_blank_slate_from_orbit();
size_t first = (size_t)memalign(i, 100);
size_t second = (size_t)memalign(j, 100);
printf("%d %d => %zu %zu\n", i, j, first, second);
check_aligned(i, first);
check_aligned(j, second);
}
}
}
void randoms() {
stage("randoms");
emmalloc_blank_slate_from_orbit();
void* start = malloc(10);
const int N = 1000;
const int BINS = 128;
void* bins[BINS];
char values[BINS];
for (int i = 0; i < BINS; i++) {
bins[i] = NULL;
}
for (int i = 0; i < RANDOM_ITERS; i++) {
unsigned int r = random();
int alloc = r & 1;
r >>= 1;
int calloc_ = r & 1;
r >>= 1;
int bin = r & 127;
r >>= 7;
unsigned int size = r & 65535;
r >>= 16;
int useShifts = r & 1;
r >>= 1;
unsigned int shifts = r & 15;
r >>= 4;
if (size == 0) size = 1;
if (useShifts) {
size >>= shifts; // spread out values logarithmically
}
if (alloc || !bins[bin]) {
if (bins[bin]) {
char value = values[bin];
assert(*(char*)(bins[bin]) == value /* one */);
bins[bin] = realloc(bins[bin], size);
if (bins[bin]) {
assert(*(char*)(bins[bin]) == value /* two */);
}
} else {
if (calloc_) {
bins[bin] = malloc(size);
} else {
bins[bin] = calloc(size, 1);
}
values[bin] = random();
if (bins[bin]) {
*(char*)(bins[bin]) = values[bin];
assert(*(char*)(bins[bin]) == values[bin] /* three */);
}
}
} else {
free(bins[bin]);
bins[bin] = NULL;
}
}
for (int i = 0; i < BINS; i++) {
if (bins[i]) free(bins[i]);
}
assert(!emmalloc_validate_memory_regions());
}
int main() {
stage("beginning");
basics();
previous_sbrk();
test_calloc();
test_realloc();
aligned();
randoms();
stage("the_end");
}