Add an I/O class to access a restricted range of bytes from another object
authorMikko Rasa <tdb@tdb.fi>
Thu, 8 Nov 2012 19:51:30 +0000 (21:51 +0200)
committerMikko Rasa <tdb@tdb.fi>
Thu, 8 Nov 2012 19:51:30 +0000 (21:51 +0200)
source/io/slice.cpp [new file with mode: 0644]
source/io/slice.h [new file with mode: 0644]

diff --git a/source/io/slice.cpp b/source/io/slice.cpp
new file mode 100644 (file)
index 0000000..423c1a6
--- /dev/null
@@ -0,0 +1,111 @@
+#include <stdexcept>
+#include "slice.h"
+
+using namespace std;
+
+namespace Msp {
+namespace IO {
+
+Slice::Slice(Seekable &b, SeekOffset s, SeekOffset l):
+       below(b),
+       start_offset(s),
+       length(l),
+       position(0),
+       sync_position(true)
+{
+       if(s<0 || l<0)
+               throw invalid_argument("Slice");
+
+       mode = below.get_mode()&M_RDWR;
+       below.signal_flush_required.connect(sigc::mem_fun(this, &Slice::flush));
+}
+
+void Slice::flush()
+{
+       sync_position = true;
+}
+
+unsigned Slice::prepare_op(unsigned size, Mode m)
+{
+       check_access(m);
+
+       if(sync_position)
+               seek(position, S_BEG);
+
+       SeekOffset remaining = start_offset+length-position;
+       if(size>remaining)
+               size = remaining;
+       if(!size && m==M_READ)
+               set_eof();
+
+       return size;
+}
+
+unsigned Slice::do_write(const char *data, unsigned size)
+{
+       size = prepare_op(size, M_WRITE);
+       if(!size)
+               return 0;
+
+       unsigned len = below.write(data, size);
+       position += len;
+       return len;
+}
+
+unsigned Slice::do_read(char *data, unsigned size)
+{
+       size = prepare_op(size, M_READ);
+       if(!size)
+               return 0;
+
+       unsigned len = below.read(data, size);
+       if(!len && below.eof())
+               set_eof();
+       position += len;
+       return len;
+}
+
+unsigned Slice::put(char c)
+{
+       if(!prepare_op(1, M_WRITE))
+               return 0;
+
+       unsigned len = below.put(c);
+       position += len;
+       return len;
+}
+
+int Slice::get()
+{
+       if(!prepare_op(1, M_READ))
+               return 0;
+
+       int c = below.get();
+       if(c==-1 && below.eof())
+               set_eof();
+       else
+               ++position;
+       return c;
+}
+
+SeekOffset Slice::seek(SeekOffset off, SeekType type)
+{
+       if(type==S_BEG)
+               off += start_offset;
+       else if(type==S_CUR)
+               off += position;
+       else if(type==S_END)
+               off += start_offset+length;
+
+       if(off<start_offset || off>start_offset+length)
+               throw bad_seek(off, type);
+
+       signal_flush_required.emit();
+
+       position = below.seek(off, S_BEG)-start_offset;
+       sync_position = false;
+       return position;
+}
+
+} // namespace IO
+} // namespace Msp
diff --git a/source/io/slice.h b/source/io/slice.h
new file mode 100644 (file)
index 0000000..f611585
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef MSP_IO_SLICE_H_
+#define MSP_IO_SLICE_H_
+
+#include "seekable.h"
+
+namespace Msp {
+namespace IO {
+
+/**
+Presents a part of seekable I/O object as if it was a complete object.  This
+allows in-place access to embedded resources where the loader expects to
+receive a complete file.
+
+When a Slice is created, its read/write offset is placed at the beginning of
+its range.  If the offset of the underlying object is changed, the Slice will
+restore it before the next access.  This enables multiple Slices to be created
+on top of the same object.
+*/
+class Slice: public Seekable
+{
+private:
+       Seekable &below;
+       SeekOffset start_offset;
+       SeekOffset length;
+       SeekOffset position;
+       bool sync_position;
+
+public:
+       Slice(Seekable &, SeekOffset, SeekOffset);
+
+private:
+       void flush();
+
+       unsigned prepare_op(unsigned, Mode);
+protected:
+       virtual unsigned do_write(const char *, unsigned);
+       virtual unsigned do_read(char *, unsigned);
+
+public:
+       virtual unsigned put(char);
+       virtual int get();
+
+       virtual SeekOffset seek(SeekOffset, SeekType);
+       virtual SeekOffset tell() const { return position; }
+};
+
+} // namespace IO
+} // namespace Msp
+
+#endif