Browse Source

File abstraction (work in progress)

main
PaulStoffregen 4 years ago
parent
commit
507dc71293
3 changed files with 237 additions and 232 deletions
  1. +95
    -106
      File.cpp
  2. +107
    -73
      SD.cpp
  3. +35
    -53
      SD.h

+ 95
- 106
File.cpp View File

/*


SD - a slightly more friendly wrapper for sdfatlib

This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
#include <Arduino.h>
#include <SD.h>
#include <limits.h>


License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
#ifndef __SD_t3_H__


(C) Copyright 2010 SparkFun Electronics
#include <utility/SdFat.h>
#include <utility/SdFatUtil.h>


*/


#include <SD.h>
#ifndef __SD_t3_H__
void SD_File::whoami()
{
//Serial.printf(" SD_File this=%x, f=%x\n", (int)this, (int)f.get());
Serial.printf(" SD_File this=%x, refcount=%u\n",
(int)this, getRefcount());
static int whoamicount = 0;
if (++whoamicount > 20) while (1) ;
}


/* for debugging file open/close leaks
uint8_t nfilecount=0;
*/


File::File(SdFile f, const char *n) {
// oh man you are kidding me, new() doesnt exist? Ok we do it by hand!
_file = (SdFile *)malloc(sizeof(SdFile));
if (_file) {
memcpy(_file, &f, sizeof(SdFile));
strncpy(_name, n, 12);
_name[12] = 0;
/* for debugging file open/close leaks
nfilecount++;
Serial.print("Created \"");
Serial.print(n);
Serial.print("\": ");
Serial.println(nfilecount, DEC);
*/
}
SD_File::SD_File(const SdFile &file, const char *n)
{
_file = new SdFile();
memcpy(_file, &file, sizeof(SdFile)); // should use a copy constructor?
strncpy(_name, n, 12);
_name[12] = 0;
//f = (File *)this;
} }


File::File(void) {
_file = 0;
_name[0] = 0;
//Serial.print("Created empty file object");
SD_File::SD_File(void)
{
_file = nullptr;
_name[0] = 0;
//f = (File *)this;
} }


File::~File(void) {
// Serial.print("Deleted file object");
SD_File::~SD_File(void)
{
close();
} }


// returns a pointer to the file name // returns a pointer to the file name
char *File::name(void) {
return _name;
const char * SD_File::name(void) {
return _name;
} }


// a directory is a special type of file // a directory is a special type of file
boolean File::isDirectory(void) {
return (_file && _file->isDir());
bool SD_File::isDirectory(void)
{
return (_file && _file->isDir());
} }




size_t File::write(uint8_t val) {
return write(&val, 1);
size_t SD_File::write(uint8_t val)
{
return write(&val, 1);
} }


size_t File::write(const uint8_t *buf, size_t size) {
size_t t;
if (!_file) {
setWriteError();
return 0;
}
_file->clearWriteError();
t = _file->write(buf, size);
if (_file->getWriteError()) {
setWriteError();
return 0;
}
return t;
size_t SD_File::write(const void *buf, size_t size)
{
if (_file == nullptr) {
setWriteError();
return 0;
}
_file->clearWriteError();
size_t t = _file->write(buf, size);
if (_file->getWriteError()) {
setWriteError();
return 0;
}
return t;
} }


int File::peek() {
if (! _file)
return 0;

int c = _file->read();
if (c != -1) _file->seekCur(-1);
return c;
int SD_File::peek()
{
if (_file == nullptr) return 0;
int c = _file->read();
if (c != -1) _file->seekCur(-1);
return c;
} }


int File::read() {
if (_file)
return _file->read();
return -1;
int SD_File::read()
{
if (_file == nullptr) return -1;
return _file->read();
} }


// buffered read for more efficient, high speed reading // buffered read for more efficient, high speed reading
int File::read(void *buf, uint16_t nbyte) {
if (_file)
return _file->read(buf, nbyte);
return 0;
size_t SD_File::read(void *buf, size_t nbyte) {
if (_file == nullptr) return 0;
return _file->read(buf, nbyte);
} }


int File::available() {
if (! _file) return 0;
uint32_t n = size() - position();
return n > 0X7FFF ? 0X7FFF : n;
int SD_File::available()
{
if (_file == nullptr) return 0;
uint32_t n = size() - position();
if (n > INT_MAX) n = INT_MAX;
return n;
} }


