--- /dev/null
+package "mspio"
+{
+ require "mspstrings";
+ require "mspcore";
+ require "sigc++-2.0";
+
+ library "mspio"
+ {
+ source "source";
+ install true;
+ install_headers "msp/io";
+ };
+};
--- /dev/null
+/* $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 <iostream>
+
+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<unsigned char>(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 "<<this<<" set_events "<<e<<'\n';
+ events=e;
+ signal_events_changed.emit(events);
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifndef MSP_IO_BASE_H_
+#define MSP_IO_BASE_H_
+
+#include <sigc++/sigc++.h>
+#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<void> signal_data_available;
+
+ /**
+ Emitted when there is no more data to be read.
+ */
+ sigc::signal<void> 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<void> signal_closing;
+
+ /**
+ Emitted when the I/O object has closed.
+ */
+ sigc::signal<void> signal_closed;
+
+ /**
+ Emitted when the mask of interesting events changes. Mainly for use by
+ EventDispatcher.
+ */
+ sigc::signal<void, PollEvent> signal_events_changed;
+
+ /**
+ Emitted when the object is deleted. Mainly for use by EventDispatcher.
+ */
+ sigc::signal<void> 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
--- /dev/null
+/* $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_used<buf_size)
+ {
+ out_buf[out_used++]=c;
+ return 1;
+ }
+ else
+ return do_write(&c, 1);
+}
+
+void Buffered::flush()
+{
+ if(out_used==0)
+ return;
+
+ unsigned len=below.write(out_buf, out_used);
+ if(len<out_used)
+ {
+ memmove(out_buf, out_buf+len, out_used-len);
+ out_used-=len;
+ throw Exception("Couldn't flush all data");
+ }
+ out_used=0;
+}
+
+bool Buffered::getline(std::string &line)
+{
+ for(unsigned i=0; i<in_avail; ++i)
+ if(in_ptr[i]=='\n')
+ {
+ line.assign(in_ptr, i);
+ in_ptr+=i+1;
+ in_avail-=i+1;
+ return true;
+ }
+
+ return Base::getline(line);
+}
+
+int Buffered::get()
+{
+ if(in_avail>0)
+ {
+ --in_avail;
+ return *in_ptr++;
+ }
+
+ char c;
+ if(do_read(&c, 1)==0)
+ return -1;
+ return static_cast<unsigned char>(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+size<buf_size)
+ {
+ // All data fits in the buffer
+ memcpy(out_buf+out_used, buf, size);
+ out_used+=size;
+
+ return size;
+ }
+ else
+ {
+ int ret=0;
+ bool ok=true;
+
+ while(size>0)
+ {
+ // 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
--- /dev/null
+/* $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
--- /dev/null
+/* $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 <msp/core/error.h>
+
+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
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <msp/time/units.h>
+#include "base.h"
+#include "eventdispatcher.h"
+#include "poll.h"
+
+#include <iostream>
+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
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifndef EVENTDISPATCHER_H_
+#define EVENTDISPATCHER_H_
+
+#include <sigc++/connection.h>
+#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<Base *, Slot> SlotMap;
+
+ Poller poller;
+ SlotMap objects;
+
+ void object_events_changed(PollEvent, Base *);
+ void object_deleted(Base *);
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifndef WIN32
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+#include <msp/strings/formatter.h>
+#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<int>(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
--- /dev/null
+/* $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 <string>
+#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
--- /dev/null
+/* $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
--- /dev/null
+#ifndef WIN32
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#include <msp/strings/formatter.h>
+#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
--- /dev/null
+/* $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
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <errno.h>
+#include <msp/strings/formatter.h>
+#include <msp/time/units.h>
+#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<<' '<<ev<<'\n';
+
+ SlotMap::iterator i=objects.find(&obj);
+ if(i!=objects.end())
+ {
+ if(ev)
+ i->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<Time::zero)
+ throw InvalidParameterValue("Invalid timeout");
+
+ return do_poll(static_cast<int>(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<HANDLE *>(&pfd.front()), false, timeout);
+ if(/*ret>=WAIT_OBJECT_0 &&*/ ret<WAIT_OBJECT_0+pfd.size())
+ {
+ SlotMap::iterator i=objects.begin();
+ advance(i, ret-WAIT_OBJECT_0);
+ poll_result.push_back(Slot(i->second.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<pollfd>::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<Time::zero)
+ throw InvalidParameterValue("Invalid timeout");
+
+ return do_poll(obj, pe, static_cast<int>(timeout/Time::msec));
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+/* $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 <poll.h>
+#endif
+#include <list>
+#include <map>
+#include <vector>
+#include <msp/time/timedelta.h>
+#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<Slot> 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<Base *, Slot> SlotMap;
+
+#ifdef WIN32
+ struct pollfd
+ {
+ Handle fd;
+ };
+#endif
+
+ SlotMap objects;
+ std::vector<pollfd> 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
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifdef WIN32
+#include <windows.h>
+#endif
+#include <msp/core/error.h>
+#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
--- /dev/null
+/* $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
--- /dev/null
+/* $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 <windows.h>
+#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
--- /dev/null
+/* $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<size)
+ pos+=obj.read(buf+pos, size-pos);
+
+ return pos;
+}
+
+} // namespace IO
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspio
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifndef MSP_IO_UTILS_H_
+#define MSP_IO_UTILS_H_
+
+namespace Msp {
+namespace IO {
+
+class Base;
+
+/**
+Reads data from an object. Does not return until the requested amount of data
+is read, regardless of the blocking mode of the object.
+
+Note: If the data is not immediately available and the object is in non-blocking
+mode, this function effectively becomes a busyloop until it can get more data.
+*/
+unsigned read_all(Base &, char *, unsigned);
+
+} // namespace IO
+} // namespace Msp
+
+#endif