00001 #ifndef BOOST_ADAPTBX_PYTHON_FILE_STREAM_H
00002 #define BOOST_ADAPTBX_PYTHON_FILE_STREAM_H
00003
00004 #include <boost/python/object.hpp>
00005 #include <boost/python/str.hpp>
00006 #include <boost/python/extract.hpp>
00007
00008 #include <boost/optional.hpp>
00009 #include <boost/utility/typed_in_place_factory.hpp>
00010
00011 #include <streambuf>
00012 #include <iostream>
00013
00014 namespace boost_adaptbx { namespace file_conversion {
00015
00016 namespace python = boost::python;
00017
00018
00020
00120 class python_file_buffer : public std::basic_streambuf<char>
00121 {
00122 private:
00123 typedef std::basic_streambuf<char> base_t;
00124
00125 public:
00126
00127
00128
00129
00130 typedef base_t::char_type char_type;
00131 typedef base_t::int_type int_type;
00132 typedef base_t::pos_type pos_type;
00133 typedef base_t::off_type off_type;
00134 typedef base_t::traits_type traits_type;
00135
00136
00137 inline
00138 static
00139 int
00140 traits_type_eof() { return traits_type::eof(); }
00141
00143
00146 static std::size_t buffer_size;
00147
00149 python_file_buffer(python::object& python_file)
00150 : py_read (getattr(python_file, "read", python::object())),
00151 py_write(getattr(python_file, "write", python::object())),
00152 py_seek (getattr(python_file, "seek", python::object())),
00153 py_tell (getattr(python_file, "tell", python::object())),
00154 write_buffer(0),
00155 pos_of_read_buffer_end_in_py_file(0),
00156 pos_of_write_buffer_end_in_py_file(buffer_size),
00157 farthest_pptr(0)
00158 {
00159 if (py_write != python::object()) {
00160
00161 write_buffer = new char[buffer_size + 1];
00162 write_buffer[buffer_size] = '\0';
00163 setp(write_buffer, write_buffer + buffer_size);
00164 farthest_pptr = pptr();
00165 }
00166 else {
00167
00168 setp(0, 0);
00169 }
00170 if (py_tell != python::object()) {
00171 off_type py_pos = python::extract<off_type>(py_tell());
00172 pos_of_read_buffer_end_in_py_file = py_pos;
00173 pos_of_write_buffer_end_in_py_file = py_pos;
00174 }
00175 }
00176
00177
00179 virtual ~python_file_buffer() {
00180 if (write_buffer) delete[] write_buffer;
00181 }
00182
00184 virtual int_type underflow() {
00185 int_type const failure = traits_type::eof();
00186 if (py_read == python::object()) {
00187 PyErr_SetString(PyExc_AttributeError,
00188 "That Python file object has no 'read' attribute");
00189 python::throw_error_already_set();
00190 }
00191 read_buffer = py_read(buffer_size);
00192 char *read_buffer_data;
00193 python::ssize_t py_n_read;
00194 if (PyString_AsStringAndSize(read_buffer.ptr(),
00195 &read_buffer_data, &py_n_read) == -1) {
00196 setg(0, 0, 0);
00197 PyErr_SetString(PyErr_Occurred(),
00198 "The method 'read' of the Python file object "
00199 "did not return a string.");
00200 python::throw_error_already_set();
00201 }
00202 off_type n_read = (off_type)py_n_read;
00203 pos_of_read_buffer_end_in_py_file += n_read;
00204 setg(read_buffer_data, read_buffer_data, read_buffer_data + n_read);
00205
00206 if (n_read == 0) return failure;
00207 return traits_type::to_int_type(read_buffer_data[0]);
00208 }
00209
00211 virtual int_type overflow(int_type c=traits_type_eof()) {
00212 if (py_write == python::object()) {
00213 PyErr_SetString(PyExc_AttributeError,
00214 "That Python file object has no 'write' attribute");
00215 python::throw_error_already_set();
00216 }
00217 farthest_pptr = std::max(farthest_pptr, pptr());
00218 off_type n_written = (off_type)(farthest_pptr - pbase());
00219 python::str chunk(pbase(), farthest_pptr);
00220 py_write(chunk);
00221 if (!traits_type::eq_int_type(c, traits_type::eof())) {
00222 py_write(traits_type::to_char_type(c));
00223 n_written++;
00224 }
00225 if (n_written) {
00226 pos_of_write_buffer_end_in_py_file += n_written;
00227 setp(pbase(), epptr());
00228
00229 farthest_pptr = pptr();
00230 }
00231 return traits_type::eq_int_type(
00232 c, traits_type::eof()) ? traits_type::not_eof(c) : c;
00233 }
00234
00236
00242 virtual int sync() {
00243 int result = 0;
00244 farthest_pptr = std::max(farthest_pptr, pptr());
00245 if (farthest_pptr && farthest_pptr > pbase()) {
00246 off_type delta = pptr() - farthest_pptr;
00247 int_type status = overflow();
00248 if (traits_type::eq_int_type(status, traits_type::eof())) result = -1;
00249 if (py_seek != python::object()) py_seek(delta, 1);
00250 }
00251 else if (gptr() && gptr() < egptr()) {
00252 if (py_seek != python::object()) py_seek(gptr() - egptr(), 1);
00253 }
00254 return result;
00255 }
00256
00258
00264 virtual
00265 pos_type seekoff(off_type off, std::ios_base::seekdir way,
00266 std::ios_base::openmode which= std::ios_base::in
00267 | std::ios_base::out)
00268 {
00269
00270
00271
00272
00273
00274 int const failure = off_type(-1);
00275
00276 if (py_seek == python::object()) {
00277 PyErr_SetString(PyExc_AttributeError,
00278 "That Python file object has no 'seek' attribute");
00279 python::throw_error_already_set();
00280 }
00281
00282
00283 if (which == std::ios_base::in && !gptr()) {
00284 if (traits_type::eq_int_type(underflow(), traits_type::eof())) {
00285 return failure;
00286 }
00287 }
00288
00289
00290 int whence;
00291 switch (way) {
00292 case std::ios_base::beg:
00293 whence = 0;
00294 break;
00295 case std::ios_base::cur:
00296 whence = 1;
00297 break;
00298 case std::ios_base::end:
00299 whence = 2;
00300 break;
00301 default:
00302 return failure;
00303 }
00304
00305
00306 boost::optional<off_type> result = seekoff_without_calling_python(
00307 off, way, which);
00308 if (!result) {
00309
00310 if (which == std::ios_base::out) overflow();
00311 if (way == std::ios_base::cur) {
00312 if (which == std::ios_base::in) off -= egptr() - gptr();
00313 else if (which == std::ios_base::out) off += pptr() - pbase();
00314 }
00315 py_seek(off, whence);
00316 result = off_type(python::extract<off_type>(py_tell()));
00317 if (which == std::ios_base::in) underflow();
00318 }
00319 return *result;
00320 }
00321
00323 virtual
00324 pos_type seekpos(pos_type sp,
00325 std::ios_base::openmode which= std::ios_base::in
00326 | std::ios_base::out)
00327 {
00328 return python_file_buffer::seekoff(sp, std::ios_base::beg, which);
00329 }
00330
00331 private:
00332 python::object py_read, py_write, py_seek, py_tell;
00333
00334
00335
00336
00337
00338
00339 python::object read_buffer;
00340
00341
00342
00343
00344 char *write_buffer;
00345 char *farthest_pptr;
00346
00347 off_type pos_of_read_buffer_end_in_py_file,
00348 pos_of_write_buffer_end_in_py_file;
00349
00350
00351 boost::optional<off_type> seekoff_without_calling_python(
00352 off_type off,
00353 std::ios_base::seekdir way,
00354 std::ios_base::openmode which)
00355 {
00356 boost::optional<off_type> const failure;
00357
00358
00359 off_type buf_begin, buf_end, buf_cur, upper_bound;
00360 off_type pos_of_buffer_end_in_py_file;
00361 if (which == std::ios_base::in) {
00362 pos_of_buffer_end_in_py_file = pos_of_read_buffer_end_in_py_file;
00363 buf_begin = reinterpret_cast<std::streamsize>(eback());
00364 buf_cur = reinterpret_cast<std::streamsize>(gptr());
00365 buf_end = reinterpret_cast<std::streamsize>(egptr());
00366 upper_bound = buf_end;
00367 }
00368 else if (which == std::ios_base::out) {
00369 pos_of_buffer_end_in_py_file = pos_of_write_buffer_end_in_py_file;
00370 buf_begin = reinterpret_cast<std::streamsize>(pbase());
00371 buf_cur = reinterpret_cast<std::streamsize>(pptr());
00372 buf_end = reinterpret_cast<std::streamsize>(epptr());
00373 farthest_pptr = std::max(farthest_pptr, pptr());
00374 upper_bound = reinterpret_cast<std::streamsize>(farthest_pptr) + 1;
00375 }
00376
00377
00378 off_type buf_sought;
00379 if (way == std::ios_base::cur) {
00380 buf_sought = buf_cur + off;
00381 }
00382 else if (way == std::ios_base::beg) {
00383 buf_sought = buf_end + (off - pos_of_buffer_end_in_py_file);
00384 }
00385
00386
00387 if (buf_sought < buf_begin || buf_sought >= upper_bound) return failure;
00388
00389
00390 if (which == std::ios_base::in) gbump(buf_sought - buf_cur);
00391 else if (which == std::ios_base::out) pbump(buf_sought - buf_cur);
00392 return pos_of_buffer_end_in_py_file + (buf_sought - buf_end);
00393 }
00394 };
00395
00396
00397 std::size_t python_file_buffer::buffer_size = 1024;
00398
00399
00400 namespace details {
00401 template <class BaseType>
00402 class gen_stream : public BaseType
00403 {
00404 public:
00405 gen_stream(python_file_buffer const *sb)
00406 : BaseType(const_cast<python_file_buffer *>(sb))
00407 {
00408 this->exceptions(std::ios_base::badbit);
00409 }
00410 };
00411 }
00412
00413
00414 typedef details::gen_stream<std::istream> istream;
00415 typedef details::gen_stream<std::ostream> ostream;
00416 typedef details::gen_stream<std::iostream> iostream;
00417
00418 }}
00419
00420 #endif // GUARD