database: Design and test updates
I add: - Autosaving option - Real iterators - Better accessor functions The new design and unit test also allows me to remove the 300,000+ line "database.good" file that the old tests were based on. Signed-off-by: Anna Schumaker <schumaker.anna@gmail.com>
This commit is contained in:
parent
b251f27bb5
commit
fc8dc9d55e
258
DESIGN
258
DESIGN
|
@ -115,7 +115,7 @@ On-disk files:
|
||||||
}
|
}
|
||||||
|
|
||||||
- File:
|
- File:
|
||||||
class File : std::fstream {
|
class File : public std::fstream {
|
||||||
private:
|
private:
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
OpenMode mode;
|
OpenMode mode;
|
||||||
|
@ -207,8 +207,7 @@ Database Entry:
|
||||||
|
|
||||||
virtual void write(File &) = 0;
|
virtual void write(File &) = 0;
|
||||||
virtual void read(File &) = 0;
|
virtual void read(File &) = 0;
|
||||||
virtual void print() = 0;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
- API:
|
- API:
|
||||||
DatabaseEntry :: DatabaseEntry():
|
DatabaseEntry :: DatabaseEntry():
|
||||||
|
@ -227,33 +226,138 @@ Database Entry:
|
||||||
void DatabaseEntry :: read(File &);
|
void DatabaseEntry :: read(File &);
|
||||||
This function is called to read a DatabaseEntry from a file.
|
This function is called to read a DatabaseEntry from a file.
|
||||||
|
|
||||||
void DatabaseEntry :: print();
|
|
||||||
This function will only exist if CONFIG_TEST is enabled and is
|
|
||||||
called to print a DatabaseEntry to console.
|
|
||||||
|
|
||||||
|
|
||||||
|
Database:
|
||||||
Database: (lib/database.cpp)
|
Databases are a generic store for information used by Ocarina. Users
|
||||||
Ocarina 5.x created a different save file format for each type of
|
need to inherit from a DatabaseEntry class (above) to properly use a
|
||||||
data that needed to be stored (preferences, library paths, playlists).
|
database.
|
||||||
I intend to unify everything into a generic file format that can be
|
|
||||||
accessed through a generic database interface. The database code will
|
|
||||||
be in charge of printing the "valid" bit for each DatabaseEntry so that
|
|
||||||
child classes do not need to call into the parent class. If valid ==
|
|
||||||
true, the DatabaseEntry will be streamed out followed by a newline. If
|
|
||||||
valid == false the database will print the next entry in the vector.
|
|
||||||
|
|
||||||
Modules should inherit from the DatabasEntry class and implement their
|
|
||||||
own read() and write() functions. The "valid" field will be stored
|
|
||||||
before these functions are called, and the entry will be skipped if
|
|
||||||
valid is set to false.
|
|
||||||
|
|
||||||
The Database class is a templated class, so code could potentially
|
The Database class is a templated class, so code could potentially
|
||||||
get messy. Normal class declarations can still exist in the file
|
get messy. Normal class declarations can still exist in the file
|
||||||
include/database.h and member functions can be written in the file
|
include/database.h and member functions are written in the file
|
||||||
include/database.hpp, which will be included by database.h. Any
|
include/database.hpp, which will be included by database.h. Any
|
||||||
function not relying on a template can be written in lib/database.cpp.
|
function not relying on a template can be written in lib/database.cpp.
|
||||||
|
|
||||||
|
- Automatic saving
|
||||||
|
Databases can save automatically whenever a new value is inserted or
|
||||||
|
deleted. This will be more efficient for Databases that do not change
|
||||||
|
often but will be a huge performance hit for Databases that have many
|
||||||
|
changes at once. All databases will be loaded automatically from disk.
|
||||||
|
|
||||||
|
- Primary keys
|
||||||
|
Databases use the primary_key() function of a DatabaseEntry to enforce
|
||||||
|
uniqueness. This key is used when inserting a new value into the
|
||||||
|
Database, and will not be updated after.
|
||||||
|
|
||||||
|
- Valid bit
|
||||||
|
The "valid" bit of a DatabaseEntry is completely managed by the entry's
|
||||||
|
Database container. It will be set to true when an entry is inserted
|
||||||
|
and false when deleted. The Database is also in charge of writing the
|
||||||
|
valid bit to file.
|
||||||
|
|
||||||
|
- Database:
|
||||||
|
template <class T>
|
||||||
|
class Database {
|
||||||
|
private:
|
||||||
|
std::vector<T> _db;
|
||||||
|
std::map<std::string, unsigned int> _keys;
|
||||||
|
unsigned int _size; /* Number of valid rows */
|
||||||
|
bool _autosave;
|
||||||
|
File _file;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef std::vector<T>::iterator iterator;
|
||||||
|
typedef std::vector<T>::const_iterator const_iterator;
|
||||||
|
|
||||||
|
Database(std::string, bool);
|
||||||
|
void save();
|
||||||
|
void load();
|
||||||
|
|
||||||
|
unsigned int insert(T);
|
||||||
|
void remove(unsigned int);
|
||||||
|
unsigned int size();
|
||||||
|
unsigned int actual_size();
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
iterator next(iterator &);
|
||||||
|
|
||||||
|
iterator at(unsigned int);
|
||||||
|
iterator find(const std::string &);
|
||||||
|
};
|
||||||
|
|
||||||
|
- File format:
|
||||||
|
File << db.size() << endl
|
||||||
|
File << INDEX_0 << db[INDEX_0].valid << db[INDEX_0] << endl;
|
||||||
|
File << INDEX_1 << db[INDEX_1].valid << db[INDEX_1] << endl;
|
||||||
|
...
|
||||||
|
|
||||||
|
- API:
|
||||||
|
Database :: Database(std::string filepath, bool autosave);
|
||||||
|
Initialize a database using "filepath" as a location to store
|
||||||
|
data on disk. If the file already exists, read the data into
|
||||||
|
the backing vector.
|
||||||
|
|
||||||
|
void Database :: save();
|
||||||
|
Save the database to disk.
|
||||||
|
|
||||||
|
void Database :: load();
|
||||||
|
Load the database from disk.
|
||||||
|
|
||||||
|
unsigned int Database :: insert(T &item);
|
||||||
|
Look up the item in the _keys map.
|
||||||
|
If we find an item with the same key:
|
||||||
|
- Return the index of the item to the caller.
|
||||||
|
Otherwise:
|
||||||
|
- Add the new item to the end of the _db.
|
||||||
|
- Add the new item to _keys.
|
||||||
|
- Set item.valid = true.
|
||||||
|
_ Increment _size.
|
||||||
|
- If autosave == true: save().
|
||||||
|
- Return the index of the new item.
|
||||||
|
|
||||||
|
unsigned int Database :: remove();
|
||||||
|
- Remove the item from the _keys map.
|
||||||
|
- Set item.valid = false.
|
||||||
|
- If autosave == true: save().
|
||||||
|
- Decrement _size.
|
||||||
|
|
||||||
|
unsigned int Database :: size();
|
||||||
|
return _size;
|
||||||
|
|
||||||
|
unsigned int Database :: actual_size();
|
||||||
|
return _db.size();
|
||||||
|
|
||||||
|
iterator Database :: begin();
|
||||||
|
Return _db.end() if there are no valid entries
|
||||||
|
If the first entry is valid:
|
||||||
|
- return _db.begin();
|
||||||
|
Otherwise:
|
||||||
|
- return an iterator to the first valid entry.
|
||||||
|
|
||||||
|
iterator Database :: end();
|
||||||
|
return _db.end();
|
||||||
|
|
||||||
|
iterator Database :: next(iterator &cur);
|
||||||
|
Return the next DatabaseEntry with valid == true or _db.end()
|
||||||
|
if there are no valid entries left.
|
||||||
|
|
||||||
|
iterator Database :: at(unsigned int i);
|
||||||
|
If _db[i].valid == true:
|
||||||
|
Return an iterator pointing to _db[i];
|
||||||
|
Otherwise:
|
||||||
|
Return _db.end();
|
||||||
|
|
||||||
|
iterator Database :: find(const std::string &key);
|
||||||
|
If key is in the _keys map:
|
||||||
|
Return an iterator pointing to the corresponding entry.
|
||||||
|
Otherwise:
|
||||||
|
Return _db.end();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- IndexEntry:
|
- IndexEntry:
|
||||||
class IndexEntry : public DatabaseEntry {
|
class IndexEntry : public DatabaseEntry {
|
||||||
public:
|
public:
|
||||||
|
@ -269,116 +373,6 @@ Database: (lib/database.cpp)
|
||||||
File << key << endl;
|
File << key << endl;
|
||||||
File << values.size() << values[0] << .. << values[N] << endl;
|
File << values.size() << values[0] << .. << values[N] << endl;
|
||||||
|
|
||||||
- Database:
|
|
||||||
template <class T>
|
|
||||||
class Database {
|
|
||||||
private:
|
|
||||||
vector<T> db;
|
|
||||||
map<std::string, unsigned int> keys;
|
|
||||||
unsigned int _size; /* Number of valid rows */
|
|
||||||
File file;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Database::Database(filename, flags);
|
|
||||||
void load();
|
|
||||||
void save();
|
|
||||||
void clear();
|
|
||||||
void print();
|
|
||||||
void print_keys();
|
|
||||||
|
|
||||||
unsigned int insert(T);
|
|
||||||
void remove(unsigned int);
|
|
||||||
unsigned int size();
|
|
||||||
unsigned int num_rows();
|
|
||||||
|
|
||||||
unsigned int first();
|
|
||||||
unsigned int last();
|
|
||||||
unsigned int next(unsigned int &);
|
|
||||||
bool has_key(const std :: string &);
|
|
||||||
unsigned int find_index(const std::string &);
|
|
||||||
T &find(const std::string &);
|
|
||||||
T &operator[](unsigned int);
|
|
||||||
};
|
|
||||||
|
|
||||||
File << db.size() << endl
|
|
||||||
File << INDEX_0 << db[INDEX_0].valid << db[INDEX_0] << endl;
|
|
||||||
File << INDEX_1 << db[INDEX_1].valid << db[INDEX_1] << endl;
|
|
||||||
...
|
|
||||||
|
|
||||||
- API:
|
|
||||||
Database :: Database(filename, flags);
|
|
||||||
Initializes database to use ~/.ocarina{-debug}/filename.
|
|
||||||
Set up flags.
|
|
||||||
|
|
||||||
void Database :: load();
|
|
||||||
Reads a saved database from disk.
|
|
||||||
|
|
||||||
void Database :: save();
|
|
||||||
Saves the database to disk.
|
|
||||||
|
|
||||||
void Database :: clear();
|
|
||||||
This function exists only if CONFIG_TEST is enabled.
|
|
||||||
Clear the database contents in-memory, but do NOT write
|
|
||||||
to disk.
|
|
||||||
|
|
||||||
void Database :: print();
|
|
||||||
This function exists only If CONFIG_TEST is enabled.
|
|
||||||
Following a similar format for writing to disk, print the
|
|
||||||
database to the console in a human-readable format.
|
|
||||||
|
|
||||||
void Database :: print_keys();
|
|
||||||
This function exists only if CONFIG_TEST is enabled.
|
|
||||||
Print out the collected primary keys in the database.
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
unsigned int Database :: insert(T &);
|
|
||||||
Adds a new item to the db and marks it as valid. A reverse
|
|
||||||
mapping is also created into the keys map to map the primary
|
|
||||||
key back to the id of the newly added item.
|
|
||||||
|
|
||||||
void Database :: remove(unsigned int index);
|
|
||||||
Mark db[index] as invalid (quick deletion).
|
|
||||||
|
|
||||||
unsigned int Database :: size();
|
|
||||||
Returns number of valid rows in the database.
|
|
||||||
|
|
||||||
unsigned int Database :: num_rows();
|
|
||||||
Return db.size().
|
|
||||||
|
|
||||||
unsigned int Database :: first();
|
|
||||||
Return the id to the first valid row. Return db.size() if
|
|
||||||
there are no valid rows.
|
|
||||||
|
|
||||||
unsigned int Database :: last();
|
|
||||||
Return the id of the last valid row. Return db.size() if
|
|
||||||
there are no valid rows.
|
|
||||||
|
|
||||||
unsigned int Database :: next(unsigned int &id);
|
|
||||||
Return the id of the next valid row or return db.size() if
|
|
||||||
there are no valid rows.
|
|
||||||
|
|
||||||
bool Database :: has_key(const std::string &key);
|
|
||||||
Return true if an item with primary key "key" exists in the
|
|
||||||
database, and false otherwise.
|
|
||||||
|
|
||||||
unsigned int Database :: find_index(const std::string key);
|
|
||||||
If the key exists in the database, return the index of the
|
|
||||||
database item with that key. Throw -EEXIST if the key is not
|
|
||||||
in the database.
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T &Database :: find(const std::string &key);
|
|
||||||
Search for primary key "key" in the database. The reverse
|
|
||||||
mapping should be used to make this operation faster.
|
|
||||||
Throw -EEXIST if the key is not found in the mapping.
|
|
||||||
Throw -EINVAL if the key points to an invalid entry.
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T &Database :: operator[](unsigned int index);
|
|
||||||
Return a reference to db[index].
|
|
||||||
Throw -EEXIST if index is out of range.
|
|
||||||
Throw -EINVAL if index is invalid.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Filter: (lib/filter.cpp)
|
Filter: (lib/filter.cpp)
|
||||||
|
|
|
@ -20,9 +20,6 @@ public:
|
||||||
virtual std::string primary_key() = 0;
|
virtual std::string primary_key() = 0;
|
||||||
virtual void write(File &) = 0;
|
virtual void write(File &) = 0;
|
||||||
virtual void read(File &) = 0;
|
virtual void read(File &) = 0;
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
virtual void print() = 0;
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,55 +45,57 @@ public:
|
||||||
template <class T>
|
template <class T>
|
||||||
class Database {
|
class Database {
|
||||||
private:
|
private:
|
||||||
std::vector<T> db;
|
std::vector<T> _db;
|
||||||
std::map<const std::string, unsigned int> keys;
|
std::map<const std::string, unsigned int> _keys;
|
||||||
unsigned int _size;
|
unsigned int _size;
|
||||||
File file;
|
bool _autosave;
|
||||||
|
File _file;
|
||||||
|
|
||||||
|
void autosave();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Database(std::string);
|
typedef typename std::vector<T>::iterator iterator;
|
||||||
|
typedef typename std::vector<T>::const_iterator const_iterator;
|
||||||
|
|
||||||
|
Database(std::string, bool);
|
||||||
~Database();
|
~Database();
|
||||||
void save();
|
void save();
|
||||||
void load();
|
void load();
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void clear();
|
|
||||||
void print();
|
|
||||||
void print_keys();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
unsigned int insert(T);
|
unsigned int insert(T);
|
||||||
void remove(unsigned int);
|
void remove(unsigned int);
|
||||||
unsigned int size();
|
unsigned int size();
|
||||||
unsigned int num_rows();
|
unsigned int actual_size();
|
||||||
|
|
||||||
unsigned int first();
|
iterator begin();
|
||||||
unsigned int last();
|
iterator end();
|
||||||
unsigned int next(unsigned int);
|
iterator next(iterator &);
|
||||||
bool has_key(const std::string &);
|
|
||||||
unsigned int find_index(const std::string &);
|
iterator at(unsigned int);
|
||||||
T &find(const std::string &);
|
iterator find(const std::string &);
|
||||||
T &operator[](unsigned int);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void index_insert(Database<IndexEntry> &db,
|
static inline void index_insert(Database<IndexEntry> &db,
|
||||||
const std::string &key,
|
const std::string &key,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
try {
|
Database<IndexEntry>::iterator it = db.find(key);
|
||||||
db.find(key).insert(val);
|
if (it == db.end()) {
|
||||||
} catch (int error) {
|
|
||||||
db.insert(IndexEntry(key, val));
|
db.insert(IndexEntry(key, val));
|
||||||
}
|
} else
|
||||||
|
it->insert(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void index_remove(Database<IndexEntry> &db,
|
static inline void index_remove(Database<IndexEntry> &db,
|
||||||
const std::string &key,
|
const std::string &key,
|
||||||
unsigned int val)
|
unsigned int val)
|
||||||
{
|
{
|
||||||
unsigned int i = db.find_index(key);
|
Database<IndexEntry>::iterator it = db.find(key);
|
||||||
db[i].remove(val);
|
if (it != db.end()) {
|
||||||
if (db[i].values.size() == 0)
|
it->remove(val);
|
||||||
db.remove(db.find_index(key));
|
if (it->values.size() == 0)
|
||||||
|
db.remove(it - db.begin());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
Database<T> :: Database(std::string filepath)
|
Database<T> :: Database(std::string filepath, bool autosave)
|
||||||
: _size(0), file(filepath, FILE_TYPE_DATA)
|
: _size(0), _autosave(autosave), _file(filepath, FILE_TYPE_DATA)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,21 +23,25 @@ Database<T> :: ~Database()
|
||||||
template <class T>
|
template <class T>
|
||||||
void Database<T> :: save()
|
void Database<T> :: save()
|
||||||
{
|
{
|
||||||
try {
|
if (_file.open(OPEN_WRITE) == false)
|
||||||
file.open(OPEN_WRITE);
|
|
||||||
} catch (int error) {
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_file << _db.size() << std::endl;
|
||||||
|
for (unsigned int i = 0; i < _db.size(); i++) {
|
||||||
|
_file << _db[i].valid << " ";
|
||||||
|
if (_db[i].valid == true)
|
||||||
|
_db[i].write(_file);
|
||||||
|
_file << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
file << db.size() << std::endl;
|
_file.close();
|
||||||
for (unsigned int i = 0; i < db.size(); i++) {
|
}
|
||||||
file << db[i].valid << " ";
|
|
||||||
if (db[i].valid == true)
|
|
||||||
db[i].write(file);
|
|
||||||
file << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
template <class T>
|
||||||
|
void Database<T> :: autosave()
|
||||||
|
{
|
||||||
|
if (_autosave == true)
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -45,90 +49,55 @@ void Database<T> :: load()
|
||||||
{
|
{
|
||||||
unsigned int db_size;
|
unsigned int db_size;
|
||||||
|
|
||||||
if (file.exists() == false)
|
if (_file.exists() == false)
|
||||||
|
return;
|
||||||
|
else if (_file.open(OPEN_READ) == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
_file >> db_size;
|
||||||
file.open(OPEN_READ);
|
_db.resize(db_size);
|
||||||
} catch (int error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file >> db_size;
|
|
||||||
db.resize(db_size);
|
|
||||||
for (unsigned int i = 0; i < db_size; i++) {
|
for (unsigned int i = 0; i < db_size; i++) {
|
||||||
file >> db[i].valid;
|
_file >> _db[i].valid;
|
||||||
if (db[i].valid == true) {
|
if (_db[i].valid == true) {
|
||||||
db[i].read(file);
|
_db[i].read(_file);
|
||||||
keys.insert(std::pair<std::string, unsigned int>(db[i].primary_key(), i));
|
_keys.insert(std::pair<std::string, unsigned int>(_db[i].primary_key(), i));
|
||||||
_size++;
|
_size++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
template <class T>
|
|
||||||
void Database<T> :: clear()
|
|
||||||
{
|
|
||||||
db.clear();
|
|
||||||
keys.clear();
|
|
||||||
_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void Database<T> :: print()
|
|
||||||
{
|
|
||||||
:: print("Allocated rows: %u\n", db.size());
|
|
||||||
:: print("Valid rows: %u\n", _size);
|
|
||||||
for (unsigned int i = 0; i < db.size(); i++) {
|
|
||||||
if (db[i].valid == true) {
|
|
||||||
:: print("db[%u] = ", i);
|
|
||||||
db[i].print();
|
|
||||||
:: print("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void Database<T> :: print_keys()
|
|
||||||
{
|
|
||||||
std::map<const std::string, unsigned int>::iterator it;
|
|
||||||
:: print("Found keys:");
|
|
||||||
for (it = keys.begin(); it != keys.end(); it++)
|
|
||||||
:: print(" %s", it->first.c_str());
|
|
||||||
:: print("\n");
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned int Database<T> :: insert(T val)
|
unsigned int Database<T> :: insert(T val)
|
||||||
{
|
{
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
typename std::map<const std::string, unsigned int>::iterator it;
|
iterator it = find(val.primary_key());
|
||||||
|
|
||||||
it = keys.find(val.primary_key());
|
if (it != end())
|
||||||
if (it != keys.end())
|
return it - _db.begin();
|
||||||
return it->second;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check primary key stuff here
|
* Check primary key stuff here
|
||||||
*/
|
*/
|
||||||
id = db.size();
|
id = _db.size();
|
||||||
db.push_back(val);
|
_db.push_back(val);
|
||||||
keys.insert(std::pair<const std::string, unsigned int>(val.primary_key(), id));
|
_keys[val.primary_key()] = id;
|
||||||
db[id].valid = true;
|
_db[id].valid = true;
|
||||||
_size++;
|
_size++;
|
||||||
|
autosave();
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void Database<T> :: remove(unsigned int id)
|
void Database<T> :: remove(unsigned int id)
|
||||||
{
|
{
|
||||||
keys.erase(db[id].primary_key());
|
if (id >= actual_size())
|
||||||
db[id].valid = false;
|
return;
|
||||||
|
_keys.erase(_db[id].primary_key());
|
||||||
|
_db[id].valid = false;
|
||||||
_size--;
|
_size--;
|
||||||
|
autosave();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -138,82 +107,53 @@ unsigned int Database<T> :: size()
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned int Database<T> :: num_rows()
|
unsigned int Database<T> :: actual_size()
|
||||||
{
|
{
|
||||||
return db.size();
|
return _db.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned int Database<T> :: first()
|
typename Database<T>::iterator Database<T> :: begin()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < db.size(); i++) {
|
iterator it = _db.begin();
|
||||||
if (db[i].valid == true)
|
if ( (*it).valid == true )
|
||||||
return i;
|
return it;
|
||||||
}
|
return next(it);
|
||||||
|
|
||||||
return db.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned int Database<T> :: last()
|
typename Database<T>::iterator Database<T> :: end()
|
||||||
{
|
{
|
||||||
if (_size == 0)
|
return _db.end();
|
||||||
return db.size();
|
|
||||||
|
|
||||||
for (unsigned int i = db.size() - 1; i >= 0; i--) {
|
|
||||||
if (db[i].valid == true)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned int Database<T> :: next(unsigned int id)
|
typename Database<T>::iterator Database<T> :: next(iterator &it)
|
||||||
{
|
{
|
||||||
for (unsigned int i = id + 1; i < db.size(); i++) {
|
do {
|
||||||
if (db[i].valid == true)
|
it++;
|
||||||
return i;
|
} while ((it != end()) && ((*it).valid == false));
|
||||||
}
|
return it;
|
||||||
|
|
||||||
return db.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool Database<T> :: has_key(const std::string &key)
|
typename Database<T>::iterator Database<T> :: at(unsigned int id)
|
||||||
|
{
|
||||||
|
if (id >= actual_size())
|
||||||
|
return end();
|
||||||
|
|
||||||
|
if (_db[id].valid == false)
|
||||||
|
return end();
|
||||||
|
return _db.begin() + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
typename Database<T>::iterator Database<T> :: find(const std::string &key)
|
||||||
{
|
{
|
||||||
std::map<const std::string, unsigned int>::iterator it;
|
std::map<const std::string, unsigned int>::iterator it;
|
||||||
it = keys.find(key);
|
it = _keys.find(key);
|
||||||
return it != keys.end();
|
if (it == _keys.end())
|
||||||
|
return end();
|
||||||
|
return _db.begin() + it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
|
||||||
unsigned int Database<T> :: find_index(const std::string &key)
|
|
||||||
{
|
|
||||||
std::map<const std::string, unsigned int>::iterator it;
|
|
||||||
it = keys.find(key);
|
|
||||||
if (it == keys.end())
|
|
||||||
throw -E_EXIST;
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T &Database<T> :: find(const std::string &key)
|
|
||||||
{
|
|
||||||
unsigned int index = find_index(key);
|
|
||||||
if (db[index].valid == false)
|
|
||||||
throw -E_INVAL;
|
|
||||||
return db[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T &Database<T> :: operator[](unsigned int id)
|
|
||||||
{
|
|
||||||
if (id >= db.size())
|
|
||||||
throw -E_EXIST;
|
|
||||||
else if (db[id].valid == false)
|
|
||||||
throw -E_INVAL;
|
|
||||||
return db[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* OCARINA_DATABASE_HPP */
|
#endif /* OCARINA_DATABASE_HPP */
|
||||||
|
|
|
@ -36,9 +36,6 @@ namespace library
|
||||||
std::string primary_key();
|
std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void print();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,9 +52,6 @@ namespace library
|
||||||
std::string primary_key();
|
std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void print();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,9 +66,6 @@ namespace library
|
||||||
std::string primary_key();
|
std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void print();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,9 +98,6 @@ namespace library
|
||||||
std::string primary_key();
|
std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void print();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
static Database<IndexEntry> filter_index("");
|
static Database<IndexEntry> filter_index("", false);
|
||||||
static std::map<std::string, std::string> lowercase_cache;
|
static std::map<std::string, std::string> lowercase_cache;
|
||||||
static unsigned int lowercase_cache_hits = 0;
|
static unsigned int lowercase_cache_hits = 0;
|
||||||
|
|
||||||
|
@ -107,10 +107,10 @@ void filter :: add(const std::string &text, unsigned int track_id)
|
||||||
|
|
||||||
static void find_intersection(std::string &text, std::set<unsigned int> &res)
|
static void find_intersection(std::string &text, std::set<unsigned int> &res)
|
||||||
{
|
{
|
||||||
IndexEntry *terms = &filter_index.find(text);
|
Database<IndexEntry>::iterator it = filter_index.find(text);
|
||||||
std::set<unsigned int> tmp;
|
std::set<unsigned int> tmp;
|
||||||
|
|
||||||
set_intersection(terms->values.begin(), terms->values.end(),
|
set_intersection(it->values.begin(), it->values.end(),
|
||||||
res.begin(), res.end(),
|
res.begin(), res.end(),
|
||||||
std::inserter<std::set<unsigned int> >(tmp, tmp.begin()));
|
std::inserter<std::set<unsigned int> >(tmp, tmp.begin()));
|
||||||
res.swap(tmp);
|
res.swap(tmp);
|
||||||
|
@ -127,7 +127,7 @@ void filter :: search(const std::string &text, std::set<unsigned int> &res)
|
||||||
|
|
||||||
it = parsed.begin();
|
it = parsed.begin();
|
||||||
try {
|
try {
|
||||||
res = filter_index.find(*it).values;
|
res = filter_index.find(*it)->values;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
191
lib/library.cpp
191
lib/library.cpp
|
@ -11,11 +11,11 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
static Database<library :: Album> album_db("album.db");
|
static Database<library :: Album> album_db("album.db", false);
|
||||||
static Database<library :: AGInfo> artist_db("artist.db");
|
static Database<library :: AGInfo> artist_db("artist.db", false);
|
||||||
static Database<library :: AGInfo> genre_db("genre.db");
|
static Database<library :: AGInfo> genre_db("genre.db", false);
|
||||||
static Database<library :: Track> track_db("track.db");
|
static Database<library :: Track> track_db("track.db", false);
|
||||||
static Database<library :: Library> library_db("library.db");
|
static Database<library :: Library> library_db("library.db", false);
|
||||||
|
|
||||||
struct ImportData {
|
struct ImportData {
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
|
@ -76,16 +76,6 @@ void library :: AGInfo :: write(File &f)
|
||||||
f << name;
|
f << name;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void library :: AGInfo :: print()
|
|
||||||
{
|
|
||||||
if (db_type == DB_ARTIST)
|
|
||||||
:: print("Artist: %s", name.c_str());
|
|
||||||
else
|
|
||||||
:: print("Genre: %s", name.c_str());
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -129,13 +119,6 @@ void library :: Album :: write(File &f)
|
||||||
f << artist_id << " " << year << " " << name;
|
f << artist_id << " " << year << " " << name;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void library :: Album :: print()
|
|
||||||
{
|
|
||||||
:: print("Album: %s (%u) by %s", name.c_str(), year, artist_db[artist_id].name.c_str());
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -169,18 +152,6 @@ void library :: Library :: write(File &f)
|
||||||
f << enabled << " " << root_path;
|
f << enabled << " " << root_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void library :: Library :: print()
|
|
||||||
{
|
|
||||||
:: print("%s", root_path.c_str());
|
|
||||||
if (enabled == true)
|
|
||||||
:: print(" (enabled)");
|
|
||||||
else
|
|
||||||
:: print(" (disabled)");
|
|
||||||
:: print(", size = %u", size);
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -204,7 +175,7 @@ library :: Track :: Track(TagLib :: Tag *tag, TagLib :: AudioProperties *audio,
|
||||||
unsigned int minutes, seconds;
|
unsigned int minutes, seconds;
|
||||||
|
|
||||||
full_path = path;
|
full_path = path;
|
||||||
filepath = path.substr(library_db[library_id].root_path.size() + 1);
|
filepath = path.substr(library_db.at(library_id)->root_path.size() + 1);
|
||||||
title_lower = filter :: to_lowercase(title);
|
title_lower = filter :: to_lowercase(title);
|
||||||
|
|
||||||
minutes = length / 60;
|
minutes = length / 60;
|
||||||
|
@ -228,7 +199,7 @@ library :: Track :: Track(struct ImportData *data, unsigned int lib,
|
||||||
unsigned int minutes, seconds;
|
unsigned int minutes, seconds;
|
||||||
|
|
||||||
full_path = data->filepath;
|
full_path = data->filepath;
|
||||||
filepath = full_path.substr(library_db[library_id].root_path.size() + 1);
|
filepath = full_path.substr(library_db.at(library_id)->root_path.size() + 1);
|
||||||
title_lower = filter :: to_lowercase(title);
|
title_lower = filter :: to_lowercase(title);
|
||||||
|
|
||||||
minutes = length / 60;
|
minutes = length / 60;
|
||||||
|
@ -254,8 +225,8 @@ void library :: Track :: read(File &f)
|
||||||
title = f.getline();
|
title = f.getline();
|
||||||
filepath = f.getline();
|
filepath = f.getline();
|
||||||
title_lower = filter :: to_lowercase(title);
|
title_lower = filter :: to_lowercase(title);
|
||||||
full_path = library_db[library_id].root_path + "/" + filepath;
|
full_path = library_db.at(library_id)->root_path + "/" + filepath;
|
||||||
library_db[library_id].size++;
|
library_db.at(library_id)->size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: Track :: write(File &f)
|
void library :: Track :: write(File &f)
|
||||||
|
@ -267,20 +238,6 @@ void library :: Track :: write(File &f)
|
||||||
f << filepath;
|
f << filepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void library :: Track :: print()
|
|
||||||
{
|
|
||||||
:: print("%u. %s by %s from %s (%u)\n", track, title.c_str(),
|
|
||||||
artist_db[artist_id].name.c_str(),
|
|
||||||
album_db[album_id].name.c_str(), album_db[album_id].year);
|
|
||||||
:: print(" Genre: %s, Length: %u (seconds)\n",
|
|
||||||
genre_db[genre_id].name.c_str(), length);
|
|
||||||
:: print(" Play count: %u, last played %u/%u/%u\n", play_count,
|
|
||||||
last_day, last_month, last_year);
|
|
||||||
:: print(" %s", filepath.c_str());
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -313,11 +270,11 @@ static void read_tags(unsigned int lib_id, const std :: string &path)
|
||||||
genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, tag));
|
genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, tag));
|
||||||
track_id = track_db.insert(library :: Track(tag, audio, lib_id,
|
track_id = track_db.insert(library :: Track(tag, audio, lib_id,
|
||||||
artist_id, album_id, genre_id, path));
|
artist_id, album_id, genre_id, path));
|
||||||
library_db[lib_id].size++;
|
library_db.at(lib_id)->size++;
|
||||||
|
|
||||||
filter::add(artist_db[artist_id].name, track_id);
|
filter::add(artist_db.at(artist_id)->name, track_id);
|
||||||
filter::add(album_db[album_id].name, track_id);
|
filter::add(album_db.at(album_id)->name, track_id);
|
||||||
filter::add(track_db[track_id].title, track_id);
|
filter::add(track_db.at(track_id)->title, track_id);
|
||||||
get_callbacks()->on_library_track_add(track_id);
|
get_callbacks()->on_library_track_add(track_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +290,7 @@ static bool process_path(unsigned int lib_id, const std :: string &dir,
|
||||||
scan.path = path;
|
scan.path = path;
|
||||||
idle :: schedule (do_scan_path, scan);
|
idle :: schedule (do_scan_path, scan);
|
||||||
} else {
|
} else {
|
||||||
if (track_db.has_key(path) == false) {
|
if (track_db.find(path) != track_db.end()) {
|
||||||
read_tags(lib_id, path);
|
read_tags(lib_id, path);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
@ -370,7 +327,7 @@ static void do_scan_path(struct scan_info &scan)
|
||||||
if (changed == true) {
|
if (changed == true) {
|
||||||
save_all_dbs();
|
save_all_dbs();
|
||||||
get_callbacks()->on_library_update(scan.lib_id,
|
get_callbacks()->on_library_update(scan.lib_id,
|
||||||
&library_db[scan.lib_id]);
|
&(*library_db.at(scan.lib_id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,26 +339,27 @@ static void do_validate_library(unsigned int &lib_id)
|
||||||
if (track_db.size() == 0)
|
if (track_db.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i = track_db.first(); i <= track_db.last(); i = track_db.next(i)) {
|
Database<library :: Track>::iterator it;
|
||||||
if (track_db[i].library_id != lib_id)
|
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||||
|
if ((*it).library_id != lib_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
path = library_db[lib_id].root_path + "/" + track_db[i].filepath;
|
path = library_db.at(lib_id)->root_path + "/" + (*it).filepath;
|
||||||
if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS) == false) {
|
if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS) == false) {
|
||||||
dprint("Removing file: %s\n", path.c_str());
|
dprint("Removing file: %s\n", path.c_str());
|
||||||
track_db.remove(i);
|
track_db.remove(it - track_db.begin());
|
||||||
library_db[lib_id].size--;
|
library_db.at(lib_id)->size--;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed == true)
|
if (changed == true)
|
||||||
get_callbacks()->on_library_update(lib_id, &library_db[lib_id]);
|
get_callbacks()->on_library_update(lib_id, &(*library_db.at(lib_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_update_library(unsigned int lib_id)
|
static void do_update_library(unsigned int lib_id)
|
||||||
{
|
{
|
||||||
struct scan_info scan = { lib_id, library_db[lib_id].root_path };
|
struct scan_info scan = { lib_id, library_db.at(lib_id)->root_path };
|
||||||
idle :: schedule(do_validate_library, lib_id);
|
idle :: schedule(do_validate_library, lib_id);
|
||||||
idle :: schedule(do_scan_path, scan);
|
idle :: schedule(do_scan_path, scan);
|
||||||
}
|
}
|
||||||
|
@ -430,11 +388,11 @@ static void do_import_track(File &f, unsigned int lib_id)
|
||||||
genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, genre));
|
genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, genre));
|
||||||
track_id = track_db.insert(library :: Track(&data, lib_id, artist_id,
|
track_id = track_db.insert(library :: Track(&data, lib_id, artist_id,
|
||||||
album_id, genre_id));
|
album_id, genre_id));
|
||||||
library_db[lib_id].size++;
|
library_db.at(lib_id)->size++;
|
||||||
|
|
||||||
filter::add(artist_db[artist_id].name, track_id);
|
filter::add(artist_db.at(artist_id)->name, track_id);
|
||||||
filter::add(album_db[album_id].name, track_id);
|
filter::add(album_db.at(album_id)->name, track_id);
|
||||||
filter::add(track_db[track_id].title, track_id);
|
filter::add(track_db.at(track_id)->title, track_id);
|
||||||
get_callbacks()->on_library_track_add(track_id);
|
get_callbacks()->on_library_track_add(track_id);
|
||||||
|
|
||||||
if (banned == true)
|
if (banned == true)
|
||||||
|
@ -460,20 +418,20 @@ static void do_import_library(std::string &s)
|
||||||
f >> id >> enabled >> next_id >> size;
|
f >> id >> enabled >> next_id >> size;
|
||||||
|
|
||||||
/* Assign this path a new id */
|
/* Assign this path a new id */
|
||||||
if (library_db.has_key(path)) {
|
if (library_db.find(path) != library_db.end()) {
|
||||||
print("Library already contains path: %s, skipping\n", path.c_str());
|
print("Library already contains path: %s, skipping\n", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
print("Adding path: %s\n", path.c_str());
|
print("Adding path: %s\n", path.c_str());
|
||||||
id = library_db.insert(library :: Library(path, enabled));
|
id = library_db.insert(library :: Library(path, enabled));
|
||||||
get_callbacks()->on_library_add(id, &library_db[id]);
|
get_callbacks()->on_library_add(id, &(*library_db.at(id)));
|
||||||
library_db.save();
|
library_db.save();
|
||||||
|
|
||||||
f.getline(); /* Get rest of line */
|
f.getline(); /* Get rest of line */
|
||||||
for (unsigned int i = 0; i < size; i++)
|
for (unsigned int i = 0; i < size; i++)
|
||||||
do_import_track(f, id);
|
do_import_track(f, id);
|
||||||
save_all_dbs();
|
save_all_dbs();
|
||||||
get_callbacks()->on_library_update(id, &library_db[id]);
|
get_callbacks()->on_library_update(id, &(*library_db.at(id)));
|
||||||
|
|
||||||
library :: update_path(id);
|
library :: update_path(id);
|
||||||
}
|
}
|
||||||
|
@ -494,16 +452,22 @@ void library :: init()
|
||||||
library_db.load();
|
library_db.load();
|
||||||
track_db.load();
|
track_db.load();
|
||||||
|
|
||||||
for (i = track_db.first(); i < track_db.num_rows(); i = track_db.next(i)) {
|
Database<Track>::iterator it;
|
||||||
filter::add(artist_db[track_db[i].artist_id].name, i);
|
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||||
filter::add(album_db[track_db[i].album_id].name, i);
|
i = (it - track_db.begin());
|
||||||
filter::add(track_db[i].title, i);
|
filter::add(artist_db.at((*it).artist_id)->name, i);
|
||||||
|
filter::add(album_db.at((*it).album_id)->name, i);
|
||||||
|
filter::add((*it).title, i);
|
||||||
|
|
||||||
if (library_db[track_db[i].library_id].enabled)
|
if (library_db.at((*it).library_id)->enabled)
|
||||||
get_callbacks()->on_library_track_add(i);
|
get_callbacks()->on_library_track_add(i);
|
||||||
}
|
}
|
||||||
for (i = library_db.first(); i < library_db.num_rows(); i = library_db.next(i))
|
|
||||||
get_callbacks()->on_library_add(i, &library_db[i]);
|
Database<Library>::iterator l_it;
|
||||||
|
for (l_it = library_db.begin(); l_it != library_db.end(); l_it = library_db.next(l_it)) {
|
||||||
|
i = l_it - library_db.begin();
|
||||||
|
get_callbacks()->on_library_add(i, &(*library_db.at(i)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: add_path(const std::string &dir)
|
void library :: add_path(const std::string &dir)
|
||||||
|
@ -511,24 +475,25 @@ void library :: add_path(const std::string &dir)
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false)
|
if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false)
|
||||||
throw -E_INVAL;
|
throw -E_INVAL;
|
||||||
if (library_db.has_key(dir))
|
if (library_db.find(dir) != library_db.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
id = library_db.insert(library :: Library(dir, true));
|
id = library_db.insert(library :: Library(dir, true));
|
||||||
library_db.save();
|
library_db.save();
|
||||||
|
|
||||||
get_callbacks()->on_library_add(id, &library_db[id]);
|
get_callbacks()->on_library_add(id, &(*library_db.at(id)));
|
||||||
update_path(id);
|
update_path(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: del_path(unsigned int id)
|
void library :: del_path(unsigned int id)
|
||||||
{
|
{
|
||||||
unsigned int t;
|
Database<Track>::iterator it;
|
||||||
|
|
||||||
for (t = track_db.first(); t < track_db.num_rows(); t = track_db.next(t)) {
|
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||||
if (track_db[t].library_id == id) {
|
if ((*it).library_id == id) {
|
||||||
get_callbacks()->on_library_track_del(t);
|
unsigned int track_id = it - track_db.begin();
|
||||||
track_db.remove(t);
|
get_callbacks()->on_library_track_del(track_id);
|
||||||
|
track_db.remove(track_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,27 +507,30 @@ void library :: update_path(unsigned int id)
|
||||||
{
|
{
|
||||||
if (id > library_db.size())
|
if (id > library_db.size())
|
||||||
return;
|
return;
|
||||||
if (library_db[id].valid == false)
|
if (library_db.at(id)->valid == false)
|
||||||
return;
|
return;
|
||||||
do_update_library(id);
|
do_update_library(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: update_all()
|
void library :: update_all()
|
||||||
{
|
{
|
||||||
unsigned int i;
|
Database<Library>::iterator it;
|
||||||
for (i = library_db.first(); i < library_db.num_rows(); i = library_db.next(i))
|
|
||||||
update_path(i);
|
for (it = library_db.begin(); it != library_db.end(); it = library_db.next(it))
|
||||||
|
update_path(it - library_db.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: set_enabled(unsigned int id, bool enabled)
|
void library :: set_enabled(unsigned int id, bool enabled)
|
||||||
{
|
{
|
||||||
unsigned int t;
|
unsigned int t;
|
||||||
|
Database<Track>::iterator it;
|
||||||
|
|
||||||
library_db[id].enabled = enabled;
|
library_db.at(id)->enabled = enabled;
|
||||||
library_db.save();
|
library_db.save();
|
||||||
|
|
||||||
for (t = track_db.first(); t < track_db.num_rows(); t = track_db.next(t)) {
|
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
|
||||||
if (track_db[t].library_id == id) {
|
if ((*it).library_id == id) {
|
||||||
|
t = it - track_db.begin();
|
||||||
if (enabled)
|
if (enabled)
|
||||||
get_callbacks()->on_library_track_add(t);
|
get_callbacks()->on_library_track_add(t);
|
||||||
else
|
else
|
||||||
|
@ -573,27 +541,27 @@ void library :: set_enabled(unsigned int id, bool enabled)
|
||||||
|
|
||||||
void library :: lookup(unsigned int id, library :: Song *song)
|
void library :: lookup(unsigned int id, library :: Song *song)
|
||||||
{
|
{
|
||||||
if (id >= track_db.num_rows())
|
if (id >= track_db.actual_size())
|
||||||
throw -E_EXIST;
|
throw -E_EXIST;
|
||||||
|
|
||||||
song->track = &track_db[id];
|
song->track = &(*track_db.at(id));
|
||||||
if (song->track->valid == false)
|
if (song->track->valid == false)
|
||||||
throw -E_EXIST;
|
throw -E_EXIST;
|
||||||
|
|
||||||
song->track_id = id;
|
song->track_id = id;
|
||||||
song->artist = &artist_db[song->track->artist_id];
|
song->artist = &(*artist_db.at(song->track->artist_id));
|
||||||
song->album = &album_db[song->track->album_id];
|
song->album = &(*album_db.at(song->track->album_id));
|
||||||
song->genre = &genre_db[song->track->genre_id];
|
song->genre = &(*genre_db.at(song->track->genre_id));
|
||||||
song->library = &library_db[song->track->library_id];
|
song->library = &(*library_db.at(song->track->library_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
library :: Library *library :: lookup_path(unsigned int id)
|
library :: Library *library :: lookup_path(unsigned int id)
|
||||||
{
|
{
|
||||||
if (id >= library_db.num_rows())
|
if (id >= library_db.actual_size())
|
||||||
throw -E_EXIST;
|
throw -E_EXIST;
|
||||||
if (library_db[id].valid == false)
|
if (library_db.at(id)->valid == false)
|
||||||
throw -E_EXIST;
|
throw -E_EXIST;
|
||||||
return &library_db[id];
|
return &(*library_db.at(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: import()
|
void library :: import()
|
||||||
|
@ -622,10 +590,10 @@ void library :: track_played(unsigned int id)
|
||||||
time_t the_time = time(NULL);
|
time_t the_time = time(NULL);
|
||||||
struct tm *now = localtime(&the_time);
|
struct tm *now = localtime(&the_time);
|
||||||
|
|
||||||
track_db[id].play_count++;
|
track_db.at(id)->play_count++;
|
||||||
track_db[id].last_day = now->tm_mday;
|
track_db.at(id)->last_day = now->tm_mday;
|
||||||
track_db[id].last_month = now->tm_mon + 1;
|
track_db.at(id)->last_month = now->tm_mon + 1;
|
||||||
track_db[id].last_year = now->tm_year + 1900;
|
track_db.at(id)->last_year = now->tm_year + 1900;
|
||||||
|
|
||||||
track_db.save();
|
track_db.save();
|
||||||
get_callbacks()->on_library_track_updated(id);
|
get_callbacks()->on_library_track_updated(id);
|
||||||
|
@ -636,28 +604,19 @@ void library :: print_db(DB_Type type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DB_ALBUM:
|
case DB_ALBUM:
|
||||||
album_db.print();
|
|
||||||
break;
|
break;
|
||||||
case DB_ARTIST:
|
case DB_ARTIST:
|
||||||
artist_db.print();
|
|
||||||
break;
|
break;
|
||||||
case DB_GENRE:
|
case DB_GENRE:
|
||||||
genre_db.print();
|
|
||||||
break;
|
break;
|
||||||
case DB_LIBRARY:
|
case DB_LIBRARY:
|
||||||
library_db.print();
|
|
||||||
break;
|
break;
|
||||||
case DB_TRACK:
|
case DB_TRACK:
|
||||||
track_db.print();
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void library :: reset()
|
void library :: reset()
|
||||||
{
|
{
|
||||||
album_db.clear();
|
|
||||||
artist_db.clear();
|
|
||||||
genre_db.clear();
|
|
||||||
library_db.clear();
|
|
||||||
track_db.clear();
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_TEST */
|
#endif /* CONFIG_TEST */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <playlist.h>
|
#include <playlist.h>
|
||||||
|
|
||||||
static std::set<unsigned int> empty_set;
|
static std::set<unsigned int> empty_set;
|
||||||
static Database<IndexEntry> playlist_db("playlist.db");
|
static Database<IndexEntry> playlist_db("playlist.db", false);
|
||||||
static Playqueue playlist_pq(PQ_ENABLED);
|
static Playqueue playlist_pq(PQ_ENABLED);
|
||||||
static std::string cur_pq;
|
static std::string cur_pq;
|
||||||
|
|
||||||
|
@ -85,11 +85,10 @@ void playlist :: select(const std::string &name)
|
||||||
const std::set<unsigned int> &playlist :: get_tracks(const std::string &name)
|
const std::set<unsigned int> &playlist :: get_tracks(const std::string &name)
|
||||||
{
|
{
|
||||||
if ((name == "Banned") || (name == "Favorites")) {
|
if ((name == "Banned") || (name == "Favorites")) {
|
||||||
try {
|
Database<IndexEntry>::iterator it = playlist_db.find(name);
|
||||||
return playlist_db.find(name).values;
|
if (it != playlist_db.end())
|
||||||
} catch (int error) {
|
return it->values;
|
||||||
return empty_set;
|
return empty_set;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw -E_EXIST;
|
throw -E_EXIST;
|
||||||
}
|
}
|
||||||
|
@ -102,6 +101,5 @@ Playqueue *playlist :: get_pq()
|
||||||
#ifdef CONFIG_TEST
|
#ifdef CONFIG_TEST
|
||||||
void playlist :: clear()
|
void playlist :: clear()
|
||||||
{
|
{
|
||||||
playlist_db.clear();
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_TEST */
|
#endif /* CONFIG_TEST */
|
||||||
|
|
|
@ -8,7 +8,7 @@ if sys.argv.count("tests") > 0:
|
||||||
|
|
||||||
src = SConscript("src/Sconscript")
|
src = SConscript("src/Sconscript")
|
||||||
|
|
||||||
tests = [ "version", "file", "db_entry" ]
|
tests = [ "version", "file", "db_entry", "database" ]
|
||||||
#scripts = [ "print", "file", "database", "index", "filter", "idle", "playlist",
|
#scripts = [ "print", "file", "database", "index", "filter", "idle", "playlist",
|
||||||
# "library", "playqueue", "deck", "audio", "gui" ]
|
# "library", "playqueue", "deck", "audio", "gui" ]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2014 (c) Anna Schumaker
|
||||||
|
|
||||||
|
. $(dirname $0)/_functions
|
||||||
|
|
||||||
|
function test_autosave
|
||||||
|
{
|
||||||
|
new_test "Database Test (n = $1, autosave = true)"
|
||||||
|
src/database.run -a $1
|
||||||
|
if [ ! -f $DATA_DIR/database.db ]; then
|
||||||
|
echo "ERROR: $DATA_DIR/database.db doesn't exist!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_noautosave
|
||||||
|
{
|
||||||
|
new_test "Database Test (n = $1, autosave = false)"
|
||||||
|
src/database.run $1
|
||||||
|
if [ -f $DATA_DIR/database.db ]; then
|
||||||
|
echo "ERROR: $DATA_DIR/database.db exists!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_test
|
||||||
|
{
|
||||||
|
rm $DATA_DIR/* 2>/dev/null || true
|
||||||
|
|
||||||
|
if [ $1 -le 1000 ]; then
|
||||||
|
test_autosave $1
|
||||||
|
else
|
||||||
|
test_noautosave $1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test 10
|
||||||
|
echo
|
||||||
|
run_test 100
|
||||||
|
echo
|
||||||
|
run_test 1000
|
||||||
|
echo
|
||||||
|
run_test 10000
|
||||||
|
echo
|
||||||
|
run_test 100000
|
|
@ -1,6 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
Import("Test", "CONFIG")
|
|
||||||
|
|
||||||
CONFIG.DATABASE = True
|
|
||||||
|
|
||||||
Test("database", "database.cpp")
|
|
|
@ -1,195 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2013 (c) Anna Schumaker.
|
|
||||||
*/
|
|
||||||
#include <database.h>
|
|
||||||
#include <print.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
class DBTest : public DatabaseEntry {
|
|
||||||
public:
|
|
||||||
unsigned int value;
|
|
||||||
|
|
||||||
DBTest();
|
|
||||||
DBTest(unsigned int);
|
|
||||||
void write(File &);
|
|
||||||
void read(File &);
|
|
||||||
void print();
|
|
||||||
};
|
|
||||||
|
|
||||||
DBTest :: DBTest()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DBTest :: DBTest(unsigned int val)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
|
|
||||||
value = val;
|
|
||||||
ss << value;
|
|
||||||
primary_key = ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DBTest :: write(File &file)
|
|
||||||
{
|
|
||||||
file << value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DBTest :: read(File &file)
|
|
||||||
{
|
|
||||||
file >> value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DBTest :: print()
|
|
||||||
{
|
|
||||||
:: print("%u", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void print_db(Database<DBTest> &db)
|
|
||||||
{
|
|
||||||
print("Database size: %u\n", db.size());
|
|
||||||
print("Num rows: %u\n", db.num_rows());
|
|
||||||
print("First: %u\n", db.first());
|
|
||||||
print("Last: %u\n", db.last());
|
|
||||||
|
|
||||||
for (unsigned int i = db.first(); i != db.num_rows(); i = db.next(i))
|
|
||||||
print("db[%u] = %u\n", i, db[i].value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_insertion(Database<DBTest> &db)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 1; i <= 100000; i++)
|
|
||||||
db.insert(DBTest(i));
|
|
||||||
print_db(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_deletion(Database<DBTest> &db)
|
|
||||||
{
|
|
||||||
for (unsigned int i = db.first(); i < db.num_rows(); i = db.next(i)) {
|
|
||||||
db.remove(i);
|
|
||||||
i = db.next(i);
|
|
||||||
}
|
|
||||||
print_db(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test everything except reloading.
|
|
||||||
*/
|
|
||||||
void test_0()
|
|
||||||
{
|
|
||||||
print("Test 0\n");
|
|
||||||
Database<DBTest> db("test.db");
|
|
||||||
|
|
||||||
test_insertion(db);
|
|
||||||
test_deletion(db);
|
|
||||||
test_deletion(db);
|
|
||||||
|
|
||||||
db.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reload the database from disk.
|
|
||||||
*/
|
|
||||||
void test_1()
|
|
||||||
{
|
|
||||||
print("\nTest 1\n");
|
|
||||||
Database<DBTest> db("test.db");
|
|
||||||
db.load();
|
|
||||||
print_db(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to save a db with an empty filepath.
|
|
||||||
*/
|
|
||||||
void test_2()
|
|
||||||
{
|
|
||||||
print("\nTest 2\n");
|
|
||||||
Database<DBTest> db("");
|
|
||||||
|
|
||||||
test_insertion(db);
|
|
||||||
db.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to load a db with an empty filepath
|
|
||||||
*/
|
|
||||||
void test_3()
|
|
||||||
{
|
|
||||||
print("\nTest 3\n");
|
|
||||||
Database<DBTest> db("");
|
|
||||||
db.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test unique insertion
|
|
||||||
*/
|
|
||||||
void test_4()
|
|
||||||
{
|
|
||||||
print("\nTest 4\n");
|
|
||||||
Database<DBTest> db("");
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 100; i++)
|
|
||||||
db.insert(DBTest(i % 5));
|
|
||||||
|
|
||||||
print_db(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test the database's built-in print() function
|
|
||||||
*/
|
|
||||||
void test_5()
|
|
||||||
{
|
|
||||||
print("\nTest 5\n");
|
|
||||||
Database<DBTest> db("");
|
|
||||||
for (unsigned int i = 0; i < 10; i++)
|
|
||||||
db.insert(DBTest(i));
|
|
||||||
db.print();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test the database's clear() function
|
|
||||||
*/
|
|
||||||
void test_6()
|
|
||||||
{
|
|
||||||
print("\nTest 6\n");
|
|
||||||
Database<DBTest> db("");
|
|
||||||
for (unsigned int i = 0; i < 10; i++)
|
|
||||||
db.insert(DBTest(i));
|
|
||||||
print_db(db);
|
|
||||||
db.clear();
|
|
||||||
print_db(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Print databases with no valid rows
|
|
||||||
*/
|
|
||||||
void test_7()
|
|
||||||
{
|
|
||||||
print("\nTest 7\n");
|
|
||||||
Database<DBTest> db("");
|
|
||||||
print_db(db);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 10; i++)
|
|
||||||
db.insert(DBTest(i));
|
|
||||||
print_db(db);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 10; i++)
|
|
||||||
db.remove(i);
|
|
||||||
print_db(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
test_0();
|
|
||||||
test_1();
|
|
||||||
test_2();
|
|
||||||
test_3();
|
|
||||||
test_4();
|
|
||||||
test_5();
|
|
||||||
test_6();
|
|
||||||
test_7();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
300097
tests/database/database.good
300097
tests/database/database.good
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 (c) Anna Schumaker.
|
||||||
|
* Test a Database
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <database.h>
|
||||||
|
#include <print.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int test_num = 0;
|
||||||
|
|
||||||
|
|
||||||
|
class IntEntry : public DatabaseEntry {
|
||||||
|
public:
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
IntEntry();
|
||||||
|
IntEntry(unsigned int);
|
||||||
|
std::string primary_key();
|
||||||
|
void write(File &);
|
||||||
|
void read(File &);
|
||||||
|
void print();
|
||||||
|
};
|
||||||
|
|
||||||
|
IntEntry :: IntEntry() : val(0) {}
|
||||||
|
IntEntry :: IntEntry(unsigned int v) : val(v) {}
|
||||||
|
std::string IntEntry :: primary_key()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << val;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntEntry :: write(File &f) { f << val; }
|
||||||
|
void IntEntry :: read(File &f) { f >> val; }
|
||||||
|
void IntEntry :: print() { :: print(primary_key().c_str()); }
|
||||||
|
|
||||||
|
void test_results(bool success, unsigned int line)
|
||||||
|
{
|
||||||
|
print(" %u: ", test_num);
|
||||||
|
if (success)
|
||||||
|
print("Success!\n");
|
||||||
|
else {
|
||||||
|
print("FAILED (%u) =(\n", line);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
test_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_size(Database<IntEntry> &db, unsigned int size,
|
||||||
|
unsigned int actual, unsigned int line)
|
||||||
|
{
|
||||||
|
test_results( (db.size() == size) || (db.actual_size() == actual), line );
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool autosave = false;
|
||||||
|
unsigned int n = 0, size = 0, actual = 0;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "a")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'a':
|
||||||
|
autosave = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = atoi(argv[optind]);
|
||||||
|
Database<IntEntry> db("database.db", autosave);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0: Test initial size
|
||||||
|
*/
|
||||||
|
test_size(db, size, actual, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1: Test insertion
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < n; i++) {
|
||||||
|
if (db.insert(IntEntry(i)) != i)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
size += n;
|
||||||
|
actual += n;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2: Test that size changes
|
||||||
|
*/
|
||||||
|
test_size(db, size, actual, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3: Test inserting ... again.
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < n; i++) {
|
||||||
|
if (db.insert(IntEntry(i)) != i)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4: Test that size didn't change
|
||||||
|
*/
|
||||||
|
test_size(db, size, actual, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 5: Test that size changes when removing
|
||||||
|
* Also test out-of-bounds removal
|
||||||
|
*
|
||||||
|
* Note: This test removes all even-index entries
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < n + 10; i+=2) {
|
||||||
|
db.remove(i);
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
test_size(db, size, actual, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 6: Test that removing again doesn't change anything
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < n; i+=2)
|
||||||
|
db.remove(i);
|
||||||
|
test_size(db, size, actual, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 7: Test iterating
|
||||||
|
*/
|
||||||
|
Database<IntEntry>::iterator it;
|
||||||
|
unsigned int index = 1;
|
||||||
|
for (it = db.begin(); it != db.end(); it = db.next(it)) {
|
||||||
|
if ((*it).val != index)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
index+=2;
|
||||||
|
};
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 8. Test access by id
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < n + 10; i++) {
|
||||||
|
Database<IntEntry>::iterator it = db.at(i);
|
||||||
|
if (((i % 2) == 0) && (it != db.end()))
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
if ((i >= n) && (it != db.end()))
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 9. Test inserting once again
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < n; i++) {
|
||||||
|
index = db.insert(i);
|
||||||
|
if ((i % 2) == 0) {
|
||||||
|
size++;
|
||||||
|
actual++;
|
||||||
|
if (index != (n + (i / 2)))
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
} else {
|
||||||
|
if (index != i)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 10. Test that size changed for every other insert
|
||||||
|
*/
|
||||||
|
test_size(db, size, actual, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Everything after this point tests loading from a file
|
||||||
|
*/
|
||||||
|
if (autosave == false)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Database<IntEntry> db2("database.db", autosave);
|
||||||
|
db2.load();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 11. Sizes should match
|
||||||
|
*/
|
||||||
|
test_size(db2, db.size(), db.actual_size(), __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 12. Values should match
|
||||||
|
*/
|
||||||
|
Database<IntEntry>::iterator it2 = db2.begin();
|
||||||
|
for (it = db.begin(); it != db.end(); it++) {
|
||||||
|
if (it->valid != it2->valid)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
if (it->valid == true) {
|
||||||
|
if (it->val != it2->val)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
it2++;
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue