Panda3D
|
00001 // Filename: typedWritable.cxx 00002 // Created by: jason (08Jun00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "typedWritable.h" 00016 #include "bamWriter.h" 00017 #include "bamReader.h" 00018 #include "datagramOutputFile.h" 00019 #include "datagramInputFile.h" 00020 #include "lightMutexHolder.h" 00021 #include "bam.h" 00022 00023 LightMutex TypedWritable::_bam_writers_lock; 00024 00025 TypeHandle TypedWritable::_type_handle; 00026 TypedWritable* const TypedWritable::Null = (TypedWritable*)0L; 00027 00028 #ifdef HAVE_PYTHON 00029 #include "py_panda.h" 00030 #ifndef CPPPARSER 00031 extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter; 00032 #endif // CPPPARSER 00033 #endif // HAVE_PYTHON 00034 00035 //////////////////////////////////////////////////////////////////// 00036 // Function: TypedWritable::Destructor 00037 // Access: Public, Virtual 00038 // Description: 00039 //////////////////////////////////////////////////////////////////// 00040 TypedWritable:: 00041 ~TypedWritable() { 00042 // Remove the object pointer from the BamWriters that reference it. 00043 if (_bam_writers != (BamWriters *)NULL) { 00044 BamWriters temp; 00045 { 00046 LightMutexHolder holder(_bam_writers_lock); 00047 _bam_writers->swap(temp); 00048 delete _bam_writers; 00049 _bam_writers = NULL; 00050 } 00051 BamWriters::iterator wi; 00052 for (wi = temp.begin(); wi != temp.end(); ++wi) { 00053 BamWriter *writer = (*wi); 00054 writer->object_destructs(this); 00055 } 00056 } 00057 } 00058 00059 //////////////////////////////////////////////////////////////////// 00060 // Function: TypedWritable::write_datagram 00061 // Access: Public, Virtual 00062 // Description: Writes the contents of this object to the datagram 00063 // for shipping out to a Bam file. 00064 //////////////////////////////////////////////////////////////////// 00065 void TypedWritable:: 00066 write_datagram(BamWriter *, Datagram &) { 00067 } 00068 00069 //////////////////////////////////////////////////////////////////// 00070 // Function: TypedWritable::update_bam_nested 00071 // Access: Public, Virtual 00072 // Description: Called by the BamWriter when this object has not 00073 // itself been modified recently, but it should check 00074 // its nested objects for updates. 00075 //////////////////////////////////////////////////////////////////// 00076 void TypedWritable:: 00077 update_bam_nested(BamWriter *) { 00078 } 00079 00080 //////////////////////////////////////////////////////////////////// 00081 // Function: TypedWritable::complete_pointers 00082 // Access: Public, Virtual 00083 // Description: Receives an array of pointers, one for each time 00084 // manager->read_pointer() was called in fillin(). 00085 // Returns the number of pointers processed. 00086 // 00087 // This is the callback function that is made by the 00088 // BamReader at some later point, after all of the 00089 // required pointers have been filled in. It is 00090 // necessary because there might be forward references 00091 // in a bam file; when we call read_pointer() in 00092 // fillin(), the object may not have been read from the 00093 // file yet, so we do not have a pointer available at 00094 // that time. Thus, instead of returning a pointer, 00095 // read_pointer() simply reserves a later callback. 00096 // This function provides that callback. The calling 00097 // object is responsible for keeping track of the number 00098 // of times it called read_pointer() and extracting the 00099 // same number of pointers out of the supplied vector, 00100 // and storing them appropriately within the object. 00101 //////////////////////////////////////////////////////////////////// 00102 int TypedWritable:: 00103 complete_pointers(TypedWritable **, BamReader *) { 00104 return 0; 00105 } 00106 00107 //////////////////////////////////////////////////////////////////// 00108 // Function: TypedWritable::require_fully_complete 00109 // Access: Public, Virtual 00110 // Description: Some objects require all of their nested pointers to 00111 // have been completed before the objects themselves can 00112 // be completed. If this is the case, override this 00113 // method to return true, and be careful with circular 00114 // references (which would make the object unreadable 00115 // from a bam file). 00116 //////////////////////////////////////////////////////////////////// 00117 bool TypedWritable:: 00118 require_fully_complete() const { 00119 return false; 00120 } 00121 00122 //////////////////////////////////////////////////////////////////// 00123 // Function: TypedWritable::fillin 00124 // Access: Public, Virtual 00125 // Description: This internal function is intended to be called by 00126 // each class's make_from_bam() method to read in all of 00127 // the relevant data from the BamFile for the new 00128 // object. It is also called directly by the BamReader 00129 // to re-read the data for an object that has been 00130 // placed on the stream for an update. 00131 //////////////////////////////////////////////////////////////////// 00132 void TypedWritable:: 00133 fillin(DatagramIterator &, BamReader *) { 00134 } 00135 00136 00137 //////////////////////////////////////////////////////////////////// 00138 // Function: TypedWritable::finalize 00139 // Access: Public, Virtual 00140 // Description: Called by the BamReader to perform any final actions 00141 // needed for setting up the object after all objects 00142 // have been read and all pointers have been completed. 00143 //////////////////////////////////////////////////////////////////// 00144 void TypedWritable:: 00145 finalize(BamReader *) { 00146 } 00147 00148 //////////////////////////////////////////////////////////////////// 00149 // Function: TypedWritable::as_reference_count 00150 // Access: Public, Virtual 00151 // Description: Returns the pointer cast to a ReferenceCount pointer, 00152 // if it is in fact of that type. 00153 //////////////////////////////////////////////////////////////////// 00154 ReferenceCount *TypedWritable:: 00155 as_reference_count() { 00156 return NULL; 00157 } 00158 00159 #ifdef HAVE_PYTHON 00160 //////////////////////////////////////////////////////////////////// 00161 // Function: TypedWritable::__reduce__ 00162 // Access: Published 00163 // Description: This special Python method is implement to provide 00164 // support for the pickle module. 00165 // 00166 // This hooks into the native pickle and cPickle 00167 // modules, but it cannot properly handle 00168 // self-referential BAM objects. 00169 //////////////////////////////////////////////////////////////////// 00170 PyObject *TypedWritable:: 00171 __reduce__(PyObject *self) const { 00172 return __reduce_persist__(self, NULL); 00173 } 00174 #endif // HAVE_PYTHON 00175 00176 #ifdef HAVE_PYTHON 00177 //////////////////////////////////////////////////////////////////// 00178 // Function: TypedWritable::__reduce_persist__ 00179 // Access: Published 00180 // Description: This special Python method is implement to provide 00181 // support for the pickle module. 00182 // 00183 // This is similar to __reduce__, but it provides 00184 // additional support for the missing persistent-state 00185 // object needed to properly support self-referential 00186 // BAM objects written to the pickle stream. This hooks 00187 // into the pickle and cPickle modules implemented in 00188 // direct/src/stdpy. 00189 //////////////////////////////////////////////////////////////////// 00190 PyObject *TypedWritable:: 00191 __reduce_persist__(PyObject *self, PyObject *pickler) const { 00192 // We should return at least a 2-tuple, (Class, (args)): the 00193 // necessary class object whose constructor we should call 00194 // (e.g. this), and the arguments necessary to reconstruct this 00195 // object. 00196 00197 // Check that we have a decode_from_bam_stream python method. If not, 00198 // we can't use this interface. 00199 PyObject *method = PyObject_GetAttrString(self, "decode_from_bam_stream"); 00200 if (method == NULL) { 00201 ostringstream stream; 00202 stream << "Cannot pickle objects of type " << get_type() << "\n"; 00203 string message = stream.str(); 00204 PyErr_SetString(PyExc_TypeError, message.c_str()); 00205 return NULL; 00206 } 00207 Py_DECREF(method); 00208 00209 BamWriter *writer = NULL; 00210 if (pickler != NULL) { 00211 PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter"); 00212 if (py_writer == NULL) { 00213 // It's OK if there's no bamWriter. 00214 PyErr_Clear(); 00215 } else { 00216 DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); 00217 Py_DECREF(py_writer); 00218 } 00219 } 00220 00221 // First, streamify the object, if possible. 00222 string bam_stream; 00223 if (!encode_to_bam_stream(bam_stream, writer)) { 00224 ostringstream stream; 00225 stream << "Could not bamify object of type " << get_type() << "\n"; 00226 string message = stream.str(); 00227 PyErr_SetString(PyExc_TypeError, message.c_str()); 00228 return NULL; 00229 } 00230 00231 // Start by getting this class object. 00232 PyObject *this_class = PyObject_Type(self); 00233 if (this_class == NULL) { 00234 return NULL; 00235 } 00236 00237 PyObject *func; 00238 if (writer != NULL) { 00239 // The modified pickle support: call the "persistent" version of 00240 // this function, which receives the unpickler itself as an 00241 // additional parameter. 00242 func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream_persist"); 00243 if (func == NULL) { 00244 PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream_persist()"); 00245 Py_DECREF(this_class); 00246 return NULL; 00247 } 00248 00249 } else { 00250 // The traditional pickle support: call the non-persistent version 00251 // of this function. 00252 00253 func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream"); 00254 if (func == NULL) { 00255 PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream()"); 00256 Py_DECREF(this_class); 00257 return NULL; 00258 } 00259 } 00260 00261 PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size()); 00262 Py_DECREF(func); 00263 Py_DECREF(this_class); 00264 return result; 00265 } 00266 #endif // HAVE_PYTHON 00267 00268 //////////////////////////////////////////////////////////////////// 00269 // Function: TypedWritable::encode_to_bam_stream 00270 // Access: Published 00271 // Description: Converts the TypedWritable object into a single 00272 // stream of data using a BamWriter, and stores that 00273 // data in the indicated string. Returns true on 00274 // success, false on failure. 00275 // 00276 // This is a convenience method particularly useful for 00277 // cases when you are only serializing a single object. 00278 // If you have many objects to process, it is more 00279 // efficient to use the same BamWriter to serialize all 00280 // of them together. 00281 //////////////////////////////////////////////////////////////////// 00282 bool TypedWritable:: 00283 encode_to_bam_stream(string &data, BamWriter *writer) const { 00284 data.clear(); 00285 ostringstream stream; 00286 00287 // We use nested scoping to ensure the destructors get called in the 00288 // right order. 00289 { 00290 DatagramOutputFile dout; 00291 if (!dout.open(stream)) { 00292 return false; 00293 } 00294 00295 if (writer == NULL) { 00296 // Create our own writer. 00297 00298 if (!dout.write_header(_bam_header)) { 00299 return false; 00300 } 00301 00302 BamWriter writer(&dout); 00303 if (!writer.init()) { 00304 return false; 00305 } 00306 00307 if (!writer.write_object(this)) { 00308 return false; 00309 } 00310 } else { 00311 // Use the existing writer. 00312 writer->set_target(&dout); 00313 bool result = writer->write_object(this); 00314 writer->set_target(NULL); 00315 if (!result) { 00316 return false; 00317 } 00318 } 00319 } 00320 00321 data = stream.str(); 00322 return true; 00323 } 00324 00325 //////////////////////////////////////////////////////////////////// 00326 // Function: TypedWritable::decode_raw_from_bam_stream 00327 // Access: Published, Static 00328 // Description: Reads the string created by a previous call to 00329 // encode_to_bam_stream(), and extracts the single 00330 // object on that string. Returns true on success, 00331 // false on on error. 00332 // 00333 // This variant sets the TypedWritable and 00334 // ReferenceCount pointers separately; both are pointers 00335 // to the same object. The reference count is not 00336 // incremented; it is the caller's responsibility to 00337 // manage the reference count. 00338 // 00339 // Note that this method cannot be used to retrieve 00340 // objects that do not inherit from ReferenceCount, 00341 // because these objects cannot persist beyond the 00342 // lifetime of the BamReader that reads them. To 00343 // retrieve these objects from a bam stream, you must 00344 // construct a BamReader directly. 00345 // 00346 // If you happen to know that the particular object in 00347 // question inherits from TypedWritableReferenceCount or 00348 // PandaNode, consider calling the variant of 00349 // decode_from_bam_stream() defined for those methods, 00350 // which presents a simpler interface. 00351 //////////////////////////////////////////////////////////////////// 00352 bool TypedWritable:: 00353 decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, 00354 const string &data, BamReader *reader) { 00355 istringstream stream(data); 00356 00357 DatagramInputFile din; 00358 if (!din.open(stream)) { 00359 return false; 00360 } 00361 00362 if (reader == NULL) { 00363 // Create a local reader. 00364 00365 string head; 00366 if (!din.read_header(head, _bam_header.size())) { 00367 return false; 00368 } 00369 00370 if (head != _bam_header) { 00371 return false; 00372 } 00373 00374 BamReader reader(&din); 00375 if (!reader.init()) { 00376 return false; 00377 } 00378 00379 if (!reader.read_object(ptr, ref_ptr)) { 00380 return false; 00381 } 00382 00383 if (!reader.resolve()) { 00384 return false; 00385 } 00386 00387 if (ref_ptr == NULL) { 00388 // Can't support non-reference-counted objects. 00389 return false; 00390 } 00391 00392 // Protect the pointer from accidental deletion when the BamReader 00393 // goes away. 00394 ref_ptr->ref(); 00395 00396 } else { 00397 // Use the existing reader. 00398 reader->set_source(&din); 00399 if (!reader->read_object(ptr, ref_ptr)) { 00400 reader->set_source(NULL); 00401 return false; 00402 } 00403 00404 if (!reader->resolve()) { 00405 reader->set_source(NULL); 00406 return false; 00407 } 00408 00409 if (ref_ptr == NULL) { 00410 // Can't support non-reference-counted objects. 00411 reader->set_source(NULL); 00412 return false; 00413 } 00414 00415 // This BamReader isn't going away, but we have to balance the 00416 // unref() below. 00417 ref_ptr->ref(); 00418 reader->set_source(NULL); 00419 } 00420 00421 00422 // Now decrement the ref count, without deleting the object. This 00423 // may reduce the reference count to zero, but that's OK--we trust 00424 // the caller to manage the reference count from this point on. 00425 ref_ptr->unref(); 00426 return true; 00427 } 00428 00429 #ifdef HAVE_PYTHON 00430 //////////////////////////////////////////////////////////////////// 00431 // Function: TypedWritable::find_global_decode 00432 // Access: Public, Static 00433 // Description: This is a support function for __reduce__(). It 00434 // searches for the global function 00435 // py_decode_TypedWritable_from_bam_stream() in this 00436 // class's module, or in the module for any base class. 00437 // (It's really looking for the libpanda module, but we 00438 // can't be sure what name that module was loaded under, 00439 // so we search upwards this way.) 00440 // 00441 // Returns: new reference on success, or NULL on failure. 00442 //////////////////////////////////////////////////////////////////// 00443 PyObject *TypedWritable:: 00444 find_global_decode(PyObject *this_class, const char *func_name) { 00445 PyObject *module_name = PyObject_GetAttrString(this_class, "__module__"); 00446 if (module_name != NULL) { 00447 // borrowed reference 00448 PyObject *sys_modules = PyImport_GetModuleDict(); 00449 if (sys_modules != NULL) { 00450 // borrowed reference 00451 PyObject *module = PyDict_GetItem(sys_modules, module_name); 00452 if (module != NULL){ 00453 PyObject *func = PyObject_GetAttrString(module, (char *)func_name); 00454 if (func != NULL) { 00455 Py_DECREF(module_name); 00456 return func; 00457 } 00458 } 00459 } 00460 } 00461 Py_DECREF(module_name); 00462 00463 PyObject *bases = PyObject_GetAttrString(this_class, "__bases__"); 00464 if (bases != NULL) { 00465 if (PySequence_Check(bases)) { 00466 Py_ssize_t size = PySequence_Size(bases); 00467 for (Py_ssize_t i = 0; i < size; ++i) { 00468 PyObject *base = PySequence_GetItem(bases, i); 00469 if (base != NULL) { 00470 PyObject *func = find_global_decode(base, func_name); 00471 Py_DECREF(base); 00472 if (func != NULL) { 00473 Py_DECREF(bases); 00474 return func; 00475 } 00476 } 00477 } 00478 } 00479 Py_DECREF(bases); 00480 } 00481 00482 return NULL; 00483 } 00484 #endif // HAVE_PYTHON 00485 00486 #ifdef HAVE_PYTHON 00487 //////////////////////////////////////////////////////////////////// 00488 // Function: py_decode_TypedWritable_from_bam_stream 00489 // Access: Published 00490 // Description: This wrapper is defined as a global function to suit 00491 // pickle's needs. 00492 // 00493 // This hooks into the native pickle and cPickle 00494 // modules, but it cannot properly handle 00495 // self-referential BAM objects. 00496 //////////////////////////////////////////////////////////////////// 00497 PyObject * 00498 py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) { 00499 return py_decode_TypedWritable_from_bam_stream_persist(NULL, this_class, data); 00500 } 00501 #endif // HAVE_PYTHON 00502 00503 00504 #ifdef HAVE_PYTHON 00505 //////////////////////////////////////////////////////////////////// 00506 // Function: py_decode_TypedWritable_from_bam_stream_persist 00507 // Access: Published 00508 // Description: This wrapper is defined as a global function to suit 00509 // pickle's needs. 00510 // 00511 // This is similar to 00512 // py_decode_TypedWritable_from_bam_stream, but it 00513 // provides additional support for the missing 00514 // persistent-state object needed to properly support 00515 // self-referential BAM objects written to the pickle 00516 // stream. This hooks into the pickle and cPickle 00517 // modules implemented in direct/src/stdpy. 00518 //////////////////////////////////////////////////////////////////// 00519 PyObject * 00520 py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const string &data) { 00521 00522 PyObject *py_reader = NULL; 00523 if (pickler != NULL) { 00524 py_reader = PyObject_GetAttrString(pickler, "bamReader"); 00525 if (py_reader == NULL) { 00526 // It's OK if there's no bamReader. 00527 PyErr_Clear(); 00528 } 00529 } 00530 00531 // We need the function PandaNode::decode_from_bam_stream or 00532 // TypedWritableReferenceCount::decode_from_bam_stream, which 00533 // invokes the BamReader to reconstruct this object. Since we use 00534 // the specific object's class as the pointer, we get the particular 00535 // instance of decode_from_bam_stream appropriate to this class. 00536 00537 PyObject *func = PyObject_GetAttrString(this_class, "decode_from_bam_stream"); 00538 if (func == NULL) { 00539 return NULL; 00540 } 00541 00542 PyObject *result; 00543 if (py_reader != NULL){ 00544 result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), data.size(), py_reader); 00545 Py_DECREF(py_reader); 00546 } else { 00547 result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size()); 00548 } 00549 00550 if (result == NULL) { 00551 return NULL; 00552 } 00553 00554 if (result == Py_None) { 00555 Py_DECREF(result); 00556 PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream"); 00557 return NULL; 00558 } 00559 00560 return result; 00561 } 00562 #endif // HAVE_PYTHON 00563