don't fail in wxTransferStreamToFile if file size is exact multiple of 4KB (bug 1835918)
[wxWidgets.git] / src / common / sstream.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: common/sstream.cpp
3 // Purpose: string-based streams implementation
4 // Author: Vadim Zeitlin
5 // Modified by: Ryan Norton (UTF8 UNICODE)
6 // Created: 2004-09-19
7 // RCS-ID: $Id$
8 // Copyright: (c) 2004 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_STREAMS
28
29 #include "wx/sstream.h"
30
31 // ============================================================================
32 // wxStringInputStream implementation
33 // ============================================================================
34
35 // ----------------------------------------------------------------------------
36 // construction/destruction
37 // ----------------------------------------------------------------------------
38
39 // TODO: Do we want to include the null char in the stream? If so then
40 // just add +1 to m_len in the ctor
41 wxStringInputStream::wxStringInputStream(const wxString& s)
42 #if wxUSE_UNICODE
43 // FIXME-UTF8: use wxCharBufferWithLength if we have it
44 : m_str(s), m_buf(s.utf8_str()), m_len(strlen(m_buf))
45 #else
46 : m_str(s), m_buf(s.mb_str()), m_len(s.length())
47 #endif
48 {
49 #if wxUSE_UNICODE
50 wxASSERT_MSG(m_buf.data() != NULL, _T("Could not convert string to UTF8!"));
51 #endif
52 m_pos = 0;
53 }
54
55 // ----------------------------------------------------------------------------
56 // getlength
57 // ----------------------------------------------------------------------------
58
59 wxFileOffset wxStringInputStream::GetLength() const
60 {
61 return m_len;
62 }
63
64 // ----------------------------------------------------------------------------
65 // seek/tell
66 // ----------------------------------------------------------------------------
67
68 wxFileOffset wxStringInputStream::OnSysSeek(wxFileOffset ofs, wxSeekMode mode)
69 {
70 switch ( mode )
71 {
72 case wxFromStart:
73 // nothing to do, ofs already ok
74 break;
75
76 case wxFromEnd:
77 ofs += m_len;
78 break;
79
80 case wxFromCurrent:
81 ofs += m_pos;
82 break;
83
84 default:
85 wxFAIL_MSG( _T("invalid seek mode") );
86 return wxInvalidOffset;
87 }
88
89 if ( ofs < 0 || ofs > wx_static_cast(wxFileOffset, m_len) )
90 return wxInvalidOffset;
91
92 // FIXME: this can't be right
93 m_pos = wx_truncate_cast(size_t, ofs);
94
95 return ofs;
96 }
97
98 wxFileOffset wxStringInputStream::OnSysTell() const
99 {
100 return wx_static_cast(wxFileOffset, m_pos);
101 }
102
103 // ----------------------------------------------------------------------------
104 // actual IO
105 // ----------------------------------------------------------------------------
106
107 size_t wxStringInputStream::OnSysRead(void *buffer, size_t size)
108 {
109 const size_t sizeMax = m_len - m_pos;
110
111 if ( size >= sizeMax )
112 {
113 if ( sizeMax == 0 )
114 {
115 m_lasterror = wxSTREAM_EOF;
116 return 0;
117 }
118
119 size = sizeMax;
120 }
121
122 memcpy(buffer, m_buf.data() + m_pos, size);
123 m_pos += size;
124
125 return size;
126 }
127
128 // ============================================================================
129 // wxStringOutputStream implementation
130 // ============================================================================
131
132 // ----------------------------------------------------------------------------
133 // seek/tell
134 // ----------------------------------------------------------------------------
135
136 wxFileOffset wxStringOutputStream::OnSysTell() const
137 {
138 return wx_static_cast(wxFileOffset, m_pos);
139 }
140
141 // ----------------------------------------------------------------------------
142 // actual IO
143 // ----------------------------------------------------------------------------
144
145 size_t wxStringOutputStream::OnSysWrite(const void *buffer, size_t size)
146 {
147 const char *p = wx_static_cast(const char *, buffer);
148
149 #if wxUSE_UNICODE_WCHAR
150 // the part of the string we have here may be incomplete, i.e. it can stop
151 // in the middle of an UTF-8 character and so converting it would fail; if
152 // this is the case, accumulate the part which we failed to convert until
153 // we get the rest (and also take into account the part which we might have
154 // left unconverted before)
155 const char *src;
156 size_t srcLen;
157 if ( m_unconv.GetDataLen() )
158 {
159 // append the new data to the data remaining since the last time
160 m_unconv.AppendData(p, size);
161 src = m_unconv;
162 srcLen = m_unconv.GetDataLen();
163 }
164 else // no unconverted data left, avoid extra copy
165 {
166 src = p;
167 srcLen = size;
168 }
169
170 size_t wlen;
171 wxWCharBuffer wbuf(m_conv.cMB2WC(src, srcLen, &wlen));
172 if ( wbuf )
173 {
174 // conversion succeeded, clear the unconverted buffer
175 m_unconv = wxMemoryBuffer(0);
176
177 m_str->append(wbuf, wlen);
178 }
179 else // conversion failed
180 {
181 // remember unconverted data if there had been none before (otherwise
182 // we've already got it in the buffer)
183 if ( src == p )
184 m_unconv.AppendData(src, srcLen);
185
186 // pretend that we wrote the data anyhow, otherwise the caller would
187 // believe there was an error and this might not be the case, but do
188 // not update m_pos as m_str hasn't changed
189 return size;
190 }
191 #else // !wxUSE_UNICODE_WCHAR
192 // no recoding necessary, the data is supposed to already be in UTF-8 (if
193 // supported) or ASCII otherwise
194 m_str->append(p, size);
195 #endif // wxUSE_UNICODE_WCHAR/!wxUSE_UNICODE_WCHAR
196
197 // update position
198 m_pos += size;
199
200 // return number of bytes actually written
201 return size;
202 }
203
204 #endif // wxUSE_STREAMS
205