Created
December 14, 2010 23:46
-
-
Save suma/741359 to your computer and use it in GitHub Desktop.
Document::writeのDOCTYPE_BUFFER/DOCTYPE_ORIGINALを分離したいが..
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <limits> | |
#include <QFile> | |
#include <QDataStream> | |
#include <QUndoStack> | |
#include "document.h" | |
#include "filemapreader.h" | |
const size_t Document::DEFAULT_BUFFER_SIZE = 0x100000; | |
class EmptyOriginal : public DocumentOriginal | |
{ | |
public: | |
quint64 length() const | |
{ | |
return 0; | |
} | |
void get(quint64, uchar *, quint64) const | |
{ | |
// TODO: throw Exception | |
} | |
}; | |
class FileOriginal : public DocumentOriginal | |
{ | |
protected: | |
size_t buffer_size_; | |
QFile *file_; | |
// pointer to mapped | |
mutable uchar *ptr_; | |
// mapped offset and size | |
mutable quint64 offset_; | |
mutable quint64 size_; | |
public: | |
FileOriginal(QFile *file, size_t buffer_size = Document::DEFAULT_BUFFER_SIZE) | |
: buffer_size_(buffer_size) // buffer_size = 0x10 ^ N | |
, file_(file) | |
, ptr_(NULL) | |
, offset_(0) | |
, size_(0) | |
{ | |
// check buffer_size | |
uint size; | |
for (size = 1; size < 0x10000000; size <<= 1) { | |
if (size == buffer_size) { | |
break; | |
} | |
} | |
if (size == 0x10000000) { | |
buffer_size_ = (uint)Document::DEFAULT_BUFFER_SIZE; | |
} | |
if (length() > 0) { | |
remap(0); | |
} | |
} | |
virtual ~FileOriginal() | |
{ | |
delete file_; | |
} | |
quint64 length() const | |
{ | |
return file_->size(); | |
} | |
void get(quint64 pos, uchar *buf, quint64 len) const | |
{ | |
Q_ASSERT(pos <= length()); | |
Q_ASSERT(len <= length()); | |
Q_ASSERT(pos <= length() - len); | |
while (len > 0) { | |
if (!isOffsetCovered(pos)) { | |
remap(pos); | |
} | |
// get start position | |
uint start = static_cast<uint>(pos & (buffer_size_ - 1)); | |
// get copy size | |
uint copy_size = qMin((quint64)size_ - start, len); | |
// copy to memory | |
memcpy(buf, ptr_ + start, copy_size); | |
pos += copy_size; | |
len -= copy_size; | |
} | |
} | |
private: | |
void remap(quint64 pos) const | |
{ | |
Q_ASSERT(pos < length()); | |
if (ptr_ != NULL) { | |
file_->unmap(ptr_); | |
} | |
offset_ = pos & ~(buffer_size_ - 1); | |
size_ = qMin(length() - offset_, (quint64)buffer_size_); | |
ptr_ = file_->map(offset_, size_); | |
} | |
bool isOffsetCovered(quint64 pos) const | |
{ | |
Q_ASSERT(pos <= length()); | |
return offset_ <= pos && pos - offset_ <= size_; | |
} | |
}; | |
bool Document::WriteCallback::writeCallback(quint64) | |
{ | |
return true; | |
} | |
void Document::WriteCallback::writeCompleted() | |
{ | |
} | |
class Document::FragmentCopier | |
{ | |
public: | |
FragmentCopier(const Document *doc, uchar *buf) | |
: document_(doc) | |
, buf_(buf) | |
{ | |
} | |
FragmentCopier &operator=(DocumentFragment fragment) | |
{ | |
document_->copy(fragment.type(), fragment.position(), fragment.length(), buf_); | |
buf_ += fragment.length(); | |
return *this; | |
} | |
FragmentCopier &operator*() | |
{ | |
return *this; | |
} | |
FragmentCopier &operator++() | |
{ | |
return *this; | |
} | |
FragmentCopier &operator++(int) | |
{ | |
return *this; | |
} | |
private: | |
const Document *document_; | |
uchar *buf_; | |
}; | |
Document::Document() | |
: impl_(new DocumentImpl()) | |
, original_(new EmptyOriginal()) | |
, file_(NULL) | |
, undo_stack_(new QUndoStack(this)) | |
{ | |
} | |
Document::Document(QFile *file) | |
: impl_(new DocumentImpl()) | |
, original_(new FileOriginal(file)) | |
, file_(file) | |
, undo_stack_(new QUndoStack(this)) | |
{ | |
impl_->insert_data(0, 0, file->size(), DOCTYPE_ORIGINAL); | |
} | |
Document::Document(QFile *file, uint buffer_size) | |
: impl_(new DocumentImpl()) | |
, original_(new FileOriginal(file, buffer_size)) | |
, file_(file) | |
, undo_stack_(new QUndoStack(this)) | |
{ | |
impl_->insert_data(0, 0, file->size(), DOCTYPE_ORIGINAL); | |
} | |
Document::~Document() | |
{ | |
delete undo_stack_; | |
delete file_; | |
delete impl_; | |
delete original_; | |
} | |
quint64 Document::length() const | |
{ | |
return impl_->length(); | |
} | |
void Document::get(quint64 pos, uchar *buf, uint len) const | |
{ | |
get(pos, len, FragmentCopier(this, buf)); | |
} | |
void Document::copy(uint type, quint64 pos, quint64 len, uchar *buf) const | |
{ | |
Q_ASSERT(buf != NULL); | |
switch (type) { | |
case DOCTYPE_BUFFER: | |
Q_ASSERT(pos <= buffer_.size()); | |
Q_ASSERT(len <= buffer_.size()); | |
Q_ASSERT(pos <= buffer_.size() - len); | |
memcpy(buf, &buffer_[static_cast<uint>(pos)], len); | |
break; | |
case DOCTYPE_ORIGINAL: | |
Q_ASSERT(pos <= original_->length()); | |
Q_ASSERT(len <= original_->length()); | |
Q_ASSERT(pos <= original_->length() - len); | |
original_->get(pos, buf, len); | |
break; | |
default: | |
; | |
} | |
} | |
void Document::insert(quint64 pos, const uchar *buf, uint len) | |
{ | |
Q_ASSERT(buf != NULL); | |
Q_ASSERT(len != 0); | |
Q_ASSERT(pos <= length()); | |
const quint64 bufPos = buffer_.size(); | |
buffer_.insert(buffer_.end(), buf, buf + len); | |
impl_->insert_data(pos, bufPos, len, DOCTYPE_BUFFER); | |
emit inserted(pos, static_cast<quint64>(len)); | |
} | |
void Document::insert(quint64 pos, DocumentFragment fragment) | |
{ | |
impl_->insert_data(pos, fragment.position(), fragment.length(), fragment.type()); | |
emit inserted(pos, fragment.length()); | |
} | |
void Document::remove(quint64 pos, quint64 len) | |
{ | |
Q_ASSERT(pos <= length()); | |
Q_ASSERT(len <= length()); | |
Q_ASSERT(pos <= length() - len); | |
impl_->remove_data(pos, len); | |
emit removed(pos, len); | |
} | |
Document::Buffer &Document::buffer() | |
{ | |
return buffer_; | |
} | |
QUndoStack *Document::undoStack() const | |
{ | |
return undo_stack_; | |
} | |
bool Document::write(WriteCallback *callback) | |
{ | |
// TODO: check file_->name() can be written | |
// | |
// write tempfile | |
// move tempfile to file_->name() | |
// file_ reopen and reconstruct piecetable | |
return true; | |
} | |
// 実装中... | |
bool Document::write(QFile *out, WriteCallback *callback) | |
{ | |
if (out == NULL) { | |
return false; | |
} | |
// TODO: check file_->name() can be written | |
QDataStream outStream(out); | |
FragmentList fragments; | |
get(0, length(), std::back_inserter(fragments)); | |
FragmentList::iterator it = fragments.begin(); | |
const uint BLOCK_SIZE = 1024 * 1024 * 4; | |
quint64 completed = 0; | |
FileMapReader *reader = NULL; | |
if (!out->seek(0)) { | |
goto label_error; | |
} | |
reader = new FileMapReader(NULL, BLOCK_SIZE); // FIXME: FileOriginal.file() | |
// piece table | |
while (it != fragments.end()) { | |
uchar *data = NULL; | |
if (it->type() == DOCTYPE_BUFFER) { | |
data = &buffer_[static_cast<uint>(it->position())]; | |
} else if (it->type() == DOCTYPE_ORIGINAL) { | |
reader->seek(it->position()); | |
} | |
// write piece buffer | |
for (quint64 len = it->length(); len != 0; ) { | |
uint copy_size = static_cast<uint>(qMin((quint64)BLOCK_SIZE, len)); | |
if (it->type() == DOCTYPE_ORIGINAL) { | |
data = reader->get(); | |
} | |
// write block | |
int res = outStream.writeRawData(reinterpret_cast<char*>(data), copy_size); | |
if (res == -1) { | |
// エラーでござる | |
goto label_error; | |
} | |
size_t wrote_size = static_cast<size_t>(res); | |
// increment blocks | |
completed += wrote_size; | |
len -= wrote_size; | |
if (it->type() == DOCTYPE_BUFFER) { | |
data += wrote_size; | |
} else if (it->type() == DOCTYPE_ORIGINAL) { | |
*reader += wrote_size; | |
} | |
// callback | |
if (callback != NULL && !callback->writeCallback(completed)) { | |
goto label_cancel; | |
} | |
} | |
++it; | |
} | |
if (callback != NULL) { | |
callback->writeCompleted(); | |
} | |
delete reader; | |
return true; | |
label_error: | |
// handling error | |
delete reader; | |
return false; | |
label_cancel: | |
// Delete file | |
delete reader; | |
return false; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <QObject> | |
#include <QString> | |
#include <vector> | |
#include "document_i.h" | |
class DocumentImpl; | |
class QFile; | |
class QUndoStack; | |
class DocumentFragment | |
{ | |
private: | |
uint type_; | |
quint64 position_; | |
quint64 length_; | |
public: | |
DocumentFragment(uint type, quint64 pos, quint64 len) | |
: type_(type) | |
, position_(pos) | |
, length_(len) | |
{ | |
} | |
uint type() const | |
{ | |
return type_; | |
} | |
quint64 position() const | |
{ | |
return position_; | |
} | |
quint64 length() const | |
{ | |
return length_; | |
} | |
}; | |
class DocumentOriginal | |
{ | |
public: | |
virtual quint64 length() const = 0; | |
virtual void get(quint64 pos, uchar *buf, quint64 len) const = 0; | |
}; | |
class Document : public QObject | |
{ | |
Q_OBJECT | |
public: | |
typedef std::vector<uchar> Buffer; | |
typedef std::vector<DocumentFragment> FragmentList; | |
enum { | |
DOCTYPE_BUFFER = 0, | |
DOCTYPE_ORIGINAL = 1, | |
}; | |
class WriteCallback | |
{ | |
public: | |
virtual bool writeCallback(quint64 completed); | |
virtual void writeCompleted(); | |
}; | |
public: | |
// 空で作成 | |
Document(); | |
// ファイルから開く | |
Document(QFile *file); | |
Document(QFile *file, uint buffer_size); | |
// 既存のドキュメントをコピー | |
Document(const Document &doc, bool writemode); | |
// TODO: static | |
// static Document * openFile(QFile*file); | |
// static Document * create(); | |
virtual ~Document(); | |
//-- immutable methods | |
// | |
quint64 length() const; | |
// copy buffer | |
void get(quint64 pos, uchar *buf, uint len) const; | |
// copy DocumentFragment from piece table | |
template <class T> | |
T get(quint64 pos, quint64 len, T result) const | |
{ | |
Q_ASSERT(pos <= length()); | |
Q_ASSERT(len <= length()); | |
Q_ASSERT(pos <= length() - len); | |
uint x = impl_->documents_.findNode(pos); | |
Q_ASSERT(x != 0); | |
const quint64 diff = pos - impl_->documents_.position(x); | |
if (diff) { | |
const quint64 fragmentSize = impl_->documents_.size(x) - diff; | |
DocumentData *X = impl_->documents_.fragment(x); | |
if (fragmentSize < len) { | |
*result = DocumentFragment(X->type, X->bufferPosition + diff, fragmentSize); | |
++result; | |
len -= fragmentSize; | |
x = impl_->documents_.next(x); | |
} else { | |
*result = DocumentFragment(X->type, X->bufferPosition + diff, len); | |
return result; | |
} | |
} | |
Q_ASSERT(x != 0); | |
while (0 < len) { | |
const quint64 fragmentSize = impl_->documents_.size(x); | |
const uint copy_size = (static_cast<quint64>(len) < fragmentSize) ? len : static_cast<uint>(fragmentSize); | |
const DocumentData *X = impl_->documents_.fragment(x); | |
*result = DocumentFragment(X->type, X->bufferPosition, copy_size); | |
++result; | |
len -= copy_size; | |
x = impl_->documents_.next(x); | |
} | |
return result; | |
} | |
bool write(WriteCallback *callback); | |
bool write(QFile *out, WriteCallback *callback); | |
//-- mutable methods | |
void insert(quint64 pos, const uchar *buf, uint len); | |
// file method | |
// save, saveAs, .... | |
// | |
// | |
QUndoStack *undoStack() const; | |
Buffer &buffer(); | |
public slots: | |
//-- mutable methods | |
void insert(quint64 pos, DocumentFragment fragment); | |
void remove(quint64 pos, quint64 len); | |
signals: | |
void inserted(quint64 pos, quint64 len); | |
void removed(quint64 pos, quint64 len); | |
public: | |
const static size_t DEFAULT_BUFFER_SIZE; | |
private: | |
// ドキュメントの実体をバッファへコピーする | |
void copy(uint type, quint64 pos, quint64 len, uchar *buf) const; | |
// Fragment Copy iterator | |
class FragmentCopier; | |
// Document Buffer Writer | |
class BufferWriter; | |
protected: | |
DocumentImpl *impl_; | |
DocumentOriginal *original_; | |
QFile *file_; | |
QUndoStack *undo_stack_; | |
Buffer buffer_; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment