Adding abstracted storage for in-memory or file backed data

This commit is contained in:
Brad Davis 2017-02-17 09:18:51 -08:00
parent b4745657e0
commit d1b91cb436
4 changed files with 258 additions and 0 deletions

View file

@ -0,0 +1,86 @@
//
// Created by Bradley Austin Davis on 2016/02/17
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Storage.h"
#include <QFileInfo>
using namespace storage;
MemoryStoragePointer Storage::toMemoryStorage() const {
return std::make_unique<MemoryStorage>(size(), data());
}
FileStoragePointer Storage::toFileStorage(const QString& filename) const {
return FileStorage::create(filename, size(), data());
}
MemoryStorage::MemoryStorage(size_t size, const uint8_t* data) {
_data.resize(size);
memcpy(_data.data(), data, size);
}
const uint8_t* MemoryStorage::data() const {
return _data.data();
}
size_t MemoryStorage::size() const {
return _data.size();
}
FileStoragePointer FileStorage::create(const QString& filename, size_t size, const uint8_t* data) {
QFile file(filename);
if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) {
throw std::runtime_error("Unable to open file for writing");
}
if (!file.resize(size)) {
throw std::runtime_error("Unable to resize file");
}
{
auto mapped = file.map(0, size);
if (!mapped) {
throw std::runtime_error("Unable to map file");
}
memcpy(mapped, data, size);
if (!file.unmap(mapped)) {
throw std::runtime_error("Unable to unmap file");
}
}
file.close();
return std::make_unique<FileStorage>(filename);
}
FileStorage::FileStorage(const QString& filename) : _file(filename) {
if (!_file.open(QFile::ReadOnly)) {
throw std::runtime_error("Unable to open file");
}
_mapped = _file.map(0, _file.size());
if (!_mapped) {
throw std::runtime_error("Unable to map file");
}
}
FileStorage::~FileStorage() {
if (_mapped) {
if (!_file.unmap(_mapped)) {
throw std::runtime_error("Unable to unmap file");
}
}
if (_file.isOpen()) {
_file.close();
}
}
const uint8_t* FileStorage::data() const {
return _mapped;
}
size_t FileStorage::size() const {
return _file.size();
}

View file

@ -0,0 +1,65 @@
//
// Created by Bradley Austin Davis on 2016/02/17
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_Storage_h
#define hifi_Storage_h
#include <stdint.h>
#include <vector>
#include <memory>
#include <QFile>
#include <QString>
namespace storage {
class Storage;
using StoragePointer = std::unique_ptr<Storage>;
class MemoryStorage;
using MemoryStoragePointer = std::unique_ptr<MemoryStorage>;
class FileStorage;
using FileStoragePointer = std::unique_ptr<FileStorage>;
class Storage {
public:
virtual ~Storage() {}
virtual const uint8_t* data() const = 0;
virtual size_t size() const = 0;
FileStoragePointer toFileStorage(const QString& filename) const;
MemoryStoragePointer toMemoryStorage() const;
};
class MemoryStorage : public Storage {
public:
MemoryStorage(size_t size, const uint8_t* data);
const uint8_t* data() const override;
size_t size() const override;
private:
std::vector<uint8_t> _data;
};
class FileStorage : public Storage {
public:
static FileStoragePointer create(const QString& filename, size_t size, const uint8_t* data);
FileStorage(const QString& filename);
~FileStorage();
// Prevent copying
FileStorage(const FileStorage& other) = delete;
FileStorage& operator=(const FileStorage& other) = delete;
const uint8_t* data() const override;
size_t size() const override;
private:
QFile _file;
uint8_t* _mapped { nullptr };
};
}
#endif // hifi_Storage_h

View file

@ -0,0 +1,75 @@
//
// Created by Bradley Austin Davis on 2016/02/17
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "StorageTests.h"
QTEST_MAIN(StorageTests)
using namespace storage;
StorageTests::StorageTests() {
for (size_t i = 0; i < _testData.size(); ++i) {
_testData[i] = (uint8_t)rand();
}
_testFile = QDir::tempPath() + "/" + QUuid::createUuid().toString();
}
StorageTests::~StorageTests() {
QFileInfo fileInfo(_testFile);
if (fileInfo.exists()) {
QFile(_testFile).remove();
}
}
void StorageTests::testConversion() {
{
QFileInfo fileInfo(_testFile);
QCOMPARE(fileInfo.exists(), false);
}
StoragePointer storagePointer = std::make_unique<MemoryStorage>(_testData.size(), _testData.data());
QCOMPARE(storagePointer->size(), (quint64)_testData.size());
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
// Convert to a file
storagePointer = storagePointer->toFileStorage(_testFile);
{
QFileInfo fileInfo(_testFile);
QCOMPARE(fileInfo.exists(), true);
QCOMPARE(fileInfo.size(), (qint64)_testData.size());
}
QCOMPARE(storagePointer->size(), (quint64)_testData.size());
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
// Convert to memory
storagePointer = storagePointer->toMemoryStorage();
QCOMPARE(storagePointer->size(), (quint64)_testData.size());
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), _testData.size()), 0);
{
// ensure the file is unaffected
QFileInfo fileInfo(_testFile);
QCOMPARE(fileInfo.exists(), true);
QCOMPARE(fileInfo.size(), (qint64)_testData.size());
}
// truncate the data as a new memory object
auto newSize = _testData.size() / 2;
storagePointer = std::make_unique<MemoryStorage>(newSize, storagePointer->data());
QCOMPARE(storagePointer->size(), (quint64)newSize);
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);
// Convert back to file
storagePointer = storagePointer->toFileStorage(_testFile);
QCOMPARE(storagePointer->size(), (quint64)newSize);
QCOMPARE(memcmp(_testData.data(), storagePointer->data(), newSize), 0);
{
// ensure the file is truncated
QFileInfo fileInfo(_testFile);
QCOMPARE(fileInfo.exists(), true);
QCOMPARE(fileInfo.size(), (qint64)newSize);
}
}

View file

@ -0,0 +1,32 @@
//
// Created by Bradley Austin Davis on 2016/02/17
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_StorageTests_h
#define hifi_StorageTests_h
#include <QtTest/QtTest>
#include <shared/Storage.h>
#include <array>
class StorageTests : public QObject {
Q_OBJECT
public:
StorageTests();
~StorageTests();
private slots:
void testConversion();
private:
std::array<uint8_t, 1025> _testData;
QString _testFile;
};
#endif // hifi_StorageTests_h