void File::flush() {
if (_file)
_file->sync();
void SD_File::flush()
{
if (_file == nullptr) return;
_file->sync();
} }


boolean File::seek(uint32_t pos) {
if (! _file) return false;
return _file->seekSet(pos);
bool SD_File::seek(uint32_t pos, int mode)
{
if (_file == nullptr) return false;
return _file->seekSet(pos);
} }


uint32_t File::position() {
if (! _file) return -1;
return _file->curPosition();
uint32_t SD_File::position() const
{
if (_file == nullptr) return 0;
return _file->curPosition();
} }


uint32_t File::size() {
if (! _file) return 0;
return _file->fileSize();
uint32_t SD_File::size() const
{
if (_file == nullptr) return 0;
return _file->fileSize();
} }


void File::close() {
if (_file) {
_file->close();
free(_file);
_file = 0;

/* for debugging file open/close leaks
nfilecount--;
Serial.print("Deleted ");
Serial.println(nfilecount, DEC);
*/
}
void SD_File::close()
{
if (_file == nullptr) return;
_file->close();
//delete _file; // TODO: how to handle this?
_file = nullptr;
} }


File::operator bool() {
if (_file)
return _file->isOpen();
return false;
SD_File::operator bool() const
{
if (_file == nullptr) return false;
return _file->isOpen();
} }
#endif #endif

+ 107
- 73
SD.cpp View File

#include <Arduino.h>
#include "SD.h"

#include <utility/SdFat.h>
#include <utility/SdFatUtil.h>




#if 1
/* /*


SD - a slightly more friendly wrapper for sdfatlib SD - a slightly more friendly wrapper for sdfatlib
`SD` object which can be interacted with in a similar `SD` object which can be interacted with in a similar
manner to other standard global objects like `Serial` and `Ethernet`. manner to other standard global objects like `Serial` and `Ethernet`.


* Boilerplate initialisation code is contained in one method named
* Boilerplate initialisation code is contained in one method named
`begin` and no further objects need to be created in order to access `begin` and no further objects need to be created in order to access
the SD card. the SD card.


* Calls to `open` can supply a full path name including parent
* Calls to `open` can supply a full path name including parent
directories which simplifies interacting with files in subdirectories. directories which simplifies interacting with files in subdirectories.


* Utility methods are provided to determine whether a file exists * Utility methods are provided to determine whether a file exists


Implementation Notes Implementation Notes


In order to handle multi-directory path traversal, functionality that
In order to handle multi-directory path traversal, functionality that
requires this ability is implemented as callback functions. requires this ability is implemented as callback functions.


Individual methods call the `walkPath` function which performs the actual Individual methods call the `walkPath` function which performs the actual
if (path[offset] == '/') { if (path[offset] == '/') {
offset++; offset++;
} }
// Copy the next next path segment // Copy the next next path segment
while (bufferOffset < MAX_COMPONENT_LEN while (bufferOffset < MAX_COMPONENT_LEN
&& (path[offset] != '/') && (path[offset] != '/')






boolean walkPath(const char *filepath, SdFile& parentDir,
boolean (*callback)(SdFile& parentDir,
bool walkPath(const char *filepath, SdFile *parentDir,
bool (*callback)(SdFile *parentDir,
char *filePathComponent, char *filePathComponent,
boolean isLastComponent,
bool isLastComponent,
void *object), void *object),
void *object = NULL) { void *object = NULL) {
/* /*
When given a file path (and parent directory--normally root), When given a file path (and parent directory--normally root),
this function traverses the directories in the path and at each this function traverses the directories in the path and at each
level calls the supplied callback function while also providing level calls the supplied callback function while also providing
SdFile subfile1; SdFile subfile1;
SdFile subfile2; SdFile subfile2;


char buffer[PATH_COMPONENT_BUFFER_LEN];
char buffer[PATH_COMPONENT_BUFFER_LEN];


unsigned int offset = 0; unsigned int offset = 0;


SdFile *p_parent; SdFile *p_parent;
SdFile *p_child; SdFile *p_child;


SdFile *p_tmp_sdfile;
SdFile *p_tmp_sdfile;
p_child = &subfile1; p_child = &subfile1;
p_parent = &parentDir;
p_parent = parentDir;


while (true) { while (true) {


boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
bool moreComponents = getNextPathComponent(filepath, &offset, buffer);


boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
bool shouldContinue = callback(p_parent, buffer, !moreComponents, object);


if (!shouldContinue) { if (!shouldContinue) {
// TODO: Don't repeat this code? // TODO: Don't repeat this code?
// If it's one we've created then we // If it's one we've created then we
// don't need the parent handle anymore. // don't need the parent handle anymore.
if (p_parent != &parentDir) {
if (p_parent != parentDir) {
(*p_parent).close(); (*p_parent).close();
} }
return false; return false;
} }
if (!moreComponents) { if (!moreComponents) {
break; break;
} }
boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
bool exists = (*p_child).open(*p_parent, buffer, O_RDONLY);


// If it's one we've created then we // If it's one we've created then we
// don't need the parent handle anymore. // don't need the parent handle anymore.
if (p_parent != &parentDir) {
if (p_parent != parentDir) {
(*p_parent).close(); (*p_parent).close();
} }
// Handle case when it doesn't exist and we can't continue... // Handle case when it doesn't exist and we can't continue...
if (exists) { if (exists) {
// We alternate between two file handles as we go down // We alternate between two file handles as we go down
// the path. // the path.
if (p_parent == &parentDir) {
if (p_parent == parentDir) {
p_parent = &subfile2; p_parent = &subfile2;
} }


return false; return false;
} }
} }
if (p_parent != &parentDir) {
if (p_parent != parentDir) {
(*p_parent).close(); // TODO: Return/ handle different? (*p_parent).close(); // TODO: Return/ handle different?
} }




*/ */


