1/************************************************************************************************
2 NC_ALLOC.CPP
3
4 * Copyright (c) 1997
5 * Mark of the Unicorn, Inc.
6 *
7 * Permission to use, copy, modify, distribute and sell this software
8 * and its documentation for any purpose is hereby granted without fee,
9 * provided that the above copyright notice appear in all copies and
10 * that both that copyright notice and this permission notice appear
11 * in supporting documentation.  Mark of the Unicorn makes no
12 * representations about the suitability of this software for any
13 * purpose.  It is provided "as is" without express or implied warranty.
14
15************************************************************************************************/
16
17#include "nc_alloc.h"
18#include <string>
19
20#if defined (EH_NEW_HEADERS)
21#  include <new>
22#  include <cassert>
23#  include <cstdlib>
24#else
25#  include <assert.h>
26#  include <stdlib.h>
27#  include <new.h>
28#endif
29
30#if defined (EH_NEW_IOSTREAMS)
31#  include <iostream>
32#else
33#  include <iostream.h>
34#endif
35
36long alloc_count = 0;
37long object_count = 0;
38long TestController::possible_failure_count = 0;
39const char* TestController::current_test = "<unknown>";
40const char* TestController::current_test_category = "no category";
41const char* TestController::current_container = 0;
42bool  TestController::nc_verbose = true;
43bool  TestController::never_fail = false;
44bool  TestController::track_allocations = false;
45bool  TestController::leak_detection_enabled = false;
46TestController gTestController;
47
48//************************************************************************************************
49void TestController::maybe_fail(long) {
50  if (never_fail || Failure_threshold() == kNotInExceptionTest)
51    return;
52
53  // throw if allocation would satisfy the threshold
54  if (possible_failure_count++ >= Failure_threshold()) {
55    // what about doing some standard new_handler() behavior here (to test it!) ???
56
57    // reset and simulate an out-of-memory failure
58    Failure_threshold() = kNotInExceptionTest;
59#ifndef EH_NO_EXCEPTIONS
60    throw EH_STD::bad_alloc();
61#endif
62  }
63}
64
65#if defined (EH_HASHED_CONTAINERS_IMPLEMENTED)
66#  if defined (__SGI_STL)
67#    if defined (EH_NEW_HEADERS)
68#      include <hash_set>
69#    else
70#      include <hash_set.h>
71#    endif
72#  elif defined (__MSL__)
73#    include <hashset.h>
74#  else
75#    error what do I include to get hash_set?
76#  endif
77#else
78#  if defined (EH_NEW_HEADERS)
79#    include <set>
80#  else
81#    include <set.h>
82#  endif
83#endif
84
85#if !defined (EH_HASHED_CONTAINERS_IMPLEMENTED)
86typedef EH_STD::set<void*, EH_STD::less<void*> > allocation_set;
87#else
88
89USING_CSTD_NAME(size_t)
90
91struct hash_void {
92  size_t operator()(void* x) const { return (size_t)x; }
93};
94
95typedef EH_STD::hash_set<void*, ::hash_void, EH_STD::equal_to<void*> > allocation_set;
96#endif
97
98static allocation_set& alloc_set() {
99  static allocation_set s;
100  return s;
101}
102
103// Prevents infinite recursion during allocation
104static bool using_alloc_set = false;
105
106#if !defined (NO_FAST_ALLOCATOR)
107//
108//  FastAllocator -- speeds up construction of TestClass objects when
109// TESTCLASS_DEEP_DATA is enabled, and speeds up tracking of allocations
110// when the suite is run with the -t option.
111//
112class FastAllocator {
113public:
114  //FastAllocator() : mFree(0), mUsed(0) {}
115  static void *Allocate(size_t s) {
116    void *result = 0;
117
118    if (s <= sizeof(Block)) {
119      if (mFree != 0) {
120        result = mFree;
121        mFree = mFree->next;
122      }
123      else if (mBlocks != 0 && mUsed < kBlockCount) {
124        result =  (void*)&mBlocks[mUsed++];
125      }
126    }
127    return result;
128  }
129
130  static bool Free(void* p) {
131    Block* b = (Block*)p;
132    if (mBlocks == 0 || b < mBlocks || b >= mBlocks + kBlockCount)
133      return false;
134    b->next = mFree;
135    mFree = b;
136    return true;
137  }
138
139  struct Block;
140  friend struct Block;
141
142  enum {
143    // Number of fast allocation blocks to create.
144    kBlockCount = 1500,
145
146    // You may need to adjust this number for your platform.
147    // A good choice will speed tests. A bad choice will still work.
148    kMinBlockSize = 48
149  };
150
151  struct Block {
152    union {
153      Block *next;
154      double dummy; // fbp - force alignment
155      char dummy2[kMinBlockSize];
156    };
157  };
158
159  static Block* mBlocks;
160  static Block *mFree;
161  static size_t mUsed;
162};
163
164FastAllocator::Block *FastAllocator::mBlocks =
165(FastAllocator::Block*)EH_CSTD::calloc( sizeof(FastAllocator::Block), FastAllocator::kBlockCount );
166FastAllocator::Block *FastAllocator::mFree;
167size_t FastAllocator::mUsed;
168
169
170static FastAllocator gFastAllocator;
171#endif
172
173inline char* AllocateBlock(size_t s) {
174#if !defined (NO_FAST_ALLOCATOR)
175  char * const p = (char*)gFastAllocator.Allocate( s );
176  if (p != 0)
177    return p;
178#endif
179
180  return (char*)EH_CSTD::malloc(s);
181}
182
183static void* OperatorNew( size_t s ) {
184  if (!using_alloc_set) {
185    simulate_possible_failure();
186    ++alloc_count;
187  }
188
189  char *p = AllocateBlock(s);
190
191  if (gTestController.TrackingEnabled() &&
192      gTestController.LeakDetectionEnabled() &&
193      !using_alloc_set) {
194    using_alloc_set = true;
195    bool inserted = alloc_set().insert(p).second;
196    // Suppress warning about unused variable.
197    inserted;
198    EH_ASSERT(inserted);
199    using_alloc_set = false;
200  }
201
202  return p;
203}
204
205void* _STLP_CALL operator new(size_t s)
206#ifdef EH_DELETE_HAS_THROW_SPEC
207throw(EH_STD::bad_alloc)
208#endif
209{ return OperatorNew( s ); }
210
211#ifdef EH_USE_NOTHROW
212void* _STLP_CALL operator new(size_t size, const EH_STD::nothrow_t&) throw() {
213  try {
214    return OperatorNew( size );
215  }
216  catch (...) {
217    return 0;
218  }
219}
220#endif
221
222#if 1 /* defined (EH_VECTOR_OPERATOR_NEW) */
223void* _STLP_CALL operator new[](size_t size ) throw(EH_STD::bad_alloc) {
224  return OperatorNew( size );
225}
226
227#  ifdef EH_USE_NOTHROW
228void* _STLP_CALL operator new[](size_t size, const EH_STD::nothrow_t&) throw() {
229  try {
230    return OperatorNew(size);
231  }
232  catch (...) {
233    return 0;
234  }
235}
236#  endif
237
238void _STLP_CALL operator delete[](void* ptr) throw()
239{ operator delete( ptr ); }
240#endif
241
242#if defined (EH_DELETE_HAS_THROW_SPEC)
243void _STLP_CALL operator delete(void* s) throw()
244#else
245void _STLP_CALL operator delete(void* s)
246#endif
247{
248  if ( s != 0 ) {
249    if ( !using_alloc_set ) {
250      --alloc_count;
251
252      if ( gTestController.TrackingEnabled() && gTestController.LeakDetectionEnabled() ) {
253        using_alloc_set = true;
254        allocation_set::iterator p = alloc_set().find( (char*)s );
255        EH_ASSERT( p != alloc_set().end() );
256        alloc_set().erase( p );
257        using_alloc_set = false;
258      }
259    }
260# if ! defined (NO_FAST_ALLOCATOR)
261    if ( !gFastAllocator.Free( s ) )
262# endif
263      EH_CSTD::free(s);
264  }
265}
266
267
268/*===================================================================================
269  ClearAllocationSet  (private helper)
270
271  EFFECTS:  Empty the set of allocated blocks.
272====================================================================================*/
273void TestController::ClearAllocationSet() {
274  if (!using_alloc_set) {
275    using_alloc_set = true;
276    alloc_set().clear();
277    using_alloc_set = false;
278  }
279}
280
281
282bool TestController::ReportLeaked() {
283  EndLeakDetection();
284
285  EH_ASSERT( !using_alloc_set || (alloc_count == static_cast<int>(alloc_set().size())) );
286
287  if (alloc_count != 0 || object_count != 0) {
288    EH_STD::cerr<<"\nEH TEST FAILURE !\n";
289    PrintTestName(true);
290    if (alloc_count)
291      EH_STD::cerr << "ERROR : " << alloc_count << " outstanding allocations.\n";
292    if (object_count)
293      EH_STD::cerr << "ERROR : " << object_count << " non-destroyed objects.\n";
294    alloc_count = object_count = 0;
295    return true;
296  }
297  return false;
298}
299
300
301
302/*===================================================================================
303  PrintTestName
304
305  EFFECTS: Prints information about the current test. If err is false, ends with
306    an ellipsis, because the test is ongoing. If err is true an error is being
307    reported, and the output ends with an endl.
308====================================================================================*/
309
310void TestController::PrintTestName(bool err) {
311  if (current_container)
312    EH_STD::cerr<<"["<<current_container<<"] :";
313  EH_STD::cerr<<"testing "<<current_test <<" (" << current_test_category <<")";
314  if (err)
315    EH_STD::cerr<<EH_STD::endl;
316  else
317    EH_STD::cerr<<" ... ";
318}
319
320void TestController::ReportSuccess(int count) {
321  if (nc_verbose)
322    EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
323}
324
325long& TestController::Failure_threshold() {
326  static long failure_threshold = kNotInExceptionTest;
327  return failure_threshold;
328}
329