diff --git a/src/apps/installer/BlockingQueue.h b/src/apps/installer/BlockingQueue.h new file mode 100644 index 0000000000..fa5d0fc909 --- /dev/null +++ b/src/apps/installer/BlockingQueue.h @@ -0,0 +1,195 @@ +// BlockingQueue.h +// +// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef BLOCKING_QUEUE_H +#define BLOCKING_QUEUE_H + +#include + +#include + +#include "AutoLocker.h" +#include "Vector.h" + +typedef BLocker Locker; + +template +class BlockingQueue : public Locker { +public: + BlockingQueue(const char* name = NULL); + ~BlockingQueue(); + + status_t InitCheck() const; + + status_t Close(bool deleteElements, + const Vector** elements = NULL); + + status_t Push(Element* element); + status_t Pop(Element** element, + bigtime_t timeout = B_INFINITE_TIMEOUT); + status_t Peek(Element** element); + status_t Remove(Element* element); + + int32 Size(); + +private: + Vector fElements; + sem_id fElementSemaphore; +}; + +// constructor +template +BlockingQueue::BlockingQueue(const char* name) + : fElements(), + fElementSemaphore(-1) +{ + fElementSemaphore = create_sem(0, (name ? name : "blocking queue")); +} + +// destructor +template +BlockingQueue::~BlockingQueue() +{ + if (fElementSemaphore >= 0) + delete_sem(fElementSemaphore); +} + +// InitCheck +template +status_t +BlockingQueue::InitCheck() const +{ + return (fElementSemaphore < 0 ? fElementSemaphore : B_OK); +} + +// Close +template +status_t +BlockingQueue::Close(bool deleteElements, + const Vector** elements) +{ + AutoLocker _(this); + status_t error = delete_sem(fElementSemaphore); + if (error != B_OK) + return error; + fElementSemaphore = -1; + if (elements) + *elements = &fElements; + if (deleteElements) { + int32 count = fElements.Count(); + for (int32 i = 0; i < count; i++) + delete fElements.ElementAt(i); + } + return error; +} + +// Push +template +status_t +BlockingQueue::Push(Element* element) +{ + AutoLocker _(this); + if (fElementSemaphore < 0) + return B_NO_INIT; + status_t error = fElements.PushBack(element); + if (error != B_OK) + return error; + error = release_sem(fElementSemaphore); + if (error != B_OK) + fElements.Erase(fElements.Count() - 1); + return error; +} + +// Pop +template +status_t +BlockingQueue::Pop(Element** element, bigtime_t timeout) +{ + status_t error = acquire_sem_etc(fElementSemaphore, 1, B_RELATIVE_TIMEOUT, + timeout); + if (error != B_OK) + return error; + AutoLocker _(this); + if (fElementSemaphore < 0) + return B_NO_INIT; + int32 count = fElements.Count(); + if (count == 0) + return B_ERROR; + *element = fElements.ElementAt(0); + fElements.Erase(0); + return B_OK; +} + +// Peek +template +status_t +BlockingQueue::Peek(Element** element) +{ + AutoLocker _(this); + if (fElementSemaphore < 0) + return B_NO_INIT; + int32 count = fElements.Count(); + if (count == 0) + return B_ENTRY_NOT_FOUND; + *element = fElements.ElementAt(0); + return B_OK; +} + +// Remove +template +status_t +BlockingQueue::Remove(Element* element) +{ + status_t error = acquire_sem_etc(fElementSemaphore, 1, + B_RELATIVE_TIMEOUT, 0); + if (error != B_OK) + return error; + AutoLocker _(this); + if (fElementSemaphore < 0) + return B_NO_INIT; + int32 count = fElements.Remove(element); + if (count == 0) { + release_sem(fElementSemaphore); + return B_ENTRY_NOT_FOUND; + } + if (count > 1) { + ERROR(("ERROR: BlockingQueue::Remove(): Removed %ld elements!\n", + count)); + } + return error; +} + +// Size +template +int32 +BlockingQueue::Size() +{ + AutoLocker _(this); + return (fElements.Count()); +} + +#endif // BLOCKING_QUEUE_H diff --git a/src/apps/installer/CopyEngine.cpp b/src/apps/installer/CopyEngine.cpp index 21cfb4a6cb..08a9d330da 100644 --- a/src/apps/installer/CopyEngine.cpp +++ b/src/apps/installer/CopyEngine.cpp @@ -4,19 +4,27 @@ */ #include "CopyEngine.h" -#include "InstallerWindow.h" -#include "PartitionMenuItem.h" -#include "FSUndoRedo.h" -#include "FSUtils.h" + #include + #include #include #include #include +#include +#include #include #include #include +#include "CopyEngine2.h" +#include "InstallerWindow.h" +#include "FSUndoRedo.h" +#include "FSUtils.h" +#include "PackageViews.h" +#include "PartitionMenuItem.h" + + //#define COPY_TRACE #ifdef COPY_TRACE #define CALLED() printf("CALLED %s\n",__PRETTY_FUNCTION__) @@ -130,6 +138,7 @@ CopyEngine::Start(BMenu *srcMenu, BMenu *targetMenu) status_t err = B_OK; int32 entries = 0; entry_ref testRef; +bigtime_t now; fControl->Reset(); @@ -276,8 +285,21 @@ CopyEngine::Start(BMenu *srcMenu, BMenu *targetMenu) // copy source volume targetDir.Rewind(); srcDir.SetTo(srcDirectory.Path()); +now = system_time(); +#if 0 err = CopyFolder(srcDir, targetDir); - +#else + { + BMessenger messenger(fWindow); + CopyEngine2 engine(messenger, new BMessage(STATUS_MESSAGE)); + err = engine.CopyFolder(srcDirectory.Path(), targetDirectory.Path(), + fCancelLock); +if (err != B_OK) +printf("error: %s\n", strerror(err)); + } +#endif +printf("copy time: %.3fs\n", (system_time() - now) / 1000000.0); + if (err != B_OK || fControl->CheckUserCanceled()) goto error; @@ -347,6 +369,7 @@ CopyEngine::CopyFolder(BDirectory &srcDir, BDirectory &targetDir) void CopyEngine::ScanDisksPartitions(BMenu *srcMenu, BMenu *targetMenu) { + // NOTE: This is actually executed in the window thread. BDiskDevice device; BPartition *partition = NULL; diff --git a/src/apps/installer/CopyEngine.h b/src/apps/installer/CopyEngine.h index 56915d0ddf..2e725489f7 100644 --- a/src/apps/installer/CopyEngine.h +++ b/src/apps/installer/CopyEngine.h @@ -15,6 +15,7 @@ #include #include +class BLocker; class InstallerWindow; const uint32 ENGINE_START = 'eSRT'; @@ -27,6 +28,7 @@ class CopyEngine : public BLooper { void SetPackagesList(BList *list); void SetSpaceRequired(off_t bytes) { fSpaceRequired = bytes; }; bool Cancel(); + void SetLock(BLocker* lock) { fCancelLock = lock; } private: void LaunchInitScript(BPath &path); void LaunchFinishScript(BPath &path); @@ -39,6 +41,7 @@ class CopyEngine : public BLooper { InstallerCopyLoopControl *fControl; BList *fPackages; off_t fSpaceRequired; + BLocker* fCancelLock; }; #endif /* _CopyEngine_h */ diff --git a/src/apps/installer/CopyEngine2.cpp b/src/apps/installer/CopyEngine2.cpp new file mode 100644 index 0000000000..081976c0f1 --- /dev/null +++ b/src/apps/installer/CopyEngine2.cpp @@ -0,0 +1,424 @@ +#include "CopyEngine2.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "AutoLocker.h" + +using std::nothrow; + + +CopyEngine2::CopyEngine2(const BMessenger& messenger, BMessage* message) + : + fBufferQueue(), + fWriterThread(-1), + fQuitting(false), + + fBytesRead(0), + fItemsCopied(0), + fTimeRead(0), + + fBytesWritten(0), + fTimeWritten(0), + + fBytesToCopy(0), + fItemsToCopy(0), + + fCurrentTargetFolder(NULL), + fCurrentItem(NULL), + + fMessenger(messenger), + fMessage(message) +{ + fWriterThread = spawn_thread(_WriteThreadEntry, "buffer writer", + B_NORMAL_PRIORITY, this); + + if (fWriterThread >= B_OK) + resume_thread(fWriterThread); +} + + +CopyEngine2::~CopyEngine2() +{ + while (fBufferQueue.Size() > 0) + snooze(10000); + + fQuitting = true; + if (fWriterThread >= B_OK) { + int32 exitValue; + wait_for_thread(fWriterThread, &exitValue); + } + + delete fMessage; +} + + +status_t +CopyEngine2::CopyFolder(const char* source, const char* destination, + BLocker* locker) +{ + fBytesRead = 0; + fItemsCopied = 0; + fTimeRead = 0; + + fBytesWritten = 0; + fTimeWritten = 0; + + fBytesToCopy = 0; + fItemsToCopy = 0; + + fCurrentTargetFolder = NULL; + fCurrentItem = NULL; + + status_t ret = _CollectCopyInfo(source); + if (ret < B_OK) + return ret; + + if (fMessage) { + BMessage message(*fMessage); + message.AddString("status", "Performing installation."); + fMessenger.SendMessage(&message); + } + +printf("%lld bytes to read in %lld files\n", fBytesToCopy, fItemsToCopy); + + return _CopyFolder(source, destination, locker); +} + + +status_t +CopyEngine2::CopyFile(const BEntry& _source, const BEntry& _destination, + BLocker* locker) +{ + AutoLocker lock(locker); + if (locker != NULL && !lock.IsLocked()) { + // We are supposed to quit +printf("CopyFile - cancled\n"); + return B_CANCELED; + } + + BFile source(&_source, B_READ_ONLY); + status_t ret = source.InitCheck(); + if (ret < B_OK) + return ret; + + BFile* destination = new (nothrow) BFile(&_destination, + B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); + ret = destination->InitCheck(); + if (ret < B_OK) { + delete destination; + return ret; + } + + int32 loopIteration = 0; + + while (true) { + if (fBufferQueue.Size() >= BUFFER_COUNT) { + // the queue is "full", just wait a bit, the + // write thread will empty it + snooze(1000); + continue; + } + + // allocate buffer + Buffer* buffer = new (nothrow) Buffer(destination); + if (!buffer || !buffer->buffer) { + delete destination; + delete buffer; + fprintf(stderr, "reading loop: out of memory\n"); + return B_NO_MEMORY; + } + + // fill buffer + ssize_t read = source.Read(buffer->buffer, buffer->size); + if (read < 0) { + ret = (status_t)read; + fprintf(stderr, "Failed to read data: %s\n", strerror(ret)); + break; + } + + fBytesRead += read; + loopIteration += 1; + if (loopIteration % 10 == 0) + _UpdateProgress(); + + buffer->deleteFile = read == 0; + if (read > 0) + buffer->validBytes = (size_t)read; + else + buffer->validBytes = 0; + + // enqueue the buffer + ret = fBufferQueue.Push(buffer); + if (ret < B_OK) { + buffer->deleteFile = false; + delete buffer; + delete destination; + return ret; + } + + // quit if done + if (read == 0) + break; + } + + return ret; +} + + +// #pragma mark - + + +status_t +CopyEngine2::_CollectCopyInfo(const char* _source) +{ + BDirectory source(_source); + status_t ret = source.InitCheck(); + if (ret < B_OK) + return ret; + + BEntry entry; + while (source.GetNextEntry(&entry) == B_OK) { + struct stat statInfo; + entry.GetStat(&statInfo); + + if (S_ISDIR(statInfo.st_mode)) { + // handle recursive directory copy + BPath srcFolder; + ret = entry.GetPath(&srcFolder); + if (ret < B_OK) + return ret; + + ret = _CollectCopyInfo(srcFolder.Path()); + if (ret < B_OK) + return ret; + } else if (S_ISLNK(statInfo.st_mode)) { + } else { + // file data + off_t size; + ret = entry.GetSize(&size); + if (ret < B_OK) + return ret; + + fBytesToCopy += size; + } + + fItemsToCopy++; + } + + return B_OK; +} + + +status_t +CopyEngine2::_CopyFolder(const char* _source, const char* _destination, + BLocker* locker) +{ + fCurrentTargetFolder = _destination; + + BDirectory source(_source); + status_t ret = source.InitCheck(); + if (ret < B_OK) + return ret; + + ret = create_directory(_destination, 0777); + if (ret < B_OK && ret != B_FILE_EXISTS) + return ret; + + BDirectory destination(_destination); + ret = destination.InitCheck(); + if (ret < B_OK) + return ret; + + BEntry entry; + while (source.GetNextEntry(&entry) == B_OK) { + AutoLocker lock(locker); + if (locker != NULL && !lock.IsLocked()) { + // We are supposed to quit + return B_CANCELED; + } + + char name[B_FILE_NAME_LENGTH]; + status_t ret = entry.GetName(name); + if (ret < B_OK) + return ret; + + fItemsCopied++; + fCurrentItem = name; + + _UpdateProgress(); + + struct stat statInfo; + entry.GetStat(&statInfo); + + BEntry copy(&destination, name); + + if (S_ISDIR(statInfo.st_mode)) { + // handle recursive directory copy + BPath srcFolder; + ret = entry.GetPath(&srcFolder); + if (ret < B_OK) + return ret; + + BPath dstFolder; + ret = copy.GetPath(&dstFolder); + if (ret < B_OK) + return ret; + + if (locker != NULL) + lock.Unlock(); + + ret = _CopyFolder(srcFolder.Path(), dstFolder.Path(), locker); + if (ret < B_OK) + return ret; + + if (locker != NULL && !lock.Lock()) { + // We are supposed to quit + return B_CANCELED; + } + } else if (S_ISLNK(statInfo.st_mode)) { + // copy symbolic links + BSymLink srcLink(&entry); + if (ret < B_OK) + return ret; + + char linkPath[B_PATH_NAME_LENGTH]; + ssize_t read = srcLink.ReadLink(linkPath, B_PATH_NAME_LENGTH - 1); + if (read < 0) + return (status_t)read; + + // just in case it already exists... + copy.Remove(); + + BSymLink dstLink; + ret = destination.CreateSymLink(name, linkPath, &dstLink); + if (ret < B_OK) + return ret; + } else { + // copy file data + // NOTE: Do not pass the locker, we simply keep holding the lock! + ret = CopyFile(entry, copy); + if (ret < B_OK) + return ret; + } + + // copy attributes + BNode sourceNode(&entry); + BNode targetNode(©); + char attrName[B_ATTR_NAME_LENGTH]; + while (sourceNode.GetNextAttrName(attrName) == B_OK) { + attr_info info; + if (sourceNode.GetAttrInfo(attrName, &info) < B_OK) + continue; + size_t size = 4096; + uint8 buffer[size]; + off_t offset = 0; + ssize_t read = sourceNode.ReadAttr(attrName, info.type, + offset, buffer, min_c(size, info.size)); + while (read >= 0) { + targetNode.WriteAttr(attrName, info.type, offset, buffer, read); + offset += read; + read = sourceNode.ReadAttr(attrName, info.type, + offset, buffer, min_c(size, info.size - offset)); + if (read == 0) + break; + } + } + + // copy basic attributes + copy.SetPermissions(statInfo.st_mode); + copy.SetOwner(statInfo.st_uid); + copy.SetGroup(statInfo.st_gid); + copy.SetModificationTime(statInfo.st_mtime); + copy.SetCreationTime(statInfo.st_crtime); + } + + return B_OK; +} + + +void +CopyEngine2::_UpdateProgress() +{ + if (fMessage != NULL) { + BMessage message(*fMessage); + float progress = 100.0 * fBytesRead / fBytesToCopy; + message.AddFloat("progress", progress); + message.AddInt32("current", fItemsCopied); + message.AddInt32("maximum", fItemsToCopy); + message.AddString("item", fCurrentItem); + message.AddString("folder", fCurrentTargetFolder); + fMessenger.SendMessage(&message); + } +} + + +int32 +CopyEngine2::_WriteThreadEntry(void* cookie) +{ + CopyEngine2* engine = (CopyEngine2*)cookie; + engine->_WriteThread(); + return B_OK; +} + + +void +CopyEngine2::_WriteThread() +{ + bigtime_t bufferWaitTimeout = 100000; + + while (!fQuitting) { + + bigtime_t now = system_time(); + + Buffer* buffer; + status_t ret = fBufferQueue.Pop(&buffer, bufferWaitTimeout); + if (ret == B_TIMED_OUT) { + // no buffer, timeout + continue; + } else if (ret == B_NO_INIT) { + // real error + return; + } else if (ret != B_OK) { + // no buffer, queue error + snooze(10000); + continue; + } + + if (!buffer->deleteFile) { + ssize_t written = buffer->file->Write(buffer->buffer, + buffer->validBytes); + if (written != (ssize_t)buffer->validBytes) { + // TODO: this should somehow be propagated back + // to the main thread! + fprintf(stderr, "Failed to write data: %s\n", + strerror((status_t)written)); + } + fBytesWritten += written; + } + + delete buffer; + + // measure performance + fTimeWritten += system_time() - now; + } + + double megaBytes = (double)fBytesWritten / (1024 * 1024); + double seconds = (fTimeWritten / 1000000); + if (seconds > 0) { + printf("%.2f MB written (%.2f MB/s)\n", megaBytes, + megaBytes / seconds); + } +} + + diff --git a/src/apps/installer/CopyEngine2.h b/src/apps/installer/CopyEngine2.h new file mode 100644 index 0000000000..8c68b947bf --- /dev/null +++ b/src/apps/installer/CopyEngine2.h @@ -0,0 +1,91 @@ +#ifndef COPY_ENGINE_2_H +#define COPY_ENGINE_2_H + + +#include +#include +#include + +#include "BlockingQueue.h" + +class BFile; +class BLocker; +class BMessage; +class BMessenger; + +class CopyEngine2 { +public: + CopyEngine2(const BMessenger& messenger, + BMessage* message); + virtual ~CopyEngine2(); + + status_t CopyFolder(const char* source, + const char* destination, + BLocker* locker = NULL); + + status_t CopyFile(const BEntry& entry, + const BEntry& destination, + BLocker* locker = NULL); + +private: + status_t _CollectCopyInfo(const char* source); + status_t _CopyFolder(const char* source, + const char* destination, + BLocker* locker = NULL); + + void _UpdateProgress(); + + static int32 _WriteThreadEntry(void* cookie); + void _WriteThread(); + +private: + enum { + BUFFER_COUNT = 16, + BUFFER_SIZE = 1024 * 1024 + }; + struct Buffer { + Buffer(BFile* file) + : file(file) + , buffer(malloc(BUFFER_SIZE)) + , size(BUFFER_SIZE) + , validBytes(0) + , deleteFile(false) + { + } + ~Buffer() + { + if (deleteFile) + delete file; + free(buffer); + } + BFile* file; + void* buffer; + size_t size; + size_t validBytes; + bool deleteFile; + }; + + BlockingQueue fBufferQueue; + + thread_id fWriterThread; + volatile bool fQuitting; + + off_t fBytesRead; + uint64 fItemsCopied; + bigtime_t fTimeRead; + + off_t fBytesWritten; + bigtime_t fTimeWritten; + + off_t fBytesToCopy; + uint64 fItemsToCopy; + + const char* fCurrentTargetFolder; + const char* fCurrentItem; + + BMessenger fMessenger; + BMessage* fMessage; +}; + + +#endif // COPY_ENGINE_2_H diff --git a/src/apps/installer/FSUtils.cpp b/src/apps/installer/FSUtils.cpp index 645f30ed23..b3aaabbdaa 100644 --- a/src/apps/installer/FSUtils.cpp +++ b/src/apps/installer/FSUtils.cpp @@ -360,7 +360,7 @@ SetUpPoseLocation(ino_t sourceParentIno, ino_t destParentIno, if (loc && loc != (BPoint *)-1) { // loc of -1 is used when copying/moving into a window in list mode // where copying positions would not work - // ToSo: + // TODO: // should push all this logic to upper levels FSSetPoseLocation(destParentIno, destNode, *loc); } diff --git a/src/apps/installer/InstallerWindow.cpp b/src/apps/installer/InstallerWindow.cpp index d7eee70719..7e998348c5 100644 --- a/src/apps/installer/InstallerWindow.cpp +++ b/src/apps/installer/InstallerWindow.cpp @@ -1,6 +1,6 @@ /* * Copyright 2009, Stephan Aßmus - * Copyright 2005-2008, Jérôme DUVAL. + * Copyright 2005-2008, Jérôme DUVAL * All rights reserved. Distributed under the terms of the MIT License. */ @@ -13,24 +13,31 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include +#include #include +#include #include +#include #include #include #include "tracker_private.h" +#include "CopyEngine.h" #include "DialogPane.h" +#include "PackageViews.h" #include "PartitionMenuItem.h" @@ -187,19 +194,24 @@ SeparatorView::PreferredSize() // #pragma mark - +static BLayoutItem* +layout_item_for(BView* view) +{ + BLayout* layout = view->Parent()->GetLayout(); + int32 index = layout->IndexOfView(view); + return layout->ItemAt(index); +} + + InstallerWindow::InstallerWindow() : BWindow(BRect(-2000, -2000, -1800, -1800), "Installer", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS), fNeedsToCenterOnScreen(true), - fDriveSetupLaunched(false), fInstallStatus(kReadyForInstall), - - fPackagesLayoutItem(NULL), - fSizeViewLayoutItem(NULL) + fCopyEngine(new CopyEngine(this)), + fCopyEngineLock(NULL) { - fCopyEngine = new CopyEngine(this); - LogoView* logoView = new LogoView(); fStatusView = new BTextView("statusView", be_plain_font, NULL, B_WILL_DRAW); @@ -210,24 +222,6 @@ InstallerWindow::InstallerWindow() BSize logoSize = logoView->MinSize(); fStatusView->SetExplicitMinSize(BSize(logoSize.width * 0.66, B_SIZE_UNSET)); - fBeginButton = new BButton("begin_button", "Begin", - new BMessage(BEGIN_MESSAGE)); - fBeginButton->MakeDefault(true); - fBeginButton->SetEnabled(false); - - fSetupButton = new BButton("setup_button", - "Setup partitions" B_UTF8_ELLIPSIS, new BMessage(SETUP_MESSAGE)); - - fPackagesView = new PackagesView("packages_view"); - BScrollView* packagesScrollView = new BScrollView("packagesScroll", - fPackagesView, B_WILL_DRAW, false, true); - - fDrawButton = new PaneSwitch("options_button"); - fDrawButton->SetLabels("Hide Optional Packages", "Show Optional Packages"); - fDrawButton->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE)); - fDrawButton->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); - fDrawButton->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); - fDestMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false); fSrcMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false); @@ -235,10 +229,22 @@ InstallerWindow::InstallerWindow() NULL); fSrcMenuField->SetAlignment(B_ALIGN_RIGHT); - fDestMenuField = new BMenuField("destMenuField", "Onto: ", fDestMenu, - NULL); + fDestMenuField = new BMenuField("destMenuField", "Onto: ", fDestMenu, NULL); fDestMenuField->SetAlignment(B_ALIGN_RIGHT); + fPackagesSwitch = new PaneSwitch("options_button"); + fPackagesSwitch->SetLabels("Hide Optional Packages", + "Show Optional Packages"); + fPackagesSwitch->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE)); + fPackagesSwitch->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, + B_SIZE_UNLIMITED)); + fPackagesSwitch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, + B_ALIGN_TOP)); + + fPackagesView = new PackagesView("packages_view"); + BScrollView* packagesScrollView = new BScrollView("packagesScroll", + fPackagesView, B_WILL_DRAW, false, true); + const char* requiredDiskSpaceString = "Additional disk space required: 0.0 KB"; fSizeView = new BStringView("size_view", requiredDiskSpaceString); @@ -247,6 +253,17 @@ InstallerWindow::InstallerWindow() fSizeView->SetExplicitAlignment( BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE)); + fProgressBar = new BStatusBar("progress", "Install Progress: "); + fProgressBar->SetMaxValue(100.0); + + fBeginButton = new BButton("begin_button", "Begin", + new BMessage(BEGIN_MESSAGE)); + fBeginButton->MakeDefault(true); + fBeginButton->SetEnabled(false); + + fSetupButton = new BButton("setup_button", + "Setup partitions" B_UTF8_ELLIPSIS, new BMessage(SETUP_MESSAGE)); + SetLayout(new BGroupLayout(B_HORIZONTAL)); AddChild(BGroupLayoutBuilder(B_VERTICAL) .Add(BGroupLayoutBuilder(B_HORIZONTAL) @@ -263,13 +280,12 @@ InstallerWindow::InstallerWindow() .Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 2) - .Add(fDrawButton, 0, 3, 2) + .Add(fPackagesSwitch, 0, 3, 2) .Add(packagesScrollView, 0, 4, 2) - .Add(fSizeView, 0, 5, 2) + .Add(fProgressBar, 0, 5, 2) + .Add(fSizeView, 0, 6, 2) ) - .AddStrut(5) - .Add(BGroupLayoutBuilder(B_HORIZONTAL) .Add(fSetupButton) .AddGlue() @@ -279,17 +295,15 @@ InstallerWindow::InstallerWindow() ) ); - // Make the optional packages invisible on start - BLayout* layout = packagesScrollView->Parent()->GetLayout(); - int32 index = layout->IndexOfView(packagesScrollView); - fPackagesLayoutItem = layout->ItemAt(index); - - layout = fSizeView->Parent()->GetLayout(); - index = layout->IndexOfView(fSizeView); - fSizeViewLayoutItem = layout->ItemAt(index); + // Make the optional packages and progress bar invisible on start + fPackagesLayoutItem = layout_item_for(packagesScrollView); + fPkgSwitchLayoutItem = layout_item_for(fPackagesSwitch); + fSizeViewLayoutItem = layout_item_for(fSizeView); + fProgressLayoutItem = layout_item_for(fProgressBar); fPackagesLayoutItem->SetVisible(false); fSizeViewLayoutItem->SetVisible(false); + fProgressLayoutItem->SetVisible(false); // finish creating window if (!be_roster->IsRunning(kDeskbarSignature)) @@ -339,9 +353,17 @@ InstallerWindow::MessageReceived(BMessage *msg) { switch (msg->what) { case RESET_INSTALL: + delete fCopyEngineLock; + fCopyEngineLock = NULL; + fInstallStatus = kReadyForInstall; fBeginButton->SetEnabled(true); _DisableInterface(false); + + fProgressLayoutItem->SetVisible(false); + fPkgSwitchLayoutItem->SetVisible(true); + _ShowOptionalPackages(); + fBeginButton->SetLabel("Begin"); break; case START_SCAN: @@ -351,23 +373,39 @@ InstallerWindow::MessageReceived(BMessage *msg) switch (fInstallStatus) { case kReadyForInstall: { + delete fCopyEngineLock; + fCopyEngineLock = new BLocker("copy engine lock"); BList* list = new BList(); int32 size = 0; fPackagesView->GetPackagesToInstall(list, &size); + fCopyEngine->SetLock(fCopyEngineLock); fCopyEngine->SetPackagesList(list); fCopyEngine->SetSpaceRequired(size); fInstallStatus = kInstalling; BMessenger(fCopyEngine).SendMessage(ENGINE_START); fBeginButton->SetLabel("Stop"); _DisableInterface(true); + + fProgressBar->SetTo(0.0, NULL, NULL); + + fPkgSwitchLayoutItem->SetVisible(false); + fPackagesLayoutItem->SetVisible(false); + fSizeViewLayoutItem->SetVisible(false); + fProgressLayoutItem->SetVisible(true); break; } case kInstalling: - if (fCopyEngine->Cancel()) { - fInstallStatus = kCancelled; - _SetStatusMessage("Installation cancelled."); - } + { + _QuitCopyEngine(true); +// if (fCopyEngine->Cancel()) { +// fInstallStatus = kCancelled; +// _SetStatusMessage("Installation cancelled."); +// fProgressLayoutItem->SetVisible(false); +// fPkgSwitchLayoutItem->SetVisible(true); +// _ShowOptionalPackages(); +// } break; + } case kFinished: PostMessage(B_QUIT_REQUESTED); break; @@ -388,7 +426,8 @@ InstallerWindow::MessageReceived(BMessage *msg) case SETUP_MESSAGE: _LaunchDriveSetup(); break; - case PACKAGE_CHECKBOX: { + case PACKAGE_CHECKBOX: + { char buffer[15]; fPackagesView->GetTotalSizeAsString(buffer); char string[255]; @@ -396,22 +435,47 @@ InstallerWindow::MessageReceived(BMessage *msg) fSizeView->SetText(string); break; } - case STATUS_MESSAGE: { + case STATUS_MESSAGE: + { if (fInstallStatus != kInstalling) break; - const char *status; - if (msg->FindString("status", &status) == B_OK) { - fLastStatus = fStatusView->Text(); - _SetStatusMessage(status); - } else - _SetStatusMessage(fLastStatus.String()); + float progress; + if (msg->FindFloat("progress", &progress) == B_OK) { + const char* currentItem; + if (msg->FindString("item", ¤tItem) != B_OK) + currentItem = "???"; + BString trailingLabel; + int32 currentCount; + int32 maximumCount; + if (msg->FindInt32("current", ¤tCount) == B_OK + && msg->FindInt32("maximum", &maximumCount) == B_OK) { + trailingLabel << currentCount << " of " << maximumCount; + } else { + trailingLabel << "?? of ??"; + } + fProgressBar->SetTo(progress, currentItem, + trailingLabel.String()); + } else { + const char *status; + if (msg->FindString("status", &status) == B_OK) { + fLastStatus = fStatusView->Text(); + _SetStatusMessage(status); + } else + _SetStatusMessage(fLastStatus.String()); + } break; } case INSTALL_FINISHED: + delete fCopyEngineLock; + fCopyEngineLock = NULL; + fBeginButton->SetLabel("Quit"); _SetStatusMessage("Installation completed."); fInstallStatus = kFinished; _DisableInterface(false); + fProgressLayoutItem->SetVisible(false); + fPkgSwitchLayoutItem->SetVisible(true); + _ShowOptionalPackages(); break; case B_SOME_APP_LAUNCHED: case B_SOME_APP_QUIT: @@ -447,6 +511,7 @@ InstallerWindow::QuitRequested() "Installer window.", "OK"))->Go(); return false; } + _QuitCopyEngine(false); fCopyEngine->PostMessage(B_QUIT_REQUESTED); be_app->PostMessage(B_QUIT_REQUESTED); return true; @@ -460,8 +525,8 @@ void InstallerWindow::_ShowOptionalPackages() { if (fPackagesLayoutItem && fSizeViewLayoutItem) { - fPackagesLayoutItem->SetVisible(fDrawButton->Value()); - fSizeViewLayoutItem->SetVisible(fDrawButton->Value()); + fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value()); + fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value()); } } @@ -575,6 +640,8 @@ InstallerWindow::_UpdateControls() "pop-up menus. Then click \"Begin\"."); } + fInstallStatus = kReadyForInstall; + fBeginButton->SetLabel("Begin"); fBeginButton->SetEnabled(srcItem && dstItem); } @@ -631,6 +698,44 @@ InstallerWindow::_SetStatusMessage(const char *text) } +void +InstallerWindow::_QuitCopyEngine(bool askUser) +{ + if (fCopyEngineLock == NULL) + return; + + // first of all block the copy engine + fCopyEngineLock->Lock(); + + bool quit = true; + if (askUser) { + quit = (new BAlert("cancel", + "Are you sure you want to to stop the installation?", + "Continue", "Stop", 0, + B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0; + } + + if (quit) { + int32 tries = 0; + // wait until the engine blocks + while (fCopyEngineLock->CountLockRequests() < 2) { + // TODO: There is a race here, the copy engine + // may have finished before we locked the engine + // lock. That's why we limit the number of tries + // here. + tries++; + if (tries > 100) + break; + snooze(3000); + } + // make it quit by having it's lock fail + delete fCopyEngineLock; + fCopyEngineLock = NULL; + } else + fCopyEngineLock->Unlock(); +} + + // #pragma mark - diff --git a/src/apps/installer/InstallerWindow.h b/src/apps/installer/InstallerWindow.h index 6f00d98e0a..7d9b200135 100644 --- a/src/apps/installer/InstallerWindow.h +++ b/src/apps/installer/InstallerWindow.h @@ -1,27 +1,29 @@ /* - * Copyright 2005, Jérôme DUVAL. All rights reserved. - * Distributed under the terms of the MIT License. + * Copyright 2009, Stephan Aßmus + * Copyright 2005, Jérôme DUVAL + * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef INSTALLER_WINDOW_H #define INSTALLER_WINDOW_H -#include -#include -#include -#include -#include #include -#include #include -#include "CopyEngine.h" -#include "PackageViews.h" - namespace BPrivate { class PaneSwitch; }; +using namespace BPrivate; +class BButton; class BLayoutItem; +class BLocker; +class BMenu; +class BMenuField; +class BStatusBar; +class BStringView; +class BTextView; +class CopyEngine; +class PackagesView; enum InstallStatus { kReadyForInstall, @@ -36,6 +38,7 @@ const uint32 RESET_INSTALL = 'iRSI'; const char PACKAGES_DIRECTORY[] = "_packages_"; const char VAR_DIRECTORY[] = "var"; + class InstallerWindow : public BWindow { public: InstallerWindow(); @@ -56,31 +59,39 @@ private: void _PublishPackages(); void _SetStatusMessage(const char* text); + void _QuitCopyEngine(bool askUser); + static int _ComparePackages(const void* firstArg, const void* secondArg); - BButton* fBeginButton; - BButton* fSetupButton; - PaneSwitch* fDrawButton; - - bool fNeedsToCenterOnScreen; - - bool fDriveSetupLaunched; - InstallStatus fInstallStatus; - BTextView* fStatusView; BMenu* fSrcMenu; BMenu* fDestMenu; BMenuField* fSrcMenuField; BMenuField* fDestMenuField; + + PaneSwitch* fPackagesSwitch; PackagesView* fPackagesView; BStringView* fSizeView; + BStatusBar* fProgressBar; + + BLayoutItem* fPkgSwitchLayoutItem; BLayoutItem* fPackagesLayoutItem; BLayoutItem* fSizeViewLayoutItem; + BLayoutItem* fProgressLayoutItem; + + BButton* fBeginButton; + BButton* fSetupButton; + + bool fNeedsToCenterOnScreen; + + bool fDriveSetupLaunched; + InstallStatus fInstallStatus; CopyEngine* fCopyEngine; BString fLastStatus; + BLocker* fCopyEngineLock; }; #endif // INSTALLER_WINDOW_H diff --git a/src/apps/installer/Jamfile b/src/apps/installer/Jamfile index 3d9f2c1125..5d663bfe61 100644 --- a/src/apps/installer/Jamfile +++ b/src/apps/installer/Jamfile @@ -5,6 +5,7 @@ SubDirHdrs [ FDirName $(HAIKU_TOP) src kits tracker ] ; Application Installer : CopyEngine.cpp + CopyEngine2.cpp FSUtils.cpp InstallerApp.cpp InstallerCopyLoopControl.cpp diff --git a/src/apps/installer/Vector.h b/src/apps/installer/Vector.h new file mode 100644 index 0000000000..d7f52a2b90 --- /dev/null +++ b/src/apps/installer/Vector.h @@ -0,0 +1,798 @@ +// Vector.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef _VECTOR_H +#define _VECTOR_H + +#include +#include +#include + +#include + +template class VectorIterator; + +// for convenience +#define _VECTOR_TEMPLATE_LIST template +#define _VECTOR_CLASS_NAME Vector + +/*! + \class Vector + \brief A generic vector implementation. +*/ +template +class Vector { +public: + typedef VectorIterator Iterator; + typedef VectorIterator ConstIterator; + +private: + static const size_t kDefaultChunkSize = 10; + static const size_t kMaximalChunkSize = 1024 * 1024; + +public: + Vector(size_t chunkSize = kDefaultChunkSize); + ~Vector(); + + status_t PushFront(const Value &value); + status_t PushBack(const Value &value); + + void PopFront(); + void PopBack(); + + status_t Insert(const Value &value, int32 index); + status_t Insert(const Value &value, const Iterator &iterator); + + int32 Remove(const Value &value); + Iterator Erase(int32 index); + Iterator Erase(const Iterator &iterator); + + inline int32 Count() const; + inline bool IsEmpty() const; + void MakeEmpty(); + + inline Iterator Begin(); + inline ConstIterator Begin() const; + inline Iterator End(); + inline ConstIterator End() const; + inline Iterator Null(); + inline ConstIterator Null() const; + inline Iterator IteratorForIndex(int32 index); + inline ConstIterator IteratorForIndex(int32 index) const; + + inline const Value &ElementAt(int32 index) const; + inline Value &ElementAt(int32 index); + + int32 IndexOf(const Value &value, int32 start = 0) const; + Iterator Find(const Value &value); + Iterator Find(const Value &value, const Iterator &start); + ConstIterator Find(const Value &value) const; + ConstIterator Find(const Value &value, const ConstIterator &start) const; + + inline Value &operator[](int32 index); + inline const Value &operator[](int32 index) const; + + // debugging + int32 GetCapacity() const { return fCapacity; } + +private: + inline static void _MoveItems(Value *values, int32 offset, int32 count); + bool _Resize(size_t count); + inline int32 _IteratorIndex(const Iterator &iterator) const; + inline int32 _IteratorIndex(const ConstIterator &iterator) const; + +private: + size_t fCapacity; + size_t fChunkSize; + int32 fItemCount; + Value *fItems; +}; + + +// VectorIterator +template +class VectorIterator { +private: + typedef VectorIterator Iterator; + +public: + inline VectorIterator() + : fElement(NULL) + { + } + + inline VectorIterator(const Iterator &other) + : fElement(other.fElement) + { + } + + inline Iterator &operator++() + { + if (fElement) + ++fElement; + return *this; + } + + inline Iterator operator++(int) + { + Iterator it(*this); + ++*this; + return it; + } + + inline Iterator &operator--() + { + if (fElement) + --fElement; + return *this; + } + + inline Iterator operator--(int) + { + Iterator it(*this); + --*this; + return it; + } + + inline Iterator &operator=(const Iterator &other) + { + fElement = other.fElement; + return *this; + } + + + inline bool operator==(const Iterator &other) const + { + return (fElement == other.fElement); + } + + inline bool operator!=(const Iterator &other) const + { + return !(*this == other); + } + + inline Value &operator*() const + { + return *fElement; + } + + inline Value *operator->() const + { + return fElement; + } + + inline operator bool() const + { + return fElement; + } + +// private +public: + inline VectorIterator(Value *element) + : fElement(element) + { + } + + inline Value *Element() const + { + return fElement; + } + +protected: + Value *fElement; +}; + + +// Vector + +// constructor +/*! \brief Creates an empty vector. + \param chunkSize The granularity for the vector's capacity, i.e. the + minimal number of elements the capacity grows or shrinks when + necessary. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Vector(size_t chunkSize) + : fCapacity(0), + fChunkSize(chunkSize), + fItemCount(0), + fItems(NULL) +{ + if (fChunkSize == 0 || fChunkSize > kMaximalChunkSize) + fChunkSize = kDefaultChunkSize; + _Resize(0); +} + +// destructor +/*! \brief Frees all resources associated with the object. + + The contained elements are destroyed. Note, that, if the element + type is a pointer type, only the pointer is destroyed, not the object + it points to. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::~Vector() +{ + MakeEmpty(); + free(fItems); +} + +// PushFront +/*! \brief Inserts a copy of the supplied value at the beginning of the + vector. + \param value The element to be inserted. + \return + - \c B_OK: Everything went fine. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::PushFront(const Value &value) +{ + return Insert(value, 0); +} + +// PushBack +/*! \brief Inserts a copy of the supplied value at the end of the vector. + \param value The element to be inserted. + \return + - \c B_OK: Everything went fine. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::PushBack(const Value &value) +{ + return Insert(value, fItemCount); +} + +// PopFront +/*! \brief Removes the first element of the vector. + + Invocation on an empty vector is harmless. +*/ +_VECTOR_TEMPLATE_LIST +void +_VECTOR_CLASS_NAME::PopFront() +{ + if (fItemCount > 0) + Erase(0); +} + +// PopBack +/*! \brief Removes the last element of the vector. + + Invocation on an empty vector is harmless. +*/ +_VECTOR_TEMPLATE_LIST +void +_VECTOR_CLASS_NAME::PopBack() +{ + if (fItemCount > 0) + Erase(fItemCount - 1); +} + +// _MoveItems +/*! \brief Moves elements within an array. + \param items The elements to be moved. + \param offset The index to which the elements shall be moved. May be + negative. + \param count The number of elements to be moved. +*/ +_VECTOR_TEMPLATE_LIST +inline +void +_VECTOR_CLASS_NAME::_MoveItems(Value* items, int32 offset, int32 count) +{ + if (count > 0 && offset != 0) + memmove(items + offset, items, count * sizeof(Value)); +} + +// Insert +/*! \brief Inserts a copy of the the supplied value at the given index. + \param value The value to be inserted. + \param index The index at which to insert the new element. It must + hold: 0 <= \a index <= Count(). + \return + - \c B_OK: Everything went fine. + - \c B_BAD_VALUE: \a index is out of range. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::Insert(const Value &value, int32 index) +{ + if (index < 0 || index > fItemCount) + return B_BAD_VALUE; + if (!_Resize(fItemCount + 1)) + return B_NO_MEMORY; + _MoveItems(fItems + index, 1, fItemCount - index - 1); + new(fItems + index) Value(value); + return B_OK; +} + +// Insert +/*! \brief Inserts a copy of the the supplied value at the given position. + \param value The value to be inserted. + \param iterator An iterator specifying the position at which to insert + the new element. + \return + - \c B_OK: Everything went fine. + - \c B_BAD_VALUE: \a iterator is is invalid. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::Insert(const Value &value, const Iterator &iterator) +{ + int32 index = _IteratorIndex(iterator); + if (index >= 0) + return Insert(value, index); + return B_BAD_VALUE; +} + +// Remove +/*! \brief Removes all elements of the supplied value. + \param value The value of the elements to be removed. + \return The number of removed occurrences. +*/ +_VECTOR_TEMPLATE_LIST +int32 +_VECTOR_CLASS_NAME::Remove(const Value &value) +{ + int32 count = 0; + for (int32 i = fItemCount - 1; i >= 0; i--) { + if (ElementAt(i) == value) { + Erase(i); + count++; + } + } + return count; +} + +// Erase +/*! \brief Removes the element at the given index. + \param index The position of the element to be removed. + \return An iterator referring to the element now being located at index + \a index (End(), if it was the last element that has been + removed), or Null(), if \a index was out of range. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Erase(int32 index) +{ + if (index >= 0 && index < fItemCount) { + fItems[index].~Value(); + _MoveItems(fItems + index + 1, -1, fItemCount - index - 1); + _Resize(fItemCount - 1); + return Iterator(fItems + index); + } + return Null(); +} + +// Erase +/*! \brief Removes the element at the given position. + \param iterator An iterator referring to the element to be removed. + \return An iterator referring to the element succeeding the removed + one (End(), if it was the last element that has been + removed), or Null(), if \a iterator was an invalid iterator + (in this case including End()). +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Erase(const Iterator &iterator) +{ + int32 index = _IteratorIndex(iterator); + if (index >= 0 && index < fItemCount) + return Erase(index); + return Null(); +} + +// Count +/*! \brief Returns the number of elements the vector contains. + \return The number of elements the vector contains. +*/ +_VECTOR_TEMPLATE_LIST +inline +int32 +_VECTOR_CLASS_NAME::Count() const +{ + return fItemCount; +} + +// IsEmpty +/*! \brief Returns whether the vector is empty. + \return \c true, if the vector is empty, \c false otherwise. +*/ +_VECTOR_TEMPLATE_LIST +inline +bool +_VECTOR_CLASS_NAME::IsEmpty() const +{ + return (fItemCount == 0); +} + +// MakeEmpty +/*! \brief Removes all elements from the vector. +*/ +_VECTOR_TEMPLATE_LIST +void +_VECTOR_CLASS_NAME::MakeEmpty() +{ + for (int32 i = 0; i < fItemCount; i++) + fItems[i].~Value(); + _Resize(0); +} + +// Begin +/*! \brief Returns an iterator referring to the beginning of the vector. + + If the vector is not empty, Begin() refers to its first element, + otherwise it is equal to End() and must not be dereferenced! + + \return An iterator referring to the beginning of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Begin() +{ + return Iterator(fItems); +} + +// Begin +/*! \brief Returns an iterator referring to the beginning of the vector. + + If the vector is not empty, Begin() refers to its first element, + otherwise it is equal to End() and must not be dereferenced! + + \return An iterator referring to the beginning of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Begin() const +{ + return ConstIterator(fItems); +} + +// End +/*! \brief Returns an iterator referring to the end of the vector. + + The position identified by End() is the one succeeding the last + element, i.e. it must not be dereferenced! + + \return An iterator referring to the end of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::End() +{ + return Iterator(fItems + fItemCount); +} + +// End +/*! \brief Returns an iterator referring to the end of the vector. + + The position identified by End() is the one succeeding the last + element, i.e. it must not be dereferenced! + + \return An iterator referring to the end of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::End() const +{ + return ConstIterator(fItems + fItemCount); +} + +// Null +/*! \brief Returns an invalid iterator. + + Null() is used as a return value, if something went wrong. It must + neither be incremented or decremented nor dereferenced! + + \return An invalid iterator. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Null() +{ + return Iterator(NULL); +} + +// Null +/*! \brief Returns an invalid iterator. + + Null() is used as a return value, if something went wrong. It must + neither be incremented or decremented nor dereferenced! + + \return An invalid iterator. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Null() const +{ + return ConstIterator(NULL); +} + +// IteratorForIndex +/*! \brief Returns an iterator for a given index. + \return An iterator referring to the same element as \a index, or + End(), if \a index is out of range. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::IteratorForIndex(int32 index) +{ + if (index >= 0 && index <= fItemCount) + return Iterator(fItems + index); + return End(); +} + +// IteratorForIndex +/*! \brief Returns an iterator for a given index. + \return An iterator referring to the same element as \a index, or + End(), if \a index is out of range. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::IteratorForIndex(int32 index) const +{ + if (index >= 0 && index <= fItemCount) + return ConstIterator(fItems + index); + return End(); +} + +// ElementAt +/*! \brief Returns the element at a given index. + \param index The index identifying the element to be returned. + \return The element identified by the given index. +*/ +_VECTOR_TEMPLATE_LIST +inline +const Value & +_VECTOR_CLASS_NAME::ElementAt(int32 index) const +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + // Return the 0th element by default. Unless the allocation failed, there + // is always a 0th element -- uninitialized perhaps. + return fItems[0]; +} + +// ElementAt +/*! \brief Returns the element at a given index. + \param index The index identifying the element to be returned. + \return The element identified by the given index. +*/ +_VECTOR_TEMPLATE_LIST +inline +Value & +_VECTOR_CLASS_NAME::ElementAt(int32 index) +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + // Return the 0th element by default. Unless the allocation failed, there + // is always a 0th element -- uninitialized perhaps. + return fItems[0]; +} + +// IndexOf +/*! \brief Returns the index of the next element with the specified value. + \param value The value of the element to be found. + \param start The index at which to be started to search for the element. + \return The index of the found element, or \c -1, if no further element + with the given value could be found or \a index is out of range. +*/ +_VECTOR_TEMPLATE_LIST +int32 +_VECTOR_CLASS_NAME::IndexOf(const Value &value, int32 start) const +{ + if (start >= 0) { + for (int32 i = start; i < fItemCount; i++) { + if (fItems[i] == value) + return i; + } + } + return -1; +} + +// Find +/*! \brief Returns an iterator referring to the next element with the + specified value. + \param value The value of the element to be found. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Find(const Value &value) +{ + return Find(value, Begin()); +} + +// Find +/*! \brief Returns an iterator referring to the next element with the + specified value. + \param value The value of the element to be found. + \param start And iterator specifying where to start searching for the + element. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found or \a start was + invalid. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Find(const Value &value, const Iterator &start) +{ + int32 index = IndexOf(value, _IteratorIndex(start)); + if (index >= 0) + return Iterator(fItems + index); + return End(); +} + +// Find +/*! \brief Returns an iterator referring to the of the next element with the + specified value. + \param value The value of the element to be found. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Find(const Value &value) const +{ + return Find(value, Begin()); +} + +// Find +/*! \brief Returns an iterator referring to the of the next element with the + specified value. + \param value The value of the element to be found. + \param start And iterator specifying where to start searching for the + element. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found or \a start was + invalid. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Find(const Value &value, const ConstIterator &start) const +{ + int32 index = IndexOf(value, _IteratorIndex(start)); + if (index >= 0) + return ConstIterator(fItems + index); + return End(); +} + +// [] +/*! \brief Semantically equivalent to ElementAt(). +*/ +_VECTOR_TEMPLATE_LIST +inline +Value & +_VECTOR_CLASS_NAME::operator[](int32 index) +{ + return ElementAt(index); +} + +// [] +/*! \brief Semantically equivalent to ElementAt(). +*/ +_VECTOR_TEMPLATE_LIST +inline +const Value & +_VECTOR_CLASS_NAME::operator[](int32 index) const +{ + return ElementAt(index); +} + +// _Resize +/*! \brief Resizes the vector. + + The internal element array will be grown or shrunk to the next multiple + of \a fChunkSize >= \a count, but no less than \a fChunkSize. + + Also adjusts \a fItemCount according to the supplied \a count, but does + not invoke a destructor or constructor on any element. + + \param count The number of element. + \return \c true, if everything went fine, \c false, if the memory + allocation failed. +*/ +_VECTOR_TEMPLATE_LIST +bool +_VECTOR_CLASS_NAME::_Resize(size_t count) +{ + bool result = true; + // calculate the new capacity + int32 newSize = count; + if (newSize <= 0) + newSize = 1; + newSize = ((newSize - 1) / fChunkSize + 1) * fChunkSize; + // resize if necessary + if ((size_t)newSize != fCapacity) { + Value* newItems = (Value*)realloc(fItems, newSize * sizeof(Value)); + if (newItems) { + fItems = newItems; + fCapacity = newSize; + } else + result = false; + } + if (result) + fItemCount = count; + return result; +} + +// _IteratorIndex +/*! \brief Returns index of the element the supplied iterator refers to. + \return The index of the element the supplied iterator refers to, or + \c -1, if the iterator is invalid (End() is considered valid + here, and Count() is returned). +*/ +_VECTOR_TEMPLATE_LIST +inline +int32 +_VECTOR_CLASS_NAME::_IteratorIndex(const Iterator &iterator) const +{ + if (iterator.Element()) { + int32 index = iterator.Element() - fItems; + if (index >= 0 && index <= fItemCount) + return index; + } + return -1; +} + +// _IteratorIndex +/*! \brief Returns index of the element the supplied iterator refers to. + \return The index of the element the supplied iterator refers to, or + \c -1, if the iterator is invalid (End() is considered valid + here, and Count() is returned). +*/ +_VECTOR_TEMPLATE_LIST +inline +int32 +_VECTOR_CLASS_NAME::_IteratorIndex(const ConstIterator &iterator) const +{ + if (iterator.Element()) { + int32 index = iterator.Element() - fItems; + if (index >= 0 && index <= fItemCount) + return index; + } + return -1; +} + +#endif // _VECTOR_H