root/src/kits/support/List.cpp
/*
 * Copyright 2001-2014 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              The Storage Kit Team
 *              Stephan Aßmus
 *              Rene Gollent
 *              John Scipione, jscipione@gmail.com
 *              Isaac Yonemoto
 */

//!     BList class provides storage for pointers. Not thread safe.


#include <List.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


// helper function
static inline void
move_items(void** items, int32 offset, int32 count)
{
        if (count > 0 && offset != 0)
                memmove(items + offset, items, count * sizeof(void*));
}


BList::BList(int32 blockSize)
        :
        fObjectList(NULL),
        fPhysicalSize(0),
        fItemCount(0),
        fBlockSize(blockSize),
        fResizeThreshold(0)
{
        if (fBlockSize <= 0)
                fBlockSize = 1;
}


BList::BList(const BList& other)
        :
        fObjectList(NULL),
        fPhysicalSize(0),
        fItemCount(0),
        fBlockSize(other.fBlockSize)
{
        *this = other;
}


BList::~BList()
{
        free(fObjectList);
}


BList&
BList::operator=(const BList& other)
{
        if (&other != this) {
                fBlockSize = other.fBlockSize;
                if (_ResizeArray(other.fItemCount)) {
                        fItemCount = other.fItemCount;
                        memcpy(fObjectList, other.fObjectList, fItemCount * sizeof(void*));
                }
        }

        return *this;
}


bool
BList::operator==(const BList& other) const
{
        if (&other == this)
                return true;

        if (other.fItemCount != fItemCount)
                return false;

        if (fItemCount > 0) {
                return memcmp(fObjectList, other.fObjectList,
                        fItemCount * sizeof(void*)) == 0;
        }

        return true;
}


bool
BList::operator!=(const BList& other) const
{
        return !(*this == other);
}


bool
BList::AddItem(void* item, int32 index)
{
        if (index < 0 || index > fItemCount)
                return false;

        bool result = true;

        if (fItemCount + 1 > fPhysicalSize)
                result = _ResizeArray(fItemCount + 1);
        if (result) {
                ++fItemCount;
                move_items(fObjectList + index, 1, fItemCount - index - 1);
                fObjectList[index] = item;
        }
        return result;
}


bool
BList::AddItem(void* item)
{
        bool result = true;
        if (fPhysicalSize > fItemCount) {
                fObjectList[fItemCount] = item;
                ++fItemCount;
        } else {
                if ((result = _ResizeArray(fItemCount + 1))) {
                        fObjectList[fItemCount] = item;
                        ++fItemCount;
                }
        }
        return result;
}


bool
BList::AddList(const BList* list, int32 index)
{
        bool result = (list && index >= 0 && index <= fItemCount);
        if (result && list->fItemCount > 0) {
                int32 count = list->fItemCount;
                if (fItemCount + count > fPhysicalSize)
                        result = _ResizeArray(fItemCount + count);

                if (result) {
                        fItemCount += count;
                        move_items(fObjectList + index, count, fItemCount - index - count);
                        memcpy(fObjectList + index, list->fObjectList,
                                list->fItemCount * sizeof(void*));
                }
        }

        return result;
}


bool
BList::AddList(const BList* list)
{
        bool result = (list != NULL);
        if (result && list->fItemCount > 0) {
                int32 index = fItemCount;
                int32 count = list->fItemCount;
                if (fItemCount + count > fPhysicalSize)
                        result = _ResizeArray(fItemCount + count);

                if (result) {
                        fItemCount += count;
                        memcpy(fObjectList + index, list->fObjectList,
                                list->fItemCount * sizeof(void*));
                }
        }

        return result;
}


bool
BList::RemoveItem(void* item)
{
        int32 index = IndexOf(item);
        bool result = (index >= 0);
        if (result)
                RemoveItem(index);
        return result;
}


void*
BList::RemoveItem(int32 index)
{
        void* item = NULL;
        if (index >= 0 && index < fItemCount) {
                item = fObjectList[index];
                move_items(fObjectList + index + 1, -1, fItemCount - index - 1);
                --fItemCount;
                if (fItemCount <= fResizeThreshold)
                        _ResizeArray(fItemCount);
        }
        return item;
}


bool
BList::RemoveItems(int32 index, int32 count)
{
        bool result = (index >= 0 && index <= fItemCount);
        if (result) {
                if (index + count > fItemCount)
                        count = fItemCount - index;
                if (count > 0) {
                        move_items(fObjectList + index + count, -count,
                                           fItemCount - index - count);
                        fItemCount -= count;
                        if (fItemCount <= fResizeThreshold)
                                _ResizeArray(fItemCount);
                } else
                        result = false;
        }
        return result;
}


bool
BList::ReplaceItem(int32 index, void* item)
{
        bool result = false;

        if (index >= 0 && index < fItemCount) {
                fObjectList[index] = item;
                result = true;
        }
        return result;
}


void
BList::MakeEmpty()
{
        fItemCount = 0;
        _ResizeArray(0);
}


// #pragma mark - Reordering items.


void
BList::SortItems(int (*compareFunc)(const void*, const void*))
{
        if (compareFunc)
                qsort(fObjectList, fItemCount, sizeof(void*), compareFunc);
}


