+/////////////////////////////////////////////////////////////////////////////
+// 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