diff --git a/groups/bsl/bslma/bslma_allocator.cpp b/groups/bsl/bslma/bslma_allocator.cpp index 96529f416d..865809584e 100644 --- a/groups/bsl/bslma/bslma_allocator.cpp +++ b/groups/bsl/bslma/bslma_allocator.cpp @@ -4,6 +4,8 @@ #include BSLS_IDENT("$Id$ $CSID$") +#include + #include #include #include @@ -35,11 +37,27 @@ Allocator::~Allocator() // PROTECTED MANIPULATORS void *Allocator::do_allocate(std::size_t bytes, std::size_t) { - return this->allocate(bytes); + void *p = this->allocate(bytes); + + if (0 == bytes && 0 == p) { + // `do_allocate` is not allowed to return null, so return special ptr. + return bslma::MemoryResourceImpSupport::singularPointer(); + } + + return p; } -void Allocator::do_deallocate(void *p, std::size_t, std::size_t) +void Allocator::do_deallocate(void *p, std::size_t bytes, std::size_t) { + (void) bytes; // Not used in an OPT build + + if (bslma::MemoryResourceImpSupport::singularPointer() == p) { + // Special pointer indicates zero-byte `memory_resource` allocation. + BSLS_ASSERT(0 == bytes); + p = 0; // Change pointer to zero-byte expected value for `Allocator`. + } + + // Forward to `Allocator` interface. this->deallocate(p); } diff --git a/groups/bsl/bslma/bslma_allocator.t.cpp b/groups/bsl/bslma/bslma_allocator.t.cpp index 46ba710e03..dcde39b417 100644 --- a/groups/bsl/bslma/bslma_allocator.t.cpp +++ b/groups/bsl/bslma/bslma_allocator.t.cpp @@ -208,7 +208,7 @@ class my_Allocator : public bslma::Allocator { d_fun = 1; d_arg = s; ++d_allocateCount; - return this; + return s ? this : 0; } // MANIPULATORS @@ -246,21 +246,31 @@ class my_NewDeleteAllocator : public bslma::Allocator { ~my_NewDeleteAllocator() BSLS_KEYWORD_OVERRIDE { } void *allocate(size_type size) BSLS_KEYWORD_OVERRIDE { + ++d_count; + + if (0 == size) { + return 0; + } + unsigned *p = (unsigned *) operator new( size + bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT); *p = MAGIC; - ++d_count; return (char *) p + bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT; } void deallocate(void *address) BSLS_KEYWORD_OVERRIDE { + ++d_count; + + if (0 == address) { + return; + } + unsigned *p = (unsigned *) ((bsls::AlignmentUtil::MaxAlignedType *) address - 1); ASSERT(MAGIC == *p); *p = DELETED; - ++d_count; operator delete(p); } @@ -1131,6 +1141,32 @@ int main(int argc, char *argv[]) // The default implementation of `do_is_equal` cannot be tested because // it does call a virtual function that we can intercept. + // Test zero-byte allocations. + // TBD: This is a very basic and undocumented test. A more + // sophisticated test will be added as part of the fix for DRQS + // 176364960. + my_NewDeleteAllocator myA; + + // Zero-byte allocation through `bslma::Allocator` interface. + void *p1 = myA.allocate(0); + ASSERT(0 == p1); + ASSERT(1 == myA.getCount()); // Increments even for zero bytes + + myA.deallocate(p1); // Can deallocate null pointer + ASSERT(2 == myA.getCount()); // Increments even for zero bytes + + // Zero-byte allocation through `bsl::memory_resource` interface. + bsl::memory_resource& myR = myA; + void *p2 = myR.allocate(0); + ASSERT(0 != p2); // Non-zero return + ASSERT(3 == myA.getCount()); // Increments even for zero bytes + + ASSERT(myR.allocate(0) == p2); // Returns same pointer every time + ASSERT(4 == myA.getCount()); + + myR.deallocate(p2, 0); // Can deallocate zero-size block + ASSERT(5 == myA.getCount()); // Increments even for zero bytes + } break; default: { diff --git a/groups/bsl/bslma/bslma_memoryresourceimpsupport.cpp b/groups/bsl/bslma/bslma_memoryresourceimpsupport.cpp new file mode 100644 index 0000000000..369d8d6728 --- /dev/null +++ b/groups/bsl/bslma/bslma_memoryresourceimpsupport.cpp @@ -0,0 +1,40 @@ +// bslma_memoryresourceimpsupport.cpp -*-C++-*- + +#include + +#include // for testing only + +#include +BSLS_IDENT_RCSID(bslma_memoryresourceimpsupport_cpp, "$Id$ $CSID$") + +namespace BloombergLP { +namespace bslma { + +// CLASS DATA + +// The `d_data` member of the singular object contains a bit pattern that is +// easily recognized as *bad* data in the debugger. +const MemoryResourceImpSupport_AlignedData +MemoryResourceImpSupport::s_singularObject = { + 0xDeadBeefCafeF00dULL, + bsls::AlignmentUtil::MaxAlignedType() +}; + +} // close package namespace +} // close enterprise namespace + +// ---------------------------------------------------------------------------- +// Copyright 2024 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bsl/bslma/bslma_memoryresourceimpsupport.h b/groups/bsl/bslma/bslma_memoryresourceimpsupport.h new file mode 100644 index 0000000000..5520b9f2de --- /dev/null +++ b/groups/bsl/bslma/bslma_memoryresourceimpsupport.h @@ -0,0 +1,241 @@ +// bslma_memoryresourceimpsupport.h -*-C++-*- +#ifndef INCLUDED_BSLMA_MEMORYRESOURCEIMPSUPPORT +#define INCLUDED_BSLMA_MEMORYRESOURCEIMPSUPPORT + +#include +BSLS_IDENT("$Id: $") + +//@PURPOSE: Provide support for implementing memory resources +// +//@CLASSES: +// bslma::MemoryResourceImpSupport - namespace for resource support functions +// +//@SEE_ALSO: bslma_memoryresource, bslma_allocator +// +//@DESCRIPTION: This component provides support functions and types for +// implementing a class derived from `bsl::memory_resource`. Currently, it +// provides a utility function, `singularPointer`, that provides a pointer +// suitable for an `allocate` function to return when a zero-sized allocation +// is requested. +// +///Usage +///----- +// This section illustrates intended use of this component. +// +///Example 1: Write a `memory_resource` using `singularPointer` +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// In this example, we derive a `NewDeleteResource` class, derived from +// `bsl::memory_resource`, that allocates and deallocate using `operator new` +// and `operator delete`, respectively, and uses `singularPointer()` as a +// special value when allocating and allocating zero bytes. +// +// First, we define the class interface, which implements the protected virtual +// functions `do_allocate`, `do_deallocate`, and `do_is_equal`: +// ``` +// #include +// #include +// #include +// #include +// +// /// Memory resource that allocates using `operator new`. +// class NewDeleteResource : public bsl::memory_resource { +// protected: +// // PROTECTED MANIPULATORS +// +// /// Return the specified `size` bytes with the specified `alignment` +// /// allocated from the global heap using `operator new`. The behavior +// /// is undefined unless the specified `alignment` is less than or equal +// /// to the maximum platform alignment. +// void *do_allocate(std::size_t size, std::size_t alignment) +// BSLS_KEYWORD_OVERRIDE; +// +// /// Deallocate the memory block specified by `p` having the specified +// /// `size` and `alignment` from the global heap using `operator +// /// delete`. The behavior is undefined unless `p` was returned from a +// /// previous call to `allocate`, using the same `size` and `alignment`. +// void do_deallocate(void *p, std::size_t size, std::size_t alignment) +// BSLS_KEYWORD_OVERRIDE; +// +// /// Return `true` if `x` is a `NewDeleteResource` and `false` +// /// otherwise. +// bool do_is_equal(const bsl::memory_resource& x) const +// BSLS_KEYWORD_NOEXCEPT BSLS_KEYWORD_OVERRIDE; +// }; +// ``` +// Next, we implement the `do_allocate` method, which forwards most requests to +// `operator new`. Section [basic.stc.dynamic.allocation] of the C++ standard, +// however states that the return value of an allocation function when the +// requested size is zero is a *non-null* pointer to a suitably aligned block +// of storage, so we use `singularPointer` to provide the address in such +// circumstances: +// ``` +// #include // `align_val_t` and aligned `new` +// +// void *NewDeleteResource::do_allocate(std::size_t size, +// std::size_t alignment) +// { +// if (0 == size) { +// return bslma::MemoryResourceImpSupport::singularPointer(); +// } +// +// #ifdef __cpp_aligned_new +// return ::operator new(size, std::align_val_t(alignment)); +// #else +// (void) alignment; +// BSLS_ASSERT(alignment <= bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT); +// return ::operator new(size); +// #endif +// } +// ``` +// Next, we implement the `do_deallocate` method, which forwards most requests +// to `operator delete`, but does nothing if the incoming pointer was the +// result of a zero-sized allocation: +// ``` +// void NewDeleteResource::do_deallocate(void *p, +// std::size_t size, +// std::size_t alignment) +// { +// (void) size; // Not used in OPT build +// +// if (bslma::MemoryResourceImpSupport::singularPointer() == p) { +// BSLS_ASSERT(0 == size); +// return; +// } +// +// BSLS_ASSERT(0 < size); +// #ifdef __cpp_aligned_new +// ::operator delete(p, std::align_val_t(alignment)); +// #else +// (void) (size, alignment); +// BSLS_ASSERT(alignment <= bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT); +// ::operator delete(p); +// #endif +// } +// ``` +// Next, we complete the implementation with `do_is_equal`. All instances of +// `NewDeleteResource` compare equal, so we need only check that the argument +// is a `NewDeleteResource`. As a short-cut, we check if the address of `b` is +// the same as `this`, to quickly catch the common case that they are both +// pointers to the singleton object: +// ``` +// bool NewDeleteResource::do_is_equal(const bsl::memory_resource& b) const +// BSLS_KEYWORD_NOEXCEPT +// { +// return this == &b || 0 != dynamic_cast(&b); +// } +// ``` +// Now, when we call `allocate`, we observe that each non-zero allocation +// yields a different pointer, whereas all of the zero allocations yield the +// same address. +// ``` +// int main() +// { +// NewDeleteResource r; +// void *p1 = r.allocate(1, 1); +// void *p2 = r.allocate(8); +// assert(p1 != p2); +// +// void *p3 = r.allocate(0, 1); +// void *p4 = r.allocate(0, 4); +// assert(p3 != p1); +// assert(p3 != p2); +// assert(p3 == p4); +// ``` +// Finally, when we deallocate memory, we would expect nothing to happen when +// deallocating the zero-sized blocks `p3` and `p4`: +// ``` +// r.deallocate(p1, 1, 1); +// r.deallocate(p2, 8); +// r.deallocate(p3, 0, 1); +// r.deallocate(p4, 0, 4); +// } +// ``` + +#include + +#include +#include + +namespace BloombergLP { +namespace bslma { + + // =========================================== + // struct MemoryResourceImpSupport_AlignedData + // =========================================== + +/// COMPONENT-PRIVATE struct -- DO NOT USE. Maximally aligned `struct` holding +/// an unsigned 64-bit integer. This `struct` is exposed in the component +/// header so that `MemoryResourceImpSupport::singularPointer` can be +/// implemented as `constexpr` and `inline`. +struct MemoryResourceImpSupport_AlignedData { + unsigned long long d_data; + bsls::AlignmentUtil::MaxAlignedType d_alignment; +}; + + // ============================== + // class MemoryResourceImpSupport + // ============================== + +/// Namespace for functions that support implementing a memory resource. +struct MemoryResourceImpSupport { + + private: + + // PRIVATE CLASS DATA + + /// Singleton used to produce singular address distinct from all other + /// addresses. + static const MemoryResourceImpSupport_AlignedData s_singularObject; + + public: + // CLASS METHODS + + /// Return a maximally aligned, non-null pointer that cannnot organically + /// result from any operation other than calling this function; i.e., it is + /// a *singular* pointer. Within a single program execution, no other + /// current or future object, of any storage duration, will have an address + /// matching this function's return value. The returned address may be + /// used by an allocation function to indicate a *non-allocation*, e.g., + /// the result of allocating zero bytes. The behavior is undefined if the + /// returned pointer is dereferenced. Note that the same address is + /// returned each time this function is called. + BSLS_KEYWORD_CONSTEXPR static void *singularPointer(); +}; + +// ============================================================================ +// TEMPLATE AND INLINE FUNCTION IMPLEMENTATIONS +// ============================================================================ + +// CLASS METHODS + +inline BSLS_KEYWORD_CONSTEXPR void *MemoryResourceImpSupport::singularPointer() +{ + // The singleton object is 'const', so any attempt to construct an object + // through the returned pointer should result in a protection error. + return const_cast(&s_singularObject.d_data); +} + +} // close package namespace + + +// FREE OPERATORS + +} // close enterprise namespace + +#endif // ! defined(INCLUDED_BSLMA_MEMORYRESOURCEIMPSUPPORT) + +// ---------------------------------------------------------------------------- +// Copyright 2024 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bsl/bslma/bslma_memoryresourceimpsupport.t.cpp b/groups/bsl/bslma/bslma_memoryresourceimpsupport.t.cpp new file mode 100644 index 0000000000..3c01e51f0f --- /dev/null +++ b/groups/bsl/bslma/bslma_memoryresourceimpsupport.t.cpp @@ -0,0 +1,430 @@ +// bslma_memoryresourceimpsupport.t.cpp -*-C++-*- + +#include +#include + +#include +#include +#include +#include + +#include // `printf` +#include // `atoi` +#include // `align_val_t` and aligned `new` + +#ifdef BDE_VERIFY +// Suppress some pedantic bde_verify checks in this test driver +#pragma bde_verify -FD01 // Function declaration requires contract +#pragma bde_verify -FD03 // Parameter not documented in function contract +#pragma bde_verify -FABC01 // Function not in alphabetical order +#pragma bde_verify -TP19 // Missing or malformed standard test driver section +#pragma bde_verify -TY02 // Template parameter uses single-letter name +#endif + +using std::printf; +using std::fprintf; +using std::fflush; +using namespace BloombergLP; + +// ============================================================================ +// TEST PLAN +// ---------------------------------------------------------------------------- +// Overview +// -------- +// +// This test driver simply has one test case for each independent utility +// provided by the component. +// ---------------------------------------------------------------------------- +// [ 2] void *singularPointer(); +// ---------------------------------------------------------------------------- +// [ 1] BREATHING TEST +// [ 3] USAGE EXAMPLES +// [-1] CONCERN: `singularPointer()` points to garbage +// [-1] CONCERN: `singularPointer()` points to non-writable memory +// ---------------------------------------------------------------------------- + +// ============================================================================ +// STANDARD BSL ASSERT TEST FUNCTION +// ---------------------------------------------------------------------------- + +namespace { + +int testStatus = 0; + +void aSsErT(bool condition, const char *message, int line) +{ + if (condition) { + printf("Error " __FILE__ "(%d): %s (failed)\n", line, message); + + if (0 <= testStatus && testStatus <= 100) { + ++testStatus; + } + } +} + +// ============================================================================ +// STANDARD BSL TEST DRIVER MACRO ABBREVIATIONS +// ---------------------------------------------------------------------------- + +#define ASSERT BSLS_BSLTESTUTIL_ASSERT +#define ASSERTV BSLS_BSLTESTUTIL_ASSERTV + +#define Q BSLS_BSLTESTUTIL_Q // Quote identifier literally. +#define P BSLS_BSLTESTUTIL_P // Print identifier and value. +#define P_ BSLS_BSLTESTUTIL_P_ // P(X) without '\n'. +#define T_ BSLS_BSLTESTUTIL_T_ // Print a tab (w/o newline). +#define L_ BSLS_BSLTESTUTIL_L_ // current Line number + +// ============================================================================ +// SEMI-STANDARD NEGATIVE-TESTING MACROS +// ---------------------------------------------------------------------------- + +#define ASSERT_SAFE_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_PASS(EXPR) +#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR) +#define ASSERT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_PASS(EXPR) +#define ASSERT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_FAIL(EXPR) +#define ASSERT_OPT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_PASS(EXPR) +#define ASSERT_OPT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR) + +// ============================================================================ +// VERBOSITY +// ---------------------------------------------------------------------------- + +int verbose = 0; +int veryVerbose = 0; +int veryVeryVerbose = 0; +int veryVeryVeryVerbose = 0; // For test allocators + +// ============================================================================ +// GLOBAL TYPEDEFS/CONSTANTS FOR TESTING +// ---------------------------------------------------------------------------- + +typedef bslma::MemoryResourceImpSupport Util; + +const std::ptrdiff_t k_MAX_ALIGNMENT = bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT; + +// ============================================================================ +// USAGE EXAMPLES +// ---------------------------------------------------------------------------- + +///Example 1: Write a `memory_resource` using `singularPointer` +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// In this example, we derive a `NewDeleteResource` class, derived from +// `bsl::memory_resource`, that allocates and deallocate using `operator new` +// and `operator delete`, respectively, and uses `singularPointer()` as a +// special value when allocating and allocating zero bytes. +// +// First, we define the class interface, which implements the protected virtual +// functions `do_allocate`, `do_deallocate`, and `do_is_equal`: +// ``` +// #include +// #include +// #include +// #include + + /// Memory resource that allocates using `operator new`. + class NewDeleteResource : public bsl::memory_resource { + protected: + // PROTECTED MANIPULATORS + + /// Return the specified `size` bytes with the specified `alignment` + /// allocated from the global heap using `operator new`. The behavior + /// is undefined unless the specified `alignment` is less than or equal + /// to the maximum platform alignment. + void *do_allocate(std::size_t size, std::size_t alignment) + BSLS_KEYWORD_OVERRIDE; + + /// Deallocate the memory block specified by `p` having the specified + /// `size` and `alignment` from the global heap using `operator + /// delete`. The behavior is undefined unless `p` was returned from a + /// previous call to `allocate`, using the same `size` and `alignment`. + void do_deallocate(void *p, std::size_t size, std::size_t alignment) + BSLS_KEYWORD_OVERRIDE; + + /// Return `true` if `x` is a `NewDeleteResource` and `false` + /// otherwise. + bool do_is_equal(const bsl::memory_resource& x) const + BSLS_KEYWORD_NOEXCEPT BSLS_KEYWORD_OVERRIDE; + }; +// ``` +// Next, we implement the `do_allocate` method, which forwards most requests to +// `operator new`. Section [basic.stc.dynamic.allocation] of the C++ standard, +// however states that the return value of an allocation function when the +// requested size is zero is a *non-null* pointer to a suitably aligned block +// of storage, so we use `singularPointer` to provide the address in such +// circumstances: +// ``` +// #include // `align_val_t` and aligned `new` + + void *NewDeleteResource::do_allocate(std::size_t size, + std::size_t alignment) + { + if (0 == size) { + return bslma::MemoryResourceImpSupport::singularPointer(); + } + + #ifdef __cpp_aligned_new + return ::operator new(size, std::align_val_t(alignment)); + #else + (void) alignment; + BSLS_ASSERT(alignment <= bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT); + return ::operator new(size); + #endif + } +// ``` +// Next, we implement the `do_deallocate` method, which forwards most requests +// to `operator delete`, but does nothing if the incoming pointer was the +// result of a zero-sized allocation: +// ``` + void NewDeleteResource::do_deallocate(void *p, + std::size_t size, + std::size_t alignment) + { + (void) size; // Not used in OPT build + + if (bslma::MemoryResourceImpSupport::singularPointer() == p) { + BSLS_ASSERT(0 == size); + return; + } + + BSLS_ASSERT(0 < size); + #ifdef __cpp_aligned_new + ::operator delete(p, std::align_val_t(alignment)); + #else + (void) (size, alignment); + BSLS_ASSERT(alignment <= bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT); + ::operator delete(p); + #endif + } +// ``` +// Next, we complete the implementation with `do_is_equal`. All instances of +// `NewDeleteResource` compare equal, so we need only check that the argument +// is a `NewDeleteResource`. As a short-cut, we check if the address of `b` is +// the same as `this`, to quickly catch the common case that they are both +// pointers to the singleton object: +// ``` + bool NewDeleteResource::do_is_equal(const bsl::memory_resource& b) const + BSLS_KEYWORD_NOEXCEPT + { + return this == &b || 0 != dynamic_cast(&b); + } +// ``` +// Now, when we call `allocate`, we observe that each non-zero allocation +// yields a different pointer, whereas all of the zero allocations yield the +// same address. +// ``` + void usageExample1() + { + NewDeleteResource r; + void *p1 = r.allocate(1, 1); + void *p2 = r.allocate(8); + ASSERT(p1 != p2); + + void *p3 = r.allocate(0, 1); + void *p4 = r.allocate(0, 4); + ASSERT(p3 != p1); + ASSERT(p3 != p2); + ASSERT(p3 == p4); +// ``` +// Finally, when we deallocate memory, we would expect nothing to happen when +// deallocating the zero-sized blocks `p3` and `p4`: +// ``` + r.deallocate(p1, 1, 1); + r.deallocate(p2, 8); + r.deallocate(p3, 0, 1); + r.deallocate(p4, 0, 4); + } +// ``` + +} // close unnamed namespace + +// ============================================================================ +// MAIN PROGRAM +// ---------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int test = argc > 1 ? std::atoi(argv[1]) : 0; + verbose = argc > 2; + veryVerbose = argc > 3; + veryVeryVerbose = argc > 4; + veryVeryVeryVerbose = argc > 5; + + printf("TEST " __FILE__ " CASE %d\n", test); + + switch (test) { case 0: // Zero is always the leading case. + case 3: { + // -------------------------------------------------------------------- + // USAGE EXAMPLES + // + // Concerns: + // 1. That the usage examples shown in the component-level + // documentation compile and run as described. + // + // Plan: + // 1. Copy the usage examples from the component header, changing + // `assert` to `ASSERT` and execute them. + // + // Testing: + // USAGE EXAMPLES + // -------------------------------------------------------------------- + + if (verbose) printf("\nUSAGE EXAMPLES" + "\n==============\n"); + + usageExample1(); + + } break; + + case 2: { + // -------------------------------------------------------------------- + // TESTING `singularPointer` + // + // Concerns: + // 1. A call to `Util::singularPointer` returns a non-const, non-null + // pointer. + // + // 2. The same pointer is returned every time. + // + // 3. The address stored in the pointer has (at least) the maximum + // platform alignment. + // + // 4. In C++11 and later, `Util::singularPointer()` is a compile-time + // constant. + // + // Plan: + // 1. Call `Util::singularPointer` and store the result in `void *` + // variable. Verify that the resulting pointer is not null. (C-1) + // + // 2. Call `Util::singularPointer` a severl more times and verify that + // the returned value is always the same as the first one. (C-2) + // + // 3. Verify that the pointer returned in step 1 is aligned + // appropriately. (C-3) + // + // 4. Initialize a `constexpr` pointer to the return value of calling + // `singlePointer` and verify that it compiles succesfully. (C-4) + // + // Testing: + // void *singularPointer(); + // -------------------------------------------------------------------- + + if (verbose) printf("\nTESTING `singularPointer`" + "\n=========================\n"); + + void *p = Util::singularPointer(); + ASSERT(0 != p); + + for (int i = 0; i < 5; ++i) { + ASSERT(Util::singularPointer() == p); + } + + // Test alignment + ASSERT(0 == bsls::AlignmentUtil::calculateAlignmentOffset(p, + k_MAX_ALIGNMENT)); + + // Test that it is evaluated at compile time + BSLS_KEYWORD_CONSTEXPR static void *const sp = Util::singularPointer(); + ASSERT(sp == p); + + } break; + + case 1: { + // -------------------------------------------------------------------- + // BREATHING TEST + // This case exercises (but does not fully test) basic functionality. + // + // Concerns: + // 1. The class is sufficiently functional to enable comprehensive + // testing in subsequent test cases. + // + // Plan: + // 1. Execute each method to verify functionality for simple cases. + // + // Testing: + // BREATHING TEST + // -------------------------------------------------------------------- + + if (verbose) printf("\nBREATHING TEST" + "\n==============\n"); + + // Test that `singularPointer` returns a non-null pointer + void *p1 = Util::singularPointer(); + ASSERT(0 != p1); + + // Test that `singularPointer` returns the same value each time. + void *p2 = Util::singularPointer(); + ASSERT(0 != p2); + ASSERT(p1 == p2); + + } break; + + case -1: { + // -------------------------------------------------------------------- + // NEGATIVE TESTS FOR `singularPointer` + // + // Concerns: + // 1. Dereferencing the pointer returned by `Util::singlularPointer` + // yields garbage on read. + // + // 2. Dereferencing the pointer returned by `Util::singlularPointer` + // results in a segmentation violation on write. + // + // Plan + // 1. Read an integral value through the (suitably cast) result of + // `Util::singularPointer`. Verify manually that there is no useful + // data there (ideally not zeros). 2 Cast + // + // 2. Write a byte through the (suitably cast) result of + // `Util::singularPointer`. Verify that a segmentation violation + // results. + // + // Testing: + // CONCERN: `singularPointer()` points to garbage + // CONCERN: `singularPointer()` points to non-writable memory + // -------------------------------------------------------------------- + + if (verbose) printf("\nNEGATIVE TESTS FOR `singularPointer`" + "\n====================================\n"); + + // Print the first two integers at the return address + const unsigned int* p = + static_cast(Util::singularPointer()); + printf("Data at `singularPointer()`: 0x%x 0x%x\n", p[0], p[1]); + + if (verbose) printf("Write to memory at `singularPointer()`\n"); + fflush(stdout); + *static_cast(Util::singularPointer()) = 1; + if (verbose) printf("Write succeeded\n"); + ASSERT(false && "Expected SEGV"); + + } break; + + default: { + fprintf(stderr, "WARNING: CASE `%d' NOT FOUND.\n", test); + testStatus = -1; + } + } + + if (testStatus > 0) { + fprintf(stderr, "Error, non-zero test status = %d.\n", testStatus); + } + + return testStatus; +} + +// ---------------------------------------------------------------------------- +// Copyright 2024 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bsl/bslma/package/bslma.mem b/groups/bsl/bslma/package/bslma.mem index 602dec88d9..047d561d03 100644 --- a/groups/bsl/bslma/package/bslma.mem +++ b/groups/bsl/bslma/package/bslma.mem @@ -38,6 +38,7 @@ bslma_managedptr_members bslma_managedptr_pairproxy bslma_managedptrdeleter bslma_memoryresource +bslma_memoryresourceimpsupport bslma_newdeleteallocator bslma_polymorphicallocator bslma_polymorphicallocator_cpp03