mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-18 12:43:44 -04:00
StringStream
This commit is contained in:
parent
3d63d36cb7
commit
a99dcb1547
@ -31,6 +31,8 @@ forcetype ConfigVariableManager
|
||||
forcetype ConfigVariableSearchPath
|
||||
forcetype ConfigVariableString
|
||||
|
||||
forcetype ios_base
|
||||
forcetype ios
|
||||
forcetype istream
|
||||
forcetype ostream
|
||||
forcetype iostream
|
||||
|
@ -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
|
||||
// Function: Multifile::read_subfile
|
||||
// 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.
|
||||
// Description: Fills a pvector with the entire contents of
|
||||
// the indicated subfile.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
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
|
||||
// Access: Public
|
||||
// Description: Extracts the nth subfile to the indicated ostream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
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);
|
||||
|
||||
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 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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 \
|
||||
|
@ -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
panda/src/putil/stringStream.I
Normal file
107
panda/src/putil/stringStream.I
Normal file
@ -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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// 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
panda/src/putil/stringStream.cxx
Normal file
34
panda/src/putil/stringStream.cxx
Normal file
@ -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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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
panda/src/putil/stringStream.h
Normal file
53
panda/src/putil/stringStream.h
Normal file
@ -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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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
panda/src/putil/stringStreamBuf.I
Normal file
30
panda/src/putil/stringStreamBuf.I
Normal file
@ -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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// 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
panda/src/putil/stringStreamBuf.cxx
Normal file
296
panda/src/putil/stringStreamBuf.cxx
Normal file
@ -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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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
panda/src/putil/stringStreamBuf.h
Normal file
60
panda/src/putil/stringStreamBuf.h
Normal file
@ -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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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
|
Loading…
x
Reference in New Issue
Block a user