boolean callback_pathExists(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
bool callback_pathExists(SdFile *parentDir, char *filePathComponent,
bool isLastComponent, void *object) {
/* /*


Callback used to determine if a file/directory exists in parent Callback used to determine if a file/directory exists in parent
*/ */
SdFile child; SdFile child;


boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
bool exists = child.open(parentDir, filePathComponent, O_RDONLY);
if (exists) { if (exists) {
child.close();
child.close();
} }
return exists; return exists;
} }






boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
bool callback_makeDirPath(SdFile *parentDir, char *filePathComponent,
bool isLastComponent, void *object) {
/* /*


Callback used to create a directory in the parent directory if Callback used to create a directory in the parent directory if
Returns true if a directory was created or it already existed. Returns true if a directory was created or it already existed.


*/ */
boolean result = false;
bool result = false;
SdFile child; SdFile child;
result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
if (!result) { if (!result) {
result = child.makeDir(parentDir, filePathComponent); result = child.makeDir(parentDir, filePathComponent);
}
}
return result; return result;
} }




/* /*


boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
bool callback_openPath(SdFile *parentDir, char *filePathComponent,
bool isLastComponent, void *object) {


Callback used to open a file specified by a filepath that may Callback used to open a file specified by a filepath that may
specify one or more directories above it. specify one or more directories above it.






boolean callback_remove(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
bool callback_remove(SdFile *parentDir, char *filePathComponent,
bool isLastComponent, void *object) {
if (isLastComponent) { if (isLastComponent) {
return SdFile::remove(parentDir, filePathComponent); return SdFile::remove(parentDir, filePathComponent);
} }
return true; return true;
} }


boolean callback_rmdir(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
bool callback_rmdir(SdFile *parentDir, char *filePathComponent,
bool isLastComponent, void *object) {
if (isLastComponent) { if (isLastComponent) {
SdFile f; SdFile f;
if (!f.open(parentDir, filePathComponent, O_READ)) return false; if (!f.open(parentDir, filePathComponent, O_READ)) return false;






boolean SDClass::begin(uint8_t csPin) {
bool SDClass::begin(uint8_t csPin) {
/* /*


Performs the initialisation required by the sdfatlib library. Performs the initialisation required by the sdfatlib library.
Return true if initialization succeeds, false otherwise. Return true if initialization succeeds, false otherwise.


*/ */
card = new Sd2Card();
volume = new SdVolume();
root = new SdFile();



if (root.isOpen()) root.close();
if (root->isOpen()) root->close();


return card.init(SPI_HALF_SPEED, csPin) &&
volume.init(card) &&
root.openRoot(volume);
return card->init(SPI_HALF_SPEED, csPin) &&
volume->init(card) &&
root->openRoot(volume);
} }




