Wolfgang Thallergcc 15.1.0ada185d1 year agoHistory
374 lines · 12.9 KB · D
RawDownload
/** * D binding to C++ std::allocator. * * Copyright: Copyright (c) 2019 D Language Foundation * License: Distributed under the *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). *    (See accompanying file LICENSE) * Authors:   Manu Evans * Source:    $(DRUNTIMESRC core/stdcpp/allocator.d) */ module core.stdcpp.allocator; import core.stdcpp.new_;import core.stdcpp.xutility : StdNamespace, __cpp_sized_deallocation, __cpp_aligned_new; extern(C++, (StdNamespace)): /** * Allocators are classes that define memory models to be used by some parts of * the C++ Standard Library, and most specifically, by STL containers. */extern(C++, class)struct allocator(T){    static assert(!is(T == const), "The C++ Standard forbids containers of const elements because allocator!(const T) is ill-formed.");    static assert(!is(T == immutable), "immutable is not representable in C++");    static assert(!is(T == class), "Instantiation with `class` is not supported; D can't mangle the base (non-pointer) type of a class. Use `extern (C++, class) struct T { ... }` instead.");extern(D):     ///    this(U)(ref allocator!U) {}     ///    alias size_type = size_t;    ///    alias difference_type = ptrdiff_t;    ///    alias pointer = T*;    ///    alias value_type = T;     ///    enum propagate_on_container_move_assignment = true;    ///    enum is_always_equal = true;     ///    alias rebind(U) = allocator!U;     version (CppRuntime_Microsoft)    {        import core.stdcpp.xutility : _MSC_VER;         ///        T* allocate(size_t count) @nogc        {            static if (_MSC_VER <= 1800)            {                import core.stdcpp.xutility : _Xbad_alloc;                if (count == 0)                    return null;                void* mem;                if ((size_t.max / T.sizeof < count) || (mem = __cpp_new(count * T.sizeof)) is null)                    _Xbad_alloc();                return cast(T*)mem;            }            else            {                enum _Align = _New_alignof!T;                 static size_t _Get_size_of_n(T)(const size_t _Count)                {                    static if (T.sizeof == 1)                        return _Count;                    else                    {                        enum size_t _Max_possible = size_t.max / T.sizeof;                        return _Max_possible < _Count ? size_t.max : _Count * T.sizeof;                    }                }                 const size_t _Bytes = _Get_size_of_n!T(count);                if (_Bytes == 0)                    return null;                 static if (!__cpp_aligned_new || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)                {                    version (INTEL_ARCH)                    {                        if (_Bytes >= _Big_allocation_threshold)                            return cast(T*)_Allocate_manually_vector_aligned(_Bytes);                    }                    return cast(T*)__cpp_new(_Bytes);                }                else                {                    size_t _Passed_align = _Align;                    version (INTEL_ARCH)                    {                        if (_Bytes >= _Big_allocation_threshold)                            _Passed_align = _Align < _Big_allocation_alignment ? _Big_allocation_alignment : _Align;                    }                    return cast(T*)__cpp_new_aligned(_Bytes, cast(align_val_t)_Passed_align);                }            }        }        ///        void deallocate(T* ptr, size_t count) @nogc        {            static if (_MSC_VER <= 1800)            {                __cpp_delete(ptr);            }            else            {                // this is observed from VS2017                void* _Ptr = ptr;                size_t _Bytes = T.sizeof * count;                 enum _Align = _New_alignof!T;                static if (!__cpp_aligned_new || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)                {                    version (INTEL_ARCH)                    {                        if (_Bytes >= _Big_allocation_threshold)                            _Adjust_manually_vector_aligned(_Ptr, _Bytes);                    }                    static if (_MSC_VER <= 1900)                        __cpp_delete(ptr);                    else                        __cpp_delete_size(_Ptr, _Bytes);                }                else                {                    size_t _Passed_align = _Align;                    version (INTEL_ARCH)                    {                        if (_Bytes >= _Big_allocation_threshold)                            _Passed_align = _Align < _Big_allocation_alignment ? _Big_allocation_alignment : _Align;                    }                    __cpp_delete_size_aligned(_Ptr, _Bytes, cast(align_val_t)_Passed_align);                }            }        }         ///        enum size_t max_size = size_t.max / T.sizeof;    }    else version (CppRuntime_GNU)    {        ///        T* allocate(size_t count, const(void)* = null) @nogc        {//            if (count > max_size)//                std::__throw_bad_alloc();             static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)                return cast(T*)__cpp_new_aligned(count * T.sizeof, cast(align_val_t)T.alignof);            else                return cast(T*)__cpp_new(count * T.sizeof);        }        ///        void deallocate(T* ptr, size_t count) @nogc        {            // NOTE: GCC doesn't seem to use the sized delete when it's available...             static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)                __cpp_delete_aligned(cast(void*)ptr, cast(align_val_t)T.alignof);            else                __cpp_delete(cast(void*)ptr);        }         ///        enum size_t max_size = (ptrdiff_t.max < size_t.max ? cast(size_t)ptrdiff_t.max : size_t.max) / T.sizeof;    }    else version (CppRuntime_LLVM)    {        ///        T* allocate(size_t count, const(void)* = null) @nogc        {//            if (count > max_size)//                __throw_length_error("allocator!T.allocate(size_t n) 'n' exceeds maximum supported size");             static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)                return cast(T*)__cpp_new_aligned(count * T.sizeof, cast(align_val_t)T.alignof);            else                return cast(T*)__cpp_new(count * T.sizeof);        }        ///        void deallocate(T* ptr, size_t count) @nogc        {            static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)            {                static if (__cpp_sized_deallocation)                    return __cpp_delete_size_aligned(cast(void*)ptr, count * T.sizeof, cast(align_val_t)T.alignof);                else                    return __cpp_delete_aligned(cast(void*)ptr, cast(align_val_t)T.alignof);            }            else static if (__cpp_sized_deallocation)                return __cpp_delete_size(cast(void*)ptr, count * T.sizeof);            else                return __cpp_delete(cast(void*)ptr);        }         ///        enum size_t max_size = size_t.max / T.sizeof;    }    else    {        static assert(false, "C++ runtime not supported");    }} ///extern(C++, (StdNamespace))struct allocator_traits(Alloc){    import core.internal.traits : isTrue;     ///    alias allocator_type = Alloc;    ///    alias value_type = allocator_type.value_type;    ///    alias size_type = allocator_type.size_type;    ///    alias difference_type = allocator_type.difference_type;    ///    alias pointer = allocator_type.pointer;     ///    enum propagate_on_container_copy_assignment = isTrue!(allocator_type, "propagate_on_container_copy_assignment");    ///    enum propagate_on_container_move_assignment = isTrue!(allocator_type, "propagate_on_container_move_assignment");    ///    enum propagate_on_container_swap = isTrue!(allocator_type, "propagate_on_container_swap");    ///    enum is_always_equal = isTrue!(allocator_type, "is_always_equal");     ///    template rebind_alloc(U)    {        static if (__traits(hasMember, allocator_type, "rebind"))            alias rebind_alloc = allocator_type.rebind!U;        else            alias rebind_alloc = allocator_type!U;    }    ///    alias rebind_traits(U) = allocator_traits!(rebind_alloc!U);     ///    static size_type max_size()(auto ref allocator_type a)    {        static if (__traits(hasMember, allocator_type, "max_size"))            return a.max_size();        else            return size_type.max / value_type.sizeof;    }     ///    static allocator_type select_on_container_copy_construction()(auto ref allocator_type a)    {        static if (__traits(hasMember, allocator_type, "select_on_container_copy_construction"))            return a.select_on_container_copy_construction();        else            return a;    }} private: // MSVC has some bonus complexity!version (CppRuntime_Microsoft){    // some versions of VS require a `* const` pointer mangling hack    // we need a way to supply the target VS version to the compile    version = NeedsMangleHack;     version (X86)        version = INTEL_ARCH;    version (X86_64)        version = INTEL_ARCH;     // HACK: should we guess _DEBUG for `debug` builds?    version (_DEBUG)        enum _DEBUG = true;    else version (NDEBUG)        enum _DEBUG = false;    else    {        import core.stdcpp.xutility : __CXXLIB__;        enum _DEBUG = __CXXLIB__.length && 'd' == __CXXLIB__[$-1]; // libcmtd, msvcrtd    }     enum _New_alignof(T) = T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ? T.alignof : __STDCPP_DEFAULT_NEW_ALIGNMENT__;     version (INTEL_ARCH)    {        enum size_t _Big_allocation_threshold = 4096;        enum size_t _Big_allocation_alignment = 32;        enum isPowerOf2(size_t v) = v && !(v & (v - 1));         static assert(2 * (void*).sizeof <= _Big_allocation_alignment, "Big allocation alignment should at least match vector register alignment");        static assert(isPowerOf2!_Big_allocation_alignment, "Big allocation alignment must be a power of two");        static assert(size_t.sizeof == (void*).sizeof, "uintptr_t is not the same size as size_t");         // NOTE: this must track `_DEBUG` macro used in C++...        static if (_DEBUG)            enum size_t _Non_user_size = 2 * (void*).sizeof + _Big_allocation_alignment - 1;        else            enum size_t _Non_user_size = (void*).sizeof + _Big_allocation_alignment - 1;         version (Win64)            enum size_t _Big_allocation_sentinel = 0xFAFAFAFAFAFAFAFA;        else            enum size_t _Big_allocation_sentinel = 0xFAFAFAFA;         extern(D) // Template so it gets compiled according to _DEBUG.        void* _Allocate_manually_vector_aligned()(const size_t _Bytes) @nogc        {            size_t _Block_size = _Non_user_size + _Bytes;            if (_Block_size <= _Bytes)                _Block_size = size_t.max;             const size_t _Ptr_container = cast(size_t)__cpp_new(_Block_size);            if (!(_Ptr_container != 0))                assert(false, "invalid argument");            void* _Ptr = cast(void*)((_Ptr_container + _Non_user_size) & ~(_Big_allocation_alignment - 1));            (cast(size_t*)_Ptr)[-1] = _Ptr_container;             static if (_DEBUG)                (cast(size_t*)_Ptr)[-2] = _Big_allocation_sentinel;            return (_Ptr);        }         extern(D) // Template so it gets compiled according to _DEBUG.        void _Adjust_manually_vector_aligned()(ref void* _Ptr, ref size_t _Bytes) pure nothrow @nogc        {            _Bytes += _Non_user_size;             const size_t* _Ptr_user = cast(size_t*)_Ptr;            const size_t _Ptr_container = _Ptr_user[-1];             // If the following asserts, it likely means that we are performing            // an aligned delete on memory coming from an unaligned allocation.            static if (_DEBUG)                assert(_Ptr_user[-2] == _Big_allocation_sentinel, "invalid argument");             // Extra paranoia on aligned allocation/deallocation; ensure _Ptr_container is            // in range [_Min_back_shift, _Non_user_size]            static if (_DEBUG)                enum size_t _Min_back_shift = 2 * (void*).sizeof;            else                enum size_t _Min_back_shift = (void*).sizeof;             const size_t _Back_shift = cast(size_t)_Ptr - _Ptr_container;            if (!(_Back_shift >= _Min_back_shift && _Back_shift <= _Non_user_size))                assert(false, "invalid argument");            _Ptr = cast(void*)_Ptr_container;        }    }}version (CppRuntime_LLVM){    // Helper for container swap    package(core.stdcpp) void __swap_allocator(Alloc)(ref Alloc __a1, ref Alloc __a2)    {        import core.internal.lifetime : swap;         static if (allocator_traits!Alloc.propagate_on_container_swap)            swap(__a1, __a2);    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t