Stream.h#

Parent directory (stream)

Contains a class used to abstract streams of data (files, blocks of memory, resources etc.).

Includes#

  • PVRCore/Errors.h

  • PVRCore/strings/StringFunctions.h

  • memory

  • string (CompileTimeHash.h)

  • vector

Included By#

Namespaces#

Classes#

Source Code#

#pragma once
#include "PVRCore/Errors.h"
#include "PVRCore/strings/StringFunctions.h"
#include <memory>
#include <vector>
#include <string>

namespace pvr {

class Stream;
class FileIOError : public PvrError
{
public:
    explicit FileIOError(const Stream& stream);
    FileIOError(const Stream& stream, std::string message);
    explicit FileIOError(std::string filenameOrMessage) : PvrError("[" + filenameOrMessage + "]: File IO operation failed") {}
    FileIOError(std::string filename, std::string message) : PvrError("[" + filename + "]: File IO operation failed - " + message) {}
};

class FileEOFError : public PvrError
{
public:
    explicit FileEOFError(const Stream& stream);
    FileEOFError(const Stream& stream, std::string message);
    explicit FileEOFError(std::string filenameOrMessage) : PvrError("[" + filenameOrMessage + "]: Attempted to read past the end of file.") {}
    FileEOFError(std::string filename, std::string message) : PvrError("[" + filename + "]: Attempted to read past the end of file - " + message) {}
};

class FileNotFoundError : public std::runtime_error
{
public:
    explicit FileNotFoundError(const Stream& stream);
    FileNotFoundError(const Stream& stream, std::string message);
    explicit FileNotFoundError(std::string filenameOrMessage) : std::runtime_error("[" + filenameOrMessage + "]: File not found") {}
    FileNotFoundError(std::string filename, std::string message) : std::runtime_error("[" + filename + "]: File not found - " + message) {}
};

class Stream
{
public:
    enum SeekOrigin
    {
        SeekOriginFromStart,
        SeekOriginFromCurrent,
        SeekOriginFromEnd
    };

    virtual ~Stream() = default;

    bool isReadable() const { return _isReadable; }

    bool isWritable() const { return _isWritable; }

    const std::string& getFileName() const { return _fileName; }

public:
    void read(size_t elementSize, size_t numElements, void* buffer, size_t& dataRead) const
    {
        dataRead = 0;
        if (!_isReadable) { throw InvalidOperationError("[Stream::read]: Attempted to read non readable stream"); }
        _read(elementSize, numElements, buffer, dataRead);
    }

    void readExact(size_t elementSize, size_t numElements, void* buffer) const
    {
        size_t dataRead;
        read(elementSize, numElements, buffer, dataRead);
        if (dataRead != numElements)
        {
            throw FileEOFError(*this,
                std::string("[Stream::readExact]: Failed to read specified number of elements. Size of element: [" + std::to_string(elementSize) + "]. Attempted to read [" +
                    std::to_string(numElements) + "] but got [" + std::to_string(dataRead) + "]"));
        }
    }

    void write(size_t elementSize, size_t numElements, const void* buffer, size_t& dataWritten)
    {
        dataWritten = 0;
        if (!_isWritable) { throw InvalidOperationError("[Stream::write]: Attempt to write to non-writable stream"); }
        _write(elementSize, numElements, buffer, dataWritten);
    }

    void writeExact(size_t elementSize, size_t numElements, const void* buffer)
    {
        size_t dataWritten;
        write(elementSize, numElements, buffer, dataWritten);
        if (dataWritten != numElements) { throw FileIOError(*this, std::string("Stream::writeExact: Failed to write specified number of elements.")); }
    }

    void seek(long offset, SeekOrigin origin) const
    {
        if (!isRandomAccess()) { throw InvalidOperationError(pvr::strings::createFormatted("[pvr::Stream] Attempted to seek on non-seekable stream '%s'", getFileName().c_str())); }
        _seek(offset, origin);
    }

    bool isSeekable() const { return isRandomAccess(); }

    bool isRandomAccess() const { return _isRandomAccess; }

    size_t getPosition() const { return size_t(_getPosition()); }

    uint64_t getPosition64() const { return _getPosition(); }

    size_t getSize() const { return size_t(_getSize()); }

    uint64_t getSize64() const { return _getSize(); }

    template<typename Type_>
    std::vector<Type_> readToEnd() const
    {
        std::vector<Type_> ret;
        uint64_t mySize = getSize() - getPosition();
        uint64_t numElements = mySize / sizeof(Type_);
        ret.resize(size_t(numElements));
        size_t actuallyRead;
        read(sizeof(Type_), size_t(numElements), ret.data(), actuallyRead);
        return ret;
    }

    void readIntoCharBuffer(std::vector<char>& outString) const
    {
        outString.resize((size_t)getSize() + 1);

        size_t dataRead;
        read(1, (size_t)getSize(), outString.data(), dataRead);
    }

    template<typename T_>
    void readIntoBuffer(std::vector<T_>& outString) const
    {
        size_t initial_size = outString.size();
        outString.resize(size_t(initial_size + getSize()));

        size_t dataRead;
        return read(sizeof(T_), size_t(getSize()), outString.data() + initial_size, dataRead);
    }

    std::vector<char> readChars() const
    {
        std::vector<char> pData;
        readIntoCharBuffer(pData);
        return pData;
    }

    void readIntoString(std::string& outString) const
    {
        uint64_t sz = getSize();
        outString.resize((size_t)sz);

        if (sz > 0) { readExact(1, (size_t)getSize(), &outString[0]); }
    }
    std::string readString() const
    {
        uint64_t sz = getSize();
        std::string outString;
        outString.resize((size_t)sz);

        if (sz > 0) { readExact(1, (size_t)getSize(), &outString[0]); }
        return outString;
    }

protected:
    explicit Stream(const std::string& fileName, bool readable, bool writable, bool seekable)
        : _isReadable(readable), _isWritable(writable), _isRandomAccess(seekable), _fileName(fileName)
    {}
    bool _isReadable;
    bool _isWritable;
    bool _isRandomAccess;
    std::string _fileName;

private:
    virtual void _seek(long offset, SeekOrigin origin) const = 0;

    virtual void _write(size_t elementSize, size_t numElements, const void* buffer, size_t& dataWritten) = 0;

    virtual void _read(size_t elementSize, size_t numElements, void* buffer, size_t& dataRead) const = 0;

    virtual uint64_t _getPosition() const = 0;
    virtual uint64_t _getSize() const = 0;

    // Disable copying and assign.
    Stream& operator=(const Stream&) = delete;
    Stream(const Stream&) = delete;
};

inline FileIOError::FileIOError(const Stream& stream) : PvrError("[" + stream.getFileName() + "]: File IO operation failed") {}
inline FileIOError::FileIOError(const Stream& stream, std::string message) : PvrError("[" + stream.getFileName() + "] : File IO operation failed - " + message) {}

inline FileEOFError::FileEOFError(const Stream& stream) : PvrError("[" + stream.getFileName() + "]: File IO operation failed") {}
inline FileEOFError::FileEOFError(const Stream& stream, std::string message) : PvrError("[" + stream.getFileName() + "] : File IO operation failed - " + message) {}

inline FileNotFoundError::FileNotFoundError(const Stream& stream) : std::runtime_error("[" + stream.getFileName() + "]: File not found.") {}
inline FileNotFoundError::FileNotFoundError(const Stream& stream, std::string message) : std::runtime_error("[" + stream.getFileName() + "] : File not found - " + message) {}
} // namespace pvr