blob: a24bc16ec1d460d6e6f2ea2adae495d0794ec0ac [file] [log] [blame]
// Copyright 2014 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/containers/PodVector.h"
#include "android/base/Log.h"
#include "android/base/memory/MallocUsableSize.h"
#include <stdlib.h>
#include <string.h>
namespace android {
namespace base {
static inline void swapPointers(char** p1, char** p2) {
char* tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
PodVectorBase::PodVectorBase(const PodVectorBase& other) {
initFrom(other.begin(), other.byteSize());
}
PodVectorBase& PodVectorBase::operator=(const PodVectorBase& other) {
initFrom(other.begin(), other.byteSize());
return *this;
}
PodVectorBase::~PodVectorBase() {
if (mBegin) {
// Sanity.
::memset(mBegin, 0xee, byteSize());
::free(mBegin);
mBegin = NULL;
mEnd = NULL;
mLimit = NULL;
}
}
void PodVectorBase::initFrom(const void* from, size_t fromLen) {
if (!fromLen || !from) {
mBegin = NULL;
mEnd = NULL;
mLimit = NULL;
} else {
mBegin = static_cast<char*>(::malloc(fromLen));
PCHECK(mBegin)
<< LogString("Could not allocate %zd bytes.", fromLen);
mEnd = mLimit = mBegin + fromLen;
::memcpy(mBegin, from, fromLen);
}
}
void PodVectorBase::assignFrom(const PodVectorBase& other) {
resize(other.byteSize(), 1U);
::memmove(begin(), other.begin(), byteSize());
}
void PodVectorBase::resize(size_t newSize, size_t itemSize) {
const size_t kMaxSize = maxItemCapacity(itemSize);
CHECK(newSize <= kMaxSize) << LogString(
"Trying to resize vector to %zd items of %zd bytes "
"(%zd max allowed)",
newSize,
kMaxSize);
size_t oldCapacity = itemCapacity(itemSize);
const size_t kMinCapacity = 256 / itemSize;
if (newSize < oldCapacity) {
// Only shrink if the new size is really small.
if (newSize < oldCapacity / 2 && oldCapacity > kMinCapacity) {
reserve(newSize, itemSize);
}
} else if (newSize > oldCapacity) {
size_t newCapacity = oldCapacity;
while (newCapacity < newSize) {
size_t newCapacity2 = newCapacity + (newCapacity >> 2) + 8;
if (newCapacity2 < newCapacity || newCapacity > kMaxSize) {
newCapacity = kMaxSize;
} else {
newCapacity = newCapacity2;
}
}
reserve(newCapacity, itemSize);
}
mEnd = mBegin + newSize * itemSize;
}
void PodVectorBase::reserve(size_t newSize, size_t itemSize) {
const size_t kMaxSize = maxItemCapacity(itemSize);
CHECK(newSize <= kMaxSize) << LogString(
"Trying to allocate %zd items of %zd bytes (%zd max allowed)",
newSize,
kMaxSize);
if (newSize == 0) {
::free(mBegin);
mBegin = NULL;
mEnd = NULL;
mLimit = NULL;
return;
}
size_t oldByteSize = byteSize();
size_t newByteCapacity = newSize * itemSize;
char* newBegin = static_cast<char*>(::realloc(mBegin, newByteCapacity));
PCHECK(newBegin) << LogString(
"Could not reallocate array from %zd tp %zd items of %zd bytes",
oldByteSize / itemSize,
newSize,
itemSize);
mBegin = newBegin;
mEnd = newBegin + oldByteSize;
#if USE_MALLOC_USABLE_SIZE
size_t usableSize = malloc_usable_size(mBegin);
if (usableSize > newByteCapacity) {
newByteCapacity = usableSize - (usableSize % itemSize);
}
#endif
mLimit = newBegin + newByteCapacity;
// Sanity.
if (newByteCapacity > oldByteSize) {
::memset(mBegin + oldByteSize, 0, newByteCapacity - oldByteSize);
}
}
void PodVectorBase::removeAt(size_t itemPos, size_t itemSize) {
size_t count = itemCount(itemSize);
DCHECK(itemPos <= count) << "Item position is too large!";
if (itemPos < count) {
size_t pos = itemPos * itemSize;
::memmove(mBegin + pos,
mBegin + pos + itemSize,
byteSize() - pos - itemSize);
resize(count - 1U, itemSize);
}
}
void* PodVectorBase::insertAt(size_t itemPos, size_t itemSize) {
size_t count = this->itemCount(itemSize);
DCHECK(itemPos <= count) << "Item position is too large";
resize(count + 1, itemSize);
size_t pos = itemPos * itemSize;
if (itemPos < count) {
::memmove(mBegin + pos + itemSize,
mBegin + pos,
count * itemSize - pos);
// Sanity to avoid copying pointers and other bad stuff.
::memset(mBegin + pos, 0, itemSize);
}
return mBegin + pos;
}
void PodVectorBase::swapAll(PodVectorBase* other) {
swapPointers(&mBegin, &other->mBegin);
swapPointers(&mEnd, &other->mEnd);
swapPointers(&mLimit, &other->mLimit);
}
} // namespace base
} // namespace android