Help with class naming conventions for a FAT filesystem library

I am writing a new FAT12/FAT16/FAT32 library. I need suggestions for class naming conventions.

Please read the following and post any suggestions.

This library will support FAT file systems on any block device derived from the following class.

class FatBlockDevice {
 public:
  virtual bool readBlock(uint32_t block, uint8_t* dst) = 0;
  virtual bool writeBlock(uint32_t block, const uint8_t* dst) = 0;
  virtual bool readBlocks(uint32_t block, uint16_t count, uint8_t* dst) = 0;
  virtual bool writeBlocks(uint32_t block, uint16_t count, const uint8_t* dst) = 0;
};

I plan to support SD on SPI, SD on 4-bit SDIO, USB mass storage devices such as USB flash drives, RAM disks, and other devices.

I have completed SD SPI and realize I need naming conventions for file system classes.

For SD SPI I implemented three classes in place of the single class in SdFat.

// Like SdFat, uses an optimized SPI implementation.
FatSdSpiNative sd;

// This class uses the Arduino SPI library for SPI I/O.
FatSdSpiLib sd;

// This class uses bit-bang SPI on any pins.
FatSdSpiSoft<MISO_PIN, MOSI_PIN, SCK_PIN> sd;

My convention is “Fat” for FAT filesystem, “SdSpi” for device (SD in SPI mode), followed by “Native”, “Lib”, or “Soft” for SPI implementation.

I plan to use “FatSdio” for a class that supports the 4-bit SD SDIO bus. Most likely the first implementation will be on STM32 for ChibiOS.

I plan to use “FatUsb” for a class that supports USB mass storage. I hope to implement this class on Due.

I currently define two file classes. FatFile and FatPrintFile. FatFile can be used on Arduino or in standalone RTOSs like ChibiOS. FatPrintFile is derived from Print and FatFile like this:

class FatPrintFile : public FatFile, public Print {
// ...
};

Here is a simple test sketch. I plan to include some typedefs for better compatibility with SdFat.

/*
 * Sketch to compare size of SdFat with FatLib.
 */
#include <FatLib.h>
#include <FatSdSpi.h>

// Fastest optimized SPI version.
FatSdSpiNative sd;

// Remove comment to test Arduino SPI library version.
// FatSdSpiLib sd;

// Remove comment to test bit-bang version.
// FatSdSpiSoft<MISO, MOSI, SCK> sd;

FatPrintFile file;
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while (!Serial) {}  // wait for Leonardo

  if (!sd.begin()) {
    Serial.println("begin failed");
    return;
  }
  file.open("SIZE_TST.TXT", O_RDWR | O_CREAT | O_AT_END);

  file.println("Hello");

  file.close();
  Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}

Looks good overall.

For your physical interface abstraction, I'd reduce down to two functions maybe:

class FatBlockDevice {
 public:
  virtual bool readBlocks(uint32_t block, uint8_t* dst,  uint16_t count = 1) = 0;
  virtual bool writeBlocks(uint32_t block, const uint8_t* src,  uint16_t count = 1) = 0;
};

The lower level this interface it is, the less you'll find yourself re-inventing the wheel for each transport.

In your example sketch I don't see how "sd" and "file" are connected. Are you supposed to pass "sd" to FatPrintFile constructor or something?

can’t you make just one version:

FatSdSpi<MISO_PIN, MOSI_PIN, SCK_PIN> sd;

so the programmer has to specify pins.
If the pins are know compile time it choose the fastest version?

in short let the function itself decide it is FatSdSpiExtreme or FatSdSpiLib or FatSdSpiSoft

is that possible?

THen the user never has to think…

For your physical interface abstraction, I’d reduce down to two functions maybe:

The reason for readBlock and writeBlock is that SD and USB mass storage have both single and multiple block commands. I have optimized for the difference.

For flash devices reading or writing a single block does not allocate buffering in the device and does not read ahead or erase ahead. This is more efficient for small memory systems like UNO or small transfers on Due or Mega.

Reading or writing multiple blocks is fast and efficient for large transfers on Mega or Due.

can’t you make just one version:

FatSdSpi<MISO_PIN, MOSI_PIN, SCK_PIN> sd;

I need to distinguish between my optimized SPI implementation and using the Arduino SPI library. I can’t be sure bit-bang is not desired.

Another problem is that code for all versions needs to be linked and extra code is required to determine which version to use. This adds a lot of flash size for small boards like UNO.

fat16lib:


can’t you make just one version:

FatSdSpi<MISO_PIN, MOSI_PIN, SCK_PIN> sd;

I need to distinguish between my optimized SPI implementation and using the Arduino SPI library. I can’t be sure bit-bang is not desired.

Another problem is that code for all versions needs to be linked and extra code is required to determine which version to use. This adds a lot of flash size for small boards like UNO.

Makes sense

fat16lib:

For your physical interface abstraction, I’d reduce down to two functions maybe:

The reason for readBlock and writeBlock is that SD and USB mass storage have both single and multiple block commands. I have optimized for the difference.

The function prototypes I posted would not preclude you to optimize where possible.

if (count == 1)
else…

The function prototypes I posted would not preclude you to optimize where possible.

if (count == 1)
else…

Totally different code is required for single and multiple block transfers on SD cards. On UNO I only link the single block transfers. This saves memory on small processors. If you optimize with your interface both multiple and single block transfer code must be linked.

The calling code is the natural place to optimize since there are many paths depending if the cache is involved. Much optimization is controlled by conditional compilation of cache and transfer functions depending on the nature of the CPU and amount of RAM. Fast ARM CPUs require a different cache/transfer policy than slow processors like AVR.

I am not too worried about the blockDevice interface since single/multiple block commands are provided by common block devices and is available in existing libraries for these devices. I actually simplified existing interfaces since there are no initialization/configuration calls.

Few people will ever write a block device low level driver. I am more concerned about the API for user callable file system functions. I included the blockDevice interface to show that this FAT library can support almost any block device.