📄 tarstream.cc
字号:
#include "basicdefs.h"#include "basictypes.h"#include "tarstream.h"#include "pathutils.h"#include "stringutils.h"#include "common.h"using namespace utils_base;///////////////////////////////////////////////////////////////////////////////// TarStream///////////////////////////////////////////////////////////////////////////////TarStream::TarStream() : mode_(M_NONE), next_block_(NB_NONE), block_pos_(0), current_(NULL), current_bytes_(0) {}TarStream::~TarStream() { Close();}bool TarStream::AddFilter(const std::string& pathname) { if (pathname.empty()) return false; Pathname archive_path(pathname); archive_path.SetFolderDelimiter('/'); archive_path.Normalize(); filters_.push_back(archive_path.pathname()); return true;}bool TarStream::Open(const std::string& folder, bool read) { Close(); Pathname root_folder; root_folder.SetFolder(folder); root_folder.Normalize(); root_folder_.assign(root_folder.folder()); if (read) { std::string pattern(root_folder_); DirectoryIterator *iter = new DirectoryIterator(); if (iter->Iterate(pattern) == false) { delete iter; return false; } mode_ = M_READ; find_.push_front(iter); next_block_ = NB_FILE_HEADER; block_pos_ = BLOCK_SIZE; int error; if (SR_SUCCESS != ProcessNextEntry(find_.front(), &error)) { return false; } } else { if (!Filesystem::CreateFolder(root_folder_)) { return false; } mode_ = M_WRITE; next_block_ = NB_FILE_HEADER; block_pos_ = 0; } return true;}StreamState TarStream::GetState() const { return (M_NONE == mode_) ? SS_CLOSED : SS_OPEN;}StreamResult TarStream::Read(void* buffer, size_t buffer_len, size_t* read, int* error) { if (M_READ != mode_) { return SR_EOS; } return ProcessBuffer(buffer, buffer_len, read, error);}StreamResult TarStream::Write(const void* data, size_t data_len, size_t* written, int* error) { if (M_WRITE != mode_) { return SR_EOS; } // Note: data is not modified unless M_READ == mode_ return ProcessBuffer(const_cast<void*>(data), data_len, written, error);}void TarStream::Close() { root_folder_.clear(); next_block_ = NB_NONE; block_pos_ = 0; delete current_; current_ = NULL; current_bytes_ = 0; for (DirectoryList::iterator it = find_.begin(); it != find_.end(); ++it) { delete(*it); } find_.clear(); subfolder_.clear();}StreamResult TarStream::ProcessBuffer(void* buffer, size_t buffer_len, size_t* consumed, int* error) { size_t local_consumed; if (!consumed) consumed = &local_consumed; int local_error; if (!error) error = &local_error; StreamResult result = SR_SUCCESS; *consumed = 0; while (*consumed < buffer_len) { size_t available = BLOCK_SIZE - block_pos_; if (available == 0) { result = ProcessNextBlock(error); if (SR_SUCCESS != result) { break; } } else { size_t bytes_to_copy = utils_base::_min(available, buffer_len - *consumed); char* buffer_ptr = static_cast<char*>(buffer) + *consumed; char* block_ptr = block_ + block_pos_; if (M_READ == mode_) { memcpy(buffer_ptr, block_ptr, bytes_to_copy); } else { memcpy(block_ptr, buffer_ptr, bytes_to_copy); } *consumed += bytes_to_copy; block_pos_ += bytes_to_copy; } } // SR_EOS means no data was consumed on this operation. So we may need to // return SR_SUCCESS instead, and then we will return SR_EOS next time. if ((SR_EOS == result) && (*consumed > 0)) { result = SR_SUCCESS; } return result;}StreamResult TarStream::ProcessNextBlock(int* error) { ASSERT(NULL != error); ASSERT(M_NONE != mode_); ASSERT(BLOCK_SIZE == block_pos_); StreamResult result; if (NB_NONE == next_block_) { return SR_EOS; } else if (NB_TRAILER == next_block_) { // Trailer block is zeroed result = ProcessEmptyBlock(0, error); if (SR_SUCCESS != result) return result; next_block_ = NB_NONE; } else if (NB_FILE_HEADER == next_block_) { if (M_READ == mode_) { result = ReadNextFile(error); } else { result = WriteNextFile(error); } // If there are no more files, we are at the first trailer block if (SR_EOS == result) { block_pos_ = 0; next_block_ = NB_TRAILER; result = ProcessEmptyBlock(0, error); } if (SR_SUCCESS != result) return result; } else if (NB_DATA == next_block_) { size_t block_consumed = 0; size_t block_available = utils_base::_min<size_t>(BLOCK_SIZE, current_bytes_); while (block_consumed < block_available) { void* block_ptr = static_cast<char*>(block_) + block_consumed; size_t available = block_available - block_consumed, consumed; if (M_READ == mode_) { ASSERT(NULL != current_); result = current_->Read(block_ptr, available, &consumed, error); } else if (current_) { result = current_->Write(block_ptr, available, &consumed, error); } else { consumed = available; result = SR_SUCCESS; } switch (result) { case SR_ERROR: return result; case SR_BLOCK: case SR_EOS: ASSERT(false); *error = 0; // TODO: make errors return SR_ERROR; case SR_SUCCESS: block_consumed += consumed; break; } } current_bytes_ -= block_consumed; if (current_bytes_ == 0) { // The remainder of the block is zeroed result = ProcessEmptyBlock(block_consumed, error); if (SR_SUCCESS != result) return result; delete current_; current_ = NULL; next_block_ = NB_FILE_HEADER; } } else { ASSERT(false); } block_pos_ = 0; return SR_SUCCESS;}StreamResult TarStream::ProcessEmptyBlock(size_t start, int* error) { ASSERT(NULL != error); ASSERT(M_NONE != mode_); if (M_READ == mode_) { memset(block_ + start, 0, BLOCK_SIZE - start); } else { if (!utils_base::memory_check(block_ + start, 0, BLOCK_SIZE - start)) { *error = 0; // TODO: make errors return SR_ERROR; } } return SR_SUCCESS;}StreamResult TarStream::ReadNextFile(int* error) { ASSERT(NULL != error); ASSERT(M_READ == mode_); ASSERT(NB_FILE_HEADER == next_block_); ASSERT(BLOCK_SIZE == block_pos_); ASSERT(NULL == current_); // ReadNextFile conducts a depth-first recursive search through the directory // tree. find_ maintains a stack of open directory handles, which // corresponds to our current position in the tree. At any point, the // directory at the top (front) of the stack is being enumerated. If a // directory is found, it is opened and pushed onto the top of the stack. // When a directory enumeration completes, that directory is popped off the // top of the stack. // Note: Since ReadNextFile can only return one block of data at a time, we // cannot simultaneously return the entry for a directory, and the entry for // the first element in that directory at the same time. In this case, we // push a NULL entry onto the find_ stack, which indicates that the next // iteration should begin enumeration of the "new" directory. StreamResult result = SR_SUCCESS; while (BLOCK_SIZE == block_pos_) { ASSERT(!find_.empty()); if (NULL != find_.front()) { if (find_.front()->Next()) { result = ProcessNextEntry(find_.front(), error); if (SR_SUCCESS != result) { return result; } continue; } delete(find_.front()); } else { Pathname pattern(root_folder_); pattern.AppendFolder(subfolder_); find_.front() = new DirectoryIterator(); if (find_.front()->Iterate(pattern.pathname())) { result = ProcessNextEntry(find_.front(), error); if (SR_SUCCESS != result) { return result; } continue; } // TODO: should this be an error? LOG_F(LS_WARNING) << "Couldn't open folder: " << pattern.pathname(); } find_.pop_front(); subfolder_ = Pathname(subfolder_).parent_folder(); if (find_.empty()) { return SR_EOS; } } ASSERT(0 == block_pos_); return SR_SUCCESS;}StreamResult TarStream::WriteNextFile(int* error) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -