From c0861d1f8e3869f058bc8b152cd35a08e5b03e73 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 12 Jun 2007 12:07:58 +0000 Subject: [PATCH] Initial revision --- Build | 13 ++ source/base.cpp | 70 +++++++++ source/base.h | 161 +++++++++++++++++++++ source/buffered.cpp | 204 +++++++++++++++++++++++++++ source/buffered.h | 43 ++++++ source/error.h | 34 +++++ source/eventdispatcher.cpp | 80 +++++++++++ source/eventdispatcher.h | 53 +++++++ source/file.cpp | 282 +++++++++++++++++++++++++++++++++++++ source/file.h | 67 +++++++++ source/mode.h | 35 +++++ source/pipe.cpp | 182 ++++++++++++++++++++++++ source/pipe.h | 41 ++++++ source/poll.cpp | 223 +++++++++++++++++++++++++++++ source/poll.h | 84 +++++++++++ source/seek.cpp | 38 +++++ source/seek.h | 25 ++++ source/types.h | 28 ++++ source/utils.cpp | 23 +++ source/utils.h | 27 ++++ 20 files changed, 1713 insertions(+) create mode 100644 Build create mode 100644 source/base.cpp create mode 100644 source/base.h create mode 100644 source/buffered.cpp create mode 100644 source/buffered.h create mode 100644 source/error.h create mode 100644 source/eventdispatcher.cpp create mode 100644 source/eventdispatcher.h create mode 100644 source/file.cpp create mode 100644 source/file.h create mode 100644 source/mode.h create mode 100644 source/pipe.cpp create mode 100644 source/pipe.h create mode 100644 source/poll.cpp create mode 100644 source/poll.h create mode 100644 source/seek.cpp create mode 100644 source/seek.h create mode 100644 source/types.h create mode 100644 source/utils.cpp create mode 100644 source/utils.h diff --git a/Build b/Build new file mode 100644 index 0000000..daf676f --- /dev/null +++ b/Build @@ -0,0 +1,13 @@ +package "mspio" +{ + require "mspstrings"; + require "mspcore"; + require "sigc++-2.0"; + + library "mspio" + { + source "source"; + install true; + install_headers "msp/io"; + }; +}; diff --git a/source/base.cpp b/source/base.cpp new file mode 100644 index 0000000..174bace --- /dev/null +++ b/source/base.cpp @@ -0,0 +1,70 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include "base.h" +#include "poll.h" + +using namespace std; + +#include + +namespace Msp { +namespace IO { + +bool Base::getline(string &line) +{ + line.clear(); + + if(eof_flag) + return false; + + while(1) + { + int c=get(); + if(c==-1 || c=='\n') + break; + line+=c; + } + + return !eof_flag || !line.empty(); +} + +int Base::get() +{ + char c; + if(do_read(&c, 1)==0) + return -1; + return static_cast(c); +} + +void Base::event(PollEvent ev) +{ + if(ev&P_INPUT) + signal_data_available.emit(); + + on_event(ev); +} + +Base::~Base() +{ + signal_deleted.emit(); +} + +Base::Base(): + mode(M_READ), + events(P_NONE), + eof_flag(false) +{ } + +void Base::set_events(PollEvent e) +{ + //cout<<"object "< +#include "mode.h" +#include "poll.h" +#include "types.h" + +namespace Msp { +namespace IO { + +/** +Common interface for all I/O objects. + +A derived class must call set_events(P_NONE) before it is destroyed to avoid +leaving stale pointers in an EventDispatcher. +*/ +class Base +{ +public: + /** + Emitted when there is data available for reading. If all data is not read, + the signal is emitted again immediately. + */ + sigc::signal signal_data_available; + + /** + Emitted when there is no more data to be read. + */ + sigc::signal signal_end_of_file; + + /** + Emitted when the I/O object is about to close. Mainly intended for + buffering objects that need to flush their buffers at closing. + */ + sigc::signal signal_closing; + + /** + Emitted when the I/O object has closed. + */ + sigc::signal signal_closed; + + /** + Emitted when the mask of interesting events changes. Mainly for use by + EventDispatcher. + */ + sigc::signal signal_events_changed; + + /** + Emitted when the object is deleted. Mainly for use by EventDispatcher. + */ + sigc::signal signal_deleted; + + /** + Sets blocking mode. When blocking is enabled, most operations won't return + until they can make progress. When blocking is disabled, these operations + may return immediately with a return code indicating that nothing was done. + + Blocking is enabled by default. + */ + virtual void set_block(bool) { } + + /** + Returns the current mode flags. + */ + Mode get_mode() const { return mode; } + + /** + Writes data from a buffer. Subject to blocking. + + @param b Buffer to write from + @param c Number of bytes to write + + @return Number of bytes written + */ + unsigned write(const char *b, unsigned c) { return do_write(b, c); } + + /** + Writes a string. This is identical to calling write(s.data(), s.size()). + */ + unsigned write(const std::string &s) { return do_write(s.data(), s.size()); } + + /** + Writes a single character. This is identical to calling write(&c, 1). + */ + virtual unsigned put(char c) { return do_write(&c, 1); } + + /** + Reads data into a buffer. Subject to blocking. + + @param b Buffer to read into + @param c Maximum number of bytes to read + + @return Number of bytes read + */ + unsigned read(char *b, unsigned c) { return do_read(b, c); } + + /** + Reads characters up to the next linefeed or end-of-file. The linefeed is not + included in the line. + + @param line String to put the characters into + + @return Whether anything was read + */ + virtual bool getline(std::string &); + + /** + Reads a single character. + + @return A character, or -1 if none were available + */ + virtual int get(); + + /** + Returns the end-of-file flag. + */ + bool eof() const { return eof_flag; } + + /** + Returns a mask of the currently interesting events. Used by EventDispatcher. + */ + PollEvent get_events() const { return events; } + + /** + Returns a handle for polling. Should throw if the object does not have an + event handle. + */ + virtual Handle get_event_handle() =0; + + /** + Notifies the object of an event. Used by EventDispatcher. + */ + void event(PollEvent); + + virtual ~Base(); +protected: + Mode mode; + PollEvent events; + bool eof_flag; + + Base(); + void set_events(PollEvent); + virtual void on_event(PollEvent) { } + virtual unsigned do_write(const char *, unsigned) =0; + virtual unsigned do_read(char *, unsigned) =0; +private: + Base(const Base &); + Base &operator=(const Base &); +}; + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/buffered.cpp b/source/buffered.cpp new file mode 100644 index 0000000..4cd5cb4 --- /dev/null +++ b/source/buffered.cpp @@ -0,0 +1,204 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include "buffered.h" +#include "error.h" + +using namespace std; + +namespace Msp { +namespace IO { + +Buffered::Buffered(Base &b, unsigned s): + below(b), + buf_size(s), + in_buf(new char[buf_size]), + in_ptr(in_buf), + in_avail(0), + out_buf(new char[buf_size]), + out_used(0) +{ + mode=below.get_mode(); + below.signal_closing.connect(sigc::mem_fun(this, &Buffered::below_closing)); +} + +unsigned Buffered::put(char c) +{ + if(out_used0) + { + --in_avail; + return *in_ptr++; + } + + char c; + if(do_read(&c, 1)==0) + return -1; + return static_cast(c); +} + +Handle Buffered::get_event_handle() +{ + throw Exception("Buffered doesn't support events"); +} + +Buffered::~Buffered() +{ + try + { + flush(); + } + catch(...) + { } + + delete[] in_buf; + delete[] out_buf; +} + +void Buffered::below_closing() +{ + flush(); +} + +unsigned Buffered::do_write(const char *buf, unsigned size) +{ + if(out_used+size0) + { + // XXX sub-obtimal - should try to write directly from input first + // Fill the buffer and pass it on + unsigned head=min(buf_size-out_used, size); + + memcpy(out_buf+out_used, buf, head); + out_used+=head; + + buf+=head; + size-=head; + ret+=head; + + if(!ok) break; + + unsigned len=below.write(out_buf, out_used); + + ok=(len==out_used); + if(ok) + out_used=0; + else + { + memmove(out_buf, out_buf+len, buf_size-len); + out_used=buf_size-len; + } + } + + return ret; + } +} + +unsigned Buffered::do_read(char *buf, unsigned size) +{ + if(size<=in_avail) + { + // The request can be served from the buffer + memcpy(buf, in_ptr, size); + in_ptr+=size; + in_avail-=size; + + return size; + } + else + { + // Use whatever is left in the buffer + memcpy(buf, in_ptr, in_avail); + + buf+=in_avail; + size-=in_avail; + int ret=in_avail; + + in_ptr=in_buf; + in_avail=0; + + if(size>=buf_size) + ret+=below.read(buf, size); + else + { + // Read more data into the buffer + while(size>0) + { + in_avail=below.read(in_buf, buf_size); + if(in_avail==0) + { + eof_flag=true; + break; + } + + unsigned head=min(size, in_avail); + memcpy(buf, in_buf, head); + buf+=head; + size-=head; + + in_ptr=in_buf+head; + in_avail-=head; + ret+=head; + } + } + + return ret; + } +} + +} // namespace IO +} // namespace Msp diff --git a/source/buffered.h b/source/buffered.h new file mode 100644 index 0000000..b6584c1 --- /dev/null +++ b/source/buffered.h @@ -0,0 +1,43 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_BUFFERED_H_ +#define MSP_IO_BUFFERED_H_ + +#include "base.h" + +namespace Msp { +namespace IO { + +class Buffered: public Base +{ +public: + Buffered(Base &, unsigned =8192); + unsigned put(char); + void flush(); + bool getline(std::string &); + int get(); + int tell() const; + Handle get_event_handle(); + ~Buffered(); +private: + Base &below; + unsigned buf_size; + char *in_buf; + char *in_ptr; + unsigned in_avail; + char *out_buf; + unsigned out_used; + + void below_closing(); + unsigned do_write(const char *, unsigned); + unsigned do_read(char *, unsigned); +}; + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/error.h b/source/error.h new file mode 100644 index 0000000..8ebcf38 --- /dev/null +++ b/source/error.h @@ -0,0 +1,34 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_ERROR_H_ +#define MSP_IO_ERROR_H_ + +#include + +namespace Msp { +namespace IO { + +class Exception: public Msp::Exception +{ +public: + Exception(const std::string &w_): Msp::Exception(w_) { } +}; + +class FileNotFound: public Exception +{ +public: + FileNotFound(const std::string &w_, const std::string &f): Exception(w_), filename(f) { } + const std::string &get_filename() { return filename; } + ~FileNotFound() throw() { } +private: + std::string filename; +}; + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/eventdispatcher.cpp b/source/eventdispatcher.cpp new file mode 100644 index 0000000..04147e9 --- /dev/null +++ b/source/eventdispatcher.cpp @@ -0,0 +1,80 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include "base.h" +#include "eventdispatcher.h" +#include "poll.h" + +#include +using namespace std; + +namespace Msp { +namespace IO { + +EventDispatcher::EventDispatcher() +{ } + +void EventDispatcher::add(Base &obj) +{ + //cout<<"evdisp add "<<&obj<<'\n'; + SlotMap::iterator i=objects.find(&obj); + if(i==objects.end()) + { + i=objects.insert(SlotMap::value_type(&obj, Slot(&obj))).first; + i->second.evch_conn=obj.signal_events_changed.connect(sigc::bind(sigc::mem_fun(this, &EventDispatcher::object_events_changed), &obj)); + i->second.del_conn=obj.signal_deleted.connect(sigc::bind(sigc::mem_fun(this, &EventDispatcher::object_deleted), &obj)); + + if(obj.get_events()) + poller.set_object(obj, obj.get_events()); + } +} + +void EventDispatcher::remove(Base &obj) +{ + //cout<<"evdisp remove "<<&obj<<'\n'; + SlotMap::iterator i=objects.find(&obj); + if(i!=objects.end()) + { + i->second.evch_conn.disconnect(); + i->second.del_conn.disconnect(); + objects.erase(i); + + poller.set_object(obj, P_NONE); + } +} + +void EventDispatcher::tick(bool block) +{ + if(objects.empty()) + return; + + int ret; + if(block) + ret=poller.poll(); + else + ret=poller.poll(Time::zero); + + if(ret>0) + { + const Poller::SlotSeq &result=poller.get_result(); + for(Poller::SlotSeq::const_iterator i=result.begin(); i!=result.end(); ++i) + i->object->event(i->events); + } +} + +void EventDispatcher::object_events_changed(PollEvent ev, Base *obj) +{ + poller.set_object(*obj, ev); +} + +void EventDispatcher::object_deleted(Base *obj) +{ + remove(*obj); +} + +} // namespace IO +} // namespace Msp diff --git a/source/eventdispatcher.h b/source/eventdispatcher.h new file mode 100644 index 0000000..9bea6d2 --- /dev/null +++ b/source/eventdispatcher.h @@ -0,0 +1,53 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef EVENTDISPATCHER_H_ +#define EVENTDISPATCHER_H_ + +#include +#include "poll.h" + +namespace Msp { +namespace IO { + +/** +Put your I/O objects inside one of these to get signaled when something happens +on some of them. +*/ +class EventDispatcher +{ +public: + EventDispatcher(); + void add(Base &); + void remove(Base &); + + /** + Checks for and dispatches events. If block is true, will block until events + are available. + */ + void tick(bool =true); +private: + struct Slot + { + Base *obj; + sigc::connection evch_conn; + sigc::connection del_conn; + + Slot(Base *o): obj(o) { } + }; + typedef std::map SlotMap; + + Poller poller; + SlotMap objects; + + void object_events_changed(PollEvent, Base *); + void object_deleted(Base *); +}; + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/file.cpp b/source/file.cpp new file mode 100644 index 0000000..9b1e433 --- /dev/null +++ b/source/file.cpp @@ -0,0 +1,282 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef WIN32 +#include +#include +#include +#endif +#include +#include "error.h" +#include "file.h" + +using namespace std; + +namespace Msp { +namespace IO { + +/** +Creates a new file object and opens it. If the +create flag is true and write access is requested and the file does exist, it +is created. Otherwise a missing file is an error. + +@param fn Name of the file to open +@param m Open mode +@param cm Flags controlling creation of a new file +*/ +File::File(const string &fn, Mode m, CreateMode cm) +{ + if(!(m&M_RDWR)) + throw InvalidParameterValue("Invalid read/write mode"); + if(cm&~(C_CREATE|C_TRUNCATE)) + throw InvalidParameterValue("Invalid create mode"); + + mode=m; + +#ifdef WIN32 + int flags=0; + int create_flags=OPEN_EXISTING; + + if(mode&M_READ) + flags|=GENERIC_READ; + else if(mode&M_WRITE) + { + flags|=GENERIC_WRITE; + + switch(static_cast(cm)) + { + case C_NONE: create_flags=OPEN_EXISTING; break; + case C_CREATE: create_flags=OPEN_ALWAYS; break; + case C_TRUNCATE: create_flags=TRUNCATE_EXISTING; break; + case C_CREATE+C_TRUNCATE: create_flags=CREATE_ALWAYS; break; + } + } + + handle=CreateFile(fn.c_str(), flags, 0, 0, create_flags, FILE_ATTRIBUTE_NORMAL, 0); + if(handle==INVALID_HANDLE_VALUE) + throw SystemError(format("Can't open file '%s'", fn), GetLastError()); +#else + int flags=0; + switch(mode&M_RDWR) + { + case M_READ: flags|=O_RDONLY; break; + case M_WRITE: flags|=O_WRONLY; break; + case M_RDWR: flags|=O_RDWR; break; + default:; + } + + if(mode&M_WRITE) + { + if(cm&C_CREATE) + flags|=O_CREAT; + if(cm&C_TRUNCATE) + flags|=O_TRUNC; + } + if(mode&M_APPEND) + flags|=O_APPEND; + if(mode&M_NONBLOCK) + flags|=O_NONBLOCK; + + handle=::open(fn.c_str(), flags, 0666); + if(handle==-1) + { + int err=errno; + if(err==ENOENT) + throw FileNotFound("Can't find file "+fn, fn); + else + throw SystemError(format("Can't open file '%s'", fn), err); + } +#endif + + set_events(P_INPUT); +} + +/** +Closes the file. Any attempt to access the file after this will cause an +exception to be thrown. +*/ +void File::close() +{ + if(handle==MSP_IO_INVALID_HANDLE) + return; + + set_events(P_NONE); + + signal_closing.emit(); + +#ifdef WIN32 + CloseHandle(handle); +#else + ::close(handle); +#endif + + handle=MSP_IO_INVALID_HANDLE; + signal_closed.emit(); +} + +/** +Sets the blocking state of the file. If blocking is disabled, all operations +will return immediately, even if they can't be fully completed. +*/ +void File::set_block(bool b) +{ + check_access(M_NONE); + + mode=(mode&~M_NONBLOCK); + if(b) + mode=(mode|M_NONBLOCK); +#ifndef WIN32 + int flags=fcntl(handle, F_GETFD); + fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK)); +#endif +} + +/** +Seeks the file to the given byte offset. + +@param off Offset in bytes +@param st Seek type + +@return The resulting offset +*/ +int File::seek(int off, SeekType st) +{ + check_access(M_NONE); + + int type=sys_seek_type(st); +#ifdef WIN32 + DWORD ret=SetFilePointer(handle, off, 0, type); + if(ret==INVALID_SET_FILE_POINTER) + throw SystemError("Seek failed", GetLastError()); +#else + int ret=lseek(handle, off, type); + if(ret==-1) + throw SystemError("Seek failed", errno); +#endif + + eof_flag=false; + + return ret; +} + +/** +Returns the current read/write offset of the file. +*/ +int File::tell() const +{ + check_access(M_NONE); + +#ifdef WIN32 + DWORD ret=SetFilePointer(handle, 0, 0, FILE_CURRENT); + if(ret==INVALID_SET_FILE_POINTER) + throw SystemError("Tell failed", GetLastError()); +#else + int ret=lseek(handle, 0, SEEK_CUR); + if(ret==-1) + throw SystemError("Tell failed", errno); +#endif + + return ret; +} + +File::~File() +{ + close(); +} + +void File::check_access(Mode m) const +{ + if(handle==MSP_IO_INVALID_HANDLE) + throw InvalidState("File is not open"); + if(m==M_READ && !(mode&M_READ)) + throw InvalidState("File is not readable"); + if(m==M_WRITE && !(mode&M_WRITE)) + throw InvalidState("File is not writable"); +} + +/** +Writes data from a buffer to the file. + +@param buf Buffer to write from. +@param size Length of data to write. + +@return The number of bytes written +*/ +unsigned File::do_write(const char *buf, unsigned size) +{ + check_access(M_WRITE); + + if(size==0) + return 0; + +#ifdef WIN32 + if(mode&M_APPEND) + seek(0, S_END); + DWORD ret; + if(WriteFile(handle, buf, size, &ret, 0)==0) + throw SystemError("Writing to file failed", GetLastError()); +#else + int ret=::write(handle, buf, size); + if(ret==-1) + { + if(errno==EAGAIN) + return 0; + else + throw SystemError("Writing to file failed", errno); + } +#endif + + return ret; +} + +void File::sync() +{ +#ifndef WIN32 + fsync(handle); +#endif +} + +/** +Reads data from the file. + +@param buf Buffer to read data into. +@param size Maximum size of data to read. + +@return The number of bytes read, possibly zero +*/ +unsigned File::do_read(char *buf, unsigned size) +{ + check_access(M_READ); + + if(size==0) + return 0; + +#ifdef WIN32 + DWORD ret; + if(ReadFile(handle, buf, size, &ret, 0)==0) + throw SystemError("Reading from file failed", GetLastError()); +#else + int ret=::read(handle, buf, size); + if(ret==-1) + { + if(errno==EAGAIN) + return 0; + else + throw SystemError("Reading from file failed", errno); + } +#endif + + if(ret==0) + { + eof_flag=true; + signal_end_of_file.emit(); + } + + return ret; +} + +} // namespace IO +} // namespace Msp diff --git a/source/file.h b/source/file.h new file mode 100644 index 0000000..49b596b --- /dev/null +++ b/source/file.h @@ -0,0 +1,67 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_FILE_H_ +#define MSP_IO_FILE_H_ + +#include +#include "base.h" +#include "seek.h" + +namespace Msp { +namespace IO { + +/** +A class for reading and writing files. + +Non-blocking mode is not supported on Win32. +*/ +class File: public Base +{ +public: + enum CreateMode + { + C_NONE=0, + C_CREATE=1, + C_TRUNCATE=2 + }; + + File(const std::string &, Mode=M_READ, CreateMode =CreateMode(C_CREATE+C_TRUNCATE)); + + void close(); + + void set_block(bool); + void enable_events(); + + void sync(); + + int seek(int, SeekType); + int tell() const; + + Handle get_event_handle() { return handle; } + + ~File(); +private: + Handle handle; + + void check_access(Mode) const; + unsigned do_write(const char *, unsigned); + unsigned do_read(char *, unsigned); +}; + +inline File::CreateMode operator|(File::CreateMode m, File::CreateMode n) +{ return File::CreateMode((int)m|(int)n); } + +inline File::CreateMode operator&(File::CreateMode m, File::CreateMode n) +{ return File::CreateMode((int)m&(int)n); } + +inline File::CreateMode operator~(File::CreateMode m) +{ return File::CreateMode(~(int)m); } + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/mode.h b/source/mode.h new file mode 100644 index 0000000..1d944b1 --- /dev/null +++ b/source/mode.h @@ -0,0 +1,35 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_MODE_H_ +#define MSP_IO_MODE_H_ + +namespace Msp { +namespace IO { + +enum Mode +{ + M_NONE=0, + M_READ=1, + M_WRITE=2, + M_RDWR=M_READ|M_WRITE, + M_APPEND=4, + M_NONBLOCK=8 +}; + +inline Mode operator|(Mode m, Mode n) +{ return Mode((int)m|(int)n); } + +inline Mode operator&(Mode m, Mode n) +{ return Mode((int)m&(int)n); } + +inline Mode operator~(Mode m) +{ return Mode(~(int)m); } + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/pipe.cpp b/source/pipe.cpp new file mode 100644 index 0000000..88dd462 --- /dev/null +++ b/source/pipe.cpp @@ -0,0 +1,182 @@ +#ifndef WIN32 +#include +#include +#endif +#include +#include "pipe.h" + +using namespace std; + +namespace Msp { +namespace IO { + +Pipe::Pipe() +{ +#ifdef WIN32 + string name=format("\\\\.\\pipe\\%u.%p", GetCurrentProcessId(), this); + handle[0]=CreateNamedPipe(name.c_str(), PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, 0, 0); + if(handle[0]==INVALID_HANDLE_VALUE) + throw SystemError("Unable to create pipe", GetLastError()); + + handle[1]=CreateFile(name.c_str(), GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + if(handle[1]==INVALID_HANDLE_VALUE) + { + unsigned err=GetLastError(); + CloseHandle(handle[0]); + throw SystemError("Unable to create pipe", err); + } + + overlapped=0; + event=CreateEvent(0, true, false, 0); + buf_size=1024; + buffer=new char[buf_size]; + buf_avail=0; + buf_next=buffer; +#else + if(pipe(handle)==-1) + throw SystemError("Unable to create pipe", errno); +#endif + + set_events(P_INPUT); +} + +void Pipe::set_block(bool b) +{ + mode=(mode&~M_NONBLOCK); + if(b) + mode=(mode|M_NONBLOCK); + +#ifndef WIN32 + int flags=fcntl(handle[0], F_GETFD); + fcntl(handle[0], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK)); + flags=fcntl(handle[1], F_GETFD); + fcntl(handle[1], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK)); +#endif +} + +void Pipe::close() +{ + set_events(P_NONE); + + signal_closing.emit(); +#ifdef WIN32 + CloseHandle(handle[0]); + CloseHandle(handle[1]); +#else + ::close(handle[0]); + ::close(handle[1]); + signal_closed.emit(); +#endif +} + +Handle Pipe::get_event_handle() +{ +#ifdef WIN32 + if(!overlapped && !buf_avail) + { + overlapped=new OVERLAPPED; + memset(overlapped, 0, sizeof(OVERLAPPED)); + overlapped->hEvent=event; + + DWORD ret; + buf_next=buffer; + if(!ReadFile(handle[0], buffer, buf_size, &ret, overlapped)) + { + unsigned err=GetLastError(); + if(err!=ERROR_IO_PENDING) + throw SystemError("Failed to start an overlapped read", err); + } + else + { + buf_avail=ret; + delete overlapped; + overlapped=0; + SetEvent(event); + } + } + + return event; +#else + return handle[0]; +#endif +} + +Pipe::~Pipe() +{ + close(); +} + +unsigned Pipe::do_write(const char *buf, unsigned size) +{ + if(size==0) + return 0; + +#ifdef WIN32 + DWORD ret; + if(!WriteFile(handle[1], buf, size, &ret, 0)) + throw SystemError("Writing to pipe failed", GetLastError()); +#else + int ret=::write(handle[1], buf, size); + if(ret==-1) + { + if(errno==EAGAIN) + return 0; + else + throw SystemError("Writing to pipe failed", errno); + } +#endif + + return ret; +} + +unsigned Pipe::do_read(char *buf, unsigned size) +{ + if(size==0) + return 0; + +#ifdef WIN32 + // Initiate overlapped read if needed + get_event_handle(); + + if(overlapped) + { + DWORD ret; + if(!GetOverlappedResult(handle[0], overlapped, &ret, !buf_avail)) + throw SystemError("Reading from pipe failed", GetLastError()); + else + { + buf_avail+=ret; + delete overlapped; + overlapped=0; + } + } + + unsigned ret=min(buf_avail, size); + memcpy(buf, buf_next, ret); + buf_next+=ret; + buf_avail-=ret; + + // Initiate another overlapped read in case someone is polling us + get_event_handle(); +#else + int ret=::read(handle[0], buf, size); + if(ret==-1) + { + if(errno==EAGAIN) + return 0; + else + throw SystemError("Reading from pipe failed", errno); + } +#endif + + if(ret==0) + { + eof_flag=true; + signal_end_of_file.emit(); + } + + return ret; +} + +} // namespace IO +} // namespace Msp diff --git a/source/pipe.h b/source/pipe.h new file mode 100644 index 0000000..e8e1f53 --- /dev/null +++ b/source/pipe.h @@ -0,0 +1,41 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_PIPE_H_ +#define MSP_IO_PIPE_H_ + +#include "base.h" + +namespace Msp { +namespace IO { + +class Pipe: public Base +{ +public: + Pipe(); + void set_block(bool); + void close(); + Handle get_event_handle(); + ~Pipe(); +private: + Handle handle[2]; +#ifdef WIN32 + OVERLAPPED *overlapped; + Handle event; + unsigned buf_size; + char *buffer; + unsigned buf_avail; + char *buf_next; +#endif + + unsigned do_write(const char *, unsigned); + unsigned do_read(char *, unsigned); +}; + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/poll.cpp b/source/poll.cpp new file mode 100644 index 0000000..f8f6c51 --- /dev/null +++ b/source/poll.cpp @@ -0,0 +1,223 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include +#include +#include "error.h" +#include "base.h" +#include "poll.h" + +namespace { + +using namespace Msp; +using namespace Msp::IO; + +inline int sys_poll_event(PollEvent event) +{ + int result=0; + + if(event&~(P_INPUT|P_PRIO|P_OUTPUT)) + throw InvalidParameterValue("Invalid poll events"); + +#ifndef WIN32 + if(event&P_INPUT) + result|=POLLIN; + if(event&P_PRIO) + result|=POLLPRI; + if(event&P_OUTPUT) + result|=POLLOUT; +#endif + + return result; +} + +inline PollEvent poll_event_from_sys(int event) +{ + PollEvent result=P_NONE; + +#ifdef WIN32 + // Stop the compiler from complaining about unused parameter + event=event; +#else + if(event&POLLIN) + result=result|P_INPUT; + if(event&POLLPRI) + result=result|P_PRIO; + if(event&POLLOUT) + result=result|P_OUTPUT; + if(event&POLLERR) + result=result|P_ERROR; +#endif + + return result; +} + +inline PollEvent do_poll(Base &obj, PollEvent pe, int timeout) +{ +#ifdef WIN32 + if(timeout<0) + timeout=INFINITE; + + DWORD ret=WaitForSingleObject(obj.get_event_handle(), timeout); + if(ret==WAIT_OBJECT_0) + return pe; + else if(ret==WAIT_FAILED) + throw SystemError("Poll failed", GetLastError()); + + return P_NONE; +#else + pollfd pfd={obj.get_event_handle(), sys_poll_event(pe), 0}; + + int ret=::poll(&pfd, 1, timeout); + if(ret==-1) + { + if(errno==EINTR) + return P_NONE; + else + throw SystemError("Poll failed", errno); + } + + return poll_event_from_sys(pfd.revents); +#endif +} + +} + +namespace Msp { +namespace IO { + +Poller::Poller(): + pfd_dirty(false) +{ } + +void Poller::set_object(Base &obj, PollEvent ev) +{ + // Verify that the object has an event handle + if(ev) + obj.get_event_handle(); + + //cout<<"poller set_object "<<&obj<<' '<second.events=ev; + else + objects.erase(i); + + pfd_dirty=true; + } + else if(ev) + { +#ifdef WIN32 + if(objects.size()>=MAXIMUM_WAIT_OBJECTS) + throw InvalidState("Maximum number of wait objects reached"); +#endif + objects.insert(SlotMap::value_type(&obj, Slot(&obj, ev))); + + pfd_dirty=true; + } +} + +int Poller::poll() +{ + return do_poll(-1); +} + +int Poller::poll(const Time::TimeDelta &timeout) +{ + if(timeout(timeout/Time::msec)); +} + +void Poller::rebuild_pfd() +{ + pfd.clear(); + + pollfd p; + + for(SlotMap::iterator i=objects.begin(); i!=objects.end(); ++i) + { + p.fd=i->second.object->get_event_handle(); +#ifndef WIN32 + p.events=sys_poll_event(i->second.events); +#endif + pfd.push_back(p); + } + + pfd_dirty=false; +} + +int Poller::do_poll(int timeout) +{ + if(pfd_dirty) + rebuild_pfd(); + + poll_result.clear(); + + if(pfd.empty()) + throw InvalidState("Nothing to poll"); + +#ifdef WIN32 + if(timeout<0) + timeout=INFINITE; + + DWORD ret=WaitForMultipleObjects(pfd.size(), reinterpret_cast(&pfd.front()), false, timeout); + if(/*ret>=WAIT_OBJECT_0 &&*/ retsecond.object, i->second.events)); + + return 1; + } + else if(ret==WAIT_FAILED) + throw SystemError("Poll failed", GetLastError()); + + return 0; +#else + int ret=::poll(&pfd.front(), pfd.size(), timeout); + if(ret==-1) + { + if(errno==EINTR) + return 0; + else + throw SystemError("Poll failed", errno); + } + + int n=ret; + SlotMap::iterator j=objects.begin(); + for(std::vector::iterator i=pfd.begin(); (i!=pfd.end() && n>0); ++i,++j) + if(i->revents) + { + poll_result.push_back(Slot(j->second.object, poll_event_from_sys(i->revents))); + --n; + } + + return ret; +#endif +} + + +PollEvent poll(Base &obj, PollEvent pe) +{ + return do_poll(obj, pe, -1); +} + +PollEvent poll(Base &obj, PollEvent pe, const Time::TimeDelta &timeout) +{ + if(timeout(timeout/Time::msec)); +} + +} // namespace IO +} // namespace Msp diff --git a/source/poll.h b/source/poll.h new file mode 100644 index 0000000..6ac6149 --- /dev/null +++ b/source/poll.h @@ -0,0 +1,84 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_POLL_H_ +#define MSP_IO_POLL_H_ + +#ifndef WIN32 +#include +#endif +#include +#include +#include +#include +#include "types.h" + +namespace Msp { +namespace IO { + +class Base; + +enum PollEvent +{ + P_NONE=0, + P_INPUT=1, + P_PRIO=2, + P_OUTPUT=4, + P_ERROR=8 +}; + +inline PollEvent operator|(PollEvent e, PollEvent f) +{ return PollEvent((int)e|(int)f); } + +inline PollEvent operator&(PollEvent e, PollEvent f) +{ return PollEvent((int)e&(int)f); } + +inline PollEvent operator~(PollEvent e) +{ return PollEvent(~(int)e); } + +class Poller +{ +public: + struct Slot + { + Base *object; + PollEvent events; + + Slot(Base *o, PollEvent e): object(o), events(e) { } + }; + typedef std::list SlotSeq; + + Poller(); + void set_object(Base &, PollEvent); + int poll(); + int poll(const Time::TimeDelta &); + const SlotSeq &get_result() const { return poll_result; } +private: + typedef std::map SlotMap; + +#ifdef WIN32 + struct pollfd + { + Handle fd; + }; +#endif + + SlotMap objects; + std::vector pfd; + bool pfd_dirty; + SlotSeq poll_result; + + void rebuild_pfd(); + int do_poll(int); +}; + +PollEvent poll(Base &, PollEvent); +PollEvent poll(Base &, PollEvent, const Time::TimeDelta &); + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/seek.cpp b/source/seek.cpp new file mode 100644 index 0000000..f2d381e --- /dev/null +++ b/source/seek.cpp @@ -0,0 +1,38 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifdef WIN32 +#include +#endif +#include +#include "seek.h" + +namespace Msp { +namespace IO { + +int sys_seek_type(SeekType st) +{ +#ifdef WIN32 + if(st==S_BEG) + return FILE_BEGIN; + else if(st==S_CUR) + return FILE_CURRENT; + else if(st==S_END) + return FILE_END; +#else + if(st==S_BEG) + return SEEK_SET; + else if(st==S_CUR) + return SEEK_CUR; + else if(st==S_END) + return SEEK_END; +#endif + + throw InvalidParameterValue("Invalid seek type"); +} + +} // namespace IO +} // namespace Msp diff --git a/source/seek.h b/source/seek.h new file mode 100644 index 0000000..6d15437 --- /dev/null +++ b/source/seek.h @@ -0,0 +1,25 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_SEEK_H_ +#define MSP_IO_SEEK_H_ + +namespace Msp { +namespace IO { + +enum SeekType +{ + S_BEG, + S_CUR, + S_END +}; + +extern int sys_seek_type(SeekType); + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/types.h b/source/types.h new file mode 100644 index 0000000..bb8b923 --- /dev/null +++ b/source/types.h @@ -0,0 +1,28 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_IO_TYPES_H_ +#define MSP_IO_TYPES_H_ + +#ifdef WIN32 +#include +#endif + +namespace Msp { +namespace IO { + +#ifdef WIN32 +typedef HANDLE Handle; +#define MSP_IO_INVALID_HANDLE INVALID_HANDLE_VALUE +#else +typedef int Handle; +#define MSP_IO_INVALID_HANDLE -1 +#endif + +} // namespace IO +} // namespace Msp + +#endif diff --git a/source/utils.cpp b/source/utils.cpp new file mode 100644 index 0000000..8ce5bf3 --- /dev/null +++ b/source/utils.cpp @@ -0,0 +1,23 @@ +/* $Id$ + +This file is part of libmspio +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include "base.h" +#include "utils.h" + +namespace Msp { +namespace IO { + +unsigned read_all(Base &obj, char *buf, unsigned size) +{ + unsigned pos=0; + while(pos