PicoStorage::Dir class documentation
A directory holds a set of named directory entries, which can be either files or directories.
Each entry is identified by a name. What the directory does is mainly maintaining a mapping from names to inode numbers, even though the inode numbers aren't directly visible to the user.
The entry names are 8-bit-clean; you can put anything in a name, for example binary data. A name can even contain NULLs (bytes with value 0).
If you choose to put binary data in the names (e.g. using binary integers for names) it's up to you to handle the endianess issue (insure the integers are correctly handled when moving the storage between little endian (LE) and big endian (BE) architectures).
The encoding (e.g. ASCII, UTF-8, UTF-16, ISO-Latin) of the names is not tackled by PicoStorage. It's up to you to use any encoding you choose and handle the endianess issues if needed. The library allows to use any encoding by supporting 8-bit-clean names.
The directory implementation uses a hash table. The directory space is organized into small fixed-size (e.g. 1KB) blocks. When doing a lookup, a hash of the name is computed, and determinates in which block to look for the name; then the lookup is done only in the designated block, and not in the rest of the directory. When adding a name, similarly a specific block is designated for the name (based on the hash), and the name is added to that block. If the block starts to fill-up, the hash table is grown by gradually adding and splitting blocks.
The hashtable structure of the directory allows for constant time lookup and insert, i.e. the time doesn't depend on the size of the directory. Thus you can have extremly large directories (e.g. 1M to 10M entries) without a performance penalty.
The directory uses internally a File. In this implementation, the size of a Dir object is 20 bytes on 32bit architectures.
The methods which require a name exist in two variants: you can either pass a void* (binary) name and a lenght, or you can pass just a const char* name. In the second case, the name must be NULL-terminated (and it can't contain NULLs, obviously).
Opening (a file or a directory) means opening an already existing entry. It fails if no such name exists, or if the name exists but with the wrong type (e.g. a directory when trying to open a file).
Creating (a file or a directory) means creating a new entry. It will fail if an entry with the same name exists, regardless of its type (directory or file).
A File or Dir object is in one of two states: initialized or uninitialized. See operator bool() for details.
The methods for opening or creating return the opened/created object, which is either a Dir or a File. In the case of failure (for the reasons mentioned above) the returned object is uninitialized. You should test the return value from open/create methods to determinate if the operation succeeded before using the returned object.
File createFile(const char *name);
File createFile(const void *name, int len);
Dir createDir(const char *name);
Dir createDir(const void *name, int len);
Creates a new file or directory.
The methods have two variants, one taking a NULL-terminated string (const char *name), the other taking a binary name (which can contain any bytes) and a length (in bytes).
Parameters
- name
- the name of the file/dir to be created
- len
- the length of the name in bytes
Returns
- the newly created File/Dir, if successful;
- an uninitialized object, if the name was already existing in the directory.
File openFile(const char *name);
File openFile(const void *name, int len);
Dir openDir(const char *name);
Dir openDir(const void *name, int len);
Opens an existing file or directory.
The methods have two variants, one taking a NULL-terminated string (const char *name), the other taking a binary name (which can contain any bytes) and a length (in bytes).
Parameters
- name
- the name of the entry
- len
- the length of the name in bytes
Returns
- the opened File/Dir, if found;
- an uninitialized object, if the name was not found, in if it was of the wrong type (e.g. a directory instead of a file).
bool remove(const char *name);
bool remove(const void *name, int len);
Removes an entry from the directory. The entry can be either a file or a directory. When removing a directory, all its content is recursively removed too.Returns
- true if the entry was found (and in this case deleted).
- false if no entry with the given name was found.
void removeAllEntries();
Removes all the entries of this directory. The content of subdirectories is recursively removed too.operator bool();
Indicates if the Dir object is initialized. The discussion below considers both Dir's and File's operator bool().
When constructed with the default constructor, which takes no parameters, a File or Dir object is un-initialised; it is not associated to any real file or directory, and you can't do any operations on it (you can only call operator bool(), or copy the value to/from another object).
The methods which return a File or a Dir (createFile(), createDir(), openFile(), openDir()) return an uninitialized object to indicate failure. For example, openFile() fails if no file with the given name is not found; and createFile() fails if the name is already existing.
Usage example:
if (!someDir.createFile("foo")) {
//creation failed, name "foo" already existing
}
Returns
- true : the file is initialized, and can be used.
- false: the file is uninitialzed, you can't do other operations on it.