|
1 /* |
|
2 * Copyright (c) 1996,1997 |
|
3 * Silicon Graphics Computer Systems, Inc. |
|
4 * |
|
5 * Copyright (c) 1999 |
|
6 * Boris Fomitchev |
|
7 * |
|
8 * This material is provided "as is", with absolutely no warranty expressed |
|
9 * or implied. Any use is at your own risk. |
|
10 * |
|
11 * Permission to use or copy this software for any purpose is hereby granted |
|
12 * without fee, provided the above notices are retained on all copies. |
|
13 * Permission to modify the code and to distribute modified code is granted, |
|
14 * provided the above notices are retained, and a notice that the code was |
|
15 * modified is included with the above copyright notice. |
|
16 * |
|
17 */ |
|
18 #ifndef _STLP_FSTREAM_C |
|
19 #define _STLP_FSTREAM_C |
|
20 |
|
21 #ifndef _STLP_INTERNAL_FSTREAM_H |
|
22 # include <stl/_fstream.h> |
|
23 #endif |
|
24 |
|
25 #ifndef _STLP_INTERNAL_LIMITS |
|
26 # include <stl/_limits.h> |
|
27 #endif |
|
28 |
|
29 _STLP_BEGIN_NAMESPACE |
|
30 |
|
31 # if defined ( _STLP_NESTED_TYPE_PARAM_BUG ) |
|
32 // no wchar_t is supported for this mode |
|
33 # define __BF_int_type__ int |
|
34 # define __BF_pos_type__ streampos |
|
35 # define __BF_off_type__ streamoff |
|
36 # else |
|
37 # define __BF_int_type__ _STLP_TYPENAME_ON_RETURN_TYPE basic_filebuf<_CharT, _Traits>::int_type |
|
38 # define __BF_pos_type__ _STLP_TYPENAME_ON_RETURN_TYPE basic_filebuf<_CharT, _Traits>::pos_type |
|
39 # define __BF_off_type__ _STLP_TYPENAME_ON_RETURN_TYPE basic_filebuf<_CharT, _Traits>::off_type |
|
40 # endif |
|
41 |
|
42 |
|
43 //---------------------------------------------------------------------- |
|
44 // Public basic_filebuf<> member functions |
|
45 |
|
46 template <class _CharT, class _Traits> |
|
47 basic_filebuf<_CharT, _Traits>::basic_filebuf() |
|
48 : basic_streambuf<_CharT, _Traits>(), _M_base(), |
|
49 _M_constant_width(false), _M_always_noconv(false), |
|
50 _M_int_buf_dynamic(false), |
|
51 _M_in_input_mode(false), _M_in_output_mode(false), |
|
52 _M_in_error_mode(false), _M_in_putback_mode(false), |
|
53 _M_int_buf(0), _M_int_buf_EOS(0), |
|
54 _M_ext_buf(0), _M_ext_buf_EOS(0), |
|
55 _M_ext_buf_converted(0), _M_ext_buf_end(0), |
|
56 _M_state(_STLP_DEFAULT_CONSTRUCTED(_State_type)), |
|
57 _M_end_state(_STLP_DEFAULT_CONSTRUCTED(_State_type)), |
|
58 _M_mmap_base(0), _M_mmap_len(0), |
|
59 _M_saved_eback(0), _M_saved_gptr(0), _M_saved_egptr(0), |
|
60 _M_codecvt(0), |
|
61 _M_width(1), _M_max_width(1) |
|
62 { |
|
63 this->_M_setup_codecvt(locale(), false); |
|
64 } |
|
65 |
|
66 |
|
67 template <class _CharT, class _Traits> |
|
68 basic_filebuf<_CharT, _Traits>* |
|
69 basic_filebuf<_CharT, _Traits>::close() { |
|
70 bool __ok = this->is_open(); |
|
71 |
|
72 if (_M_in_output_mode) { |
|
73 __ok = __ok && !_Traits::eq_int_type(this->overflow(traits_type::eof()), |
|
74 traits_type::eof()); |
|
75 __ok == __ok && this->_M_unshift(); |
|
76 } |
|
77 else if (_M_in_input_mode) |
|
78 this->_M_exit_input_mode(); |
|
79 |
|
80 // Note order of arguments. We close the file even if __ok is false. |
|
81 __ok = _M_base._M_close() && __ok; |
|
82 |
|
83 // Restore the initial state, except that we don't deallocate the buffer |
|
84 // or mess with the cached codecvt information. |
|
85 _M_state = _M_end_state = _State_type(); |
|
86 _M_ext_buf_converted = _M_ext_buf_end = 0; |
|
87 |
|
88 _M_mmap_base = 0; |
|
89 _M_mmap_len = 0; |
|
90 |
|
91 this->setg(0, 0, 0); |
|
92 this->setp(0, 0); |
|
93 |
|
94 _M_saved_eback = _M_saved_gptr = _M_saved_egptr = 0; |
|
95 |
|
96 _M_in_input_mode = _M_in_output_mode = _M_in_error_mode = _M_in_putback_mode |
|
97 = false; |
|
98 |
|
99 return __ok ? this : 0; |
|
100 } |
|
101 |
|
102 // This member function is called whenever we exit input mode. |
|
103 // It unmaps the memory-mapped file, if any, and sets |
|
104 // _M_in_input_mode to false. |
|
105 template <class _CharT, class _Traits> |
|
106 void basic_filebuf<_CharT, _Traits>::_M_exit_input_mode() { |
|
107 if (_M_mmap_base != 0) |
|
108 _M_base._M_unmap(_M_mmap_base, _M_mmap_len); |
|
109 _M_in_input_mode = false; |
|
110 _M_mmap_base = 0; |
|
111 } |
|
112 |
|
113 |
|
114 |
|
115 //---------------------------------------------------------------------- |
|
116 // basic_filebuf<> helper functions. |
|
117 |
|
118 //---------------------------------------- |
|
119 // Helper functions for switching between modes. |
|
120 |
|
121 // This member function is called if we're performing the first I/O |
|
122 // operation on a filebuf, or if we're performing an input operation |
|
123 // immediately after a seek. |
|
124 template <class _CharT, class _Traits> |
|
125 bool basic_filebuf<_CharT, _Traits>::_M_switch_to_input_mode() { |
|
126 if (this->is_open() && (((int)_M_base.__o_mode() & (int)ios_base::in) != 0) |
|
127 && (_M_in_output_mode == 0) && (_M_in_error_mode == 0)) { |
|
128 if (!_M_int_buf && !_M_allocate_buffers()) |
|
129 return false; |
|
130 |
|
131 _M_ext_buf_converted = _M_ext_buf; |
|
132 _M_ext_buf_end = _M_ext_buf; |
|
133 |
|
134 _M_end_state = _M_state; |
|
135 |
|
136 _M_in_input_mode = true; |
|
137 return true; |
|
138 } |
|
139 |
|
140 return false; |
|
141 } |
|
142 |
|
143 |
|
144 // This member function is called if we're performing the first I/O |
|
145 // operation on a filebuf, or if we're performing an output operation |
|
146 // immediately after a seek. |
|
147 template <class _CharT, class _Traits> |
|
148 bool basic_filebuf<_CharT, _Traits>::_M_switch_to_output_mode() { |
|
149 if (this->is_open() && (_M_base.__o_mode() & (int)ios_base::out) && |
|
150 _M_in_input_mode == 0 && _M_in_error_mode == 0) { |
|
151 |
|
152 if (!_M_int_buf && !_M_allocate_buffers()) |
|
153 return false; |
|
154 |
|
155 // In append mode, every write does an implicit seek to the end |
|
156 // of the file. Whenever leaving output mode, the end of file |
|
157 // get put in the initial shift state. |
|
158 if (_M_base.__o_mode() & ios_base::app) |
|
159 _M_state = _State_type(); |
|
160 |
|
161 this->setp(_M_int_buf, _M_int_buf_EOS - 1); |
|
162 _M_in_output_mode = true; |
|
163 return true; |
|
164 } |
|
165 |
|
166 return false; |
|
167 } |
|
168 |
|
169 |
|
170 //---------------------------------------- |
|
171 // Helper functions for input |
|
172 |
|
173 // This member function is called if there is an error during input. |
|
174 // It puts the filebuf in error mode, clear the get area buffer, and |
|
175 // returns eof. |
|
176 // returns eof. Error mode is sticky; it is cleared only by close or |
|
177 // seek. |
|
178 |
|
179 template <class _CharT, class _Traits> |
|
180 __BF_int_type__ |
|
181 basic_filebuf<_CharT, _Traits>::_M_input_error() { |
|
182 this->_M_exit_input_mode(); |
|
183 _M_in_output_mode = false; |
|
184 _M_in_error_mode = true; |
|
185 this->setg(0, 0, 0); |
|
186 return traits_type::eof(); |
|
187 } |
|
188 |
|
189 template <class _CharT, class _Traits> |
|
190 __BF_int_type__ |
|
191 basic_filebuf<_CharT, _Traits>::_M_underflow_aux() { |
|
192 // We have the state and file position from the end of the internal |
|
193 // buffer. This round, they become the beginning of the internal buffer. |
|
194 _M_state = _M_end_state; |
|
195 |
|
196 // Fill the external buffer. Start with any leftover characters that |
|
197 // didn't get converted last time. |
|
198 if (_M_ext_buf_end > _M_ext_buf_converted) |
|
199 |
|
200 _M_ext_buf_end = copy(_M_ext_buf_converted, _M_ext_buf_end, _M_ext_buf); |
|
201 // boris : copy_backward did not work |
|
202 //_M_ext_buf_end = copy_backward(_M_ext_buf_converted, _M_ext_buf_end, |
|
203 //_M_ext_buf+ (_M_ext_buf_end - _M_ext_buf_converted)); |
|
204 else |
|
205 _M_ext_buf_end = _M_ext_buf; |
|
206 |
|
207 // Now fill the external buffer with characters from the file. This is |
|
208 // a loop because occasionally we don't get enough external characters |
|
209 // to make progress. |
|
210 for (;;) { |
|
211 ptrdiff_t __n = _M_base._M_read(_M_ext_buf_end, _M_ext_buf_EOS - _M_ext_buf_end); |
|
212 |
|
213 // Don't enter error mode for a failed read. Error mode is sticky, |
|
214 // and we might succeed if we try again. |
|
215 if (__n <= 0) |
|
216 return traits_type::eof(); |
|
217 |
|
218 // Convert the external buffer to internal characters. |
|
219 _M_ext_buf_end += __n; |
|
220 const char* __enext; |
|
221 _CharT* __inext; |
|
222 |
|
223 typename _Codecvt::result __status |
|
224 = _M_codecvt->in(_M_end_state, |
|
225 _M_ext_buf, _M_ext_buf_end, __enext, |
|
226 _M_int_buf, _M_int_buf_EOS, __inext); |
|
227 |
|
228 // Error conditions: (1) Return value of error. (2) Producing internal |
|
229 // characters without consuming external characters. (3) In fixed-width |
|
230 // encodings, producing an internal sequence whose length is inconsistent |
|
231 // with that of the internal sequence. (4) Failure to produce any |
|
232 // characters if we have enough characters in the external buffer, where |
|
233 // "enough" means the largest possible width of a single character. |
|
234 if (__status == _Codecvt::noconv) |
|
235 return _Noconv_input<_Traits>::_M_doit(this); |
|
236 else if (__status == _Codecvt::error || |
|
237 (__inext != _M_int_buf && __enext == _M_ext_buf) || |
|
238 (_M_constant_width && |
|
239 // __inext - _M_int_buf != _M_width * (__enext - _M_ext_buf)) || |
|
240 (__inext - _M_int_buf) * _M_width != (__enext - _M_ext_buf)) || |
|
241 (__inext == _M_int_buf && __enext - _M_ext_buf >= _M_max_width)) |
|
242 return _M_input_error(); |
|
243 else if (__inext != _M_int_buf) { |
|
244 _M_ext_buf_converted = _M_ext_buf + (__enext - _M_ext_buf); |
|
245 this->setg(_M_int_buf, _M_int_buf, __inext); |
|
246 return traits_type::to_int_type(*_M_int_buf); |
|
247 } |
|
248 // We need to go around the loop again to get more external characters. |
|
249 } |
|
250 } |
|
251 |
|
252 //---------------------------------------- |
|
253 // Helper functions for output |
|
254 |
|
255 // This member function is called if there is an error during output. |
|
256 // It puts the filebuf in error mode, clear the put area buffer, and |
|
257 // returns eof. Error mode is sticky; it is cleared only by close or |
|
258 // seek. |
|
259 template <class _CharT, class _Traits> |
|
260 __BF_int_type__ |
|
261 basic_filebuf<_CharT, _Traits>::_M_output_error() { |
|
262 _M_in_output_mode = false; |
|
263 _M_in_input_mode = false; |
|
264 _M_in_error_mode = true; |
|
265 this->setp(0, 0); |
|
266 return traits_type::eof(); |
|
267 } |
|
268 |
|
269 |
|
270 // Write whatever sequence of characters is necessary to get back to |
|
271 // the initial shift state. This function overwrites the external |
|
272 // buffer, changes the external file position, and changes the state. |
|
273 // Precondition: the internal buffer is empty. |
|
274 template <class _CharT, class _Traits> |
|
275 bool basic_filebuf<_CharT, _Traits>::_M_unshift() { |
|
276 if (_M_in_output_mode && !_M_constant_width) { |
|
277 typename _Codecvt::result __status; |
|
278 do { |
|
279 char* __enext = _M_ext_buf; |
|
280 __status = _M_codecvt->unshift(_M_state, |
|
281 _M_ext_buf, _M_ext_buf_EOS, __enext); |
|
282 if (__status == _Codecvt::noconv || |
|
283 (__enext == _M_ext_buf && __status == _Codecvt::ok)) |
|
284 return true; |
|
285 else if (__status == _Codecvt::error) |
|
286 return false; |
|
287 else if (!_M_write(_M_ext_buf, __enext - _M_ext_buf)) |
|
288 return false; |
|
289 } while (__status == _Codecvt::partial); |
|
290 } |
|
291 |
|
292 return true; |
|
293 } |
|
294 |
|
295 |
|
296 //---------------------------------------- |
|
297 // Helper functions for buffer allocation and deallocation |
|
298 |
|
299 // This member function is called when we're initializing a filebuf's |
|
300 // internal and external buffers. The argument is the size of the |
|
301 // internal buffer; the external buffer is sized using the character |
|
302 // width in the current encoding. Preconditions: the buffers are currently |
|
303 // null. __n >= 1. __buf is either a null pointer or a pointer to an |
|
304 // array show size is at least __n. |
|
305 |
|
306 // We need __n >= 1 for two different reasons. For input, the base |
|
307 // class always needs a buffer because of the semantics of underflow(). |
|
308 // For output, we want to have an internal buffer that's larger by one |
|
309 // element than the buffer that the base class knows about. (See |
|
310 // basic_filebuf<>::overflow() for the reason.) |
|
311 template <class _CharT, class _Traits> |
|
312 bool basic_filebuf<_CharT, _Traits>::_M_allocate_buffers(_CharT* __buf, streamsize __n) { |
|
313 //The major hypothesis in the following implementation is that size_t is unsigned. |
|
314 //We also need streamsize byte representation to be larger or equal to the int |
|
315 //representation to correctly store the encoding information. |
|
316 _STLP_STATIC_ASSERT(!numeric_limits<size_t>::is_signed && |
|
317 sizeof(streamsize) >= sizeof(int)) |
|
318 |
|
319 if (__buf == 0) { |
|
320 streamsize __bufsize = __n * sizeof(_CharT); |
|
321 //We first check that the streamsize representation can't overflow a size_t one. |
|
322 //If it can, we check that __bufsize is not higher than the size_t max value. |
|
323 if ((sizeof(streamsize) > sizeof(size_t)) && |
|
324 (__bufsize > __STATIC_CAST(streamsize, (numeric_limits<size_t>::max)()))) |
|
325 return false; |
|
326 _M_int_buf = __STATIC_CAST(_CharT*, malloc(__STATIC_CAST(size_t, __bufsize))); |
|
327 if (!_M_int_buf) |
|
328 return false; |
|
329 _M_int_buf_dynamic = true; |
|
330 } |
|
331 else { |
|
332 _M_int_buf = __buf; |
|
333 _M_int_buf_dynamic = false; |
|
334 } |
|
335 |
|
336 streamsize __ebufsiz = (max)(__n * __STATIC_CAST(streamsize, _M_width), |
|
337 __STATIC_CAST(streamsize, _M_codecvt->max_length())); |
|
338 _M_ext_buf = 0; |
|
339 if ((sizeof(streamsize) < sizeof(size_t)) || |
|
340 ((sizeof(streamsize) == sizeof(size_t)) && numeric_limits<streamsize>::is_signed) || |
|
341 (__ebufsiz <= __STATIC_CAST(streamsize, (numeric_limits<size_t>::max)()))) { |
|
342 _M_ext_buf = __STATIC_CAST(char*, malloc(__STATIC_CAST(size_t, __ebufsiz))); |
|
343 } |
|
344 |
|
345 if (!_M_ext_buf) { |
|
346 _M_deallocate_buffers(); |
|
347 return false; |
|
348 } |
|
349 |
|
350 _M_int_buf_EOS = _M_int_buf + __STATIC_CAST(ptrdiff_t, __n); |
|
351 _M_ext_buf_EOS = _M_ext_buf + __STATIC_CAST(ptrdiff_t, __ebufsiz); |
|
352 return true; |
|
353 } |
|
354 |
|
355 // Abbreviation for the most common case. |
|
356 template <class _CharT, class _Traits> |
|
357 bool basic_filebuf<_CharT, _Traits>::_M_allocate_buffers() { |
|
358 // Choose a buffer that's at least 4096 characters long and that's a |
|
359 // multiple of the page size. |
|
360 streamsize __default_bufsiz = |
|
361 ((_M_base.__page_size() + 4095UL) / _M_base.__page_size()) * _M_base.__page_size(); |
|
362 return _M_allocate_buffers(0, __default_bufsiz); |
|
363 } |
|
364 |
|
365 template <class _CharT, class _Traits> |
|
366 void basic_filebuf<_CharT, _Traits>::_M_deallocate_buffers() { |
|
367 if (_M_int_buf_dynamic) |
|
368 free(_M_int_buf); |
|
369 free(_M_ext_buf); |
|
370 _M_int_buf = 0; |
|
371 _M_int_buf_EOS = 0; |
|
372 _M_ext_buf = 0; |
|
373 _M_ext_buf_EOS = 0; |
|
374 } |
|
375 |
|
376 |
|
377 //---------------------------------------- |
|
378 // Helper functiosn for seek and imbue |
|
379 |
|
380 template <class _CharT, class _Traits> |
|
381 bool basic_filebuf<_CharT, _Traits>::_M_seek_init(bool __do_unshift) { |
|
382 // If we're in error mode, leave it. |
|
383 _M_in_error_mode = false; |
|
384 |
|
385 // Flush the output buffer if we're in output mode, and (conditionally) |
|
386 // emit an unshift sequence. |
|
387 if (_M_in_output_mode) { |
|
388 bool __ok = !traits_type::eq_int_type(this->overflow(traits_type::eof()), |
|
389 traits_type::eof()); |
|
390 if (__do_unshift) |
|
391 __ok = __ok && this->_M_unshift(); |
|
392 if (!__ok) { |
|
393 _M_in_output_mode = false; |
|
394 _M_in_error_mode = true; |
|
395 this->setp(0, 0); |
|
396 return false; |
|
397 } |
|
398 } |
|
399 |
|
400 // Discard putback characters, if any. |
|
401 if (_M_in_input_mode && _M_in_putback_mode) |
|
402 _M_exit_putback_mode(); |
|
403 |
|
404 return true; |
|
405 } |
|
406 |
|
407 |
|
408 /* Change the filebuf's locale. This member function has no effect |
|
409 * unless it is called before any I/O is performed on the stream. |
|
410 * This function is called on construction and on an imbue call. In the |
|
411 * case of the construction the codecvt facet might be a custom one if |
|
412 * the basic_filebuf user has instanciate it with a custom char_traits. |
|
413 * The user will have to call imbue before any I/O operation. |
|
414 */ |
|
415 template <class _CharT, class _Traits> |
|
416 void basic_filebuf<_CharT, _Traits>::_M_setup_codecvt(const locale& __loc, bool __on_imbue) { |
|
417 if (has_facet<_Codecvt>(__loc)) { |
|
418 _M_codecvt = &use_facet<_Codecvt>(__loc) ; |
|
419 int __encoding = _M_codecvt->encoding(); |
|
420 |
|
421 _M_width = (max)(__encoding, 1); |
|
422 _M_max_width = _M_codecvt->max_length(); |
|
423 _M_constant_width = __encoding > 0; |
|
424 _M_always_noconv = _M_codecvt->always_noconv(); |
|
425 } |
|
426 else { |
|
427 _M_codecvt = 0; |
|
428 _M_width = _M_max_width = 1; |
|
429 _M_constant_width = _M_always_noconv = false; |
|
430 if (__on_imbue) { |
|
431 //This call will generate an exception reporting the problem. |
|
432 use_facet<_Codecvt>(__loc); |
|
433 } |
|
434 } |
|
435 } |
|
436 |
|
437 _STLP_END_NAMESPACE |
|
438 |
|
439 # undef __BF_int_type__ |
|
440 # undef __BF_pos_type__ |
|
441 # undef __BF_off_type__ |
|
442 |
|
443 #endif /* _STLP_FSTREAM_C */ |
|
444 |
|
445 // Local Variables: |
|
446 // mode:C++ |
|
447 // End: |