#pragma once #include #include #include #include #include #ifdef _MSC_VER typedef __int64 off64_t; #endif namespace Balau { class FileSystem { public: static int mkdir(const char * path) throw (GeneralException); }; class ENoEnt : public GeneralException { public: ENoEnt(const char * name) : GeneralException(String("No such file or directory: `") + name + "'") { } ENoEnt(const String & name) : ENoEnt(name.to_charp()) { } }; class IOBase; template class IO; namespace Events { class BaseEvent; }; #ifdef _MSC_VER #define WARN_UNUSED_RESULT #else #define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #endif class Handle { public: virtual ~Handle() { AAssert(m_refCount == 0, "Do not use handles directly; warp them in IO<>"); } // things to implement when derivating virtual void close() throw (GeneralException) = 0; virtual bool isClosed() = 0; virtual bool isEOF() = 0; virtual const char * getName() = 0; // normal API virtual bool canSeek(); virtual bool canRead(); virtual bool canWrite(); virtual ssize_t read(void * buf, size_t count) throw (GeneralException) WARN_UNUSED_RESULT; virtual ssize_t write(const void * buf, size_t count) throw (GeneralException) WARN_UNUSED_RESULT; virtual void rseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException); virtual void wseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException); virtual off64_t rtell() throw (GeneralException); virtual off64_t wtell() throw (GeneralException); virtual off64_t getSize(); virtual time_t getMTime(); virtual bool isPendingComplete() { return true; } enum Endianness { BALAU_LITTLE_ENDIAN, BALAU_BIG_ENDIAN, }; void setEndianness(Endianness endianness) { m_bigEndianMode = endianness == BALAU_BIG_ENDIAN; } Endianness getEndianness() { return m_bigEndianMode ? BALAU_BIG_ENDIAN : BALAU_LITTLE_ENDIAN; } // helpers off64_t tell() { return rtell(); } void seek(off64_t offset, int whence = SEEK_SET) { rseek(offset, whence); } Future readU8(); Future readU16(); Future readU32(); Future readU64(); Future readI8(); Future readI16(); Future readI32(); Future readI64(); Future readBEU16(); Future readBEU32(); Future readBEU64(); Future readBEI16(); Future readBEI32(); Future readBEI64(); Future readLEU16(); Future readLEU32(); Future readLEU64(); Future readLEI16(); Future readLEI32(); Future readLEI64(); Future writeU8(uint8_t); Future writeU16(uint16_t); Future writeU32(uint32_t); Future writeU64(uint64_t); Future writeI8 (int8_t); Future writeI16(int16_t); Future writeI32(int32_t); Future writeI64(int64_t); // these need to be changed into Future<>s template ssize_t writeString(const char (&str)[L]) WARN_UNUSED_RESULT; ssize_t writeString(const String & str) WARN_UNUSED_RESULT { return forceWrite(str.to_charp(), str.strlen()); } ssize_t writeString(const char * str, ssize_t len) WARN_UNUSED_RESULT { return forceWrite(str, len); } ssize_t forceRead(void * buf, size_t count, Events::BaseEvent * evt = NULL) throw (GeneralException) WARN_UNUSED_RESULT; ssize_t forceWrite(const void * buf, size_t count, Events::BaseEvent * evt = NULL) throw (GeneralException) WARN_UNUSED_RESULT; protected: Handle() : m_refCount(0) { } private: // the IO<> refcounting mechanism void addRef() { ++m_refCount; } void delRef() { if (--m_refCount == 0) { if (!isClosed()) close(); delete this; } } friend class IOBase; template friend class IO; std::atomic m_refCount; bool m_bigEndianMode = false; Handle(const Handle &) = delete; Handle & operator=(const Handle &) = delete; }; template ssize_t Handle::writeString(const char (&str)[L]) { return writeString(str, L - 1); } class HPrinter : public Handle { public: virtual void close() throw (GeneralException) { } virtual bool isClosed() { return false; } virtual bool isEOF() { return false; } virtual bool canWrite() { return true; } virtual const char * getName() { return "HPrinter"; } virtual ssize_t write(const void * buf, size_t count) throw (GeneralException) { Printer::print("%*s", (int) count, (const char *) buf); return count; } }; class IOBase { private: IOBase() { } ~IOBase() { if (m_h) m_h->delRef(); } void setHandle(Handle * h) { m_h = h; if (m_h) m_h->addRef(); } Handle * m_h = NULL; template friend class IO; }; template class IO : public IOBase { public: IO() { } IO(T * h) { setHandle(h); } IO(const IO & io) { if (io.m_h) setHandle(io.m_h); } IO(IO && io) { m_h = io.m_h; io.m_h = NULL; } template IO(const IO & io) { if (io.m_h) setHandle(io.m_h); } template IO(IO && io) { m_h = io.m_h; io.m_h = NULL; } template bool isA() { return !!dynamic_cast(m_h); } template IO asA() { IO h(dynamic_cast(m_h)); return h; } IO & operator=(const IO & io) { if (m_h) m_h->delRef(); setHandle(io.m_h); return *this; } T * operator->() { AAssert(m_h, "Can't use %s->() with a null Handle", ClassName(this).c_str()); T * r = dynamic_cast(m_h); AAssert(r, "%s->() used with an incompatible Handle type", ClassName(this).c_str()); return r; } bool isNull() { return dynamic_cast(m_h); } }; class SeekableHandle : public Handle { public: SeekableHandle() : m_wOffset(0), m_rOffset(0) { } virtual bool canSeek(); virtual void rseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException); virtual void wseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException); virtual off64_t rtell() throw (GeneralException); virtual off64_t wtell() throw (GeneralException); virtual bool isEOF(); protected: off64_t getWOffset() { return m_wOffset; } off64_t getROffset() { return m_rOffset; } private: off64_t m_wOffset, m_rOffset; }; class ReadOnly : public Handle { public: ReadOnly(IO & io) : m_io(io) { AAssert(m_io->canRead(), "You need to use ReadOnly with a Handle that can at least read"); } virtual void close() throw (GeneralException) { m_io->close(); } virtual bool isClosed() { return m_io->isClosed(); } virtual bool isEOF() { return m_io->isEOF(); } virtual bool canSeek() { return m_io->canSeek(); } virtual bool canRead() { return true; } virtual bool canWrite() { return false; } virtual const char * getName() { return m_io->getName(); } virtual ssize_t read(void * buf, size_t count) throw (GeneralException) { return m_io->read(buf, count); } virtual ssize_t write(const void * buf, size_t count) throw (GeneralException) { throw GeneralException("Can't write"); } virtual void rseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException) { m_io->rseek(offset, whence); } virtual void wseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException) { throw GeneralException("Can't write"); } virtual off64_t rtell() throw (GeneralException) { return m_io->rtell(); } virtual off64_t wtell() throw (GeneralException) { throw GeneralException("Can't write"); } virtual off64_t getSize() { return m_io->getSize(); } virtual time_t getMTime() { return m_io->getMTime(); } private: IO m_io; }; class WriteOnly : public Handle { public: WriteOnly(IO & io) : m_io(io) { AAssert(m_io->canWrite(), "You need to use WriteOnly with a Handle that can at least write"); } virtual void close() throw (GeneralException) { m_io->close(); } virtual bool isClosed() { return m_io->isClosed(); } virtual bool isEOF() { return m_io->isEOF(); } virtual bool canSeek() { return m_io->canSeek(); } virtual bool canRead() { return false; } virtual bool canWrite() { return true; } virtual const char * getName() { return m_io->getName(); } virtual ssize_t read(void * buf, size_t count) throw (GeneralException) { throw GeneralException("Can't read"); } virtual ssize_t write(const void * buf, size_t count) throw (GeneralException) { return m_io->write(buf, count); } virtual void rseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException) { throw GeneralException("Can't read"); } virtual void wseek(off64_t offset, int whence = SEEK_SET) throw (GeneralException) { return m_io->wseek(offset, whence); } virtual off64_t rtell() throw (GeneralException) { throw GeneralException("Can't read"); } virtual off64_t wtell() throw (GeneralException) { return m_io->wtell(); } virtual off64_t getSize() { return m_io->getSize(); } virtual time_t getMTime() { return m_io->getMTime(); } private: IO m_io; }; };