index: Update the index design
I updated the design and rewrote the unit tests. This creates something more consistent with how I ended up using the index. Signed-off-by: Anna Schumaker <schumaker.anna@gmail.com>
This commit is contained in:
parent
fc8dc9d55e
commit
1b34a9a8a0
60
DESIGN
60
DESIGN
|
@ -203,7 +203,7 @@ Database Entry:
|
||||||
|
|
||||||
DatabaseEntry();
|
DatabaseEntry();
|
||||||
virtual void ~DatabaseEntry() = 0;
|
virtual void ~DatabaseEntry() = 0;
|
||||||
virtual std::string primary_key() = 0;
|
virtual const 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;
|
||||||
|
@ -213,7 +213,7 @@ Database Entry:
|
||||||
DatabaseEntry :: DatabaseEntry():
|
DatabaseEntry :: DatabaseEntry():
|
||||||
Set valid = false.
|
Set valid = false.
|
||||||
|
|
||||||
std::string DatabaseEntry :: primary_key();
|
const std::string DatabaseEntry :: primary_key();
|
||||||
This function should return a unique string representing this
|
This function should return a unique string representing this
|
||||||
DatabaseEntry instance, which will be used to prevent
|
DatabaseEntry instance, which will be used to prevent
|
||||||
duplicates in a database. This string is not expected to
|
duplicates in a database. This string is not expected to
|
||||||
|
@ -261,7 +261,7 @@ Database:
|
||||||
class Database {
|
class Database {
|
||||||
private:
|
private:
|
||||||
std::vector<T> _db;
|
std::vector<T> _db;
|
||||||
std::map<std::string, unsigned int> _keys;
|
std::map<const std::string, unsigned int> _keys;
|
||||||
unsigned int _size; /* Number of valid rows */
|
unsigned int _size; /* Number of valid rows */
|
||||||
bool _autosave;
|
bool _autosave;
|
||||||
File _file;
|
File _file;
|
||||||
|
@ -357,22 +357,70 @@ Database:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Index:
|
||||||
|
An index is a special database used to map a std::string key to
|
||||||
|
multiple integer values.
|
||||||
|
|
||||||
- IndexEntry:
|
- IndexEntry:
|
||||||
class IndexEntry : public DatabaseEntry {
|
class IndexEntry : public DatabaseEntry {
|
||||||
public:
|
public:
|
||||||
|
const std::string key;
|
||||||
set<unsigned int> values;
|
set<unsigned int> values;
|
||||||
|
|
||||||
|
IndexEntry(const std::string &);
|
||||||
|
const std::string primary_key();
|
||||||
|
void insert(unsigned int);
|
||||||
|
void remove(unsigned int);
|
||||||
|
|
||||||
void write(File &);
|
void write(File &);
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void print();
|
|
||||||
void insert(unsigned int);
|
|
||||||
void remove(unsigned int);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
File << key << endl;
|
File << key << endl;
|
||||||
File << values.size() << values[0] << .. << values[N] << endl;
|
File << values.size() << values[0] << .. << values[N] << endl;
|
||||||
|
|
||||||
|
- IndexEntry API:
|
||||||
|
IndexEntry :: IndexEntry();
|
||||||
|
Creat an empty IndexEntry.
|
||||||
|
|
||||||
|
std::string IndexEntry :: primary_key();
|
||||||
|
return key;
|
||||||
|
|
||||||
|
void IndexEntry :: insert(unsigned int value);
|
||||||
|
Add value to the values set.
|
||||||
|
|
||||||
|
void IndexEntry :: remove(unsigned int value);
|
||||||
|
Remove value from the values set.
|
||||||
|
|
||||||
|
void IndexEntry :: write(File &f);
|
||||||
|
Write the values set to a file.
|
||||||
|
|
||||||
|
void IndexEntry :: read(File &f);
|
||||||
|
Read values from a file.
|
||||||
|
|
||||||
|
- Index:
|
||||||
|
class Index : public Database<IndexEntry> {
|
||||||
|
public:
|
||||||
|
Index(const std::string &, bool);
|
||||||
|
|
||||||
|
void insert(const std::string &, unsigned int);
|
||||||
|
void remove(const std::string &, unsigned int);
|
||||||
|
};
|
||||||
|
|
||||||
|
- Index API:
|
||||||
|
Index :: Index(const std::string &filepath, bool autosave);
|
||||||
|
Pass values on to the Database constructor.
|
||||||
|
|
||||||
|
void Index :: insert(const std::string &key, unsigned int value);
|
||||||
|
Create an IndexEntry for key if one does not exist yet.
|
||||||
|
Insert value into the IndexEntry corresponding to key.
|
||||||
|
If autosave is enabled, save().
|
||||||
|
|
||||||
|
void Index :: remove(const std::string &key, unsigned int value);
|
||||||
|
Remove value from the IndexEntry corresponding to key. Do not
|
||||||
|
remove the IndexEntry, even if it is empty.
|
||||||
|
If autosave is enabled, save().
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Filter: (lib/filter.cpp)
|
Filter: (lib/filter.cpp)
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <print.h>
|
#include <print.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,30 +16,12 @@ public:
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
DatabaseEntry();
|
DatabaseEntry();
|
||||||
virtual std::string primary_key() = 0;
|
virtual const 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class IndexEntry : public DatabaseEntry {
|
|
||||||
public:
|
|
||||||
std::string key;
|
|
||||||
std::set<unsigned int> values;
|
|
||||||
|
|
||||||
IndexEntry();
|
|
||||||
IndexEntry(const std::string &, unsigned int);
|
|
||||||
~IndexEntry();
|
|
||||||
std::string primary_key();
|
|
||||||
void write(File &);
|
|
||||||
void read(File &);
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void print();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
void insert(unsigned int);
|
|
||||||
void remove(unsigned int);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class Database {
|
class Database {
|
||||||
|
@ -51,8 +32,6 @@ private:
|
||||||
bool _autosave;
|
bool _autosave;
|
||||||
File _file;
|
File _file;
|
||||||
|
|
||||||
void autosave();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef typename std::vector<T>::iterator iterator;
|
typedef typename std::vector<T>::iterator iterator;
|
||||||
typedef typename std::vector<T>::const_iterator const_iterator;
|
typedef typename std::vector<T>::const_iterator const_iterator;
|
||||||
|
@ -60,6 +39,7 @@ public:
|
||||||
Database(std::string, bool);
|
Database(std::string, bool);
|
||||||
~Database();
|
~Database();
|
||||||
void save();
|
void save();
|
||||||
|
void autosave();
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
unsigned int insert(T);
|
unsigned int insert(T);
|
||||||
|
@ -75,29 +55,6 @@ public:
|
||||||
iterator find(const std::string &);
|
iterator find(const std::string &);
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void index_insert(Database<IndexEntry> &db,
|
|
||||||
const std::string &key,
|
|
||||||
unsigned int val)
|
|
||||||
{
|
|
||||||
Database<IndexEntry>::iterator it = db.find(key);
|
|
||||||
if (it == db.end()) {
|
|
||||||
db.insert(IndexEntry(key, val));
|
|
||||||
} else
|
|
||||||
it->insert(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void index_remove(Database<IndexEntry> &db,
|
|
||||||
const std::string &key,
|
|
||||||
unsigned int val)
|
|
||||||
{
|
|
||||||
Database<IndexEntry>::iterator it = db.find(key);
|
|
||||||
if (it != db.end()) {
|
|
||||||
it->remove(val);
|
|
||||||
if (it->values.size() == 0)
|
|
||||||
db.remove(it - db.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
|
|
||||||
#endif /* OCARINA_DATABASE_H */
|
#endif /* OCARINA_DATABASE_H */
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#ifndef OCARINA_FILTER_H
|
#ifndef OCARINA_FILTER_H
|
||||||
#define OCARINA_FILTER_H
|
#define OCARINA_FILTER_H
|
||||||
|
|
||||||
#include <database.h>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace filter {
|
namespace filter {
|
||||||
|
@ -15,10 +15,6 @@ namespace filter {
|
||||||
|
|
||||||
void print_cache_stats();
|
void print_cache_stats();
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
Database<IndexEntry> &get_index();
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* OCARINA_FILTER_H */
|
#endif /* OCARINA_FILTER_H */
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 (c) Anna Schumaker.
|
||||||
|
*/
|
||||||
|
#ifndef OCARINA_INDEX_H
|
||||||
|
#define OCARINA_INDEX_H
|
||||||
|
|
||||||
|
#include <database.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
class IndexEntry : public DatabaseEntry {
|
||||||
|
public:
|
||||||
|
std::string key;
|
||||||
|
std::set<unsigned int> values;
|
||||||
|
|
||||||
|
IndexEntry();
|
||||||
|
IndexEntry(const std::string &);
|
||||||
|
const std::string primary_key();
|
||||||
|
void insert(unsigned int);
|
||||||
|
void remove(unsigned int);
|
||||||
|
|
||||||
|
void write(File &);
|
||||||
|
void read(File &);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Index : public Database<IndexEntry> {
|
||||||
|
public:
|
||||||
|
Index(const std::string &, bool);
|
||||||
|
|
||||||
|
void insert(const std::string &, unsigned int);
|
||||||
|
void remove(const std::string &, unsigned int);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* OCARINA_DATABASE_H */
|
|
@ -33,7 +33,7 @@ namespace library
|
||||||
AGInfo();
|
AGInfo();
|
||||||
AGInfo(DB_Type, TagLib :: Tag *);
|
AGInfo(DB_Type, TagLib :: Tag *);
|
||||||
AGInfo(DB_Type, const std::string &);
|
AGInfo(DB_Type, const std::string &);
|
||||||
std::string primary_key();
|
const std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
};
|
};
|
||||||
|
@ -49,7 +49,7 @@ namespace library
|
||||||
Album();
|
Album();
|
||||||
Album(TagLib :: Tag *, unsigned int);
|
Album(TagLib :: Tag *, unsigned int);
|
||||||
Album(const std::string &, unsigned int, unsigned int);
|
Album(const std::string &, unsigned int, unsigned int);
|
||||||
std::string primary_key();
|
const std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
};
|
};
|
||||||
|
@ -63,7 +63,7 @@ namespace library
|
||||||
|
|
||||||
Library();
|
Library();
|
||||||
Library(const std::string &, bool);
|
Library(const std::string &, bool);
|
||||||
std::string primary_key();
|
const std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
};
|
};
|
||||||
|
@ -95,7 +95,7 @@ namespace library
|
||||||
unsigned int, const std :: string &);
|
unsigned int, const std :: string &);
|
||||||
Track(ImportData *, unsigned int, unsigned int,
|
Track(ImportData *, unsigned int, unsigned int,
|
||||||
unsigned int, unsigned int);
|
unsigned int, unsigned int);
|
||||||
std::string primary_key();
|
const std::string primary_key();
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void write(File &);
|
void write(File &);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,67 +8,3 @@ DatabaseEntry :: DatabaseEntry()
|
||||||
: valid(false)
|
: valid(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
IndexEntry :: IndexEntry()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexEntry :: IndexEntry(const std::string &k, unsigned int v)
|
|
||||||
{
|
|
||||||
key = k;
|
|
||||||
insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexEntry :: ~IndexEntry()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string IndexEntry :: primary_key()
|
|
||||||
{
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexEntry :: write(File &f)
|
|
||||||
{
|
|
||||||
std::set<unsigned int>::iterator it;
|
|
||||||
f << key << std::endl << values.size() << " ";
|
|
||||||
for (it = values.begin(); it != values.end(); it++)
|
|
||||||
f << *it << " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexEntry :: read(File &f)
|
|
||||||
{
|
|
||||||
unsigned int num, val;
|
|
||||||
|
|
||||||
f >> key >> num;
|
|
||||||
for (unsigned int i = 0; i < num; i++) {
|
|
||||||
f >> val;
|
|
||||||
values.insert(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
void IndexEntry :: print()
|
|
||||||
{
|
|
||||||
std::set<unsigned int>::iterator it;
|
|
||||||
:: print("{");
|
|
||||||
for (it = values.begin(); it != values.end(); it++) {
|
|
||||||
if (it != values.begin())
|
|
||||||
:: print(" ");
|
|
||||||
:: print("%d", *it);
|
|
||||||
}
|
|
||||||
:: print("}");
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
||||||
void IndexEntry :: insert(unsigned int val)
|
|
||||||
{
|
|
||||||
values.insert(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexEntry :: remove(unsigned int val)
|
|
||||||
{
|
|
||||||
values.erase(val);
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <filter.h>
|
#include <filter.h>
|
||||||
|
#include <index.h>
|
||||||
#include <print.h>
|
#include <print.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
static Database<IndexEntry> filter_index("", false);
|
static Index 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;
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ static void add_substrings(const std::string &text, unsigned int track_id)
|
||||||
std::string substr;
|
std::string substr;
|
||||||
for (unsigned int i = 1; i <= text.size(); i++) {
|
for (unsigned int i = 1; i <= text.size(); i++) {
|
||||||
substr = text.substr(0, i);
|
substr = text.substr(0, i);
|
||||||
index_insert(filter_index, substr, track_id);
|
filter_index.insert(substr, track_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ 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)
|
||||||
{
|
{
|
||||||
Database<IndexEntry>::iterator it = filter_index.find(text);
|
Index::iterator it = filter_index.find(text);
|
||||||
std::set<unsigned int> tmp;
|
std::set<unsigned int> tmp;
|
||||||
|
|
||||||
set_intersection(it->values.begin(), it->values.end(),
|
set_intersection(it->values.begin(), it->values.end(),
|
||||||
|
@ -157,10 +157,3 @@ void filter :: print_cache_stats()
|
||||||
print("Lowercase cache size: %u\n", lowercase_cache.size());
|
print("Lowercase cache size: %u\n", lowercase_cache.size());
|
||||||
print("Lowercase cache hits: %u\n", lowercase_cache_hits);
|
print("Lowercase cache hits: %u\n", lowercase_cache_hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_TEST
|
|
||||||
Database<IndexEntry> &filter :: get_index()
|
|
||||||
{
|
|
||||||
return filter_index;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_TEST */
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 (c) Anna Schumaker.
|
||||||
|
*/
|
||||||
|
#include <index.h>
|
||||||
|
|
||||||
|
|
||||||
|
IndexEntry :: IndexEntry() {}
|
||||||
|
IndexEntry :: IndexEntry(const std::string &k)
|
||||||
|
: key(k)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::string IndexEntry :: primary_key()
|
||||||
|
{
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexEntry :: insert(unsigned int val)
|
||||||
|
{
|
||||||
|
values.insert(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexEntry :: remove(unsigned int val)
|
||||||
|
{
|
||||||
|
values.erase(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexEntry :: write(File &f)
|
||||||
|
{
|
||||||
|
std::set<unsigned int>::iterator it;
|
||||||
|
f << key << std::endl << values.size() << " ";
|
||||||
|
for (it = values.begin(); it != values.end(); it++)
|
||||||
|
f << *it << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexEntry :: read(File &f)
|
||||||
|
{
|
||||||
|
unsigned int num, val;
|
||||||
|
|
||||||
|
f >> key >> num;
|
||||||
|
for (unsigned int i = 0; i < num; i++) {
|
||||||
|
f >> val;
|
||||||
|
insert(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Index :: Index(const std::string &filepath, bool autosave)
|
||||||
|
: Database(filepath, autosave)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Index :: insert(const std::string &key, unsigned int val)
|
||||||
|
{
|
||||||
|
iterator it = find(key);
|
||||||
|
if (it == end())
|
||||||
|
it = at(Database :: insert(IndexEntry(key)));
|
||||||
|
|
||||||
|
it->insert(val);
|
||||||
|
autosave();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Index :: remove(const std::string &key, unsigned int val)
|
||||||
|
{
|
||||||
|
iterator it = find(key);
|
||||||
|
|
||||||
|
if (it == end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->remove(val);
|
||||||
|
autosave();
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ library :: AGInfo :: AGInfo(DB_Type type, const std::string &str)
|
||||||
throw -E_INVAL;
|
throw -E_INVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string library :: AGInfo :: primary_key()
|
const std::string library :: AGInfo :: primary_key()
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ library :: Album :: Album(const std::string &str, unsigned int yr, unsigned int
|
||||||
name_lower = filter :: to_lowercase(name);
|
name_lower = filter :: to_lowercase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string library :: Album :: primary_key()
|
const std::string library :: Album :: primary_key()
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << artist_id << "." << name << "." << year;
|
ss << artist_id << "." << name << "." << year;
|
||||||
|
@ -135,7 +135,7 @@ library :: Library :: Library(const std::string &path, bool is_enabled)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string library :: Library :: primary_key()
|
const std::string library :: Library :: primary_key()
|
||||||
{
|
{
|
||||||
return root_path;
|
return root_path;
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ library :: Track :: Track(struct ImportData *data, unsigned int lib,
|
||||||
length_str = ss.str();
|
length_str = ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string library :: Track :: primary_key()
|
const std::string library :: Track :: primary_key()
|
||||||
{
|
{
|
||||||
return full_path;
|
return full_path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
* Copyright 2013 (c) Anna Schumaker.
|
* Copyright 2013 (c) Anna Schumaker.
|
||||||
*/
|
*/
|
||||||
#include <callback.h>
|
#include <callback.h>
|
||||||
#include <database.h>
|
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
|
#include <index.h>
|
||||||
#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", false);
|
static Index 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;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ void playlist :: init()
|
||||||
void playlist :: add(const std::string &name, unsigned int track_id)
|
void playlist :: add(const std::string &name, unsigned int track_id)
|
||||||
{
|
{
|
||||||
if ((name == "Banned") || (name == "Favorites")) {
|
if ((name == "Banned") || (name == "Favorites")) {
|
||||||
index_insert(playlist_db, name, track_id);
|
playlist_db.insert(name, track_id);
|
||||||
playlist_db.save();
|
playlist_db.save();
|
||||||
if (name == cur_pq)
|
if (name == cur_pq)
|
||||||
playlist_pq.add(track_id);
|
playlist_pq.add(track_id);
|
||||||
|
@ -59,7 +59,7 @@ void playlist :: add(const std::string &name, unsigned int track_id)
|
||||||
void playlist :: del(const std::string &name, unsigned int track_id)
|
void playlist :: del(const std::string &name, unsigned int track_id)
|
||||||
{
|
{
|
||||||
if ((name == "Banned") || (name == "Favorites")) {
|
if ((name == "Banned") || (name == "Favorites")) {
|
||||||
index_remove(playlist_db, name, track_id);
|
playlist_db.remove(name, track_id);
|
||||||
playlist_db.save();
|
playlist_db.save();
|
||||||
if (name == cur_pq)
|
if (name == cur_pq)
|
||||||
playlist_pq.del_track(track_id);
|
playlist_pq.del_track(track_id);
|
||||||
|
|
|
@ -8,9 +8,8 @@ if sys.argv.count("tests") > 0:
|
||||||
|
|
||||||
src = SConscript("src/Sconscript")
|
src = SConscript("src/Sconscript")
|
||||||
|
|
||||||
tests = [ "version", "file", "db_entry", "database" ]
|
tests = [ "version", "file", "db_entry", "database", "index" ]
|
||||||
#scripts = [ "print", "file", "database", "index", "filter", "idle", "playlist",
|
#scripts = [ "filter", "idle", "playlist", "library", "playqueue", "deck", "audio", "gui" ]
|
||||||
# "library", "playqueue", "deck", "audio", "gui" ]
|
|
||||||
|
|
||||||
prev = None
|
prev = None
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2014 (c) Anna Schumaker
|
||||||
|
|
||||||
|
. $(dirname $0)/_functions
|
||||||
|
|
||||||
|
function test_autosave
|
||||||
|
{
|
||||||
|
new_test "Index Test (n = $1, autosave = true)"
|
||||||
|
src/index.run -a $1
|
||||||
|
if [ ! -f $DATA_DIR/index.idx ]; then
|
||||||
|
echo "ERROR: $DATA_DIR/index.db doesn't exist!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_noautosave
|
||||||
|
{
|
||||||
|
new_test "Index Test (n = $1, autosave = false)"
|
||||||
|
src/index.run $1
|
||||||
|
if [ -f $DATA_DIR/index.idx ]; then
|
||||||
|
echo "ERROR: $DATA_DIR/index.idx exists!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_test
|
||||||
|
{
|
||||||
|
rm $DATA_DIR/* 2>/dev/null || true
|
||||||
|
|
||||||
|
if [ $1 -lt 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("index", "index.cpp")
|
|
|
@ -1,152 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2013 (c) Anna Schumaker.
|
|
||||||
*/
|
|
||||||
#include <database.h>
|
|
||||||
#include <print.h>
|
|
||||||
|
|
||||||
void print_index(Database<IndexEntry> &index)
|
|
||||||
{
|
|
||||||
std::set<std::string>::iterator s_it;
|
|
||||||
std::set<unsigned int>::iterator u_it;
|
|
||||||
|
|
||||||
print("=== Printing index ===\n");
|
|
||||||
index.print_keys();
|
|
||||||
|
|
||||||
for (unsigned int i = index.first(); i != index.num_rows(); i = index.next(i)) {
|
|
||||||
print("index[%s] = ", index[i].primary_key.c_str());
|
|
||||||
index[i].print();
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void populate_index(Database<IndexEntry> &index)
|
|
||||||
{
|
|
||||||
std::string key_str;
|
|
||||||
|
|
||||||
for (char key = 'a'; key <= 'z'; key++) {
|
|
||||||
key_str = key;
|
|
||||||
for (unsigned int value = 0; value < 10; value++)
|
|
||||||
index_insert(index, key_str, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_keys(Database<IndexEntry> &index)
|
|
||||||
{
|
|
||||||
std::string key_str;
|
|
||||||
|
|
||||||
for (char key = 'a'; key <= 'z'; key+=2) {
|
|
||||||
key_str = key;
|
|
||||||
index.remove(index.find_index(key_str));
|
|
||||||
if (index.has_key(key_str))
|
|
||||||
print("Failed to remove key!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_values(Database<IndexEntry> &index)
|
|
||||||
{
|
|
||||||
std::string key_str;
|
|
||||||
unsigned int limit = 0;
|
|
||||||
|
|
||||||
for (char key = 'a'; key <= 'z'; key++) {
|
|
||||||
key_str = key;
|
|
||||||
for (unsigned int i = 0; i < limit; i++)
|
|
||||||
index_remove(index, key_str, i);
|
|
||||||
limit++;
|
|
||||||
if (limit == 11) {
|
|
||||||
limit = 0;
|
|
||||||
if (index.has_key(key_str))
|
|
||||||
print("Failed to remove key!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test index creation, print key access
|
|
||||||
*/
|
|
||||||
void test_0()
|
|
||||||
{
|
|
||||||
print("Test 0\n");
|
|
||||||
Database<IndexEntry> index("");
|
|
||||||
|
|
||||||
populate_index(index);
|
|
||||||
print_index(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test removing keys from an index
|
|
||||||
*/
|
|
||||||
void test_1()
|
|
||||||
{
|
|
||||||
print("Test 1\n");
|
|
||||||
Database<IndexEntry> index("");
|
|
||||||
|
|
||||||
populate_index(index);
|
|
||||||
remove_keys(index);
|
|
||||||
print_index(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test removing values from an index
|
|
||||||
*/
|
|
||||||
void test_2()
|
|
||||||
{
|
|
||||||
print("Test 2\n");
|
|
||||||
Database<IndexEntry> index("");
|
|
||||||
|
|
||||||
populate_index(index);
|
|
||||||
remove_values(index);
|
|
||||||
print_index(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save the database to disk
|
|
||||||
*/
|
|
||||||
void test_3()
|
|
||||||
{
|
|
||||||
print("Test 3\n");
|
|
||||||
Database<IndexEntry> index("test.idx");
|
|
||||||
|
|
||||||
populate_index(index);
|
|
||||||
remove_values(index);
|
|
||||||
index.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reload the database from disk
|
|
||||||
*/
|
|
||||||
void test_4()
|
|
||||||
{
|
|
||||||
print("Test 4\n");
|
|
||||||
Database<IndexEntry> index("test.idx");
|
|
||||||
|
|
||||||
index.load();
|
|
||||||
print_index(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test access to keys that don't exist
|
|
||||||
*/
|
|
||||||
void test_5()
|
|
||||||
{
|
|
||||||
print("Test 5\n");
|
|
||||||
Database<IndexEntry> index("");
|
|
||||||
print("index[nokey].size() = ");
|
|
||||||
try {
|
|
||||||
print("%u\n", index.find("nokey").values.size());
|
|
||||||
} catch (...) {
|
|
||||||
print("0\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
test_0();
|
|
||||||
test_1();
|
|
||||||
test_2();
|
|
||||||
test_3();
|
|
||||||
test_4();
|
|
||||||
test_5();
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
Test 0
|
|
||||||
=== Printing index ===
|
|
||||||
Found keys: a b c d e f g h i j k l m n o p q r s t u v w x y z
|
|
||||||
index[a] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[b] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[c] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[d] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[e] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[f] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[g] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[h] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[i] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[j] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[k] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[l] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[m] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[n] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[o] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[p] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[q] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[r] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[s] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[t] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[u] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[v] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[w] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[x] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[y] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[z] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
|
|
||||||
Test 1
|
|
||||||
=== Printing index ===
|
|
||||||
Found keys: b d f h j l n p r t v x z
|
|
||||||
index[b] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[d] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[f] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[h] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[j] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[l] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[n] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[p] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[r] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[t] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[v] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[x] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[z] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
|
|
||||||
Test 2
|
|
||||||
=== Printing index ===
|
|
||||||
Found keys: a b c d e f g h i j l m n o p q r s t u w x y z
|
|
||||||
index[a] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[b] = {1 2 3 4 5 6 7 8 9}
|
|
||||||
index[c] = {2 3 4 5 6 7 8 9}
|
|
||||||
index[d] = {3 4 5 6 7 8 9}
|
|
||||||
index[e] = {4 5 6 7 8 9}
|
|
||||||
index[f] = {5 6 7 8 9}
|
|
||||||
index[g] = {6 7 8 9}
|
|
||||||
index[h] = {7 8 9}
|
|
||||||
index[i] = {8 9}
|
|
||||||
index[j] = {9}
|
|
||||||
index[l] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[m] = {1 2 3 4 5 6 7 8 9}
|
|
||||||
index[n] = {2 3 4 5 6 7 8 9}
|
|
||||||
index[o] = {3 4 5 6 7 8 9}
|
|
||||||
index[p] = {4 5 6 7 8 9}
|
|
||||||
index[q] = {5 6 7 8 9}
|
|
||||||
index[r] = {6 7 8 9}
|
|
||||||
index[s] = {7 8 9}
|
|
||||||
index[t] = {8 9}
|
|
||||||
index[u] = {9}
|
|
||||||
index[w] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[x] = {1 2 3 4 5 6 7 8 9}
|
|
||||||
index[y] = {2 3 4 5 6 7 8 9}
|
|
||||||
index[z] = {3 4 5 6 7 8 9}
|
|
||||||
|
|
||||||
Test 3
|
|
||||||
Test 4
|
|
||||||
=== Printing index ===
|
|
||||||
Found keys: a b c d e f g h i j l m n o p q r s t u w x y z
|
|
||||||
index[a] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[b] = {1 2 3 4 5 6 7 8 9}
|
|
||||||
index[c] = {2 3 4 5 6 7 8 9}
|
|
||||||
index[d] = {3 4 5 6 7 8 9}
|
|
||||||
index[e] = {4 5 6 7 8 9}
|
|
||||||
index[f] = {5 6 7 8 9}
|
|
||||||
index[g] = {6 7 8 9}
|
|
||||||
index[h] = {7 8 9}
|
|
||||||
index[i] = {8 9}
|
|
||||||
index[j] = {9}
|
|
||||||
index[l] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[m] = {1 2 3 4 5 6 7 8 9}
|
|
||||||
index[n] = {2 3 4 5 6 7 8 9}
|
|
||||||
index[o] = {3 4 5 6 7 8 9}
|
|
||||||
index[p] = {4 5 6 7 8 9}
|
|
||||||
index[q] = {5 6 7 8 9}
|
|
||||||
index[r] = {6 7 8 9}
|
|
||||||
index[s] = {7 8 9}
|
|
||||||
index[t] = {8 9}
|
|
||||||
index[u] = {9}
|
|
||||||
index[w] = {0 1 2 3 4 5 6 7 8 9}
|
|
||||||
index[x] = {1 2 3 4 5 6 7 8 9}
|
|
||||||
index[y] = {2 3 4 5 6 7 8 9}
|
|
||||||
index[z] = {3 4 5 6 7 8 9}
|
|
||||||
|
|
||||||
Test 5
|
|
||||||
index[nokey].size() = 0
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
|
|
||||||
IntEntry();
|
IntEntry();
|
||||||
IntEntry(unsigned int);
|
IntEntry(unsigned int);
|
||||||
std::string primary_key();
|
const std::string primary_key();
|
||||||
void write(File &);
|
void write(File &);
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void print();
|
void print();
|
||||||
|
@ -28,7 +28,7 @@ public:
|
||||||
|
|
||||||
IntEntry :: IntEntry() : val(0) {}
|
IntEntry :: IntEntry() : val(0) {}
|
||||||
IntEntry :: IntEntry(unsigned int v) : val(v) {}
|
IntEntry :: IntEntry(unsigned int v) : val(v) {}
|
||||||
std::string IntEntry :: primary_key()
|
const std::string IntEntry :: primary_key()
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << val;
|
ss << val;
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
std::string key;
|
std::string key;
|
||||||
|
|
||||||
IntEntry(unsigned int, const std::string &);
|
IntEntry(unsigned int, const std::string &);
|
||||||
std::string primary_key();
|
const std::string primary_key();
|
||||||
void write(File &);
|
void write(File &);
|
||||||
void read(File &);
|
void read(File &);
|
||||||
void print();
|
void print();
|
||||||
|
@ -31,7 +31,7 @@ IntEntry :: IntEntry(unsigned int i, const std::string &s)
|
||||||
key = s;
|
key = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IntEntry :: primary_key()
|
const std::string IntEntry :: primary_key()
|
||||||
{
|
{
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 (c) Anna Schumaker.
|
||||||
|
* Test a Database
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <index.h>
|
||||||
|
#include <print.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int test_num = 0;
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool autosave = false;
|
||||||
|
unsigned int n;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "a")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'a':
|
||||||
|
autosave = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = atoi(argv[optind]);
|
||||||
|
Index index("index.idx", autosave);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0: Test inserting when there is no key
|
||||||
|
*/
|
||||||
|
index.insert("a", 0);
|
||||||
|
Index :: iterator it = index.find("a");
|
||||||
|
if (it == index.end())
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
if (it->values.size() == 0)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
test_results(*(it->values.begin()) == 0, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1: Test removing
|
||||||
|
*/
|
||||||
|
index.remove("a", 0);
|
||||||
|
test_results((index.size() == 1) && (index.find("a")->values.size() == 0), __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2: Test adding multiple values
|
||||||
|
*/
|
||||||
|
for (c = 'a'; c <= 'z'; c++) {
|
||||||
|
std::string key = "";
|
||||||
|
key += c;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < n; i++)
|
||||||
|
index.insert(key, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_results(index.size() == 26, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3: Make sure we have multiple values for each key
|
||||||
|
*/
|
||||||
|
for (c = 'a'; c <= 'z'; c++) {
|
||||||
|
std::string key = "";
|
||||||
|
key += c;
|
||||||
|
if (index.find(key)->values.size() != n)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4: Test removing multiple values
|
||||||
|
*/
|
||||||
|
for (c = 'a'; c <= 'z'; c+=2) {
|
||||||
|
std::string key = "";
|
||||||
|
key += c;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < n; i++)
|
||||||
|
index.remove(key, i);
|
||||||
|
|
||||||
|
if (index.find(key)->values.size() > 0)
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_results(index.size() == 26, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Everything after this point tests loading from a file
|
||||||
|
*/
|
||||||
|
if (autosave == false)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Index index2("index.idx", autosave);
|
||||||
|
index2.load();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 5: Load database from file
|
||||||
|
*/
|
||||||
|
test_results(index.size() == index2.size(), __LINE__);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 6: Check that everything is the right size
|
||||||
|
*/
|
||||||
|
for (c = 'a'; c <= 'z'; c++) {
|
||||||
|
std::string key = "";
|
||||||
|
key += c;
|
||||||
|
|
||||||
|
if (index.find(key)->values.size() != index2.find(key)->values.size())
|
||||||
|
test_results(false, __LINE__);
|
||||||
|
}
|
||||||
|
test_results(true, __LINE__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue