/* * custom_c++_operator_new1.cpp - custom allocator demo * * Written by Roland Mainz * Compile with: * clang++ -std=c++17 -g -Wall custom_c++_operator_new1.cpp * */ #include #include #include /* required for |main()| test/demo code */ #include #include #include #include #include /* misc */ #define STDERR_MSG(msg) \ (void)write(STDERR_FILENO, (msg), strlen(msg)) #define USE_STATIC_BUFFER_ALLOC 1 #if USE_STATIC_BUFFER_ALLOC static struct { char buffer[512]; /* more complex cases would require |pthread_once()| */ char *buf_ptr = buffer; } buf_alloc; void *my_buffer_alloc(size_t size) { #define SIZE_ALIGN(size, align) \ ((size)+((align)-1)-((size)-1)%(align)) void *mem; size_t bs; STDERR_MSG("my_buffer_alloc\n"); bs = SIZE_ALIGN(size, 16); mem = buf_alloc.buf_ptr; buf_alloc.buf_ptr += bs; /* could we satisfy the allocation request ? */ if ((buf_alloc.buf_ptr - buf_alloc.buffer) >= sizeof(buf_alloc.buffer)) return NULL; #if 1 (void)fprintf(stderr, "my_buffer_alloc(size=%ld, bs=%ld, mem=%lx\n", (long)size, (long)bs, (long)mem); #endif return mem; } void my_buffer_dealloc(void *ptr) { /* * range check - pointer must be within |buf_alloc.buffer|! * * We use this simple (and cheap) check to catch invalid * pointers, or (more important) pointers to memory from * other allocators (if we mix&match multiple different * allocators or different memory pools from the same * allocator system). */ ptrdiff_t x = (char *)ptr - buf_alloc.buffer; if ((x < 0) || (x >= (ptrdiff_t)sizeof(buf_alloc.buffer))) { /* BUG: LAZY - we should not use stdio here... */ (void)fprintf(stderr, "delete(): bad pointer %lx!\n", (long)x); } } #endif /* USE_STATIC_BUFFER_ALLOC */ void * operator new(std::size_t size) // throw(std::bad_alloc) { STDERR_MSG("##new\n"); if (size == 0) size = 1; void* p; #if USE_STATIC_BUFFER_ALLOC if ((p = my_buffer_alloc(size)) == 0) #else if ((p = ::malloc(size)) == 0) #endif /* USE_STATIC_BUFFER_ALLOC */ { throw std::bad_alloc(); } return p; } void operator delete(void* ptr) _GLIBCXX_USE_NOEXCEPT { STDERR_MSG("##delete\n"); #if USE_STATIC_BUFFER_ALLOC my_buffer_dealloc(ptr); #else if (ptr) ::free(ptr); #endif /* USE_STATIC_BUFFER_ALLOC */ } /* * static variables: * Their memory is allocated/constructor called before calling * |main()|, and their destructor is called and memory deallocated * after |main()| returned. * This causes some nasty headaches for custom |operator new|, e.g. * in case we want to rely on stdio, C++ iostream, or other * libc/libc++ modules. And don't even start to ask about headaches * related to STL... */ static int *s_f = new int(59); static std::string s = "xjsdsqwertzuiopasdfghjklöxcvbnm,poiuztrewlkjhgfdmnbvcxjsddljkjklsdfjklsddfljkx"; int main(int ac, char *av[]) { (void)puts("# >> main:start."); int *f = new int(84); 41void * 42operator new(std::size_t size) 43#if !__has_feature(cxx_noexcept) 44 throw(std::bad_alloc) 45#endif 46{ 47 if (size == 0) 48 size = 1; 49 void* p; 50 while ((p = ::malloc(size)) == 0) 51 { 52 // If malloc fails and there is a new_handler, 53 // call it to try free up memory. 54 std::new_handler nh = std::get_new_handler(); 55 if (nh) 56 nh(); 57 else 58#ifndef _LIBCPP_NO_EXCEPTIONS 59 throw std::bad_alloc(); 60#else 61 break; 62#endif 63 } 64 return p; 65} std::string s = "13123123123122354245234534534534534534656456456456456456242342341233423423524542hello"; s+=" "; s+="world"; std::cout << s << "#" << *f << "," << *s_f << "#" << std::endl; delete f; #if 0 /* test if pointer validation of |my_buffer_dealloc()| works */ delete (int *)malloc(5); #endif (void)puts("# >> main:done."); return EXIT_SUCCESS; }