OSDN Git Service

2003-11-30 Paolo Carlini <pcarlini@suse.de>
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / config / locale / gnu / codecvt_members.cc
1 // std::codecvt implementation details, GNU version -*- C++ -*-
2
3 // Copyright (C) 2002, 2003 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15
16 // You should have received a copy of the GNU General Public License along
17 // with this library; see the file COPYING.  If not, write to the Free
18 // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 // USA.
20
21 // As a special exception, you may use this file as part of a free software
22 // library without restriction.  Specifically, if other files instantiate
23 // templates or use macros or inline functions from this file, or you compile
24 // this file and link it with other files to produce an executable, this
25 // file does not by itself cause the resulting executable to be covered by
26 // the GNU General Public License.  This exception does not however
27 // invalidate any other reasons why the executable file might be covered by
28 // the GNU General Public License.
29
30 //
31 // ISO C++ 14882: 22.2.1.5 - Template class codecvt
32 //
33
34 // Written by Benjamin Kosnik <bkoz@redhat.com>
35
36 #include <locale>
37 #include <bits/c++locale_internal.h>
38
39 namespace std
40 {
41   // Specializations.
42 #ifdef _GLIBCXX_USE_WCHAR_T
43   codecvt_base::result
44   codecvt<wchar_t, char, mbstate_t>::
45   do_out(state_type& __state, const intern_type* __from, 
46          const intern_type* __from_end, const intern_type*& __from_next,
47          extern_type* __to, extern_type* __to_end,
48          extern_type*& __to_next) const
49   {
50     result __ret = ok;
51     state_type __tmp_state(__state);
52
53 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
54     __c_locale __old = __uselocale(_M_c_locale_codecvt);
55 #endif
56
57     // wcsnrtombs is *very* fast but stops if encounters NUL characters:
58     // in case we fall back to wcrtomb and then continue, in a loop.
59     // NB: wcsnrtombs is a GNU extension
60     for (__from_next = __from, __to_next = __to;
61          __from_next < __from_end && __to_next < __to_end
62          && __ret == ok;)
63       {
64         const intern_type* __from_chunk_end = wmemchr(__from_next, L'\0',
65                                                       __from_end - __from_next);
66         if (!__from_chunk_end)
67           __from_chunk_end = __from_end;
68
69         __from = __from_next;
70         const size_t __conv = wcsnrtombs(__to_next, &__from_next,
71                                          __from_chunk_end - __from_next,
72                                          __to_end - __to_next, &__state);
73         if (__conv == static_cast<size_t>(-1))
74           {
75             // In case of error, in order to stop at the exact place we
76             // have to start again from the beginning with a series of
77             // wcrtomb.
78             for (; __from < __from_next; ++__from)
79               __to_next += wcrtomb(__to_next, *__from, &__tmp_state);
80             __state = __tmp_state;
81             __ret = error;
82           }
83         else if (__from_next && __from_next < __from_chunk_end)
84           {
85             __to_next += __conv;
86             __ret = partial;
87           }
88         else
89           {
90             __from_next = __from_chunk_end;
91             __to_next += __conv;
92           }
93
94         if (__from_next < __from_end && __ret == ok)
95           {
96             extern_type __buf[MB_LEN_MAX];
97             __tmp_state = __state;
98             const size_t __conv = wcrtomb(__buf, *__from_next, &__tmp_state);
99             if (__conv > static_cast<size_t>(__to_end - __to_next))
100               __ret = partial;
101             else
102               {
103                 memcpy(__to_next, __buf, __conv);
104                 __state = __tmp_state;
105                 __to_next += __conv;
106                 ++__from_next;
107               }
108           }
109       }
110
111 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
112     __uselocale(__old);
113 #endif
114
115     return __ret; 
116   }
117   
118   codecvt_base::result
119   codecvt<wchar_t, char, mbstate_t>::
120   do_in(state_type& __state, const extern_type* __from, 
121         const extern_type* __from_end, const extern_type*& __from_next,
122         intern_type* __to, intern_type* __to_end,
123         intern_type*& __to_next) const
124   {
125     result __ret = ok;
126     state_type __tmp_state(__state);
127
128 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
129     __c_locale __old = __uselocale(_M_c_locale_codecvt);
130 #endif
131
132     // mbsnrtowcs is *very* fast but stops if encounters NUL characters:
133     // in case we store a L'\0' and then continue, in a loop.
134     // NB: mbsnrtowcs is a GNU extension
135     for (__from_next = __from, __to_next = __to;
136          __from_next < __from_end && __to_next < __to_end
137          && __ret == ok;)
138       {
139         const extern_type* __from_chunk_end;
140         __from_chunk_end = static_cast<const extern_type*>(memchr(__from_next, '\0',
141                                                                   __from_end
142                                                                   - __from_next));
143         if (!__from_chunk_end)
144           __from_chunk_end = __from_end;
145
146         __from = __from_next;
147         const size_t __conv = mbsnrtowcs(__to_next, &__from_next,
148                                          __from_chunk_end - __from_next,
149                                          __to_end - __to_next, &__state);
150         if (__conv == static_cast<size_t>(-1))
151           {
152             // In case of error, in order to stop at the exact place we
153             // have to start again from the beginning with a series of
154             // mbrtowc.
155             for (;; ++__to_next)
156               {
157                 const size_t __conv_err = mbrtowc(__to_next, __from,
158                                                   __from_end - __from,
159                                                   &__tmp_state);
160                 if (__conv_err == static_cast<size_t>(-1)
161                     || __conv_err == static_cast<size_t>(-2))
162                   break;
163                 __from += __conv_err;
164               }
165             __from_next = __from;
166             __state = __tmp_state;          
167             __ret = error;
168           }
169         else if (__from_next && __from_next < __from_chunk_end)
170           {
171             // It is unclear what to return in this case (see DR 382). 
172             __to_next += __conv;
173             __ret = partial;
174           }
175         else
176           {
177             __from_next = __from_chunk_end;
178             __to_next += __conv;
179           }
180
181         if (__from_next < __from_end && __ret == ok)
182           {
183             if (__to_next < __to_end)
184               {
185                 // XXX Probably wrong for stateful encodings
186                 __tmp_state = __state;          
187                 ++__from_next;
188                 *__to_next++ = L'\0';
189               }
190             else
191               __ret = partial;
192           }
193       }
194
195 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
196     __uselocale(__old);
197 #endif
198
199     return __ret; 
200   }
201
202   int 
203   codecvt<wchar_t, char, mbstate_t>::
204   do_encoding() const throw()
205   {
206     // XXX This implementation assumes that the encoding is
207     // stateless and is either single-byte or variable-width.
208     int __ret = 0;
209 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
210     __c_locale __old = __uselocale(_M_c_locale_codecvt);
211 #endif
212     if (MB_CUR_MAX == 1)
213       __ret = 1;
214 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
215     __uselocale(__old);
216 #endif
217     return __ret;
218   }  
219
220   int 
221   codecvt<wchar_t, char, mbstate_t>::
222   do_max_length() const throw()
223   {
224 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
225     __c_locale __old = __uselocale(_M_c_locale_codecvt);
226 #endif
227     // XXX Probably wrong for stateful encodings.
228     int __ret = MB_CUR_MAX;
229 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
230     __uselocale(__old);
231 #endif
232     return __ret;
233   }
234   
235   int 
236   codecvt<wchar_t, char, mbstate_t>::
237   do_length(state_type& __state, const extern_type* __from,
238             const extern_type* __end, size_t __max) const
239   {
240     int __ret = 0;
241     state_type __tmp_state(__state);
242
243 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
244     __c_locale __old = __uselocale(_M_c_locale_codecvt);
245 #endif
246
247     // mbsnrtowcs is *very* fast but stops if encounters NUL characters:
248     // in case we advance past it and then continue, in a loop.
249     // NB: mbsnrtowcs is a GNU extension
250   
251     // A dummy internal buffer is needed in order for mbsnrtocws to consider
252     // its fourth parameter (it wouldn't with NULL as first parameter).
253     wchar_t* __to = static_cast<wchar_t*>(__builtin_alloca(sizeof(wchar_t) 
254                                                            * __max));
255     while (__from < __end && __max)
256       {
257         const extern_type* __from_chunk_end;
258         __from_chunk_end = static_cast<const extern_type*>(memchr(__from, '\0',
259                                                                   __end
260                                                                   - __from));
261         if (!__from_chunk_end)
262           __from_chunk_end = __end;
263
264         const extern_type* __tmp_from = __from;
265         const size_t __conv = mbsnrtowcs(__to, &__from,
266                                          __from_chunk_end - __from,
267                                          __max, &__state);
268         if (__conv == static_cast<size_t>(-1))
269           {
270             // In case of error, in order to stop at the exact place we
271             // have to start again from the beginning with a series of
272             // mbrtowc.
273             for (__from = __tmp_from;;)
274               {
275                 const size_t __conv_err = mbrtowc(NULL, __from, __end - __from,
276                                                   &__tmp_state);
277                 if (__conv_err == static_cast<size_t>(-1)
278                     || __conv_err == static_cast<size_t>(-2))
279                   break;
280                 __from += __conv_err;
281               }
282             __state = __tmp_state;
283             __ret += __from - __tmp_from;
284             break;
285           }
286         if (!__from)
287           __from = __from_chunk_end;
288         
289         __ret += __from - __tmp_from;
290         __max -= __conv;
291
292         if (__from < __end && __max)
293           {
294             // XXX Probably wrong for stateful encodings
295             __tmp_state = __state;
296             ++__from;
297             ++__ret;
298             --__max;
299           }
300       }
301
302 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
303     __uselocale(__old);
304 #endif
305
306     return __ret; 
307   }
308 #endif
309 }