open_toontown_panda3d/panda/src/putil/bamCacheIndex.cxx

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 &params) {
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);
}
}