bool
BList::SwapItems(int32 indexA, int32 indexB)
{
        bool result = false;

        if (indexA >= 0 && indexA < fItemCount
                && indexB >= 0 && indexB < fItemCount) {

                void* tmpItem = fObjectList[indexA];
                fObjectList[indexA] = fObjectList[indexB];
                fObjectList[indexB] = tmpItem;

                result = true;
        }

        return result;
}


/*! This moves a list item from posititon a to position b, moving the
        appropriate block of list elements to make up for the move. For example,
        in the array:
        A B C D E F G H I J
                Moveing 1(B)->6(G) would result in this:
        A C D E F G B H I J
*/
bool
BList::MoveItem(int32 from, int32 to)
{
        if ((from >= fItemCount) || (to >= fItemCount) || (from < 0) || (to < 0))
                return false;

        void* tmpMover = fObjectList[from];
        if (from < to) {
                memmove(fObjectList + from, fObjectList + from + 1,
                        (to - from) * sizeof(void*));
        } else if (from > to) {
                memmove(fObjectList + to + 1, fObjectList + to,
                        (from - to) * sizeof(void*));
        }
        fObjectList[to] = tmpMover;

        return true;
}


// #pragma mark - Retrieving items.


void*
BList::ItemAt(int32 index) const
{
        void* item = NULL;
        if (index >= 0 && index < fItemCount)
                item = fObjectList[index];
        return item;
}


void*
BList::FirstItem() const
{
        void* item = NULL;
        if (fItemCount > 0)
                item = fObjectList[0];
        return item;
}


void*
BList::ItemAtFast(int32 index) const
{
        return fObjectList[index];
}


void*
BList::Items() const
{
        return fObjectList;
}


void*
BList::LastItem() const
{
        void* item = NULL;
        if (fItemCount > 0)
                item = fObjectList[fItemCount - 1];
        return item;
}


// #pragma mark - Querying the list.


bool
BList::HasItem(void* item) const
{
        return (IndexOf(item) >= 0);
}


bool
BList::HasItem(const void* item) const
{
        return (IndexOf(item) >= 0);
}


int32
BList::IndexOf(void* item) const
{
        for (int32 i = 0; i < fItemCount; i++) {
                if (fObjectList[i] == item)
                        return i;
        }
        return -1;
}


int32
BList::IndexOf(const void* item) const
{
        for (int32 i = 0; i < fItemCount; i++) {
                if (fObjectList[i] == item)
                        return i;
        }
        return -1;
}


int32
BList::CountItems() const
{
        return fItemCount;
}


bool
BList::IsEmpty() const
{
        return fItemCount == 0;
}


// #pragma mark - Iterating over the list.

/*!     Iterate a function over the whole list. If the function outputs a true
        value, then the process is terminated.
*/
void
BList::DoForEach(bool (*func)(void*))
{
        if (func == NULL)
                return;

        bool terminate = false;
        int32 index = 0;

        while ((!terminate) && (index < fItemCount)) {
                terminate = func(fObjectList[index]);
                index++;
        }
}


/*!     Iterate a function over the whole list. If the function outputs a true
        value, then the process is terminated. This version takes an additional
        argument which is passed to the function.
*/
void
BList::DoForEach(bool (*func)(void*, void*), void* arg)
{
        if (func == NULL)
                return;

        bool terminate = false; int32 index = 0;
        while ((!terminate) && (index < fItemCount)) {
                terminate = func(fObjectList[index], arg);
                index++;
        }
}


#if (__GNUC__ == 2)

// This is somewhat of a hack for backwards compatibility -
// the reason these functions are defined this way rather than simply
// being made private members is that if they are included, then whenever
// gcc encounters someone calling AddList() with a non-const BList pointer,
// it will try to use the private version and fail with a compiler error.

// obsolete AddList(BList* list, int32 index) and AddList(BList* list)
// AddList
extern "C" bool
AddList__5BListP5BListl(BList* self, BList* list, int32 index)
{
        return self->AddList((const BList*)list, index);
}

// AddList
extern "C" bool
AddList__5BListP5BList(BList* self, BList* list)
{
        return self->AddList((const BList*)list);
}
#endif

// FBC
void BList::_ReservedList1() {}
void BList::_ReservedList2() {}


//!     Resizes fObjectList to be large enough to contain count items.
bool
BList::_ResizeArray(int32 count)
{
        if (fPhysicalSize == count)
                return true;

        // calculate the new physical size
        // by doubling the existing size
        // until we can hold at least count items
        int32 newSize = fPhysicalSize > 0 ? fPhysicalSize : fBlockSize;
        int32 targetSize = count;
        if (targetSize <= 0)
                targetSize = fBlockSize;

        if (targetSize > fPhysicalSize) {
                while (newSize < targetSize)
                        newSize <<= 1;
        } else if (targetSize <= fResizeThreshold)
                newSize = fResizeThreshold;

        // resize if necessary
        bool result = true;
        if (newSize != fPhysicalSize) {
                void** newObjectList
                        = (void**)realloc(fObjectList, newSize * sizeof(void*));
                if (newObjectList) {
                        fObjectList = newObjectList;
                        fPhysicalSize = newSize;

                        fResizeThreshold = (fPhysicalSize / 4);
                        if (fResizeThreshold < fBlockSize)
                                fResizeThreshold = 0;
                } else
                        result = false;
        }

        return result;
}