Олег Пронин

NAME

CPP::panda::lib - Collection of useful functions and classes for C++.

SYNOPSIS

    #include <panda/lib.h>
    #include <panda/lib/hash.h>
    using namespace panda::lib;
    
    char* crypted = crypt_xor(source, slen, key, klen);
    uint32_t val = hash32(str, len);
    uint64_t val = hash64(str, len);
    
    
    #include <panda/string.h>
    using panda::string;
    
    string abc("lala");
    ... // everything that std::string supports
    
    
    #include <panda/lib/memory.h>
    void* mem = StaticMemoryPool<128>::instance()->alloc(); // extremely fast memory allocations
    void* mem = StaticMemoryPool<128>::tls_instance()->alloc(); // TLS version (still very fast)
    void* mem = ObjectAllocator::instance()->alloc(256); // dynamic-size fast allocator
    
    class MyClass : public AllocatedObject<MyClass> {}
    MyClass* obj = new MyClass(); // get fast and thread-safe allocations with less code

    class SingleThreadedClass : public AllocatedObject<SingleThreadedClass, false> {} // faster but thread-unsafe
    
    // override behaviour from thread-unsafe to thread-safe
    class MultiThreadedClass : public SingleThreadedClass, public AllocatedObject<MultiThreadedClass> {
        using AllocatedObject<MultiThreadClass>::operator new;
        using AllocatedObject<MultiThreadClass>::operator delete;
    }
    
    MemoryPool mypool(32);
    void* mem = mypool.alloc(); // custom pool with maximum speed, but thread-unsafe
   

C FUNCTIONS

uint64_t panda::lib::hash64 (const char* str, size_t len)

uint64_t panda::lib::hash64 (const char* str)

uint32_t panda::lib::hash32 (const char* str, size_t len)

uint32_t panda::lib::hash32 (const char* str)

char* panda::lib::crypt_xor (const char* source, size_t slen, const char* key, size_t klen, char* dest = NULL)

Performs XOR crypt. If 'dest' is null, mallocs and returns new buffer. Buffer must be freed by user manually via 'free'. If 'dest' is not null, places result into this buffer. It must have enough space to hold the result.

panda::lib::h2be16, h2le16, be2h16, le2h16, h2be32, h2le32, be2h32, le2h32, h2be64, h2le64, be2h64, le2h64

Endianess convertation functions. They use __builtin_bswap*, _byteswap_*, etc if possible. Otherwise they use code likely to be compiled as a single bswap or rol instruction.

    #include <panda/lib/endian.h>
    uint64_t network_order = h2be64(host_order);

panda::dynamic_pointer_cast/const_pointer_cast/static_pointer_cast

The same as std::*_pointer_cast for panda::shared_ptr and std::shared_ptr.

panda::dyn_cast<>()

Same as dynamic_cast, but has a constant execution time, MUCH faster than dynamic_cast.

C++ CLASSES

panda::string, panda::wstring, panda::u16_string, panda::u32_string, panda::basic_string

This string is fully compatible with std::string API, however it supports COW (copy-on-write), even on substr-like functions, also supports creating from external buffer with destructor and from literal strings without copying or allocating anything, and therefore runs MUCH faster and zero-copy while still memory-managing in contrast to string_view. This approach is required like air for implementing TRULLY zero-copy parsers and so on.

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(literal) string support
    Can be created from external static(literal) 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 if mystr is not in SSO, 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.
In either case, panda::basic_string will always do the operation in the way it could not be done faster even in custom C plain code for most common cases.

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, and even could not theoretically be

SYNOPSIS

    using panda::string;

    string str("abcd"); // "abcd" is not copied, COW mode.
    str.append("ef"); // str is copied on modification.
    cout << str; // prints 'abcdef'
    
    void free_buf (char* buf, size_t size) { delete [] buf; }
    char* mystr = new char[13];
    memcpy(mystr, "hello, world", 13);
    str.assign(mystr, 13, 13, free_buf); // external string mode with destructor, no allocations/copies
    string str2 = str.substr(2, 5); // COW - no copy is done even for external strings
    cout << str2; // 'llo, '
    str.offset(10); // no move/copy/allocations
    cout << str; // 'ld'
    str.insert(1, ' hello '); // 'l hello d', no allocations! because source buffer (despite of that it is external) has space for that
    
    str2.erase(2); // no move/copy/allocations still! still points to external buffer
    cout << str2; // 'll'
    
    str = str2; // COW mode
    cout << str; // 'll'
    str.append('!'); // detach on modification. no allocations though, because everything < 23 bytes goes to SSO mode
    cout << str << str2; // 'll!ll'
    
    str.clear(); // nothing deallocated
    str2.clear(); // last reference lost, free_buf() called
    

panda::string can be used in ostream's << operator.

METHODS

No docs sorry. See <panda/basic_string.h>

panda::lib::MemoryPool

Base object for fast memory allocations of particular size (commonly used for small objects allocation). This class is thread-unsafe, you can only allocate memory using this object from single thread at one time. It is about from 10x to 40x times faster than new+delete.

METHODS

MemoryPool (size_t blocksize)

