]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/fileback.cpp
Added wxBackingFile and wxBackedInputStream.
[wxWidgets.git] / src / common / fileback.cpp
diff --git a/src/common/fileback.cpp b/src/common/fileback.cpp
new file mode 100644 (file)
index 0000000..8b2261c
--- /dev/null
@@ -0,0 +1,320 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/common/fileback.cpp
+// Purpose:     Back an input stream with memory or a file
+// Author:      Mike Wetherell
+// RCS-ID:      $Id$
+// Copyright:   (c) 2006 Mike Wetherell
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#if wxUSE_BACKINGFILE
+
+#ifndef WX_PRECOMP
+    #include "wx/stream.h"
+    #include "wx/utils.h"
+    #include "wx/log.h"
+#endif
+
+#include "wx/fileback.h"
+#include "wx/private/filename.h"
+
+// Prefer wxFFile unless wxFile has large file support but wxFFile does not.
+//
+#if wxUSE_FFILE && (defined WXFFILE_LARGEFILE || !defined WXFILE_LARGEFILE)
+typedef wxFFile wxBFFile;
+static const bool wxBadSeek = false;
+#else
+typedef wxFile wxBFFile;
+static const wxFileOffset wxBadSeek = wxInvalidOffset;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Backing file implementation
+
+class wxBackingFileImpl
+{
+public:
+    wxBackingFileImpl(wxInputStream *stream,
+                      size_t bufsize,
+                      const wxString& prefix);
+    ~wxBackingFileImpl();
+
+    void Release() { if (--m_refcount == 0) delete this; }
+    wxBackingFileImpl *AddRef() { m_refcount++; return this; }
+
+    wxStreamError ReadAt(wxFileOffset pos, void *buffer, size_t *size);
+    wxFileOffset GetLength() const;
+
+private:
+    int m_refcount;
+
+    wxInputStream *m_stream;
+    wxStreamError m_parenterror;
+
+    char *m_buf;
+    size_t m_bufsize;
+    size_t m_buflen;
+
+    wxString m_prefix;
+    wxString m_filename;
+    wxBFFile m_file;
+    wxFileOffset m_filelen;
+};
+
+wxBackingFileImpl::wxBackingFileImpl(wxInputStream *stream,
+                                     size_t bufsize,
+                                     const wxString& prefix)
+  : m_refcount(1),
+    m_stream(stream),
+    m_parenterror(wxSTREAM_NO_ERROR),
+    m_buf(NULL),
+    m_bufsize(bufsize),
+    m_buflen(0),
+    m_prefix(prefix),
+    m_filelen(0)
+{
+    wxFileOffset len = m_stream->GetLength();
+
+    if (len >= 0 && len + size_t(0) < m_bufsize)
+        m_bufsize = len;
+
+    if (m_bufsize)
+        m_buf = new char[m_bufsize];
+}
+
+wxBackingFileImpl::~wxBackingFileImpl()
+{
+    delete m_stream;
+    delete [] m_buf;
+
+    if (!m_filename.empty())
+        wxRemoveFile(m_filename);
+}
+
+wxStreamError wxBackingFileImpl::ReadAt(wxFileOffset pos,
+                                        void *buffer,
+                                        size_t *size)
+{
+    size_t reqestedSize = *size;
+    *size = 0;
+
+    // size1 is the number of bytes it will read directly from the backing
+    // file. size2 is any remaining bytes not yet backed, these are returned
+    // from the buffer or read from the parent stream.
+    size_t size1, size2;
+
+    if (pos + reqestedSize <= m_filelen + size_t(0)) {
+        size1 = reqestedSize;
+        size2 = 0;
+    } else if (pos < m_filelen) {
+        size1 = m_filelen - pos;
+        size2 = reqestedSize - size1;
+    } else {
+        size1 = 0;
+        size2 = reqestedSize;
+    }
+
+    if (pos < 0)
+        return wxSTREAM_READ_ERROR;
+
+    // read the backing file
+    if (size1) {
+        if (m_file.Seek(pos) == wxBadSeek)
+            return wxSTREAM_READ_ERROR;
+
+        ssize_t n = m_file.Read(buffer, size1);
+        if (n > 0) {
+            *size = n;
+            pos += n;
+        }
+
+        if (*size < size1)
+            return wxSTREAM_READ_ERROR;
+    }
+
+    // read from the buffer or parent stream
+    if (size2)
+    {
+        while (*size < reqestedSize)
+        {
+            // if pos is further ahead than the parent has been read so far,
+            // then read forward in the parent stream
+            while (pos - m_filelen + size_t(0) >= m_buflen)
+            {
+                // if the parent is small enough, don't use a backing file
+                // just the buffer memory
+                if (!m_stream && m_filelen == 0)
+                    return m_parenterror;
+
+                // before refilling the buffer write out the current buffer
+                // to the backing file if there is anything in it
+                if (m_buflen)
+                {
+                    if (!m_file.IsOpened())
+                        if (!wxCreateTempFile(m_prefix, &m_file, &m_filename))
+                            return wxSTREAM_READ_ERROR;
+
+                    if (m_file.Seek(m_filelen) == wxBadSeek)
+                        return wxSTREAM_READ_ERROR;
+
+                    size_t count = m_file.Write(m_buf, m_buflen);
+                    m_filelen += count;
+
+                    if (count < m_buflen) {
+                        delete m_stream;
+                        m_stream = NULL;
+                        if (count > 0) {
+                            delete m_buf;
+                            m_buf = NULL;
+                            m_buflen = 0;
+                        }
+                        m_parenterror = wxSTREAM_READ_ERROR;
+                        return m_parenterror;
+                    }
+
+                    m_buflen = 0;
+
+                    if (!m_stream) {
+                        delete m_buf;
+                        m_buf = NULL;
+                    }
+                }
+
+                if (!m_stream)
+                    return m_parenterror;
+
+                // refill buffer
+                m_buflen = m_stream->Read(m_buf, m_bufsize).LastRead();
+
+                if (m_buflen < m_bufsize) {
+                    m_parenterror = m_stream->GetLastError();
+                    if (m_parenterror == wxSTREAM_NO_ERROR)
+                        m_parenterror = wxSTREAM_EOF;
+                    delete m_stream;
+                    m_stream = NULL;
+                }
+            }
+
+            // copy to the user's buffer
+            size_t start = pos - m_filelen;
+            size_t len = wxMin(m_buflen - start, reqestedSize - *size);
+
+            memcpy((char*)buffer + *size, m_buf + start, len);
+            *size += len;
+            pos += len;
+        }
+    }
+
+    return wxSTREAM_NO_ERROR;
+}
+
+wxFileOffset wxBackingFileImpl::GetLength() const
+{
+    if (m_parenterror != wxSTREAM_EOF) {
+        wxLogNull nolog;
+        return m_stream->GetLength();
+    }
+    return m_filelen + m_buflen;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Backing File, the handle part
+
+wxBackingFile::wxBackingFile(wxInputStream *stream,
+                             size_t bufsize,
+                             const wxString& prefix)
+  : m_impl(new wxBackingFileImpl(stream, bufsize, prefix))
+{
+}
+
+wxBackingFile::wxBackingFile(const wxBackingFile& backer)
+  : m_impl(backer.m_impl ? backer.m_impl->AddRef() : NULL)
+{
+}
+
+wxBackingFile& wxBackingFile::operator=(const wxBackingFile& backer)
+{
+    if (backer.m_impl != m_impl) {
+        if (m_impl)
+            m_impl->Release();
+
+        m_impl = backer.m_impl;
+
+        if (m_impl)
+            m_impl->AddRef();
+    }
+
+    return *this;
+}
+
+wxBackingFile::~wxBackingFile()
+{
+    if (m_impl)
+        m_impl->Release();
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Input stream
+
+wxBackedInputStream::wxBackedInputStream(const wxBackingFile& backer)
+  : m_backer(backer),
+    m_pos(0)
+{
+}
+
+size_t wxBackedInputStream::OnSysRead(void *buffer, size_t size)
+{
+    if (!IsOk())
+        return 0;
+
+    m_lasterror = m_backer.m_impl->ReadAt(m_pos, buffer, &size);
+    m_pos += size;
+    return size;
+}
+
+wxFileOffset wxBackedInputStream::GetLength() const
+{
+    return m_backer.m_impl->GetLength();
+}
+
+wxFileOffset wxBackedInputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
+{
+    switch (mode) {
+        case wxFromCurrent:
+        {
+            m_pos += pos;
+            break;
+        }
+        case wxFromEnd:
+        {
+            wxFileOffset len = GetLength();
+            if (len == wxInvalidOffset)
+                return wxInvalidOffset;
+            m_pos = len + pos;
+            break;
+        }
+        default:
+        {
+            m_pos = pos;
+            break;
+        }
+    }
+
+    return m_pos;
+}
+
+wxFileOffset wxBackedInputStream::OnSysTell() const
+{
+    return m_pos;
+}
+
+#endif // wxUSE_BACKINGFILE