瀏覽代碼

StringStream

David Rose 18 年之前
父節點
當前提交
a99dcb1547

+ 2 - 0
panda/src/express/config_express.N

@@ -31,6 +31,8 @@ forcetype ConfigVariableManager
 forcetype ConfigVariableSearchPath
 forcetype ConfigVariableString
 
+forcetype ios_base
+forcetype ios
 forcetype istream
 forcetype ostream
 forcetype iostream

+ 168 - 119
panda/src/express/multifile.cxx

@@ -176,6 +176,29 @@ open_read(const Filename &multifile_name) {
   return read_index();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::open_read
+//       Access: Public
+//  Description: Opens an anonymous Multifile for reading using an
+//               istream.  There must be seek functionality via
+//               seekg() and tellg() on the istream.
+//
+//               The Multifile does *not* close or delete the istream
+//               when Multifile.close() is called.  It is the caller's
+//               responsibility to ensure that the istream pointer
+//               does not destruct during the lifetime of the
+//               Multifile.
+////////////////////////////////////////////////////////////////////
+bool Multifile::
+open_read(istream *multifile_stream) {
+  close();
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
+  _read = multifile_stream;
+  _read->seekg(0, ios::beg);
+  return read_index();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::open_write
 //       Access: Published
@@ -204,6 +227,29 @@ open_write(const Filename &multifile_name) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::open_write
+//       Access: Public
+//  Description: Opens an anonymous Multifile for writing using an
+//               ostream.  There must be seek functionality via
+//               seekp() and tellp() on the pstream.
+//
+//               The Multifile does *not* close or delete the ostream
+//               when Multifile.close() is called.  It is the caller's
+//               responsibility to ensure that the ostream pointer
+//               does not destruct during the lifetime of the
+//               Multifile.
+////////////////////////////////////////////////////////////////////
+bool Multifile::
+open_write(ostream *multifile_stream) {
+  close();
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
+  _write = multifile_stream;
+  _write->seekp(0, ios::beg);
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::open_read_write
 //       Access: Published
@@ -243,6 +289,42 @@ open_read_write(const Filename &multifile_name) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::open_read_write
+//       Access: Public
+//  Description: Opens an anonymous Multifile for reading and writing
+//               using an iostream.  There must be seek functionality
+//               via seekg()/seekp() and tellg()/tellp() on the
+//               iostream.
+//
+//               The Multifile does *not* close or delete the iostream
+//               when Multifile.close() is called.  It is the caller's
+//               responsibility to ensure that the iostream pointer
+//               does not destruct during the lifetime of the
+//               Multifile.
+////////////////////////////////////////////////////////////////////
+bool Multifile::
+open_read_write(iostream *multifile_stream) {
+  close();
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
+  _read = multifile_stream;
+  _write = multifile_stream;
+  _write->seekp(0, ios::beg);
+
+  // Check whether the read stream is empty.
+  _read->seekg(0, ios::end);
+  if (_read->tellg() == (streampos)0) {
+    // The read stream is empty, which is always valid.
+    return true;
+  }
+
+  // The read stream is not empty, so we'd better have a valid
+  // Multifile.
+  _read->seekg(0, ios::beg);
+  return read_index();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::close
 //       Access: Published
@@ -361,6 +443,42 @@ add_subfile(const string &subfile_name, const Filename &filename,
   return name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::add_subfile
+//       Access: Public
+//  Description: Adds a file from a stream as a subfile to the Multifile.
+//               The indicated istream will be read and its contents
+//               added to the Multifile at the next call to flush().
+//
+//               Note that the istream must remain untouched and
+//               unused by any other code until flush() is called.  At
+//               that time, the Multifile will read the entire
+//               contents of the istream from the current file
+//               position to the end of the file.  Subsequently, the
+//               Multifile will *not* close or delete the istream.  It
+//               is the caller's responsibility to ensure that the
+//               istream pointer does not destruct during the lifetime
+//               of the Multifile.
+//
+//               Returns the subfile name on success (it might have
+//               been modified slightly), or empty string on failure.
+////////////////////////////////////////////////////////////////////
+string Multifile::
+add_subfile(const string &subfile_name, istream *subfile_data,
+            int compression_level) {
+  nassertr(is_write_valid(), string());
+
+  string name = standardize_subfile_name(subfile_name);
+  if (!name.empty()) {
+    Subfile *subfile = new Subfile;
+    subfile->_name = name;
+    subfile->_source = subfile_data;
+    add_new_subfile(subfile, compression_level);
+  }
+
+  return name;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::update_subfile
 //       Access: Published
@@ -1028,6 +1146,34 @@ extract_subfile(int index, const Filename &filename) {
   return extract_subfile_to(index, out);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::extract_subfile_to
+//       Access: Public
+//  Description: Extracts the nth subfile to the indicated ostream.
+////////////////////////////////////////////////////////////////////
+bool Multifile::
+extract_subfile_to(int index, ostream &out) {
+  nassertr(is_read_valid(), false);
+  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
+
+  istream *in = open_read_subfile(index);
+  if (in == (istream *)NULL) {
+    return false;
+  }
+
+  int byte = in->get();
+  while (!in->fail() && !in->eof()) {
+    out.put(byte);
+    byte = in->get();
+  }
+
+  bool failed = (in->fail() && !in->eof());
+  delete in;
+  nassertr(!failed, false);
+
+  return (!out.fail());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::compare_subfile
 //       Access: Published
@@ -1117,6 +1263,7 @@ ls(ostream &out) const {
   }
 }
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::read_subfile
 //       Access: Public
@@ -1125,151 +1272,53 @@ ls(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 bool Multifile::
 read_subfile(int index, string &result) {
-  nassertr(is_read_valid(), false);
-  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
   result = string();
 
-  istream *in = open_read_subfile(index);
-  if (in == (istream *)NULL) {
+  pvector<unsigned char> pv;
+  if (!read_subfile(index, pv)) {
     return false;
   }
 
-  int byte = in->get();
-  while (!in->eof() && !in->fail()) {
-    result += (char)byte;
-    byte = in->get();
+  if (!pv.empty()) {
+    result.append((const char *)&pv[0], pv.size());
   }
-  bool failed = in->fail();
-  delete in;
-  nassertr(!failed, false);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Multifile::open_read
-//       Access: Public
-//  Description: Opens an anonymous Multifile for reading using an
-//               istream.  There must be seek functionality via
-//               seekg() and tellg() on the istream.
-//
-//               This version of open_read() does not close the
-//               istream when Multifile.close() is called.
-////////////////////////////////////////////////////////////////////
-bool Multifile::
-open_read(istream *multifile_stream) {
-  close();
-  _timestamp = time(NULL);
-  _timestamp_dirty = true;
-  _read = multifile_stream;
-  _read->seekg(0, ios::beg);
-  return read_index();
-}
 
-////////////////////////////////////////////////////////////////////
-//     Function: Multifile::open_write
-//       Access: Public
-//  Description: Opens an anonymous Multifile for writing using an
-//               ostream.  There must be seek functionality via
-//               seekp() and tellp() on the pstream.
-//
-//               This version of open_write() does not close the
-//               ostream when Multifile.close() is called.
-////////////////////////////////////////////////////////////////////
-bool Multifile::
-open_write(ostream *multifile_stream) {
-  close();
-  _timestamp = time(NULL);
-  _timestamp_dirty = true;
-  _write = multifile_stream;
-  _write->seekp(0, ios::beg);
   return true;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Multifile::open_read_write
-//       Access: Public
-//  Description: Opens an anonymous Multifile for reading and writing
-//               using an iostream.  There must be seek functionality
-//               via seekg()/seekp() and tellg()/tellp() on the
-//               iostream.
-//
-//               This version of open_read_write() does not close the
-//               iostream when Multifile.close() is called.
-////////////////////////////////////////////////////////////////////
-bool Multifile::
-open_read_write(iostream *multifile_stream) {
-  close();
-  _timestamp = time(NULL);
-  _timestamp_dirty = true;
-  _read = multifile_stream;
-  _write = multifile_stream;
-  _write->seekp(0, ios::beg);
-
-  // Check whether the read stream is empty.
-  _read->seekg(0, ios::end);
-  if (_read->tellg() == (streampos)0) {
-    // The read stream is empty, which is always valid.
-    return true;
-  }
-
-  // The read stream is not empty, so we'd better have a valid
-  // Multifile.
-  _read->seekg(0, ios::beg);
-  return read_index();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Multifile::add_subfile
-//       Access: Public
-//  Description: Adds a file from a stream as a subfile to the Multifile.
-//               The indicated istream will be read and its contents
-//               added to the Multifile at the next call to flush().
-//
-//               Returns the subfile name on success (it might have
-//               been modified slightly), or empty string on failure.
-////////////////////////////////////////////////////////////////////
-string Multifile::
-add_subfile(const string &subfile_name, istream *subfile_data,
-            int compression_level) {
-  nassertr(is_write_valid(), string());
-
-  string name = standardize_subfile_name(subfile_name);
-  if (!name.empty()) {
-    Subfile *subfile = new Subfile;
-    subfile->_name = name;
-    subfile->_source = subfile_data;
-    add_new_subfile(subfile, compression_level);
-  }
-
-  return name;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Multifile::extract_subfile_to
+//     Function: Multifile::read_subfile
 //       Access: Public
-//  Description: Extracts the nth subfile to the indicated ostream.
+//  Description: Fills a pvector with the entire contents of
+//               the indicated subfile.
 ////////////////////////////////////////////////////////////////////
 bool Multifile::
-extract_subfile_to(int index, ostream &out) {
+read_subfile(int index, pvector<unsigned char> &result) {
   nassertr(is_read_valid(), false);
   nassertr(index >= 0 && index < (int)_subfiles.size(), false);
+  result.clear();
 
   istream *in = open_read_subfile(index);
   if (in == (istream *)NULL) {
     return false;
   }
 
-  int byte = in->get();
-  while (!in->fail() && !in->eof()) {
-    out.put(byte);
-    byte = in->get();
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+  in->read(buffer, buffer_size);
+  size_t count = in->gcount();
+  while (count != 0) {
+    thread_consider_yield();
+    result.insert(result.end(), buffer, buffer + count);
+    in->read(buffer, buffer_size);
+    count = in->gcount();
   }
 
-  bool failed = (in->fail() && !in->eof());
+  bool failed = in->fail() && !in->eof();
   delete in;
   nassertr(!failed, false);
-
-  return (!out.fail());
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 7 - 9
panda/src/express/multifile.h

@@ -44,8 +44,11 @@ private:
 
 PUBLISHED:
   BLOCKING bool open_read(const Filename &multifile_name);
+  BLOCKING bool open_read(istream *multifile_stream);
   BLOCKING bool open_write(const Filename &multifile_name);
+  BLOCKING bool open_write(ostream *multifile_stream);
   BLOCKING bool open_read_write(const Filename &multifile_name);
+  BLOCKING bool open_read_write(iostream *multifile_stream);
   BLOCKING void close();
 
   INLINE const Filename &get_multifile_name() const;
@@ -69,6 +72,8 @@ PUBLISHED:
 
   string add_subfile(const string &subfile_name, const Filename &filename,
                      int compression_level);
+  string add_subfile(const string &subfile_name, istream *subfile_data,
+                     int compression_level);
   string update_subfile(const string &subfile_name, const Filename &filename,
                         int compression_level);
   BLOCKING bool flush();
@@ -93,6 +98,7 @@ PUBLISHED:
   BLOCKING INLINE string read_subfile(int index);
   BLOCKING istream *open_read_subfile(int index);
   BLOCKING bool extract_subfile(int index, const Filename &filename);
+  BLOCKING bool extract_subfile_to(int index, ostream &out);
   BLOCKING bool compare_subfile(int index, const Filename &filename);
 
   void output(ostream &out) const;
@@ -102,15 +108,7 @@ PUBLISHED:
 
 public:
   bool read_subfile(int index, string &result);
-
-  // Special interfaces to work with iostreams, not necessarily files.
-  bool open_read(istream *multifile_stream);
-  bool open_write(ostream *multifile_stream);
-  bool open_read_write(iostream *multifile_stream);
-  string add_subfile(const string &subfile_name, istream *subfile_data,
-                     int compression_level);
-
-  bool extract_subfile_to(int index, ostream &out);
+  bool read_subfile(int index, pvector<unsigned char> &result);
 
 private:
   enum SubfileFlags {

+ 17 - 0
panda/src/pgraph/loader.cxx

@@ -31,6 +31,7 @@
 #include "bamCacheRecord.h"
 #include "sceneGraphReducer.h"
 #include "renderState.h"
+#include "bamFile.h"
 
 bool Loader::_file_types_loaded = false;
 TypeHandle Loader::_type_handle;
@@ -62,6 +63,22 @@ Loader(const string &name, int num_threads) :
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Loader::load_bam_stream
+//       Access: Published
+//  Description: Attempts to read a bam file from the indicated stream
+//               and return the scene graph defined there.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) Loader::
+load_bam_stream(istream &in) {
+  BamFile bam_file;
+  if (!bam_file.open_read(in)) {
+    return NULL;
+  }
+
+  return bam_file.read_node();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Loader::output
 //       Access: Published, Virtual

+ 2 - 0
panda/src/pgraph/loader.h

@@ -84,6 +84,8 @@ PUBLISHED:
 
   INLINE void load_async(AsyncTask *request);
 
+  BLOCKING PT(PandaNode) load_bam_stream(istream &in);
+
   virtual void output(ostream &out) const;
 
 private:

+ 23 - 0
panda/src/pgraph/nodePath.cxx

@@ -5940,6 +5940,29 @@ write_bam_file(const string &filename) const {
   return okflag;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::write_bam_stream
+//       Access: Published
+//  Description: Writes the contents of this node and below out to the
+//               indicated stream.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+write_bam_stream(ostream &out) const {
+  nassertr_always(!is_empty(), false);
+
+  BamFile bam_file;
+
+  bool okflag = false;
+
+  if (bam_file.open_write(out)) {
+    if (bam_file.write_object(node())) {
+      okflag = true;
+    }
+    bam_file.close();
+  }
+  return okflag;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::find_common_ancestor
 //       Access: Private, Static

+ 1 - 0
panda/src/pgraph/nodePath.h

@@ -835,6 +835,7 @@ PUBLISHED:
   INLINE string get_name() const;
 
   BLOCKING bool write_bam_file(const string &filename) const;
+  BLOCKING bool write_bam_stream(ostream &out) const;
 
 private:
   static NodePathComponent *

+ 11 - 11
panda/src/pnmimage/pnmImage.h

@@ -97,17 +97,17 @@ PUBLISHED:
   INLINE int get_read_x_size() const;
   INLINE int get_read_y_size() const;
 
-  bool read(const Filename &filename, PNMFileType *type = NULL,
-            bool report_unknown_type = true);
-  bool read(istream &data, const string &filename = string(), 
-            PNMFileType *type = NULL,
-            bool report_unknown_type = true);
-  bool read(PNMReader *reader);
-
-  bool write(const Filename &filename, PNMFileType *type = NULL) const;
-  bool write(ostream &data, const string &filename = string(),
-             PNMFileType *type = NULL) const;
-  bool write(PNMWriter *writer) const;
+  BLOCKING bool read(const Filename &filename, PNMFileType *type = NULL,
+                     bool report_unknown_type = true);
+  BLOCKING bool read(istream &data, const string &filename = string(), 
+                     PNMFileType *type = NULL,
+                     bool report_unknown_type = true);
+  BLOCKING bool read(PNMReader *reader);
+
+  BLOCKING bool write(const Filename &filename, PNMFileType *type = NULL) const;
+  BLOCKING bool write(ostream &data, const string &filename = string(),
+                      PNMFileType *type = NULL) const;
+  BLOCKING bool write(PNMWriter *writer) const;
 
   INLINE bool is_valid() const;
 

+ 29 - 0
panda/src/pnmimage/pnmImageHeader.cxx

@@ -47,6 +47,35 @@ read_header(const Filename &filename, PNMFileType *type,
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImageHeader::read_header
+//       Access: Published
+//  Description: Reads the image header information only from the
+//               indicated stream.
+//
+//               The filename is advisory only, and may be used
+//               to suggest a type if it has a known extension.
+//
+//               If type is non-NULL, it is a suggestion for the type
+//               of file it is (and a non-NULL type will override any
+//               magic number test or filename extension lookup).
+//
+//               Returns true if successful, false on error.
+////////////////////////////////////////////////////////////////////
+bool PNMImageHeader::
+read_header(istream &data, const string &filename, PNMFileType *type,
+            bool report_unknown_type) {
+  PNMReader *reader = PNMImageHeader::make_reader
+    (&data, false, filename, string(), type, report_unknown_type);
+  if (reader != (PNMReader *)NULL) {
+    (*this) = (*reader);
+    delete reader;
+    return true;
+  }
+
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImageHeader::make_reader
 //       Access: Published

+ 4 - 2
panda/src/pnmimage/pnmImageHeader.h

@@ -82,8 +82,10 @@ PUBLISHED:
   INLINE PNMFileType *get_type() const;
   INLINE void set_type(PNMFileType *type);
 
-  bool read_header(const Filename &filename, PNMFileType *type = NULL,
-                   bool report_unknown_type = true);
+  BLOCKING bool read_header(const Filename &filename, PNMFileType *type = NULL,
+                            bool report_unknown_type = true);
+  BLOCKING bool read_header(istream &data, const string &filename = string(),
+                            PNMFileType *type = NULL, bool report_unknown_type = true);
 
   PNMReader *make_reader(const Filename &filename,
                          PNMFileType *type = NULL,

+ 10 - 3
panda/src/putil/Sources.pp

@@ -59,6 +59,8 @@
     spamDeletor.h \
     sparseArray.I sparseArray.h \
     string_utils.I string_utils.N string_utils.h \
+    stringStreamBuf.I stringStreamBuf.h \
+    stringStream.I stringStream.h \
     timedCycle.I timedCycle.h typedWritable.I \
     typedWritable.h typedWritableReferenceCount.I \
     typedWritableReferenceCount.h updateSeq.I updateSeq.h \
@@ -104,7 +106,10 @@
     pta_int.cxx pta_ushort.cxx \
     spamDeletor.cxx \
     sparseArray.cxx \
-    string_utils.cxx timedCycle.cxx typedWritable.cxx \
+    string_utils.cxx \
+    stringStreamBuf.cxx \
+    stringStream.cxx \
+    timedCycle.cxx typedWritable.cxx \
     typedWritableReferenceCount.cxx updateSeq.cxx \
     uniqueIdAllocator.cxx \
     vector_double.cxx vector_float.cxx \
@@ -161,8 +166,10 @@
     pta_float.h pta_int.h pta_ushort.h \
     spamDeletor.h \
     sparseArray.I sparseArray.h \
-    string_utils.I \
-    string_utils.h timedCycle.I timedCycle.h typedWritable.I \
+    string_utils.I string_utils.h \
+    stringStreamBuf.I stringStreamBuf.h \
+    stringStream.I stringStream.h \
+    timedCycle.I timedCycle.h typedWritable.I \
     typedWritable.h typedWritableReferenceCount.I \
     typedWritableReferenceCount.h updateSeq.I updateSeq.h \
     uniqueIdAllocator.h \

+ 2 - 0
panda/src/putil/putil_composite2.cxx

@@ -16,6 +16,8 @@
 #include "spamDeletor.cxx"
 #include "sparseArray.cxx"
 #include "string_utils.cxx"
+#include "stringStreamBuf.cxx"
+#include "stringStream.cxx"
 #include "timedCycle.cxx"
 #include "typedWritable.cxx"
 #include "typedWritableReferenceCount.cxx"

+ 107 - 0
panda/src/putil/stringStream.I

@@ -0,0 +1,107 @@
+// Filename: stringStream.I
+// Created by:  drose (03Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE StringStream::
+StringStream() : iostream(&_buf) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::Constructor
+//       Access: Published
+//  Description: This version of the constructor preloads the buffer
+//               with the indicated data.
+////////////////////////////////////////////////////////////////////
+INLINE StringStream::
+StringStream(const string &source) : iostream(&_buf) {
+  write(source);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::clear_data
+//       Access: Published
+//  Description: Empties the buffer.
+////////////////////////////////////////////////////////////////////
+INLINE void StringStream::
+clear_data() {
+  _buf.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::get_data_size
+//       Access: Published
+//  Description: Returns the number of characters available to be read
+//               from the data stream.
+////////////////////////////////////////////////////////////////////
+INLINE size_t StringStream::
+get_data_size() {
+  flush();
+  return _buf.get_data_size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::read
+//       Access: Published
+//  Description: Extracts all the remaining characters from the data
+//               stream and stores them in the indicated string.
+////////////////////////////////////////////////////////////////////
+INLINE void StringStream::
+read(string &data) {
+  read(data, get_data_size());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::read
+//       Access: Published
+//  Description: Extracts up to max_length characters from the data
+//               stream and returns them as a string.
+////////////////////////////////////////////////////////////////////
+INLINE string StringStream::
+read(size_t max_length) {
+  string data;
+  read(data, max_length);
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::read
+//       Access: Published
+//  Description: Extracts all the remaining characters from the data
+//               stream and returns them as a string.
+////////////////////////////////////////////////////////////////////
+INLINE string StringStream::
+read() {
+  string data;
+  read(data);
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::write
+//       Access: Published
+//  Description: Appends the indicated data to the data stream.
+////////////////////////////////////////////////////////////////////
+INLINE void StringStream::
+write(const string &data) {
+  _buf.write_chars(data.data(), data.size());
+}

+ 34 - 0
panda/src/putil/stringStream.cxx

@@ -0,0 +1,34 @@
+// Filename: stringStream.cxx
+// Created by:  drose (03Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "stringStream.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStream::read
+//       Access: Published
+//  Description: Extracts up to max_length characters from the data
+//               stream and stores them in the indicated string.
+////////////////////////////////////////////////////////////////////
+void StringStream::
+read(string &data, size_t max_length) {
+  flush();
+  char *buffer = (char *)PANDA_MALLOC_ARRAY(max_length);
+  size_t length = _buf.read_chars(buffer, max_length);
+  data.append(buffer, length);
+  PANDA_FREE_ARRAY(buffer);
+}

+ 53 - 0
panda/src/putil/stringStream.h

@@ -0,0 +1,53 @@
+// Filename: stringStream.h
+// Created by:  drose (03Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef STRINGSTREAM_H
+#define STRINGSTREAM_H
+
+#include "pandabase.h"
+#include "stringStreamBuf.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : StringStream
+// Description : A bi-directional stream object that reads and writes
+//               data to an internal buffer, which can be appended to
+//               or read from as a string.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS StringStream : public iostream {
+PUBLISHED:
+  INLINE StringStream();
+  INLINE StringStream(const string &source);
+
+  INLINE void clear_data();
+  INLINE size_t get_data_size();
+
+  void read(string &data, size_t max_length);
+  INLINE void read(string &data);
+  INLINE string read(size_t max_length);
+  INLINE string read();
+
+  INLINE void write(const string &data);
+
+private:
+  StringStreamBuf _buf;
+};
+
+#include "stringStream.I"
+
+#endif
+

+ 30 - 0
panda/src/putil/stringStreamBuf.I

@@ -0,0 +1,30 @@
+// Filename: stringStreamBuf.I
+// Created by:  drose (03Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::get_data_size
+//       Access: Public
+//  Description: Returns the number of characters remaining in the
+//               internal data buffer, not counting data which might
+//               still be in the iostream buffer.
+////////////////////////////////////////////////////////////////////
+INLINE size_t StringStreamBuf::
+get_data_size() const {
+  return _data.size() - _gpos;
+}

+ 296 - 0
panda/src/putil/stringStreamBuf.cxx

@@ -0,0 +1,296 @@
+// Filename: stringStreamBuf.cxx
+// Created by:  drose (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "stringStreamBuf.h"
+#include "pnotify.h"
+#include "config_express.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+StringStreamBuf::
+StringStreamBuf() {
+#ifdef HAVE_IOSTREAM
+  _buffer = (char *)PANDA_MALLOC_ARRAY(2048);
+  char *ebuf = _buffer + 2048;
+  char *mbuf = _buffer + 1024;
+  setg(_buffer, mbuf, mbuf);
+  setp(mbuf, ebuf);
+
+#else
+  allocate();
+  // Chop the buffer in half.  The bottom half goes to the get buffer;
+  // the top half goes to the put buffer.
+  char *b = base();
+  char *t = ebuf();
+  char *m = b + (t - b) / 2;
+  setg(b, m, m);
+  setp(b, m);
+#endif
+
+  _gpos = 0;
+  _ppos = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+StringStreamBuf::
+~StringStreamBuf() {
+#ifdef HAVE_IOSTREAM
+  PANDA_FREE_ARRAY(_buffer);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::clear
+//       Access: Public
+//  Description: Empties the buffer.
+////////////////////////////////////////////////////////////////////
+void StringStreamBuf::
+clear() {
+  _data.clear();
+  _gpos = 0;
+  _ppos = 0;
+
+  pbump(pbase() - pptr());
+  gbump(egptr() - gptr());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::read_chars
+//       Access: Public
+//  Description: Attempts to extract the indicated number of
+//               characters from the current file position.  Returns
+//               the number of characters extracted.
+////////////////////////////////////////////////////////////////////
+size_t StringStreamBuf::
+read_chars(char *start, size_t length) {
+  if (length == 0) {
+    return 0;
+  }
+
+  // Make sure the write buffer is flushed.
+  sync();
+
+  if (_data.size() <= _gpos) {
+    return 0;
+  }
+
+  length = min(length, _data.size() - _gpos);
+  memcpy(start, &_data[_gpos], length);
+  _gpos += length;
+  return length;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::write_chars
+//       Access: Public
+//  Description: Appends the indicated stream of characters to the
+//               current file position.
+////////////////////////////////////////////////////////////////////
+void StringStreamBuf::
+write_chars(const char *start, size_t length) {
+  if (length != 0) {
+    // Make sure the read buffer is flushed.
+    size_t n = egptr() - gptr();
+    gbump(n);
+    _gpos -= n;
+
+    if (_data.size() > _ppos) {
+      // We are overwriting some data.
+      size_t remaining_length = _data.size() - _ppos;
+      size_t overwrite_length = min(remaining_length, length);
+      memcpy(&_data[_ppos], start, overwrite_length);
+      length -= overwrite_length;
+      _ppos += overwrite_length;
+      start += overwrite_length;
+    }
+     
+    if (length != 0) {
+      // We are appending some data.
+      _data.insert(_data.begin() + _ppos, start, start + length);
+      _ppos += length;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::seekoff
+//       Access: Public, Virtual
+//  Description: Implements seeking within the stream.
+////////////////////////////////////////////////////////////////////
+streampos StringStreamBuf::
+seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
+  streampos result = -1;
+
+  // Sync the iostream buffer first.
+  sync();
+
+  if (which & ios::in) {
+    // Determine the current file position.
+    size_t n = egptr() - gptr();
+    gbump(n);
+    _gpos -= n;
+    size_t cur_pos = _gpos;
+    size_t new_pos = cur_pos;
+    
+    // Now adjust the data pointer appropriately.
+    switch (dir) {
+    case ios::beg:
+      new_pos = (size_t)off;
+      break;
+      
+    case ios::cur:
+      new_pos = (size_t)((int)cur_pos + off);
+      break;
+      
+    case ios::end:
+      new_pos = (size_t)((int)_data.size() + off);
+      break;
+    }
+
+    _gpos = new_pos;
+    result = new_pos;
+  }
+
+  if (which & ios::out) {
+    // Determine the current file position.
+    size_t n = pptr() - pbase();
+    size_t cur_pos = _ppos + n;
+    size_t new_pos = cur_pos;
+    
+    // Now adjust the data pointer appropriately.
+    switch (dir) {
+    case ios::beg:
+      new_pos = (size_t)off;
+      break;
+      
+    case ios::cur:
+      new_pos = (size_t)((int)cur_pos + off);
+      break;
+      
+    case ios::end:
+      new_pos = (size_t)((int)_data.size() + off);
+      break;
+    }
+
+    _ppos = new_pos;
+    result = new_pos;
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::seekpos
+//       Access: Public, Virtual
+//  Description: A variant on seekoff() to implement seeking within a
+//               stream.
+//
+//               The MSDN Library claims that it is only necessary to
+//               redefine seekoff(), and not seekpos() as well, as the
+//               default implementation of seekpos() is supposed to
+//               map to seekoff() exactly as I am doing here; but in
+//               fact it must do something else, because seeking
+//               didn't work on Windows until I redefined this
+//               function as well.
+////////////////////////////////////////////////////////////////////
+streampos StringStreamBuf::
+seekpos(streampos pos, ios_openmode which) {
+  return seekoff(pos, ios::beg, which);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::overflow
+//       Access: Protected, Virtual
+//  Description: Called by the system ostream implementation when its
+//               internal buffer is filled, plus one character.
+////////////////////////////////////////////////////////////////////
+int StringStreamBuf::
+overflow(int ch) {
+  size_t n = pptr() - pbase();
+  if (n != 0) {
+    write_chars(pbase(), n);
+    pbump(-(int)n);
+  }
+
+  if (ch != EOF) {
+    // Write one more character.
+    char c = ch;
+    write_chars(&c, 1);
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::sync
+//       Access: Protected, Virtual
+//  Description: Called by the system iostream implementation to
+//               implement a flush operation.
+////////////////////////////////////////////////////////////////////
+int StringStreamBuf::
+sync() {
+  size_t n = pptr() - pbase();
+
+  write_chars(pbase(), n);
+  pbump(-(int)n);
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: StringStreamBuf::underflow
+//       Access: Protected, Virtual
+//  Description: Called by the system istream implementation when its
+//               internal buffer needs more characters.
+////////////////////////////////////////////////////////////////////
+int StringStreamBuf::
+underflow() {
+  // Sometimes underflow() is called even if the buffer is not empty.
+  if (gptr() >= egptr()) {
+    // Mark the buffer filled (with buffer_size bytes).
+    size_t buffer_size = egptr() - eback();
+    gbump(-(int)buffer_size);
+
+    size_t num_bytes = buffer_size;
+    size_t read_count = read_chars(gptr(), buffer_size);
+
+    if (read_count != num_bytes) {
+      // Oops, we didn't read what we thought we would.
+      if (read_count == 0) {
+        gbump(num_bytes);
+        return EOF;
+      }
+
+      // Slide what we did read to the top of the buffer.
+      nassertr(read_count < num_bytes, EOF);
+      size_t delta = num_bytes - read_count;
+      memmove(gptr() + delta, gptr(), read_count);
+      gbump(delta);
+    }
+  }
+
+  return (unsigned char)*gptr();
+}
+

+ 60 - 0
panda/src/putil/stringStreamBuf.h

@@ -0,0 +1,60 @@
+// Filename: stringStreamBuf.h
+// Created by:  drose (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef STRINGSTREAMBUF_H
+#define STRINGSTREAMBUF_H
+
+#include "pandabase.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : StringStreamBuf
+// Description : Used by StringStream to implement an stream that
+//               reads from and/or writes to a memory buffer, whose
+//               contents can be appended to or extracted at any time
+//               by application code.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA StringStreamBuf : public streambuf {
+public:
+  StringStreamBuf();
+  virtual ~StringStreamBuf();
+
+  void clear();
+
+  INLINE size_t get_data_size() const;
+  size_t read_chars(char *start, size_t length);
+  void write_chars(const char *start, size_t length);
+
+protected:
+  virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode which);
+  virtual streampos seekpos(streampos pos, ios_openmode which);
+
+  virtual int overflow(int c);
+  virtual int sync();
+  virtual int underflow();
+
+private:
+  pvector<char> _data;
+  char *_buffer;
+  size_t _ppos;
+  size_t _gpos;
+};
+
+#include "stringStreamBuf.I"
+
+#endif