eb0eb4f79f
1. When alloc or realloc failed in the function SharedBuffer::editResize, it would return a NULL pointer, then mStorage would update to be 1 by SharedBuffer::data() if no pointer check here, which is an obviously wrong address, and would cause corruption when used it e.g. in capacity(). So add the pointer check here for the return value of SharedBuffer::editResize, if it's NULL do not use it to update mStorage, to avoid the value of mStorage polluted. 2. when alloc or realloc falied in _grow & _shrink function, mStorage keep the original value, so mCount should not be updated here. Otherwise, mStorage might be 0 but mCount>0, so a corruption would happend when it try to delete items from the Vector since mCount>0. Change-Id: I7c3814e843c459834ca5eed392e8d63d1cb7d2d8 Signed-off-by: Shuo Gao <shuo.gao@intel.com> Signed-off-by: Jian Luo <jian.luo@intel.com> Signed-off-by: Bruce Beare <bruce.j.beare@intel.com> Signed-off-by: Jack Ren <jack.ren@intel.com> Author-tracking-BZ: 139626
637 lines
18 KiB
C++
637 lines
18 KiB
C++
/*
|
|
* Copyright (C) 2005 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "Vector"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <cutils/log.h>
|
|
|
|
#include <utils/Errors.h>
|
|
#include <utils/SharedBuffer.h>
|
|
#include <utils/VectorImpl.h>
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
namespace android {
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const size_t kMinVectorCapacity = 4;
|
|
|
|
static inline size_t max(size_t a, size_t b) {
|
|
return a>b ? a : b;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
|
|
: mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
|
|
{
|
|
}
|
|
|
|
VectorImpl::VectorImpl(const VectorImpl& rhs)
|
|
: mStorage(rhs.mStorage), mCount(rhs.mCount),
|
|
mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
|
|
{
|
|
if (mStorage) {
|
|
SharedBuffer::bufferFromData(mStorage)->acquire();
|
|
}
|
|
}
|
|
|
|
VectorImpl::~VectorImpl()
|
|
{
|
|
ALOGW_IF(mCount,
|
|
"[%p] subclasses of VectorImpl must call finish_vector()"
|
|
" in their destructor. Leaking %d bytes.",
|
|
this, (int)(mCount*mItemSize));
|
|
// We can't call _do_destroy() here because the vtable is already gone.
|
|
}
|
|
|
|
VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
|
|
{
|
|
LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize,
|
|
"Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
|
|
if (this != &rhs) {
|
|
release_storage();
|
|
if (rhs.mCount) {
|
|
mStorage = rhs.mStorage;
|
|
mCount = rhs.mCount;
|
|
SharedBuffer::bufferFromData(mStorage)->acquire();
|
|
} else {
|
|
mStorage = 0;
|
|
mCount = 0;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void* VectorImpl::editArrayImpl()
|
|
{
|
|
if (mStorage) {
|
|
SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
|
|
if (sb == 0) {
|
|
sb = SharedBuffer::alloc(capacity() * mItemSize);
|
|
if (sb) {
|
|
_do_copy(sb->data(), mStorage, mCount);
|
|
release_storage();
|
|
mStorage = sb->data();
|
|
}
|
|
}
|
|
}
|
|
return mStorage;
|
|
}
|
|
|
|
size_t VectorImpl::capacity() const
|
|
{
|
|
if (mStorage) {
|
|
return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
|
|
{
|
|
return insertArrayAt(vector.arrayImpl(), index, vector.size());
|
|
}
|
|
|
|
ssize_t VectorImpl::appendVector(const VectorImpl& vector)
|
|
{
|
|
return insertVectorAt(vector, size());
|
|
}
|
|
|
|
ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length)
|
|
{
|
|
if (index > size())
|
|
return BAD_INDEX;
|
|
void* where = _grow(index, length);
|
|
if (where) {
|
|
_do_copy(where, array, length);
|
|
}
|
|
return where ? index : (ssize_t)NO_MEMORY;
|
|
}
|
|
|
|
ssize_t VectorImpl::appendArray(const void* array, size_t length)
|
|
{
|
|
return insertArrayAt(array, size(), length);
|
|
}
|
|
|
|
ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
|
|
{
|
|
return insertAt(0, index, numItems);
|
|
}
|
|
|
|
ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
|
|
{
|
|
if (index > size())
|
|
return BAD_INDEX;
|
|
void* where = _grow(index, numItems);
|
|
if (where) {
|
|
if (item) {
|
|
_do_splat(where, item, numItems);
|
|
} else {
|
|
_do_construct(where, numItems);
|
|
}
|
|
}
|
|
return where ? index : (ssize_t)NO_MEMORY;
|
|
}
|
|
|
|
static int sortProxy(const void* lhs, const void* rhs, void* func)
|
|
{
|
|
return (*(VectorImpl::compar_t)func)(lhs, rhs);
|
|
}
|
|
|
|
status_t VectorImpl::sort(VectorImpl::compar_t cmp)
|
|
{
|
|
return sort(sortProxy, (void*)cmp);
|
|
}
|
|
|
|
status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state)
|
|
{
|
|
// the sort must be stable. we're using insertion sort which
|
|
// is well suited for small and already sorted arrays
|
|
// for big arrays, it could be better to use mergesort
|
|
const ssize_t count = size();
|
|
if (count > 1) {
|
|
void* array = const_cast<void*>(arrayImpl());
|
|
void* temp = 0;
|
|
ssize_t i = 1;
|
|
while (i < count) {
|
|
void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
|
|
void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
|
|
if (cmp(curr, item, state) > 0) {
|
|
|
|
if (!temp) {
|
|
// we're going to have to modify the array...
|
|
array = editArrayImpl();
|
|
if (!array) return NO_MEMORY;
|
|
temp = malloc(mItemSize);
|
|
if (!temp) return NO_MEMORY;
|
|
item = reinterpret_cast<char*>(array) + mItemSize*(i);
|
|
curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
|
|
} else {
|
|
_do_destroy(temp, 1);
|
|
}
|
|
|
|
_do_copy(temp, item, 1);
|
|
|
|
ssize_t j = i-1;
|
|
void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
|
|
do {
|
|
_do_destroy(next, 1);
|
|
_do_copy(next, curr, 1);
|
|
next = curr;
|
|
--j;
|
|
curr = reinterpret_cast<char*>(array) + mItemSize*(j);
|
|
} while (j>=0 && (cmp(curr, temp, state) > 0));
|
|
|
|
_do_destroy(next, 1);
|
|
_do_copy(next, temp, 1);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (temp) {
|
|
_do_destroy(temp, 1);
|
|
free(temp);
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void VectorImpl::pop()
|
|
{
|
|
if (size())
|
|
removeItemsAt(size()-1, 1);
|
|
}
|
|
|
|
void VectorImpl::push()
|
|
{
|
|
push(0);
|
|
}
|
|
|
|
void VectorImpl::push(const void* item)
|
|
{
|
|
insertAt(item, size());
|
|
}
|
|
|
|
ssize_t VectorImpl::add()
|
|
{
|
|
return add(0);
|
|
}
|
|
|
|
ssize_t VectorImpl::add(const void* item)
|
|
{
|
|
return insertAt(item, size());
|
|
}
|
|
|
|
ssize_t VectorImpl::replaceAt(size_t index)
|
|
{
|
|
return replaceAt(0, index);
|
|
}
|
|
|
|
ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
|
|
{
|
|
ALOG_ASSERT(index<size(),
|
|
"[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
|
|
|
|
if (index >= size()) {
|
|
return BAD_INDEX;
|
|
}
|
|
|
|
void* item = editItemLocation(index);
|
|
if (item != prototype) {
|
|
if (item == 0)
|
|
return NO_MEMORY;
|
|
_do_destroy(item, 1);
|
|
if (prototype == 0) {
|
|
_do_construct(item, 1);
|
|
} else {
|
|
_do_copy(item, prototype, 1);
|
|
}
|
|
}
|
|
return ssize_t(index);
|
|
}
|
|
|
|
ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
|
|
{
|
|
ALOG_ASSERT((index+count)<=size(),
|
|
"[%p] remove: index=%d, count=%d, size=%d",
|
|
this, (int)index, (int)count, (int)size());
|
|
|
|
if ((index+count) > size())
|
|
return BAD_VALUE;
|
|
_shrink(index, count);
|
|
return index;
|
|
}
|
|
|
|
void VectorImpl::finish_vector()
|
|
{
|
|
release_storage();
|
|
mStorage = 0;
|
|
mCount = 0;
|
|
}
|
|
|
|
void VectorImpl::clear()
|
|
{
|
|
_shrink(0, mCount);
|
|
}
|
|
|
|
void* VectorImpl::editItemLocation(size_t index)
|
|
{
|
|
ALOG_ASSERT(index<capacity(),
|
|
"[%p] editItemLocation: index=%d, capacity=%d, count=%d",
|
|
this, (int)index, (int)capacity(), (int)mCount);
|
|
|
|
if (index < capacity()) {
|
|
void* buffer = editArrayImpl();
|
|
if (buffer) {
|
|
return reinterpret_cast<char*>(buffer) + index*mItemSize;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const void* VectorImpl::itemLocation(size_t index) const
|
|
{
|
|
ALOG_ASSERT(index<capacity(),
|
|
"[%p] itemLocation: index=%d, capacity=%d, count=%d",
|
|
this, (int)index, (int)capacity(), (int)mCount);
|
|
|
|
if (index < capacity()) {
|
|
const void* buffer = arrayImpl();
|
|
if (buffer) {
|
|
return reinterpret_cast<const char*>(buffer) + index*mItemSize;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ssize_t VectorImpl::setCapacity(size_t new_capacity)
|
|
{
|
|
size_t current_capacity = capacity();
|
|
ssize_t amount = new_capacity - size();
|
|
if (amount <= 0) {
|
|
// we can't reduce the capacity
|
|
return current_capacity;
|
|
}
|
|
SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
|
|
if (sb) {
|
|
void* array = sb->data();
|
|
_do_copy(array, mStorage, size());
|
|
release_storage();
|
|
mStorage = const_cast<void*>(array);
|
|
} else {
|
|
return NO_MEMORY;
|
|
}
|
|
return new_capacity;
|
|
}
|
|
|
|
ssize_t VectorImpl::resize(size_t size) {
|
|
ssize_t result = NO_ERROR;
|
|
if (size > mCount) {
|
|
result = insertAt(mCount, size - mCount);
|
|
} else if (size < mCount) {
|
|
result = removeItemsAt(size, mCount - size);
|
|
}
|
|
return result < 0 ? result : size;
|
|
}
|
|
|
|
void VectorImpl::release_storage()
|
|
{
|
|
if (mStorage) {
|
|
const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
|
|
if (sb->release(SharedBuffer::eKeepStorage) == 1) {
|
|
_do_destroy(mStorage, mCount);
|
|
SharedBuffer::dealloc(sb);
|
|
}
|
|
}
|
|
}
|
|
|
|
void* VectorImpl::_grow(size_t where, size_t amount)
|
|
{
|
|
// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
|
|
// this, (int)where, (int)amount, (int)mCount, (int)capacity());
|
|
|
|
ALOG_ASSERT(where <= mCount,
|
|
"[%p] _grow: where=%d, amount=%d, count=%d",
|
|
this, (int)where, (int)amount, (int)mCount); // caller already checked
|
|
|
|
const size_t new_size = mCount + amount;
|
|
if (capacity() < new_size) {
|
|
const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
|
|
// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
|
|
if ((mStorage) &&
|
|
(mCount==where) &&
|
|
(mFlags & HAS_TRIVIAL_COPY) &&
|
|
(mFlags & HAS_TRIVIAL_DTOR))
|
|
{
|
|
const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
|
|
SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
|
|
if (sb) {
|
|
mStorage = sb->data();
|
|
} else {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
|
|
if (sb) {
|
|
void* array = sb->data();
|
|
if (where != 0) {
|
|
_do_copy(array, mStorage, where);
|
|
}
|
|
if (where != mCount) {
|
|
const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
|
|
void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
|
|
_do_copy(dest, from, mCount-where);
|
|
}
|
|
release_storage();
|
|
mStorage = const_cast<void*>(array);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
} else {
|
|
void* array = editArrayImpl();
|
|
if (where != mCount) {
|
|
const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
|
|
void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
|
|
_do_move_forward(to, from, mCount - where);
|
|
}
|
|
}
|
|
mCount = new_size;
|
|
void* free_space = const_cast<void*>(itemLocation(where));
|
|
return free_space;
|
|
}
|
|
|
|
void VectorImpl::_shrink(size_t where, size_t amount)
|
|
{
|
|
if (!mStorage)
|
|
return;
|
|
|
|
// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
|
|
// this, (int)where, (int)amount, (int)mCount, (int)capacity());
|
|
|
|
ALOG_ASSERT(where + amount <= mCount,
|
|
"[%p] _shrink: where=%d, amount=%d, count=%d",
|
|
this, (int)where, (int)amount, (int)mCount); // caller already checked
|
|
|
|
const size_t new_size = mCount - amount;
|
|
if (new_size*3 < capacity()) {
|
|
const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
|
|
// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
|
|
if ((where == new_size) &&
|
|
(mFlags & HAS_TRIVIAL_COPY) &&
|
|
(mFlags & HAS_TRIVIAL_DTOR))
|
|
{
|
|
const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
|
|
SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
|
|
if (sb) {
|
|
mStorage = sb->data();
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
|
|
if (sb) {
|
|
void* array = sb->data();
|
|
if (where != 0) {
|
|
_do_copy(array, mStorage, where);
|
|
}
|
|
if (where != new_size) {
|
|
const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
|
|
void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
|
|
_do_copy(dest, from, new_size - where);
|
|
}
|
|
release_storage();
|
|
mStorage = const_cast<void*>(array);
|
|
} else{
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
void* array = editArrayImpl();
|
|
void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
|
|
_do_destroy(to, amount);
|
|
if (where != new_size) {
|
|
const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
|
|
_do_move_backward(to, from, new_size - where);
|
|
}
|
|
}
|
|
mCount = new_size;
|
|
}
|
|
|
|
size_t VectorImpl::itemSize() const {
|
|
return mItemSize;
|
|
}
|
|
|
|
void VectorImpl::_do_construct(void* storage, size_t num) const
|
|
{
|
|
if (!(mFlags & HAS_TRIVIAL_CTOR)) {
|
|
do_construct(storage, num);
|
|
}
|
|
}
|
|
|
|
void VectorImpl::_do_destroy(void* storage, size_t num) const
|
|
{
|
|
if (!(mFlags & HAS_TRIVIAL_DTOR)) {
|
|
do_destroy(storage, num);
|
|
}
|
|
}
|
|
|
|
void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
|
|
{
|
|
if (!(mFlags & HAS_TRIVIAL_COPY)) {
|
|
do_copy(dest, from, num);
|
|
} else {
|
|
memcpy(dest, from, num*itemSize());
|
|
}
|
|
}
|
|
|
|
void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
|
|
do_splat(dest, item, num);
|
|
}
|
|
|
|
void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
|
|
do_move_forward(dest, from, num);
|
|
}
|
|
|
|
void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
|
|
do_move_backward(dest, from, num);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
|
|
: VectorImpl(itemSize, flags)
|
|
{
|
|
}
|
|
|
|
SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
|
|
: VectorImpl(rhs)
|
|
{
|
|
}
|
|
|
|
SortedVectorImpl::~SortedVectorImpl()
|
|
{
|
|
}
|
|
|
|
SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
|
|
{
|
|
return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
|
|
}
|
|
|
|
ssize_t SortedVectorImpl::indexOf(const void* item) const
|
|
{
|
|
return _indexOrderOf(item);
|
|
}
|
|
|
|
size_t SortedVectorImpl::orderOf(const void* item) const
|
|
{
|
|
size_t o;
|
|
_indexOrderOf(item, &o);
|
|
return o;
|
|
}
|
|
|
|
ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
|
|
{
|
|
// binary search
|
|
ssize_t err = NAME_NOT_FOUND;
|
|
ssize_t l = 0;
|
|
ssize_t h = size()-1;
|
|
ssize_t mid;
|
|
const void* a = arrayImpl();
|
|
const size_t s = itemSize();
|
|
while (l <= h) {
|
|
mid = l + (h - l)/2;
|
|
const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
|
|
const int c = do_compare(curr, item);
|
|
if (c == 0) {
|
|
err = l = mid;
|
|
break;
|
|
} else if (c < 0) {
|
|
l = mid + 1;
|
|
} else {
|
|
h = mid - 1;
|
|
}
|
|
}
|
|
if (order) *order = l;
|
|
return err;
|
|
}
|
|
|
|
ssize_t SortedVectorImpl::add(const void* item)
|
|
{
|
|
size_t order;
|
|
ssize_t index = _indexOrderOf(item, &order);
|
|
if (index < 0) {
|
|
index = VectorImpl::insertAt(item, order, 1);
|
|
} else {
|
|
index = VectorImpl::replaceAt(item, index);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
|
|
{
|
|
// naive merge...
|
|
if (!vector.isEmpty()) {
|
|
const void* buffer = vector.arrayImpl();
|
|
const size_t is = itemSize();
|
|
size_t s = vector.size();
|
|
for (size_t i=0 ; i<s ; i++) {
|
|
ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
|
|
if (err<0) {
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
|
|
{
|
|
// we've merging a sorted vector... nice!
|
|
ssize_t err = NO_ERROR;
|
|
if (!vector.isEmpty()) {
|
|
// first take care of the case where the vectors are sorted together
|
|
if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
|
|
err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
|
|
} else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
|
|
err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
|
|
} else {
|
|
// this could be made a little better
|
|
err = merge(static_cast<const VectorImpl&>(vector));
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
ssize_t SortedVectorImpl::remove(const void* item)
|
|
{
|
|
ssize_t i = indexOf(item);
|
|
if (i>=0) {
|
|
VectorImpl::removeItemsAt(i, 1);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
}; // namespace android
|
|
|