Creates object which allocates blocks of size blocksize. There is no memory overheat, because it doesn't store any additional data before/after a memory block. However if you pass blocksize less than 8 bytes, it will still allocate blocks large enough to hold 8 bytes.

void* alloc ()

Allocates new block. Can throw std::bad_alloc if no memory.

void dealloc (void* ptr)

Returns ptr back to pool. If ptr is a pointer that this object never allocated, the behaviour is undefined.

~MemoryPool

Frees internal storage and returns memory to system. All pointers ever allocated by this object become invalid.

template <int BLOCKSIZE> panda::lib::StaticMemoryPool

This class provides access to singleton memory pool objects for particular block size. It is recommended to use memory pools via this interface to reduce memory consumption and fragmentation.

METHODS

static MemoryPool* instance ()

Returns MemoryPool object for BLOCKSIZE which is global to the whole process. This object is not thread-safe.

static MemoryPool* tls_instance ()

Returns MemoryPool object for BLOCKSIZE which is global to the current thread. This object is not thread-safe.

panda::lib::ObjectAllocator

Sometimes you don't know the size of a block at compile time and therefore can't use StaticMemoryPool. From the other hand, creating MemoryPool objects for particular size every time is expensive. This class provides interface for allocating memory block of an arbitrary size. It holds a colletion of MemoryPool objects of various size which are created on-demand.

METHODS

ObjectAllocator ()

Creates allocator object. However i would recommend using singleton interface via instance/tls_instance, see below.

void* alloc (size_t size)

Allocates block size bytes long. If you pass size less than 8 bytes, it will still allocate 8 bytes.

void dealloc (void* ptr, size_t size)

Returns ptr back to pool. size is required because MemoryPool doesn't store block sizes before/after blocks to avoid memory overheat. If you pass wrong size, or a pointer that was never allocated via this object, the behaviour is undefined.

~ObjectAllocator ()

Frees internal storage of all pools and returns memory to system. All pointers ever allocated by this object become invalid.

static ObjectAllocator* instance ()

Returns ObjectAllocator object which is global to the whole process. This object is not thread-safe.

static ObjectAllocator* tls_instance ()

Returns ObjectAllocator object which is global to the current thread. This object is not thread-safe.

template <class TARGET, bool THREAD_SAFE = true> panda::lib::AllocatedObject

This class is a helper base class. If you inherit from it, objects of your class will be allocated via memory pools instead of using default new/delete operators.

Normally, you would need to write this code in order to allocate your objects via MemoryPool:

    class MyClass {
        static void* operator new (size_t size) {
            if (size == sizeof(MyClass)) return StaticMemoryPool<sizeof(MyClass)>::tls_instance()->alloc();
            return ObjectAllocator::tls_instance()->alloc(size);
        }
        static void operator delete (void* p, size_t size) {
            if (size == sizeof(MyClass)) StaticMemoryPool<sizeof(MyClass)>::tls_instance()->dealloc(p);
            else ObjectAllocator::tls_instance()->dealloc(p, size);
        }
        ...
    };
    

Size check (if/else) is needed to support inheritance, because in that case, size won't match sizeof(MyClass). Mostly, programmers use default operator ::new/::delete in case when sizes don't match, however ObjectAllocator can handle dynamic sizes and is much faster than default operators, so even in this case we save time.

To avoid writing this code every time, just inherit from AllocatedObject passing your class name as a template parameter. You can pass false as a second param to template if you don't need thread-safe allocations to achieve even more perfomance.

    class MyClass : public AllocatedObject<MyClass> { ... }

TIP

    class MyChild : public MyClass { ... }
    

In this case we will still using memory pool, however via dynamic ObjectAllocator which is slightly slower. To restore original perfomance redefine new/delete operators again passing your child class name. We will also need to resolve multiple inheritance conflicts via using operator.

    class MyChild : public MyClass, public AllocatedObject<MyChild> {
        using AllocatedObject<MyChild>::operator new;
        using AllocatedObject<MyChild>::operator delete;
        ...
    }

This code will allocate MyChild objects via static memory pool.

panda::RefCounted

A base class for refcounting.

SYNOPSIS

    #include <panda/refcnt.h>
    
    class MyClass : virtual public RefCounted { ... }
    
    MyClass* obj = new MyClass(...);
    obj->retain();
    obj->release();
    shared_ptr<MyClass> myobj(obj);

[protected] RefCounted ()

Initializes object with refcnt = 0. You will need to call retain() if you want to hold this object. Call release() if you don't need it anymore.

void retain () const

Increments refcounter.

void release () const

Decrements refcounter and deletes the object if refcounter <= 0.

int32_t refcnt () const

Returns refcount.

virtual void on_retain () const

If you define this method in your class, it will be called on every retain(), after refcnt is increased

virtual void on_release () const

If you define this method in your class, it will be called on every release(), after refcnt is decreased, but before object is deleted (in case if refcnt reached 0). If refcnt reaches 0 and you retain the object in this callback, object will survive.

template <class T> panda::shared_ptr<T>

API is the same as for std::shared_ptr.

AUTHOR

Pronin Oleg <syber@crazypanda.ru>, Crazy Panda, CP Decision LTD

LICENSE

You may distribute this code under the same terms as Perl itself.