>>26
[quote]I'm very surprised that you were using an immutable string class for C++ game development though because of the allocator stress and fragmentation it would cause[/quote]
It was no more stressful on the allocator than std::string, in fact, it was generally less stressful. When you're modifying a std::string and inserting characters, with the implementations I've seen, when you reach the capacity of it's buffer, it'll allocate a new buffer twice the size of it's current buffer, copy all of the characters over to the new one, and release the old buffer. It generally has O(2^ceil(log2(N))) memory complexity.
With our string_builder, you could use whatever container for storage on the backend, which by default was a deque, which allocates fixed-sized slices or blocks: O(m*ceil(N/m)) memory complexity where m = block size (typically 256 bytes). Used with a non-blocking stack-based allocator on the backend, it's pretty decent.
We also employed strict memory budgeting, where we'd be like, okay, we can use a 256KB set of pages for various random strings, and 2MB of pages for text assets for the current level/map.
[quote]Why not just use UTF-8 for everything[/quote]
Asiatic character sets tend to use up less memory when in UTF-16 encoding, and a lot of games usually end up getting localized for Japan and Korea at some point.
[quote]except with indirection built in so that the string data could be allocated from a special zone that compacts memory[/quote]
That's what the Allocator template parameter is for!
[quote]multi-threading is extremely difficult[/quote]
It can be difficult, but you can build your own threading library on top of pthreads or windows threading apis.
class allocator
{
static bool const is_thread_safe = true;
/* ... */
};
enum memory_order
{
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
}
template< typename T >
class atomic
{
inline void add< memory_order >(T value) {
// magic
}
};
template< class Encoding, class Allocator >
{
private:
// intrusive reference counted instance
struct instance
{
atomic< int32_t > use_count;
typename Encoding::size_type size;
typename Encoding::char_type data[];
};
void acquire() {
instance_->use_count.add< Allocator::is_thread_safe ? memory_order_consume : memory_order_relaxed >(1);
}
};
Essentially, you can make your string template work with both thread-safe and non-blocking allocators and emit the most efficient code for reference counting in both cases.