Panda3D
bamCacheIndex.cxx
1 // Filename: bamCacheIndex.cxx
2 // Created by: drose (19Jun06)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "bamCacheIndex.h"
16 #include "bamReader.h"
17 #include "bamWriter.h"
18 #include "config_util.h" // util_cat
19 #include "indent.h"
20 #include <algorithm>
21 
22 TypeHandle BamCacheIndex::_type_handle;
23 
24 
25 ////////////////////////////////////////////////////////////////////
26 // Function: BamCacheIndex::Destructor
27 // Access: Published
28 // Description:
29 ////////////////////////////////////////////////////////////////////
30 BamCacheIndex::
31 ~BamCacheIndex() {
32 #ifndef NDEBUG
33  // We need to "empty" the linked list to make the LinkedListNode
34  // destructors happy.
35  release_records();
36 #endif
37 }
38 
39 ////////////////////////////////////////////////////////////////////
40 // Function: BamCacheIndex::write
41 // Access: Public
42 // Description:
43 ////////////////////////////////////////////////////////////////////
44 void BamCacheIndex::
45 write(ostream &out, int indent_level) const {
46  indent(out, indent_level)
47  << "BamCacheIndex, " << _records.size() << " records:\n";
48 
49  Records::const_iterator ri;
50  for (ri = _records.begin(); ri != _records.end(); ++ri) {
51  BamCacheRecord *record = (*ri).second;
52  indent(out, indent_level + 2)
53  << setw(10) << record->_record_size << " "
54  << record->get_cache_filename() << " "
55  << record->get_source_pathname() << "\n";
56  }
57  out << "\n";
58  indent(out, indent_level)
59  << setw(12) << _cache_size << " bytes total\n";
60 }
61 
62 ////////////////////////////////////////////////////////////////////
63 // Function: BamCacheIndex::process_new_records
64 // Access: Private
65 // Description: Should be called after the _records index has been
66 // filled externally, this will sort the records by
67 // access time and calculate _cache_size.
68 ////////////////////////////////////////////////////////////////////
69 void BamCacheIndex::
70 process_new_records() {
71  nassertv(_cache_size == 0);
72 
73  // Fill up a vector so we can sort the records into order by access
74  // time.
75  RecordVector rv;
76  rv.reserve(_records.size());
77 
78  Records::const_iterator ri;
79  for (ri = _records.begin(); ri != _records.end(); ++ri) {
80  BamCacheRecord *record = (*ri).second;
81  _cache_size += record->_record_size;
82  rv.push_back(record);
83  }
84 
85  sort(rv.begin(), rv.end(), BamCacheRecord::SortByAccessTime());
86 
87  // Now put them into the linked list.
88  RecordVector::const_iterator rvi;
89  for (rvi = rv.begin(); rvi != rv.end(); ++rvi) {
90  BamCacheRecord *record = *rvi;
91  record->insert_before(this);
92  }
93 }
94 
95 ////////////////////////////////////////////////////////////////////
96 // Function: BamCacheIndex::release_records
97 // Access: Private
98 // Description: This is the inverse of process_new_records: it
99 // releases the records from the linked list, so that
100 // they may be added to another index or whatever.
101 // Calling this, of course, invalidates the index until
102 // process_new_records() is called again.
103 ////////////////////////////////////////////////////////////////////
104 void BamCacheIndex::
105 release_records() {
106  Records::const_iterator ri;
107  for (ri = _records.begin(); ri != _records.end(); ++ri) {
108  BamCacheRecord *record = (*ri).second;
109  record->_next = NULL;
110  record->_prev = NULL;
111  }
112  _next = this;
113  _prev = this;
114  _cache_size = 0;
115 }
116 
117 ////////////////////////////////////////////////////////////////////
118 // Function: BamCacheIndex::evict_old_file
119 // Access: Private
120 // Description: Evicts an old file from the cache. Records the
121 // record. Returns NULL if the cache is empty.
122 ////////////////////////////////////////////////////////////////////
123 PT(BamCacheRecord) BamCacheIndex::
124 evict_old_file() {
125  if (_next == this) {
126  // Nothing in the cache.
127  return NULL;
128  }
129 
130  // The first record in the linked list is the least-recently-used
131  // one.
132  PT(BamCacheRecord) record = (BamCacheRecord *)_next;
133  bool removed = remove_record(record->get_source_pathname());
134  nassertr(removed, NULL);
135 
136  return record;
137 }
138 
139 ////////////////////////////////////////////////////////////////////
140 // Function: BamCacheIndex::add_record
141 // Access: Private
142 // Description: Adds a newly-created BamCacheRecord into the index.
143 // If a matching record is already in the index, it is
144 // replaced with the new record. Returns true if the
145 // record was added, or false if the equivalent record
146 // was already there and the index is unchanged.
147 ////////////////////////////////////////////////////////////////////
148 bool BamCacheIndex::
149 add_record(BamCacheRecord *record) {
150  pair<Records::iterator, bool> result =
151  _records.insert(Records::value_type(record->get_source_pathname(), record));
152  if (!result.second) {
153  // We already had a record for this filename; it gets replaced.
154  BamCacheRecord *orig_record = (*result.first).second;
155  orig_record->remove_from_list();
156  if (*orig_record == *record) {
157  // Well, never mind. The record hasn't changed.
158  orig_record->insert_before(this);
159  return false;
160  }
161 
162  _cache_size -= orig_record->_record_size;
163  (*result.first).second = record;
164  }
165  record->insert_before(this);
166 
167  _cache_size += record->_record_size;
168  return true;
169 }
170 
171 ////////////////////////////////////////////////////////////////////
172 // Function: BamCacheIndex::remove_record
173 // Access: Private
174 // Description: Searches for the matching record in the index and
175 // removes it if it is found. Returns true if the
176 // record was found and removed, or false if there was
177 // no such record and the index is unchanged.
178 ////////////////////////////////////////////////////////////////////
179 bool BamCacheIndex::
180 remove_record(const Filename &source_pathname) {
181  Records::iterator ri = _records.find(source_pathname);
182  if (ri == _records.end()) {
183  // No entry for this record; no problem.
184  return false;
185  }
186 
187  BamCacheRecord *record = (*ri).second;
188  record->remove_from_list();
189  _cache_size -= record->_record_size;
190  _records.erase(ri);
191  return true;
192 }
193 
194 ////////////////////////////////////////////////////////////////////
195 // Function: BamCacheIndex::register_with_read_factory
196 // Access: Public, Static
197 // Description: Tells the BamReader how to create objects of type
198 // BamCacheRecord.
199 ////////////////////////////////////////////////////////////////////
200 void BamCacheIndex::
202  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
203 }
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: BamCacheIndex::write_datagram
207 // Access: Public, Virtual
208 // Description: Writes the contents of this object to the datagram
209 // for shipping out to a Bam file.
210 ////////////////////////////////////////////////////////////////////
211 void BamCacheIndex::
213  TypedWritable::write_datagram(manager, dg);
214 
215  dg.add_uint32(_records.size());
216  Records::const_iterator ri;
217  for (ri = _records.begin(); ri != _records.end(); ++ri) {
218  manager->write_pointer(dg, (*ri).second);
219  }
220 }
221 
222 ////////////////////////////////////////////////////////////////////
223 // Function: BamCacheIndex::make_from_bam
224 // Access: Protected, Static
225 // Description: This function is called by the BamReader's factory
226 // when a new object of type BamCacheIndex is encountered
227 // in the Bam file. It should create the BamCacheIndex
228 // and extract its information from the file.
229 ////////////////////////////////////////////////////////////////////
230 TypedWritable *BamCacheIndex::
231 make_from_bam(const FactoryParams &params) {
232  BamCacheIndex *object = new BamCacheIndex;
233  DatagramIterator scan;
234  BamReader *manager;
235 
236  parse_params(params, scan, manager);
237  object->fillin(scan, manager);
238 
239  return object;
240 }
241 
242 ////////////////////////////////////////////////////////////////////
243 // Function: BamCacheIndex::complete_pointers
244 // Access: Public, Virtual
245 // Description: Receives an array of pointers, one for each time
246 // manager->read_pointer() was called in fillin().
247 // Returns the number of pointers processed.
248 ////////////////////////////////////////////////////////////////////
249 int BamCacheIndex::
250 complete_pointers(TypedWritable **p_list, BamReader *manager) {
251  int pi = TypedWritable::complete_pointers(p_list, manager);
252 
253  RecordVector::iterator vi;
254  for (vi = _record_vector.begin(); vi != _record_vector.end(); ++vi) {
255  PT(BamCacheRecord) record = DCAST(BamCacheRecord, p_list[pi++]);
256  (*vi) = record;
257 
258  bool inserted = _records.insert(Records::value_type(record->get_source_pathname(), record)).second;
259  if (!inserted) {
260  util_cat.info()
261  << "Multiple cache files defining " << record->get_source_pathname()
262  << " in index.\n";
263  }
264  }
265 
266  _record_vector.clear();
267 
268  process_new_records();
269 
270  return pi;
271 }
272 
273 ////////////////////////////////////////////////////////////////////
274 // Function: BamCacheIndex::fillin
275 // Access: Protected
276 // Description: This internal function is called by make_from_bam to
277 // read in all of the relevant data from the BamFile for
278 // the new BamCacheIndex.
279 ////////////////////////////////////////////////////////////////////
280 void BamCacheIndex::
281 fillin(DatagramIterator &scan, BamReader *manager) {
282  TypedWritable::fillin(scan, manager);
283 
284  int num_records = scan.get_uint32();
285  _record_vector.reserve(num_records);
286  for (int i = 0; i < num_records; ++i) {
287  _record_vector.push_back(NULL);
288  manager->read_pointer(scan);
289  }
290 }
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class&#39;s make_from_bam() method to read in all...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This represents the in-memory index that records the list of files stored in the BamCache.
Definition: bamCacheIndex.h:37
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:192
static void register_with_read_factory()
Tells the BamReader how to create objects of type BamCacheRecord.
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:279
void read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:658