Alien-libpanda

 view release on metacpan or  search on metacpan

src/panda/basic_string.h  view on Meta::CPAN

#pragma once
#include "hash.h"
#include "from_chars.h"
#include "string_view.h"
#include <string>
#include <limits>
#include <memory>
#include <iosfwd>
#include <utility>   // swap
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <iterator>
#include <stdexcept>
#include <initializer_list>

namespace panda {

/*
 * panda::string is an std::string drop-in replacement which has the same API but is much more flexible and allows for behaviors that in other case
 * would lead to a lot of unnecessary allocations/copying.
 *
 * Most important features are:
 *
 * - Copy-On-Write support (COW).
 *       Not only when assigning the whole string but also when any form of substr() is applied.
 *       If any of the COW copies is trying to change, it detaches from the original string, copying the content it needs.
 * - External static string support.
 *       Can be created from external static(immortal) data without allocating memory and copying it.
 *       String will be allocated and copied when you first try to change it.
 *       For example if a function accepts string, you may pass it just a string literal "hello" and nothing is allocated or copied and even the length
 *       is counted in compile time.
 * - External dynamic string support.
 *       Can be created from external dynamic(mortal) data without allocating memory and copying it.
 *       External data will be deallocated via custom destructor when the last string that references to the external data is lost.
 *       As for any other subtype of panda::string copying/substr/etc of such string does not copy anything
 * - SSO support (small string optimization). Up to 23 bytes for 64bit / 11 bytes for 32bit.
 *       It does not mean that all strings <= MAX_SSO_CHARS are in SSO mode. SSO mode is used only when otherwise panda::string would have to allocate
 *       and copy something. For example if you call "otherstr = mystr.substr(offset, len)", then otherstr will not use SSO even if len <= MAX_SSO_CHARS,
 *       because it prefers to do nothing (COW-mode) instead of copying content to SSO location.
 * - Support for getting r/w internal data buffer to manually fill it.
 *       The content of other strings which shares the data with current string will not be affected.
 * - Reallocate instead of deallocate/allocate when possible, which in many cases is much faster
 * - Supports auto convertations between basic_strings with different Allocator template parameter without copying and allocating anything.
 *       For example any basic_string<...> can be assigned to/from string as if they were of the same class.
 *
 * All these features covers almost all generic use cases, including creating zero-copy cascade parsers which in other case would lead to a lot of
 * pain.
 *
 * c_str() is not supported, because strings are not null-terminated
 */

namespace string_detail {
    template <typename S>
    struct mutable_charref {
        using value_type = typename S::value_type;
        using size_type  = typename S::size_type;

        mutable_charref (S& string, size_type pos): _string(string), _pos(pos) {}

        template <typename Arg, typename = std::enable_if_t<std::is_convertible<Arg, value_type>::value>>
        mutable_charref& operator= (Arg&& value) {
            _string._detach();
            _string._str[_pos] = std::forward<Arg>(value);
            return *this;
        }

        operator value_type() const { return _string._str[_pos]; }

    private:
        S& _string;
        size_type _pos;
    };

    enum class State : uint8_t {
        INTERNAL, // has InternalBuffer, may have _storage.dtor in case of basic_string<A,B,X> <-> basic_string<A,B,Y> convertations
        EXTERNAL, // has ExternalShared, shares external data, _storage.dtor present, _storage.external->dtor present
        LITERAL,  // shares external data, no Buffer, no _storage.dtor (literal data is immortal)
        SSO       // owns small string, no Buffer, no _storage.dtor
    };

    template <class CharT>
    struct Buffer {
        size_t   capacity;
        uint32_t refcnt;
        CharT    start[(sizeof(void*)-4)/sizeof(CharT)]; // align to word size
    };

    template <class CharT>
    struct ExternalShared : Buffer<CharT> {
        using dtor_fn = void (*)(CharT*, size_t);
        dtor_fn dtor; // deallocator for ExternalShared, may differ from Alloc::deallocate !
        CharT*  ptr;  // pointer to external data originally passed to string's constructor
    };
}

template <class T>
struct DefaultStaticAllocator {
    typedef T value_type;

    static T* allocate (size_t n) {
        void* mem = malloc(n * sizeof(T));
        if (!mem) throw std::bad_alloc();
        return (T*)mem;
    }

    static void deallocate (T* mem, size_t) {



( run in 0.593 second using v1.01-cache-2.11-cpan-119454b85a5 )