// @(#)root/cont:$Id$
// Author: Fons Rademakers 13/09/95
/*************************************************************************
* Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
//////////////////////////////////////////////////////////////////////////
// //
// TOrdCollection //
// //
// Ordered collection. An ordered collection has TList insertion //
// semantics but is implemented using an array of TObject*'s. It uses //
// less space than a TList (since there is no need for the prev and //
// next pointers), but it is more costly to insert objects (since it //
// has to create a gap by copying object pointers). TOrdCollection //
// is better than TList when objects are only added at the end of the //
// collection since no copying needs to be done. //
//Begin_Html
/*
*/
//End_Html
// //
//////////////////////////////////////////////////////////////////////////
#include "TOrdCollection.h"
#include "TError.h"
ClassImp(TOrdCollection)
//______________________________________________________________________________
TOrdCollection::TOrdCollection(Int_t capacity)
{
// Create an ordered collection.
if (capacity < 0) {
Warning("TOrdCollection", "capacity (%d) < 0", capacity);
capacity = kDefaultCapacity;
} else if (capacity == 0)
capacity = kDefaultCapacity;
Init(capacity);
}
//______________________________________________________________________________
TOrdCollection::~TOrdCollection()
{
// Delete the collection. Objects are not deleted unless the TOrdCollection
// is the owner (set via SetOwner()).
if (IsOwner())
Delete();
TStorage::Dealloc(fCont);
fCont = 0;
fSize = 0;
}
//______________________________________________________________________________
void TOrdCollection::AddAt(TObject *obj, Int_t idx)
{
// Insert object at position idx in the collection.
Int_t physIdx;
if (idx > fSize) idx = fSize;
if (fGapSize <= 0)
SetCapacity(GrowBy(TMath::Max(fCapacity, (int)kMinExpand)));
if (idx == fGapStart) {
physIdx = fGapStart;
fGapStart++;
} else {
physIdx = PhysIndex(idx);
if (physIdx < fGapStart) {
MoveGapTo(physIdx);
physIdx = fGapStart;
fGapStart++;
} else {
MoveGapTo(physIdx - fGapSize);
physIdx = fGapStart + fGapSize - 1;
}
}
R__ASSERT(physIdx >= 0 && physIdx < fCapacity);
fCont[physIdx] = obj;
fGapSize--;
fSize++;
Changed();
}
//______________________________________________________________________________
void TOrdCollection::AddFirst(TObject *obj)
{
// Insert object at beginning of collection.
AddAt(obj, 0);
}
//______________________________________________________________________________
void TOrdCollection::AddLast(TObject *obj)
{
// Add object at the end of the collection.
AddAt(obj, fSize);
}
//______________________________________________________________________________
void TOrdCollection::AddBefore(const TObject *before, TObject *obj)
{
// Insert object before object before in the collection.
if (!before)
AddFirst(obj);
else {
Int_t idx = IndexOf(before);
if (idx == -1) {
Error("AddBefore", "before not found, object not added");
return;
}
if (idx == 0) {
AddFirst(obj);
return;
}
AddAt(obj, idx);
}
}
//______________________________________________________________________________
void TOrdCollection::AddAfter(const TObject *after, TObject *obj)
{
// Insert object after object after in the collection.
if (!after)
AddLast(obj);
else {
Int_t idx = IndexOf(after);
if (idx == -1) {
Error("AddAfter", "after not found, object not added");
return;
}
AddAt(obj, idx+1);
}
}
//______________________________________________________________________________
TObject *TOrdCollection::After(const TObject *obj) const
{
// Return the object after object obj. Returns 0 if obj is last
// in collection.
if (!obj) return 0;
Int_t idx = IndexOf(obj);
if (idx == -1 || idx == fSize-1) return 0;
return At(idx+1);
}
//______________________________________________________________________________
TObject *TOrdCollection::At(Int_t idx) const
{
// Returns the object at position idx. Returns 0 if idx is out of range.
if (IllegalIndex("At", idx)) return 0;
return fCont[PhysIndex(idx)];
}
//______________________________________________________________________________
TObject *TOrdCollection::Before(const TObject *obj) const
{
// Returns the object before object obj. Returns 0 if obj is first
// in collection.
if (!obj) return 0;
Int_t idx = IndexOf(obj);
if (idx == -1 || idx == 0) return 0;
return At(idx-1);
}
//______________________________________________________________________________
void TOrdCollection::Clear(Option_t *)
{
// Remove all objects from the collection. Does not delete the objects
// unless the TOrdCollection is the owner (set via SetOwner()).
if (IsOwner())
Delete();
else {
TStorage::Dealloc(fCont);
fCont = 0;
Init(fCapacity);
fSize = 0;
}
}
//______________________________________________________________________________
void TOrdCollection::Delete(Option_t *)
{
// Remove all objects from the collection AND delete all heap based objects.
for (Int_t i = 0; i < fSize; i++) {
TObject *obj = At(i);
if (obj && obj->IsOnHeap())
TCollection::GarbageCollect(obj);
}
TStorage::Dealloc(fCont);
fCont = 0;
Init(fCapacity);
fSize = 0;
}
//______________________________________________________________________________
TObject *TOrdCollection::First() const
{
// Return the first object in the collection. Returns 0 when collection
// is empty.
return At(0);
}
//______________________________________________________________________________
TObject **TOrdCollection::GetObjectRef(const TObject *obj) const
{
// return address of pointer obj
Int_t index = IndexOf(obj);
return &fCont[index];
}
//______________________________________________________________________________
TObject *TOrdCollection::Last() const
{
// Return the last object in the collection. Returns 0 when collection
// is empty.
return At(fSize-1);
}
//______________________________________________________________________________
Bool_t TOrdCollection::IllegalIndex(const char *method, Int_t idx) const
{
// Return true when index out of bounds and print error.
if (idx < 0 || idx >= fSize) {
Error(method, "index error (= %d) < 0 or > Size() (= %d)", idx, fSize);
return kTRUE;
}
return kFALSE;
}
//______________________________________________________________________________
Int_t TOrdCollection::IndexOf(const TObject *obj) const
{
// Return index of object in collection. Returns -1 when object not found.
// Uses member IsEqual() to find object.
for (Int_t i = 0; i < GetSize(); i++)
if (fCont[PhysIndex(i)]->IsEqual(obj))
return i;
return -1;
}
//______________________________________________________________________________
void TOrdCollection::Init(Int_t capacity)
{
// Initialize ordered collection.
fCapacity = capacity;
fCont = (TObject**) TStorage::Alloc(fCapacity*sizeof(TObject*)); //new TObject* [fCapacity];
memset(fCont, 0, fCapacity*sizeof(TObject*));
fGapStart = 0;
fGapSize = capacity;
Changed();
}
//______________________________________________________________________________
TIterator *TOrdCollection::MakeIterator(Bool_t dir) const
{
// Return an ordered collection iterator.
return new TOrdCollectionIter(this, dir);
}
//______________________________________________________________________________
void TOrdCollection::MoveGapTo(Int_t start)
{
// Move gap to new position. Gap needs to be moved when objects are
// inserted not at the end.
Int_t i;
R__ASSERT(start + fGapSize - 1 < fCapacity);
if (fGapSize <= 0) {
fGapStart = start;
return;
}
if (start < fGapStart) {
for (i = fGapStart - 1; i >= start; i--)
fCont[i + fGapSize] = fCont[i];
} else if (start > fGapStart) {
Int_t stop = start + fGapSize;
for (i = fGapStart + fGapSize; i < stop; i++)
fCont[i - fGapSize] = fCont[i];
}
fGapStart = start;
for (i = fGapStart; i < fGapStart + fGapSize; i++)
fCont[i] = 0;
}
//______________________________________________________________________________
void TOrdCollection::PutAt(TObject *obj, Int_t idx)
{
// Put object at index idx. Overwrites what was at idx before.
if (IllegalIndex("PutAt", idx)) return;
Int_t phx = PhysIndex(idx);
R__ASSERT(phx >= 0 && phx < fCapacity);
fCont[phx] = obj;
Changed();
}
//______________________________________________________________________________
TObject *TOrdCollection::RemoveAt(Int_t idx)
{
// Remove object at index idx.
Int_t physIdx;
if (idx == fGapStart - 1 || idx == fGapStart) {
if (idx == fGapStart)
physIdx = fGapStart + fGapSize; // at right boundary
else
physIdx = --fGapStart; // at left boundary
} else {
physIdx = PhysIndex(idx);
if (physIdx < fGapStart) {
MoveGapTo(physIdx + 1);
physIdx = --fGapStart;
} else {
MoveGapTo(physIdx - fGapSize);
physIdx = fGapStart + fGapSize;
}
}
R__ASSERT(physIdx >= 0 && physIdx < fCapacity);
TObject *obj = fCont[physIdx];
fCont[physIdx] = 0;
fGapSize++;
fSize--;
Changed();
if (LowWaterMark()) {
Int_t newCapacity = TMath::Max(int(fCapacity / kShrinkFactor), 1);
if (fCapacity > newCapacity)
SetCapacity(newCapacity);
}
return obj;
}
//______________________________________________________________________________
TObject *TOrdCollection::Remove(TObject *obj)
{
// Remove object from collection.
if (!obj) return 0;
Int_t idx = IndexOf(obj);
if (idx == -1) return 0;
return RemoveAt(idx);
}
//______________________________________________________________________________
void TOrdCollection::SetCapacity(Int_t newCapacity)
{
// Set/change ordered collection capacity.
R__ASSERT(newCapacity > 0);
R__ASSERT(fSize <= newCapacity);
if (fCapacity == newCapacity) return;
Int_t newGapSize = newCapacity - fSize;
MoveGapTo(fCapacity - fGapSize);
fCont = (TObject**) TStorage::ReAlloc(fCont, newCapacity*sizeof(TObject*),
fCapacity*sizeof(TObject*));
fGapSize = newGapSize;
fCapacity = newCapacity;
}
//______________________________________________________________________________
void TOrdCollection::Sort()
{
// If objects in collection are sortable (i.e. IsSortable() returns true
// for all objects) then sort collection.
if (fSize <= 0 || fSorted) return;
if (!At(0)->IsSortable()) {
Error("Sort", "objects in collection are not sortable");
return;
}
MoveGapTo(fCapacity - fGapSize);
QSort(fCont, 0, fSize);
fSorted = kTRUE;
}
//______________________________________________________________________________
Int_t TOrdCollection::BinarySearch(TObject *obj)
{
// Find object using a binary search. Collection must first have been
// sorted.
Int_t result;
if (!obj) return -1;
if (!fSorted) {
Error("BinarySearch", "collection must first be sorted");
return -1;
}
MoveGapTo(fCapacity - fGapSize);
Int_t base = 0;
Int_t last = base + GetSize() - 1;
while (last >= base) {
Int_t position = (base + last) / 2;
TObject *obj2 = fCont[position];
if ((obj2 == 0) || (result = obj->Compare(obj2)) == 0)
return LogIndex(position);
if (result < 0)
last = position - 1;
else
base = position + 1;
}
return -1;
}
//////////////////////////////////////////////////////////////////////////
// //
// TOrdCollectionIter //
// //
// Iterator of ordered collection. //
// //
//////////////////////////////////////////////////////////////////////////
ClassImp(TOrdCollectionIter)
//______________________________________________________________________________
TOrdCollectionIter::TOrdCollectionIter(const TOrdCollection *col, Bool_t dir): fCol(col), fDirection(dir)
{
// Create collection iterator. By default the iteration direction
// is kIterForward. To go backward use kIterBackward.
Reset();
}
//______________________________________________________________________________
TOrdCollectionIter::TOrdCollectionIter(const TOrdCollectionIter &iter) : TIterator(iter)
{
// Copy ctor.
fCol = iter.fCol;
fDirection = iter.fDirection;
fCursor = iter.fCursor;
fCurCursor = iter.fCurCursor;
}
//______________________________________________________________________________
TIterator &TOrdCollectionIter::operator=(const TIterator &rhs)
{
// Overridden assignment operator.
if (this != &rhs && rhs.IsA() == TOrdCollectionIter::Class()) {
const TOrdCollectionIter &rhs1 = (const TOrdCollectionIter &)rhs;
fCol = rhs1.fCol;
fDirection = rhs1.fDirection;
fCursor = rhs1.fCursor;
fCurCursor = rhs1.fCurCursor;
}
return *this;
}
//______________________________________________________________________________
TOrdCollectionIter &TOrdCollectionIter::operator=(const TOrdCollectionIter &rhs)
{
// Overloaded assignment operator.
if (this != &rhs) {
fCol = rhs.fCol;
fDirection = rhs.fDirection;
fCursor = rhs.fCursor;
fCurCursor = rhs.fCurCursor;
}
return *this;
}
//______________________________________________________________________________
TObject *TOrdCollectionIter::Next()
{
// Return next object in collection. Returns 0 when no more objects in
// collection.
fCurCursor = fCursor;
if (fDirection == kIterForward) {
if (fCursor < fCol->GetSize())
return fCol->At(fCursor++);
} else {
if (fCursor >= 0)
return fCol->At(fCursor--);
}
return 0;
}
//______________________________________________________________________________
void TOrdCollectionIter::Reset()
{
// Reset collection iterator.
if (fDirection == kIterForward)
fCursor = 0;
else
fCursor = fCol->GetSize() - 1;
fCurCursor = fCursor;
}
//______________________________________________________________________________
Bool_t TOrdCollectionIter::operator!=(const TIterator &aIter) const
{
// This operator compares two TIterator objects.
if (nullptr == (&aIter))
return (fCurCursor < fCol->GetSize());
if (aIter.IsA() == TOrdCollectionIter::Class()) {
const TOrdCollectionIter &iter(dynamic_cast(aIter));
return (fCurCursor != iter.fCurCursor);
}
return false; // for base class we don't implement a comparison
}
//______________________________________________________________________________
Bool_t TOrdCollectionIter::operator!=(const TOrdCollectionIter &aIter) const
{
// This operator compares two TOrdCollectionIter objects.
if (nullptr == (&aIter))
return (fCurCursor < fCol->GetSize());
return (fCurCursor != aIter.fCurCursor);
}
//______________________________________________________________________________
TObject *TOrdCollectionIter::operator*() const
{
// Return current object or nullptr.
return (((fCurCursor >= 0) && (fCurCursor < fCol->GetSize())) ?
fCol->At(fCurCursor) : nullptr);
}