mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 20:32:36 +00:00
337 lines
8.6 KiB
C++
337 lines
8.6 KiB
C++
/************************************************************************************************
|
|
NC_ALLOC.CPP
|
|
|
|
* Copyright (c) 1997
|
|
* Mark of the Unicorn, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute and sell this software
|
|
* and its documentation for any purpose is hereby granted without fee,
|
|
* provided that the above copyright notice appear in all copies and
|
|
* that both that copyright notice and this permission notice appear
|
|
* in supporting documentation. Mark of the Unicorn makes no
|
|
* representations about the suitability of this software for any
|
|
* purpose. It is provided "as is" without express or implied warranty.
|
|
|
|
************************************************************************************************/
|
|
|
|
#include "nc_alloc.h"
|
|
#include <string>
|
|
|
|
#if defined (EH_NEW_HEADERS)
|
|
# include <new>
|
|
# include <cassert>
|
|
# include <cstdlib>
|
|
#else
|
|
# include <assert.h>
|
|
# include <stdlib.h>
|
|
# include <new.h>
|
|
#endif
|
|
|
|
#if defined (EH_NEW_IOSTREAMS)
|
|
# include <iostream>
|
|
#else
|
|
# include <iostream.h>
|
|
#endif
|
|
|
|
long alloc_count = 0;
|
|
long object_count = 0;
|
|
long TestController::possible_failure_count = 0;
|
|
const char* TestController::current_test = "<unknown>";
|
|
const char* TestController::current_test_category = "no category";
|
|
const char* TestController::current_container = 0;
|
|
bool TestController::nc_verbose = true;
|
|
bool TestController::never_fail = false;
|
|
bool TestController::track_allocations = false;
|
|
bool TestController::leak_detection_enabled = false;
|
|
TestController gTestController;
|
|
|
|
//************************************************************************************************
|
|
void TestController::maybe_fail(long) {
|
|
if (never_fail || Failure_threshold() == kNotInExceptionTest)
|
|
return;
|
|
|
|
// throw if allocation would satisfy the threshold
|
|
if (possible_failure_count++ >= Failure_threshold()) {
|
|
// what about doing some standard new_handler() behavior here (to test it!) ???
|
|
|
|
// reset and simulate an out-of-memory failure
|
|
Failure_threshold() = kNotInExceptionTest;
|
|
#ifndef EH_NO_EXCEPTIONS
|
|
throw EH_STD::bad_alloc();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if defined (EH_HASHED_CONTAINERS_IMPLEMENTED)
|
|
# if defined (__SGI_STL)
|
|
# if defined (EH_NEW_HEADERS)
|
|
# include <hash_set>
|
|
# else
|
|
# include <hash_set.h>
|
|
# endif
|
|
# elif defined (__MSL__)
|
|
# include <hashset.h>
|
|
# else
|
|
# error what do I include to get hash_set?
|
|
# endif
|
|
#else
|
|
# if defined (EH_NEW_HEADERS)
|
|
# include <set>
|
|
# else
|
|
# include <set.h>
|
|
# endif
|
|
#endif
|
|
|
|
#if !defined (EH_HASHED_CONTAINERS_IMPLEMENTED)
|
|
typedef EH_STD::set<void*, EH_STD::less<void*> > allocation_set;
|
|
#else
|
|
|
|
USING_CSTD_NAME(size_t)
|
|
|
|
struct hash_void {
|
|
size_t operator()(void* x) const { return (size_t)x; }
|
|
};
|
|
|
|
typedef EH_STD::hash_set<void*, ::hash_void, EH_STD::equal_to<void*> > allocation_set;
|
|
#endif
|
|
|
|
static allocation_set& alloc_set() {
|
|
static allocation_set s;
|
|
return s;
|
|
}
|
|
|
|
// Prevents infinite recursion during allocation
|
|
static bool using_alloc_set = false;
|
|
|
|
#if !defined (NO_FAST_ALLOCATOR)
|
|
//
|
|
// FastAllocator -- speeds up construction of TestClass objects when
|
|
// TESTCLASS_DEEP_DATA is enabled, and speeds up tracking of allocations
|
|
// when the suite is run with the -t option.
|
|
//
|
|
class FastAllocator {
|
|
public:
|
|
//FastAllocator() : mFree(0), mUsed(0) {}
|
|
static void *Allocate(size_t s) {
|
|
void *result = 0;
|
|
|
|
if (s <= sizeof(Block)) {
|
|
if (mFree != 0) {
|
|
result = mFree;
|
|
mFree = mFree->next;
|
|
}
|
|
else if (mBlocks != 0 && mUsed < kBlockCount) {
|
|
result = (void*)&mBlocks[mUsed++];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool Free(void* p) {
|
|
Block* b = (Block*)p;
|
|
if (mBlocks == 0 || b < mBlocks || b >= mBlocks + kBlockCount)
|
|
return false;
|
|
b->next = mFree;
|
|
mFree = b;
|
|
return true;
|
|
}
|
|
|
|
struct Block;
|
|
friend struct Block;
|
|
|
|
enum {
|
|
// Number of fast allocation blocks to create.
|
|
kBlockCount = 1500,
|
|
|
|
// You may need to adjust this number for your platform.
|
|
// A good choice will speed tests. A bad choice will still work.
|
|
kMinBlockSize = 48
|
|
};
|
|
|
|
struct Block {
|
|
union {
|
|
Block *next;
|
|
double dummy; // fbp - force alignment
|
|
char dummy2[kMinBlockSize];
|
|
};
|
|
};
|
|
|
|
static Block* mBlocks;
|
|
static Block *mFree;
|
|
static size_t mUsed;
|
|
};
|
|
|
|
FastAllocator::Block *FastAllocator::mBlocks =
|
|
(FastAllocator::Block*)EH_CSTD::calloc( sizeof(FastAllocator::Block), FastAllocator::kBlockCount );
|
|
FastAllocator::Block *FastAllocator::mFree;
|
|
size_t FastAllocator::mUsed;
|
|
|
|
|
|
static FastAllocator gFastAllocator;
|
|
#endif
|
|
|
|
inline char* AllocateBlock(size_t s) {
|
|
#if !defined (NO_FAST_ALLOCATOR)
|
|
char * const p = (char*)gFastAllocator.Allocate( s );
|
|
if (p != 0)
|
|
return p;
|
|
#endif
|
|
|
|
return (char*)EH_CSTD::malloc(s);
|
|
}
|
|
|
|
static void* OperatorNew( size_t s ) {
|
|
if (!using_alloc_set) {
|
|
simulate_possible_failure();
|
|
++alloc_count;
|
|
}
|
|
|
|
char *p = AllocateBlock(s);
|
|
|
|
if (gTestController.TrackingEnabled() &&
|
|
gTestController.LeakDetectionEnabled() &&
|
|
!using_alloc_set) {
|
|
using_alloc_set = true;
|
|
bool inserted = alloc_set().insert(p).second;
|
|
// Suppress warning about unused variable.
|
|
inserted;
|
|
EH_ASSERT(inserted);
|
|
using_alloc_set = false;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
void* _STLP_CALL operator new(size_t s)
|
|
#ifdef EH_DELETE_HAS_THROW_SPEC
|
|
throw(EH_STD::bad_alloc)
|
|
#endif
|
|
{ return OperatorNew( s ); }
|
|
|
|
#ifdef EH_USE_NOTHROW
|
|
void* _STLP_CALL operator new(size_t size, const EH_STD::nothrow_t&) throw() {
|
|
try {
|
|
return OperatorNew( size );
|
|
}
|
|
catch (...) {
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if 1 /* defined (EH_VECTOR_OPERATOR_NEW) */
|
|
void* _STLP_CALL operator new[](size_t size ) throw(EH_STD::bad_alloc) {
|
|
return OperatorNew( size );
|
|
}
|
|
|
|
# ifdef EH_USE_NOTHROW
|
|
void* _STLP_CALL operator new[](size_t size, const EH_STD::nothrow_t&) throw() {
|
|
try {
|
|
return OperatorNew(size);
|
|
}
|
|
catch (...) {
|
|
return 0;
|
|
}
|
|
}
|
|
# endif
|
|
|
|
void _STLP_CALL operator delete[](void* ptr) throw()
|
|
{ operator delete( ptr ); }
|
|
#endif
|
|
|
|
#if defined (EH_DELETE_HAS_THROW_SPEC)
|
|
void _STLP_CALL operator delete(void* s) throw()
|
|
#else
|
|
void _STLP_CALL operator delete(void* s)
|
|
#endif
|
|
{
|
|
if ( s != 0 ) {
|
|
if ( !using_alloc_set ) {
|
|
--alloc_count;
|
|
|
|
if ( gTestController.TrackingEnabled() && gTestController.LeakDetectionEnabled() ) {
|
|
using_alloc_set = true;
|
|
allocation_set::iterator p = alloc_set().find( (char*)s );
|
|
EH_ASSERT( p != alloc_set().end() );
|
|
alloc_set().erase( p );
|
|
using_alloc_set = false;
|
|
}
|
|
}
|
|
# if ! defined (NO_FAST_ALLOCATOR)
|
|
if ( !gFastAllocator.Free( s ) )
|
|
# endif
|
|
EH_CSTD::free(s);
|
|
}
|
|
}
|
|
|
|
#if defined (EH_DELETE_HAS_THROW_SPEC)
|
|
void _STLP_CALL operator delete(void* s, uintptr_t) throw()
|
|
#else
|
|
void _STLP_CALL operator delete(void* s, uintptr_t)
|
|
#endif
|
|
{
|
|
::operator delete(s);
|
|
}
|
|
|
|
/*===================================================================================
|
|
ClearAllocationSet (private helper)
|
|
|
|
EFFECTS: Empty the set of allocated blocks.
|
|
====================================================================================*/
|
|
void TestController::ClearAllocationSet() {
|
|
if (!using_alloc_set) {
|
|
using_alloc_set = true;
|
|
alloc_set().clear();
|
|
using_alloc_set = false;
|
|
}
|
|
}
|
|
|
|
|
|
bool TestController::ReportLeaked() {
|
|
EndLeakDetection();
|
|
|
|
EH_ASSERT( !using_alloc_set || (alloc_count == static_cast<int>(alloc_set().size())) );
|
|
|
|
if (alloc_count != 0 || object_count != 0) {
|
|
EH_STD::cerr<<"\nEH TEST FAILURE !\n";
|
|
PrintTestName(true);
|
|
if (alloc_count)
|
|
EH_STD::cerr << "ERROR : " << alloc_count << " outstanding allocations.\n";
|
|
if (object_count)
|
|
EH_STD::cerr << "ERROR : " << object_count << " non-destroyed objects.\n";
|
|
alloc_count = object_count = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================================
|
|
PrintTestName
|
|
|
|
EFFECTS: Prints information about the current test. If err is false, ends with
|
|
an ellipsis, because the test is ongoing. If err is true an error is being
|
|
reported, and the output ends with an endl.
|
|
====================================================================================*/
|
|
|
|
void TestController::PrintTestName(bool err) {
|
|
if (current_container)
|
|
EH_STD::cerr<<"["<<current_container<<"] :";
|
|
EH_STD::cerr<<"testing "<<current_test <<" (" << current_test_category <<")";
|
|
if (err)
|
|
EH_STD::cerr<<EH_STD::endl;
|
|
else
|
|
EH_STD::cerr<<" ... ";
|
|
}
|
|
|
|
void TestController::ReportSuccess(int count) {
|
|
if (nc_verbose)
|
|
EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
|
|
}
|
|
|
|
long& TestController::Failure_threshold() {
|
|
static long failure_threshold = kNotInExceptionTest;
|
|
return failure_threshold;
|
|
}
|