#include <binio.h>
#include <binfile.h>
#include <string.h>
#include "database.h"
#define DB_FILEID_V10 "AdPlug Module Information Database 1.0\x10"
const unsigned short CAdPlugDatabase::hash_radix = 0xfff1;
CAdPlugDatabase::CAdPlugDatabase()
: linear_index(0), linear_logic_length(0), linear_length(0)
{
db_linear = new DB_Bucket * [hash_radix];
db_hashed = new DB_Bucket * [hash_radix];
memset(db_linear, 0, sizeof(DB_Bucket *) * hash_radix);
memset(db_hashed, 0, sizeof(DB_Bucket *) * hash_radix);
}
CAdPlugDatabase::~CAdPlugDatabase()
{
unsigned long i;
for(i = 0; i < linear_length; i++)
delete db_linear[i];
delete [] db_linear;
delete [] db_hashed;
}
bool CAdPlugDatabase::load(std::string db_name)
{
binifstream f(db_name);
if(f.error()) return false;
return load(f);
}
bool CAdPlugDatabase::load(binistream &f)
{
unsigned int idlen = strlen(DB_FILEID_V10);
char *id = new char [idlen];
unsigned long length;
f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
f.readString(id,idlen);
if(memcmp(id,DB_FILEID_V10,idlen)) {
delete [] id;
return false;
}
delete [] id;
length = f.readInt(4);
for(unsigned long i = 0; i < length; i++)
insert(CRecord::factory(f));
return true;
}
bool CAdPlugDatabase::save(std::string db_name)
{
binofstream f(db_name);
if(f.error()) return false;
return save(f);
}
bool CAdPlugDatabase::save(binostream &f)
{
unsigned long i;
f.setFlag(binio::BigEndian, false); f.setFlag(binio::FloatIEEE);
f.writeString(DB_FILEID_V10);
f.writeInt(linear_logic_length, 4);
for(i = 0; i < linear_length; i++)
if(!db_linear[i]->deleted)
db_linear[i]->record->write(f);
return true;
}
CAdPlugDatabase::CRecord *CAdPlugDatabase::search(CKey const &key)
{
if(lookup(key)) return get_record(); else return 0;
}
bool CAdPlugDatabase::lookup(CKey const &key)
{
unsigned long index = make_hash(key);
if(!db_hashed[index]) return false;
DB_Bucket *bucket = db_hashed[index];
if(!bucket->deleted && bucket->record->key == key) {
linear_index = bucket->index;
return true;
}
bucket = db_hashed[index]->chain;
while(bucket) {
if(!bucket->deleted && bucket->record->key == key) {
linear_index = bucket->index;
return true;
}
bucket = bucket->chain;
}
return false;
}
bool CAdPlugDatabase::insert(CRecord *record)
{
long index;
if(!record) return false; if(linear_length == hash_radix) return false; if(lookup(record->key)) return false;
DB_Bucket *bucket = new DB_Bucket(linear_length, record);
if(!bucket) return false;
db_linear[linear_length] = bucket;
linear_logic_length++; linear_length++;
index = make_hash(record->key);
if(!db_hashed[index]) db_hashed[index] = bucket;
else { DB_Bucket *chain = db_hashed[index];
while(chain->chain) chain = chain->chain;
chain->chain = bucket;
}
return true;
}
void CAdPlugDatabase::wipe(CRecord *record)
{
if(!lookup(record->key)) return;
wipe();
}
void CAdPlugDatabase::wipe()
{
if(!linear_length) return;
DB_Bucket *bucket = db_linear[linear_index];
if(!bucket->deleted) {
delete bucket->record;
linear_logic_length--;
bucket->deleted = true;
}
}
CAdPlugDatabase::CRecord *CAdPlugDatabase::get_record()
{
if(!linear_length) return 0;
return db_linear[linear_index]->record;
}
bool CAdPlugDatabase::go_forward()
{
if(linear_index + 1 < linear_length) {
linear_index++;
return true;
} else
return false;
}
bool CAdPlugDatabase::go_backward()
{
if(!linear_index) return false;
linear_index--;
return true;
}
void CAdPlugDatabase::goto_begin()
{
if(linear_length) linear_index = 0;
}
void CAdPlugDatabase::goto_end()
{
if(linear_length) linear_index = linear_length - 1;
}
inline unsigned long CAdPlugDatabase::make_hash(CKey const &key)
{
return (key.crc32 + key.crc16) % hash_radix;
}
CAdPlugDatabase::DB_Bucket::DB_Bucket(unsigned long nindex, CRecord *newrecord, DB_Bucket *newchain)
: index(nindex), deleted(false), chain(newchain), record(newrecord)
{
}
CAdPlugDatabase::DB_Bucket::~DB_Bucket()
{
if(!deleted) delete record;
}
CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(RecordType type)
{
switch(type) {
case Plain: return new CPlainRecord;
case SongInfo: return new CInfoRecord;
case ClockSpeed: return new CClockRecord;
default: return 0;
}
}
CAdPlugDatabase::CRecord *CAdPlugDatabase::CRecord::factory(binistream &in)
{
RecordType type;
unsigned long size;
CRecord *rec;
type = (RecordType)in.readInt(1); size = in.readInt(4);
rec = factory(type);
if(rec) {
rec->key.crc16 = in.readInt(2); rec->key.crc32 = in.readInt(4);
rec->filetype = in.readString('\0'); rec->comment = in.readString('\0');
rec->read_own(in);
return rec;
} else {
in.seek(size, binio::Add);
return 0;
}
}
void CAdPlugDatabase::CRecord::write(binostream &out)
{
out.writeInt(type, 1);
out.writeInt(get_size() + filetype.length() + comment.length() + 8, 4);
out.writeInt(key.crc16, 2); out.writeInt(key.crc32, 4);
out.writeString(filetype); out.writeInt('\0', 1);
out.writeString(comment); out.writeInt('\0', 1);
write_own(out);
}
bool CAdPlugDatabase::CRecord::user_read(std::istream &in, std::ostream &out)
{
return user_read_own(in, out);
}
bool CAdPlugDatabase::CRecord::user_write(std::ostream &out)
{
out << "Record type: ";
switch(type) {
case Plain: out << "Plain"; break;
case SongInfo: out << "SongInfo"; break;
case ClockSpeed: out << "ClockSpeed"; break;
default: out << "*** Unknown ***"; break;
}
out << std::endl;
out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl;
out << "File type: " << filetype << std::endl;
out << "Comment: " << comment << std::endl;
return user_write_own(out);
}
CAdPlugDatabase::CKey::CKey(binistream &buf)
{
make(buf);
}
bool CAdPlugDatabase::CKey::operator==(const CKey &key)
{
return ((crc16 == key.crc16) && (crc32 == key.crc32));
}
void CAdPlugDatabase::CKey::make(binistream &buf)
{
static const unsigned short magic16 = 0xa001;
static const unsigned long magic32 = 0xedb88320;
crc16 = 0; crc32 = 0xffffffffL;
while(!buf.eof())
{
unsigned char byte = buf.readInt(1);
for (int j=0;j<8;j++)
{
if ((crc16 ^ byte) & 1)
crc16 = (crc16 >> 1) ^ magic16;
else
crc16 >>= 1;
if ((crc32 ^ byte) & 1)
crc32 = (crc32 >> 1) ^ magic32;
else
crc32 >>= 1;
byte >>= 1;
}
}
crc16 &= 0xffff;
crc32 = ~crc32;
crc32 &= 0xffffffffL;
}
CInfoRecord::CInfoRecord()
{
type = SongInfo;
}
void CInfoRecord::read_own(binistream &in)
{
title = in.readString('\0');
author = in.readString('\0');
}
void CInfoRecord::write_own(binostream &out)
{
out.writeString(title); out.writeInt('\0', 1);
out.writeString(author); out.writeInt('\0', 1);
}
unsigned long CInfoRecord::get_size()
{
return title.length() + author.length() + 2;
}
bool CInfoRecord::user_read_own(std::istream &in, std::ostream &out)
{
out << "Title: "; in >> title;
out << "Author: "; in >> author;
return true;
}
bool CInfoRecord::user_write_own(std::ostream &out)
{
out << "Title: " << title << std::endl;
out << "Author: " << author << std::endl;
return true;
}
CClockRecord::CClockRecord()
: clock(0.0f)
{
type = ClockSpeed;
}
void CClockRecord::read_own(binistream &in)
{
clock = in.readFloat(binio::Single);
}
void CClockRecord::write_own(binostream &out)
{
out.writeFloat(clock, binio::Single);
}
unsigned long CClockRecord::get_size()
{
return 4;
}
bool CClockRecord::user_read_own(std::istream &in, std::ostream &out)
{
out << "Clockspeed: "; in >> clock;
return true;
}
bool CClockRecord::user_write_own(std::ostream &out)
{
out << "Clock speed: " << clock << " Hz" << std::endl;
return true;
}