Sorry, this is a bit of a mess yet and needs more refactoring...
* I've implemented my own copy engine which runs a separate writer thread. Especially when installing from one hard drive to another, it should speed things up. When installing from CD, the entire harddisk write time should at least disappear in the CD read time. When installing from/to the same drive, the new routine is as fast as the Tracker code that was used before. * When stopping/canceling the copy process, the copy engine will now actually block, instead of happily going on while the alert is showing. I've still implemented that to not leave incomplete files around, like before. * There is now a progress bar, based on data bytes in files to copy. Reason being that it's quite fast to collect this info versus also scanning all attributes. * The copy engine itself was used in another project before and was tested with SHA256 hashes of the original files and the copied files, so I think it can be trusted. * There is now a lot of code that can be removed or should be cleaned up. What was the "CopyEngine" should become the "WorkerThread" or something. * I still had a mental TODO about skipping the swap file (there is none on CDs, but when copying a harddisk installation there may be one), I just remembered this when typing this commit message... will fix that next. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30394 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
8296330edf
commit
803d0234c1
195
src/apps/installer/BlockingQueue.h
Normal file
195
src/apps/installer/BlockingQueue.h
Normal file
@ -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 <OS.h>
|
||||
|
||||
#include <Locker.h>
|
||||
|
||||
#include "AutoLocker.h"
|
||||
#include "Vector.h"
|
||||
|
||||
typedef BLocker Locker;
|
||||
|
||||
template<typename Element>
|
||||
class BlockingQueue : public Locker {
|
||||
public:
|
||||
BlockingQueue(const char* name = NULL);
|
||||
~BlockingQueue();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
status_t Close(bool deleteElements,
|
||||
const Vector<Element*>** 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<Element*> fElements;
|
||||
sem_id fElementSemaphore;
|
||||
};
|
||||
|
||||
// constructor
|
||||
template<typename Element>
|
||||
BlockingQueue<Element>::BlockingQueue(const char* name)
|
||||
: fElements(),
|
||||
fElementSemaphore(-1)
|
||||
{
|
||||
fElementSemaphore = create_sem(0, (name ? name : "blocking queue"));
|
||||
}
|
||||
|
||||
// destructor
|
||||
template<typename Element>
|
||||
BlockingQueue<Element>::~BlockingQueue()
|
||||
{
|
||||
if (fElementSemaphore >= 0)
|
||||
delete_sem(fElementSemaphore);
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
template<typename Element>
|
||||
status_t
|
||||
BlockingQueue<Element>::InitCheck() const
|
||||
{
|
||||
return (fElementSemaphore < 0 ? fElementSemaphore : B_OK);
|
||||
}
|
||||
|
||||
// Close
|
||||
template<typename Element>
|
||||
status_t
|
||||
BlockingQueue<Element>::Close(bool deleteElements,
|
||||
const Vector<Element*>** elements)
|
||||
{
|
||||
AutoLocker<Locker> _(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<typename Element>
|
||||
status_t
|
||||
BlockingQueue<Element>::Push(Element* element)
|
||||
{
|
||||
AutoLocker<Locker> _(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<typename Element>
|
||||
status_t
|
||||
BlockingQueue<Element>::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<Locker> _(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<typename Element>
|
||||
status_t
|
||||
BlockingQueue<Element>::Peek(Element** element)
|
||||
{
|
||||
AutoLocker<Locker> _(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<typename Element>
|
||||
status_t
|
||||
BlockingQueue<Element>::Remove(Element* element)
|
||||
{
|
||||
status_t error = acquire_sem_etc(fElementSemaphore, 1,
|
||||
B_RELATIVE_TIMEOUT, 0);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
AutoLocker<Locker> _(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<typename Element>
|
||||
int32
|
||||
BlockingQueue<Element>::Size()
|
||||
{
|
||||
AutoLocker<Locker> _(this);
|
||||
return (fElements.Count());
|
||||
}
|
||||
|
||||
#endif // BLOCKING_QUEUE_H
|
@ -4,19 +4,27 @@
|
||||
*/
|
||||
|
||||
#include "CopyEngine.h"
|
||||
#include "InstallerWindow.h"
|
||||
#include "PartitionMenuItem.h"
|
||||
#include "FSUndoRedo.h"
|
||||
#include "FSUtils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Alert.h>
|
||||
#include <DiskDeviceVisitor.h>
|
||||
#include <DiskDeviceTypes.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <Message.h>
|
||||
#include <Messenger.h>
|
||||
#include <Path.h>
|
||||
#include <String.h>
|
||||
#include <VolumeRoster.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <Partition.h>
|
||||
#include <Volume.h>
|
||||
|
||||
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 */
|
||||
|
424
src/apps/installer/CopyEngine2.cpp
Normal file
424
src/apps/installer/CopyEngine2.cpp
Normal file
@ -0,0 +1,424 @@
|
||||
#include "CopyEngine2.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Directory.h>
|
||||
#include <fs_attr.h>
|
||||
#include <NodeInfo.h>
|
||||
#include <Path.h>
|
||||
#include <String.h>
|
||||
#include <SymLink.h>
|
||||
|
||||
#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<BLocker> 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<BLocker> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
91
src/apps/installer/CopyEngine2.h
Normal file
91
src/apps/installer/CopyEngine2.h
Normal file
@ -0,0 +1,91 @@
|
||||
#ifndef COPY_ENGINE_2_H
|
||||
#define COPY_ENGINE_2_H
|
||||
|
||||
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
#include <Messenger.h>
|
||||
|
||||
#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<Buffer> 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
|
@ -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);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
|
||||
* 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 <Application.h>
|
||||
#include <Autolock.h>
|
||||
#include <Box.h>
|
||||
#include <Button.h>
|
||||
#include <ClassInfo.h>
|
||||
#include <Directory.h>
|
||||
#include <GridLayoutBuilder.h>
|
||||
#include <GroupLayoutBuilder.h>
|
||||
#include <LayoutUtils.h>
|
||||
#include <MenuBar.h>
|
||||
#include <MenuField.h>
|
||||
#include <Path.h>
|
||||
#include <PopUpMenu.h>
|
||||
#include <Roster.h>
|
||||
#include <Screen.h>
|
||||
#include <ScrollView.h>
|
||||
#include <SpaceLayoutItem.h>
|
||||
#include <StatusBar.h>
|
||||
#include <String.h>
|
||||
#include <TextView.h>
|
||||
#include <TranslationUtils.h>
|
||||
#include <TranslatorFormats.h>
|
||||
|
||||
#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 -
|
||||
|
||||
|
||||
|
@ -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 <superstippi@gmx.de>
|
||||
* 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 <Box.h>
|
||||
#include <Button.h>
|
||||
#include <Menu.h>
|
||||
#include <MenuField.h>
|
||||
#include <ScrollView.h>
|
||||
#include <String.h>
|
||||
#include <TextView.h>
|
||||
#include <Window.h>
|
||||
|
||||
#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
|
||||
|
@ -5,6 +5,7 @@ SubDirHdrs [ FDirName $(HAIKU_TOP) src kits tracker ] ;
|
||||
|
||||
Application Installer :
|
||||
CopyEngine.cpp
|
||||
CopyEngine2.cpp
|
||||
FSUtils.cpp
|
||||
InstallerApp.cpp
|
||||
InstallerCopyLoopControl.cpp
|
||||
|
798
src/apps/installer/Vector.h
Normal file
798
src/apps/installer/Vector.h
Normal file
@ -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 <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
template<typename Value> class VectorIterator;
|
||||
|
||||
// for convenience
|
||||
#define _VECTOR_TEMPLATE_LIST template<typename Value>
|
||||
#define _VECTOR_CLASS_NAME Vector<Value>
|
||||
|
||||
/*!
|
||||
\class Vector
|
||||
\brief A generic vector implementation.
|
||||
*/
|
||||
template<typename Value>
|
||||
class Vector {
|
||||
public:
|
||||
typedef VectorIterator<Value> Iterator;
|
||||
typedef VectorIterator<const Value> 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<typename Value>
|
||||
class VectorIterator {
|
||||
private:
|
||||
typedef VectorIterator<Value> Iterator;
|
||||
|
||||
public:
|
||||
inline VectorIterator<Value>()
|
||||
: fElement(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
inline VectorIterator<Value>(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>(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
|
Loading…
x
Reference in New Issue
Block a user