291 lines
10 KiB
C++
291 lines
10 KiB
C++
// Filename: bamCacheIndex.cxx
|
|
// Created by: drose (19Jun06)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "bamCacheIndex.h"
|
|
#include "bamReader.h"
|
|
#include "bamWriter.h"
|
|
#include "config_util.h" // util_cat
|
|
#include "indent.h"
|
|
#include <algorithm>
|
|
|
|
TypeHandle BamCacheIndex::_type_handle;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::Destructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
BamCacheIndex::
|
|
~BamCacheIndex() {
|
|
#ifndef NDEBUG
|
|
// We need to "empty" the linked list to make the LinkedListNode
|
|
// destructors happy.
|
|
release_records();
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::write
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void BamCacheIndex::
|
|
write(ostream &out, int indent_level) const {
|
|
indent(out, indent_level)
|
|
<< "BamCacheIndex, " << _records.size() << " records:\n";
|
|
|
|
Records::const_iterator ri;
|
|
for (ri = _records.begin(); ri != _records.end(); ++ri) {
|
|
BamCacheRecord *record = (*ri).second;
|
|
indent(out, indent_level + 2)
|
|
<< setw(10) << record->_record_size << " "
|
|
<< record->get_cache_filename() << " "
|
|
<< record->get_source_pathname() << "\n";
|
|
}
|
|
out << "\n";
|
|
indent(out, indent_level)
|
|
<< setw(12) << _cache_size << " bytes total\n";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::process_new_records
|
|
// Access: Private
|
|
// Description: Should be called after the _records index has been
|
|
// filled externally, this will sort the records by
|
|
// access time and calculate _cache_size.
|
|
////////////////////////////////////////////////////////////////////
|
|
void BamCacheIndex::
|
|
process_new_records() {
|
|
nassertv(_cache_size == 0);
|
|
|
|
// Fill up a vector so we can sort the records into order by access
|
|
// time.
|
|
RecordVector rv;
|
|
rv.reserve(_records.size());
|
|
|
|
Records::const_iterator ri;
|
|
for (ri = _records.begin(); ri != _records.end(); ++ri) {
|
|
BamCacheRecord *record = (*ri).second;
|
|
_cache_size += record->_record_size;
|
|
rv.push_back(record);
|
|
}
|
|
|
|
sort(rv.begin(), rv.end(), BamCacheRecord::SortByAccessTime());
|
|
|
|
// Now put them into the linked list.
|
|
RecordVector::const_iterator rvi;
|
|
for (rvi = rv.begin(); rvi != rv.end(); ++rvi) {
|
|
BamCacheRecord *record = *rvi;
|
|
record->insert_before(this);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::release_records
|
|
// Access: Private
|
|
// Description: This is the inverse of process_new_records: it
|
|
// releases the records from the linked list, so that
|
|
// they may be added to another index or whatever.
|
|
// Calling this, of course, invalidates the index until
|
|
// process_new_records() is called again.
|
|
////////////////////////////////////////////////////////////////////
|
|
void BamCacheIndex::
|
|
release_records() {
|
|
Records::const_iterator ri;
|
|
for (ri = _records.begin(); ri != _records.end(); ++ri) {
|
|
BamCacheRecord *record = (*ri).second;
|
|
record->_next = NULL;
|
|
record->_prev = NULL;
|
|
}
|
|
_next = this;
|
|
_prev = this;
|
|
_cache_size = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::evict_old_file
|
|
// Access: Private
|
|
// Description: Evicts an old file from the cache. Records the
|
|
// record. Returns NULL if the cache is empty.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(BamCacheRecord) BamCacheIndex::
|
|
evict_old_file() {
|
|
if (_next == this) {
|
|
// Nothing in the cache.
|
|
return NULL;
|
|
}
|
|
|
|
// The first record in the linked list is the least-recently-used
|
|
// one.
|
|
PT(BamCacheRecord) record = (BamCacheRecord *)_next;
|
|
bool removed = remove_record(record->get_source_pathname());
|
|
nassertr(removed, NULL);
|
|
|
|
return record;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::add_record
|
|
// Access: Private
|
|
// Description: Adds a newly-created BamCacheRecord into the index.
|
|
// If a matching record is already in the index, it is
|
|
// replaced with the new record. Returns true if the
|
|
// record was added, or false if the equivalent record
|
|
// was already there and the index is unchanged.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool BamCacheIndex::
|
|
add_record(BamCacheRecord *record) {
|
|
pair<Records::iterator, bool> result =
|
|
_records.insert(Records::value_type(record->get_source_pathname(), record));
|
|
if (!result.second) {
|
|
// We already had a record for this filename; it gets replaced.
|
|
BamCacheRecord *orig_record = (*result.first).second;
|
|
orig_record->remove_from_list();
|
|
if (*orig_record == *record) {
|
|
// Well, never mind. The record hasn't changed.
|
|
orig_record->insert_before(this);
|
|
return false;
|
|
}
|
|
|
|
_cache_size -= orig_record->_record_size;
|
|
(*result.first).second = record;
|
|
}
|
|
record->insert_before(this);
|
|
|
|
_cache_size += record->_record_size;
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::remove_record
|
|
// Access: Private
|
|
// Description: Searches for the matching record in the index and
|
|
// removes it if it is found. Returns true if the
|
|
// record was found and removed, or false if there was
|
|
// no such record and the index is unchanged.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool BamCacheIndex::
|
|
remove_record(const Filename &source_pathname) {
|
|
Records::iterator ri = _records.find(source_pathname);
|
|
if (ri == _records.end()) {
|
|
// No entry for this record; no problem.
|
|
return false;
|
|
}
|
|
|
|
BamCacheRecord *record = (*ri).second;
|
|
record->remove_from_list();
|
|
_cache_size -= record->_record_size;
|
|
_records.erase(ri);
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::register_with_read_factory
|
|
// Access: Public, Static
|
|
// Description: Tells the BamReader how to create objects of type
|
|
// BamCacheRecord.
|
|
////////////////////////////////////////////////////////////////////
|
|
void BamCacheIndex::
|
|
register_with_read_factory() {
|
|
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void BamCacheIndex::
|
|
write_datagram(BamWriter *manager, Datagram &dg) {
|
|
TypedWritable::write_datagram(manager, dg);
|
|
|
|
dg.add_uint32(_records.size());
|
|
Records::const_iterator ri;
|
|
for (ri = _records.begin(); ri != _records.end(); ++ri) {
|
|
manager->write_pointer(dg, (*ri).second);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::make_from_bam
|
|
// Access: Protected, Static
|
|
// Description: This function is called by the BamReader's factory
|
|
// when a new object of type BamCacheIndex is encountered
|
|
// in the Bam file. It should create the BamCacheIndex
|
|
// and extract its information from the file.
|
|
////////////////////////////////////////////////////////////////////
|
|
TypedWritable *BamCacheIndex::
|
|
make_from_bam(const FactoryParams ¶ms) {
|
|
BamCacheIndex *object = new BamCacheIndex;
|
|
DatagramIterator scan;
|
|
BamReader *manager;
|
|
|
|
parse_params(params, scan, manager);
|
|
object->fillin(scan, manager);
|
|
|
|
return object;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::complete_pointers
|
|
// Access: Public, Virtual
|
|
// Description: Receives an array of pointers, one for each time
|
|
// manager->read_pointer() was called in fillin().
|
|
// Returns the number of pointers processed.
|
|
////////////////////////////////////////////////////////////////////
|
|
int BamCacheIndex::
|
|
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|
int pi = TypedWritable::complete_pointers(p_list, manager);
|
|
|
|
RecordVector::iterator vi;
|
|
for (vi = _record_vector.begin(); vi != _record_vector.end(); ++vi) {
|
|
PT(BamCacheRecord) record = DCAST(BamCacheRecord, p_list[pi++]);
|
|
(*vi) = record;
|
|
|
|
bool inserted = _records.insert(Records::value_type(record->get_source_pathname(), record)).second;
|
|
if (!inserted) {
|
|
util_cat.info()
|
|
<< "Multiple cache files defining " << record->get_source_pathname()
|
|
<< " in index.\n";
|
|
}
|
|
}
|
|
|
|
_record_vector.clear();
|
|
|
|
process_new_records();
|
|
|
|
return pi;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: BamCacheIndex::fillin
|
|
// Access: Protected
|
|
// Description: This internal function is called by make_from_bam to
|
|
// read in all of the relevant data from the BamFile for
|
|
// the new BamCacheIndex.
|
|
////////////////////////////////////////////////////////////////////
|
|
void BamCacheIndex::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
TypedWritable::fillin(scan, manager);
|
|
|
|
int num_records = scan.get_uint32();
|
|
_record_vector.reserve(num_records);
|
|
for (int i = 0; i < num_records; ++i) {
|
|
_record_vector.push_back(NULL);
|
|
manager->read_pointer(scan);
|
|
}
|
|
}
|