| SeekEnd = 2 | SeekEnd = 2 | ||||
| }; | }; | ||||
| class File; | |||||
| class File : public Stream { | class File : public Stream { | ||||
| public: | public: | ||||
| constexpr File() : f(nullptr) { } | constexpr File() : f(nullptr) { } | ||||
| constexpr File(const File &file) : f(file.f) { } | |||||
| virtual ~File(); | |||||
| virtual size_t read(void *buf, size_t nbyte); | |||||
| virtual size_t write(const void *buf, size_t size); | |||||
| virtual int available(); | |||||
| virtual int read(); | |||||
| virtual int peek(); | |||||
| virtual void flush(); | |||||
| virtual bool seek(uint32_t pos, int mode); | |||||
| virtual uint32_t position() const; | |||||
| virtual uint32_t size() const; | |||||
| virtual void close(); | |||||
| virtual operator bool() const; | |||||
| virtual const char* name(); | |||||
| virtual bool isDirectory(); | |||||
| virtual File openNextFile(uint8_t mode=0); | |||||
| virtual void rewindDirectory(void); | |||||
| virtual bool seek(uint32_t pos) { | |||||
| File(File *file) { | |||||
| // "file" must only be a class derived from File | |||||
| // can we use is_same or is_polymorphic with static_assert? | |||||
| // or is_base_of | |||||
| //static_assert(std::is_same<decltype(*file),File>::value, | |||||
| //"File(File *file) constructor only accepts pointers " | |||||
| //"to derived classes, not File itself"); | |||||
| f = file; | |||||
| if (f) f->refcount++; | |||||
| } | |||||
| File(const File &file) { | |||||
| //Serial.println("File copy constructor"); | |||||
| //static int copycount=0; | |||||
| //if (++copycount > 20) while (1) ; | |||||
| f = file.f; | |||||
| if (f) f->refcount++; | |||||
| } | |||||
| File& operator = (const File &file) { | |||||
| //Serial.println("File assignment"); | |||||
| //static int assigncount=0; | |||||
| //if (++assigncount > 20) while (1) ; | |||||
| invalidate(); | |||||
| f = file.f; | |||||
| if (f) f->refcount++; | |||||
| return *this; | |||||
| } | |||||
| virtual ~File() { | |||||
| invalidate(); | |||||
| } | |||||
| #if 1 | |||||
| virtual void whoami() { // testing only | |||||
| Serial.printf(" File this=%x, f=%x\n", (int)this, (int)f); | |||||
| if (f) f->whoami(); | |||||
| } | |||||
| unsigned int getRefcount() { // testing only | |||||
| return refcount; | |||||
| } | |||||
| #endif | |||||
| virtual size_t read(void *buf, size_t nbyte) { | |||||
| return (f) ? f->read(buf, nbyte) : 0; | |||||
| } | |||||
| virtual size_t write(const void *buf, size_t size) { | |||||
| return (f) ? f->write(buf, size) : 0; | |||||
| } | |||||
| virtual int available() { | |||||
| return (f) ? f->available() : 0; | |||||
| } | |||||
| virtual int read() { | |||||
| return (f) ? f->read() : -1; | |||||
| } | |||||
| virtual int peek() { | |||||
| return (f) ? f->peek() : -1; | |||||
| } | |||||
| virtual void flush() { | |||||
| if (f) f->flush(); | |||||
| } | |||||
| virtual bool seek(uint32_t pos, int mode) { | |||||
| return (f) ? f->seek(pos, mode) : false; | |||||
| } | |||||
| virtual uint32_t position() const { | |||||
| return (f) ? f->position() : 0; | |||||
| } | |||||
| virtual uint32_t size() const { | |||||
| return (f) ? f->size() : 0; | |||||
| } | |||||
| virtual void close() { | |||||
| if (f) f->close(); | |||||
| } | |||||
| virtual operator bool() const { | |||||
| return (f) ? (bool)*f : false; | |||||
| } | |||||
| virtual const char* name() { | |||||
| return (f) ? f->name() : ""; | |||||
| } | |||||
| virtual bool isDirectory() { | |||||
| return (f) ? f->isDirectory() : false; | |||||
| } | |||||
| virtual File openNextFile(uint8_t mode=0) { | |||||
| return (f) ? f->openNextFile(mode) : *this; | |||||
| } | |||||
| virtual void rewindDirectory(void) { | |||||
| if (f) f->rewindDirectory(); | |||||
| } | |||||
| bool seek(uint32_t pos) { | |||||
| return seek(pos, SeekSet); | return seek(pos, SeekSet); | ||||
| } | } | ||||
| virtual size_t write(uint8_t b) { | |||||
| size_t write(uint8_t b) { | |||||
| return write(&b, 1); | return write(&b, 1); | ||||
| } | } | ||||
| virtual size_t write(const char *str) { | |||||
| size_t write(const char *str) { | |||||
| return write(str, strlen(str)); | return write(str, strlen(str)); | ||||
| } | } | ||||
| virtual size_t readBytes(char *buffer, size_t length) { | |||||
| size_t readBytes(char *buffer, size_t length) { | |||||
| return read(buffer, length); | return read(buffer, length); | ||||
| } | } | ||||
| virtual void whoami(); // testing only | |||||
| //protected: | |||||
| File *f; // points to actual implementing class, or nullptr | |||||
| private: | |||||
| void invalidate() { | |||||
| if (f && --(f->refcount) == 0) delete f; | |||||
| } | |||||
| union { | |||||
| // instances of base File class use this pointer | |||||
| File *f; | |||||
| // instances of derived classes (which actually access media) | |||||
| // use this reference count which is managed by the base class | |||||
| unsigned int refcount; | |||||
| }; | |||||
| }; | }; | ||||