// we'll use the pointers to swap between the two objects // we'll use the pointers to swap between the two objects
SdFile *parent = &d1; SdFile *parent = &d1;
SdFile *subdir = &d2; SdFile *subdir = &d2;
const char *origpath = filepath; const char *origpath = filepath;


while (strchr(filepath, '/')) { while (strchr(filepath, '/')) {
filepath++; filepath++;
continue; continue;
} }
if (! strchr(filepath, '/')) { if (! strchr(filepath, '/')) {
// it was in the root directory, so leave now // it was in the root directory, so leave now
break; break;
filepath += pathidx; filepath += pathidx;


if (! filepath[0]) { if (! filepath[0]) {
// it was the directory itself!
return File(parentdir, "/");
}
// it was the directory itself!
Serial.println("it was the directory itself");


// Open the file itself
SdFile file;
File ret = File(new SD_File(parentdir, "/"));
ret.whoami();
return ret;
}


// failed to open a subdir! // failed to open a subdir!
if (!parentdir.isOpen())
return File();
if (!parentdir.isOpen()) {
return File();
}


// convert mode to underlying SdFat
if (mode == FILE_READ) {
mode = O_READ;
} else { // FILE_WRITE
mode = O_READ | O_WRITE | O_CREAT;
}

// Open the file itself
SdFile file;
if ( ! file.open(parentdir, filepath, mode)) { if ( ! file.open(parentdir, filepath, mode)) {
return File();
return File();
} }
// close the parent // close the parent
parentdir.close(); parentdir.close();


if (mode & (O_APPEND | O_WRITE))
file.seekSet(file.fileSize());
return File(file, filepath);
if (mode & (O_APPEND | O_WRITE)) {
file.seekSet(file.fileSize());
}

File ret = File(new SD_File(file, filepath));

Serial.println("open() return:");
ret.whoami();
return ret;
//return SD_File(file, filepath);
} }




*/ */




//boolean SDClass::close() {
//bool SDClass::close() {
// /* // /*
// //
// Closes the file opened by the `open` method. // Closes the file opened by the `open` method.
//} //}




boolean SDClass::exists(const char *filepath) {
bool SDClass::exists(const char *filepath) {
/* /*


Returns true if the supplied file path exists. Returns true if the supplied file path exists.
} }




//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
//bool SDClass::exists(char *filepath, SdFile& parentDir) {
// /* // /*
// //
// Returns true if the supplied file path rooted at `parentDir` // Returns true if the supplied file path rooted at `parentDir`
//} //}




boolean SDClass::mkdir(const char *filepath) {
bool SDClass::mkdir(const char *filepath) {
/* /*
Makes a single directory or a heirarchy of directories. Makes a single directory or a heirarchy of directories.


A rough equivalent to `mkdir -p`. A rough equivalent to `mkdir -p`.
*/ */
return walkPath(filepath, root, callback_makeDirPath); return walkPath(filepath, root, callback_makeDirPath);
} }


boolean SDClass::rmdir(const char *filepath) {
bool SDClass::rmdir(const char *filepath) {
/* /*
Makes a single directory or a heirarchy of directories. Makes a single directory or a heirarchy of directories.


A rough equivalent to `mkdir -p`. A rough equivalent to `mkdir -p`.
*/ */
return walkPath(filepath, root, callback_rmdir); return walkPath(filepath, root, callback_rmdir);
} }


boolean SDClass::remove(const char *filepath) {
bool SDClass::remove(const char *filepath) {
return walkPath(filepath, root, callback_remove); return walkPath(filepath, root, callback_remove);
} }




