CPP::panda::lib - Collection of useful functions and classes for C++.
#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
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.
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.
__builtin_bswap*
_byteswap_*
bswap
rol
#include <panda/lib/endian.h> uint64_t network_order = h2be64(host_order);
The same as std::*_pointer_cast for panda::shared_ptr and std::shared_ptr.
Same as dynamic_cast, but has a constant execution time, MUCH faster than dynamic_cast.
dynamic_cast
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:
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.
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.
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
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.
The content of other strings which shares the data with current string will not be affected.
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, and even could not theoretically be
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.
No docs sorry. See <panda/basic_string.h>
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.
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.
blocksize
Allocates new block. Can throw std::bad_alloc if no memory.
Returns ptr back to pool. If ptr is a pointer that this object never allocated, the behaviour is undefined.
ptr
Frees internal storage and returns memory to system. All pointers ever allocated by this object become invalid.
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.
Returns MemoryPool object for BLOCKSIZE which is global to the whole process. This object is not thread-safe.
BLOCKSIZE
Returns MemoryPool object for BLOCKSIZE which is global to the current thread. This object is not thread-safe.
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.
Creates allocator object. However i would recommend using singleton interface via instance/tls_instance, see below.
Allocates block size bytes long. If you pass size less than 8 bytes, it will still allocate 8 bytes.
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.
Frees internal storage of all pools and returns memory to system. All pointers ever allocated by this object become invalid.
Returns ObjectAllocator object which is global to the whole process. This object is not thread-safe.
Returns ObjectAllocator object which is global to the current thread. This object is not thread-safe.
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.
false
class MyClass : public AllocatedObject<MyClass> { ... }
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.
using
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.
A base class for refcounting.
#include <panda/refcnt.h> class MyClass : virtual public RefCounted { ... } MyClass* obj = new MyClass(...); obj->retain(); obj->release(); shared_ptr<MyClass> myobj(obj);
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.
Increments refcounter.
Decrements refcounter and deletes the object if refcounter <= 0.
Returns refcount.
If you define this method in your class, it will be called on every retain(), after refcnt is increased
retain()
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.
release()
refcnt
retain
API is the same as for std::shared_ptr.
Pronin Oleg <syber@crazypanda.ru>, Crazy Panda, CP Decision LTD
You may distribute this code under the same terms as Perl itself.
To install CPP::panda::lib, copy and paste the appropriate command in to your terminal.
cpanm
cpanm CPP::panda::lib
CPAN shell
perl -MCPAN -e shell install CPP::panda::lib
For more information on module installation, please visit the detailed CPAN module installation guide.