/** * Ensure that enough storage is available to store at least new_size * characters plus a null byte. In addition, if we currently share * the storage with another string, unshare it, so that we can safely * write to the storage. */ void String::ensureCapacity(uint32 new_size, bool keep_old) { bool isShared; uint32 curCapacity, newCapacity; char *newStorage; int *oldRefCount = _extern._refCount; if (isStorageIntern()) { isShared = false; curCapacity = _builtinCapacity; } else { isShared = (oldRefCount && *oldRefCount > 1); curCapacity = _extern._capacity; } // Special case: If there is enough space, and we do not share // the storage, then there is nothing to do. if (!isShared && new_size < curCapacity) return; if (isShared && new_size < _builtinCapacity) { // We share the storage, but there is enough internal storage: Use that. newStorage = _storage; newCapacity = _builtinCapacity; } else { // We need to allocate storage on the heap! // Compute a suitable new capacity limit newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1)); // Allocate new storage newStorage = new char[newCapacity]; assert(newStorage); } // Copy old data if needed, elsewise reset the new storage. if (keep_old) { assert(_size < newCapacity); memcpy(newStorage, _str, _size + 1); } else { _size = 0; newStorage[0] = 0; } // Release hold on the old storage ... decRefCount(oldRefCount); // ... in favor of the new storage _str = newStorage; if (!isStorageIntern()) { // Set the ref count & capacity if we use an external storage. // It is important to do this *after* copying any old content, // else we would override data that has not yet been copied! _extern._refCount = 0; _extern._capacity = newCapacity; } }
void U32String::incRefCount() const { assert(!isStorageIntern()); if (_extern._refCount == nullptr) { if (g_refCountPool == nullptr) { g_refCountPool = new MemoryPool(sizeof(int)); assert(g_refCountPool); } _extern._refCount = (int *)g_refCountPool->allocChunk(); *_extern._refCount = 2; } else { ++(*_extern._refCount); } }
void U32String::decRefCount(int *oldRefCount) { if (isStorageIntern()) return; if (oldRefCount) { --(*oldRefCount); } if (!oldRefCount || *oldRefCount <= 0) { // The ref count reached zero, so we free the string storage // and the ref count storage. if (oldRefCount) { assert(g_refCountPool); g_refCountPool->freeChunk(oldRefCount); } delete[] _str; // Even though _str points to a freed memory block now, // we do not change its value, because any code that calls // decRefCount will have to do this afterwards anyway. } }