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: sstream.cpp 45772 2007-05-03 02:19:16Z VZ $
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 #if wxUSE_UNICODE
32     #include "wx/hashmap.h"
33 #endif
34 
35 // ============================================================================
36 // wxStringInputStream implementation
37 // ============================================================================
38 
39 // ----------------------------------------------------------------------------
40 // construction/destruction
41 // ----------------------------------------------------------------------------
42 
43 // TODO:  Do we want to include the null char in the stream?  If so then
44 // just add +1 to m_len in the ctor
wxStringInputStream(const wxString & s)45 wxStringInputStream::wxStringInputStream(const wxString& s)
46 #if wxUSE_UNICODE
47     : m_str(s), m_buf(wxMBConvUTF8().cWX2MB(s).release()), m_len(strlen(m_buf))
48 #else
49     : m_str(s), m_buf((char*)s.c_str()), m_len(s.length())
50 #endif
51 {
52 #if wxUSE_UNICODE
53     wxASSERT_MSG(m_buf != NULL, _T("Could not convert string to UTF8!"));
54 #endif
55     m_pos = 0;
56 }
57 
~wxStringInputStream()58 wxStringInputStream::~wxStringInputStream()
59 {
60 #if wxUSE_UNICODE
61     // Note: wx[W]CharBuffer uses malloc()/free()
62     free(m_buf);
63 #endif
64 }
65 
66 // ----------------------------------------------------------------------------
67 // getlength
68 // ----------------------------------------------------------------------------
69 
GetLength() const70 wxFileOffset wxStringInputStream::GetLength() const
71 {
72     return m_len;
73 }
74 
75 // ----------------------------------------------------------------------------
76 // seek/tell
77 // ----------------------------------------------------------------------------
78 
OnSysSeek(wxFileOffset ofs,wxSeekMode mode)79 wxFileOffset wxStringInputStream::OnSysSeek(wxFileOffset ofs, wxSeekMode mode)
80 {
81     switch ( mode )
82     {
83         case wxFromStart:
84             // nothing to do, ofs already ok
85             break;
86 
87         case wxFromEnd:
88             ofs += m_len;
89             break;
90 
91         case wxFromCurrent:
92             ofs += m_pos;
93             break;
94 
95         default:
96             wxFAIL_MSG( _T("invalid seek mode") );
97             return wxInvalidOffset;
98     }
99 
100     if ( ofs < 0 || ofs > wx_static_cast(wxFileOffset, m_len) )
101         return wxInvalidOffset;
102 
103     // FIXME: this can't be right
104     m_pos = wx_truncate_cast(size_t, ofs);
105 
106     return ofs;
107 }
108 
OnSysTell() const109 wxFileOffset wxStringInputStream::OnSysTell() const
110 {
111     return wx_static_cast(wxFileOffset, m_pos);
112 }
113 
114 // ----------------------------------------------------------------------------
115 // actual IO
116 // ----------------------------------------------------------------------------
117 
OnSysRead(void * buffer,size_t size)118 size_t wxStringInputStream::OnSysRead(void *buffer, size_t size)
119 {
120     const size_t sizeMax = m_len - m_pos;
121 
122     if ( size >= sizeMax )
123     {
124         if ( sizeMax == 0 )
125         {
126             m_lasterror = wxSTREAM_EOF;
127             return 0;
128         }
129 
130         size = sizeMax;
131     }
132 
133     memcpy(buffer, m_buf + m_pos, size);
134     m_pos += size;
135 
136     return size;
137 }
138 
139 // ============================================================================
140 // wxStringOutputStream implementation
141 // ============================================================================
142 
143 // ----------------------------------------------------------------------------
144 // seek/tell
145 // ----------------------------------------------------------------------------
146 
OnSysTell() const147 wxFileOffset wxStringOutputStream::OnSysTell() const
148 {
149     return wx_static_cast(wxFileOffset, m_pos);
150 }
151 
152 // ----------------------------------------------------------------------------
153 // actual IO
154 // ----------------------------------------------------------------------------
155 
156 #if wxUSE_UNICODE
157 
158 // we can't add a member to wxStringOutputStream in 2.8 branch without breaking
159 // backwards binary compatibility, so we emulate it by using a hash indexed by
160 // wxStringOutputStream pointers
161 
162 // can't use wxCharBuffer as it has incorrect copying semantics and doesn't
163 // store the length which we need here
164 WX_DECLARE_VOIDPTR_HASH_MAP(wxMemoryBuffer, wxStringStreamUnconvBuffers);
165 
166 static wxStringStreamUnconvBuffers gs_unconverted;
167 
~wxStringOutputStream()168 wxStringOutputStream::~wxStringOutputStream()
169 {
170     // TODO: check that nothing remains (i.e. the unconverted buffer is empty)?
171     gs_unconverted.erase(this);
172 }
173 
174 #endif // wxUSE_UNICODE
175 
OnSysWrite(const void * buffer,size_t size)176 size_t wxStringOutputStream::OnSysWrite(const void *buffer, size_t size)
177 {
178     const char *p = wx_static_cast(const char *, buffer);
179 
180 #if wxUSE_UNICODE
181     // the part of the string we have here may be incomplete, i.e. it can stop
182     // in the middle of an UTF-8 character and so converting it would fail; if
183     // this is the case, accumulate the part which we failed to convert until
184     // we get the rest (and also take into account the part which we might have
185     // left unconverted before)
186     const char *src;
187     size_t srcLen;
188     wxMemoryBuffer& unconv = gs_unconverted[this];
189     if ( unconv.GetDataLen() )
190     {
191         // append the new data to the data remaining since the last time
192         unconv.AppendData(p, size);
193         src = unconv;
194         srcLen = unconv.GetDataLen();
195     }
196     else // no unconverted data left, avoid extra copy
197     {
198         src = p;
199         srcLen = size;
200     }
201 
202     wxWCharBuffer wbuf(m_conv.cMB2WC(src, srcLen, NULL /* out len */));
203     if ( wbuf )
204     {
205         // conversion succeeded, clear the unconverted buffer
206         unconv = wxMemoryBuffer(0);
207 
208         *m_str += wbuf;
209     }
210     else // conversion failed
211     {
212         // remember unconverted data if there had been none before (otherwise
213         // we've already got it in the buffer)
214         if ( src == p )
215             unconv.AppendData(src, srcLen);
216 
217         // pretend that we wrote the data anyhow, otherwise the caller would
218         // believe there was an error and this might not be the case, but do
219         // not update m_pos as m_str hasn't changed
220         return size;
221     }
222 #else // !wxUSE_UNICODE
223     // append directly, no conversion necessary
224     m_str->Append(wxString(p, size));
225 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
226 
227     // update position
228     m_pos += size;
229 
230     // return number of bytes actually written
231     return size;
232 }
233 
234 #endif // wxUSE_STREAMS
235 
236