// allows you to recurse into a directory // allows you to recurse into a directory
File File::openNextFile(uint8_t mode) {
File SD_File::openNextFile(uint8_t mode)
{
dir_t p; dir_t p;


//Serial.print("\t\treading dir..."); //Serial.print("\t\treading dir...");


if (f.open(_file, name, mode)) { if (f.open(_file, name, mode)) {
//Serial.println("OK!"); //Serial.println("OK!");
return File(f, name);
return File(new SD_File(f, name));
} else { } else {
//Serial.println("ugh"); //Serial.println("ugh");
return File(); return File();
return File(); return File();
} }


void File::rewindDirectory(void) {
void SD_File::rewindDirectory(void) {
if (isDirectory()) if (isDirectory())
_file->rewind(); _file->rewind();
} }


SDClass SD; SDClass SD;
#endif #endif
#endif

+ 35
- 53
SD.h View File

#if defined(__arm__)
#include "SD_t3.h"
#endif
/*

SD - a slightly more friendly wrapper for sdfatlib

This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.

License: GNU General Public License V3
(Because sdfatlib is licensed with this.)

(C) Copyright 2010 SparkFun Electronics

*/

#ifndef __SD_H__ #ifndef __SD_H__
#define __SD_H__ #define __SD_H__


#include <Arduino.h> #include <Arduino.h>
#include <FS.h>


#include <utility/SdFat.h>
#include <utility/SdFatUtil.h>

#define FILE_READ O_READ
#define FILE_WRITE (O_READ | O_WRITE | O_CREAT)

class File : public Stream {
private:
char _name[13]; // our name
SdFile *_file; // underlying file pointer
class Sd2Card;
class SdVolume;
class SdFile;


class SD_File : public File {
public: public:
File(SdFile f, const char *name); // wraps an underlying SdFile
File(void); // 'empty' constructor
~File(void); // destructor
SD_File(const SdFile &f, const char *name);
SD_File(void);
virtual ~SD_File(void);
virtual size_t write(uint8_t); virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
virtual size_t write(const void *buf, size_t size);
virtual int read(); virtual int read();
virtual int peek(); virtual int peek();
virtual int available(); virtual int available();
virtual void flush(); virtual void flush();
int read(void *buf, uint16_t nbyte);
boolean seek(uint32_t pos);
uint32_t position();
uint32_t size();
void close();
operator bool();
char * name();

boolean isDirectory(void);
File openNextFile(uint8_t mode = O_RDONLY);
void rewindDirectory(void);
virtual size_t read(void *buf, size_t nbyte);
virtual bool seek(uint32_t pos, int mode = 0);
virtual uint32_t position() const;
virtual uint32_t size() const;
virtual void close();
virtual operator bool() const;
virtual const char * name();
virtual boolean isDirectory(void);
virtual File openNextFile(uint8_t mode = 0 /*O_RDONLY*/) override;
virtual void rewindDirectory(void);
virtual void whoami();
using Print::write; using Print::write;
private:
char _name[13]; // our name
SdFile *_file; // underlying file pointer
}; };


class SDClass {


class SDClass : public FS {


private: private:
// These are required for initialisation and use of sdfatlib // These are required for initialisation and use of sdfatlib
Sd2Card card;
SdVolume volume;
SdFile root;
Sd2Card *card;
SdVolume *volume;
SdFile *root;
// my quick&dirty iterator, should be replaced // my quick&dirty iterator, should be replaced
SdFile getParentDir(const char *filepath, int *indx); SdFile getParentDir(const char *filepath, int *indx);
public: public:
// This needs to be called to set up the connection to the SD card // This needs to be called to set up the connection to the SD card
// before other methods are used. // before other methods are used.
boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN);
bool begin(uint8_t csPin = 10 /*SD_CHIP_SELECT_PIN*/);
// Open the specified file/directory with the supplied mode (e.g. read or // Open the specified file/directory with the supplied mode (e.g. read or
// write, etc). Returns a File object for interacting with the file. // write, etc). Returns a File object for interacting with the file.
File open(const char *filename, uint8_t mode = FILE_READ); File open(const char *filename, uint8_t mode = FILE_READ);


// Methods to determine if the requested file path exists. // Methods to determine if the requested file path exists.
boolean exists(const char *filepath);
bool exists(const char *filepath);


// Create the requested directory heirarchy--if intermediate directories // Create the requested directory heirarchy--if intermediate directories
// do not exist they will be created. // do not exist they will be created.
boolean mkdir(const char *filepath);
bool mkdir(const char *filepath);
// Delete the file. // Delete the file.
boolean remove(const char *filepath);
bool remove(const char *filepath);
boolean rmdir(const char *filepath);
bool rmdir(const char *filepath);


private: private:


// It shouldn't be set directly--it is set via the parameters to `open`. // It shouldn't be set directly--it is set via the parameters to `open`.
int fileOpenMode; int fileOpenMode;
friend class File;
friend class SD_File;
friend boolean callback_openPath(SdFile&, char *, boolean, void *); friend boolean callback_openPath(SdFile&, char *, boolean, void *);
}; };



Loading…
Cancel
Save