OSDN Git Service

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