OSDN Git Service

2001-07-30 Tom Tromey <tromey@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / gnu / gcj / convert / natIconv.cc
1 // Input_iconv.java -- Java side of iconv() reader.
2
3 /* Copyright (C) 2000, 2001  Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 /* Author: Tom Tromey <tromey@redhat.com>.  */
12
13 #include <config.h>
14
15 #include <gcj/cni.h>
16 #include <jvm.h>
17
18 #include <gnu/gcj/convert/Input_iconv.h>
19 #include <gnu/gcj/convert/Output_iconv.h>
20 #include <java/io/CharConversionException.h>
21 #include <java/io/UnsupportedEncodingException.h>
22
23 #include <errno.h>
24
25 #ifdef HAVE_ICONV
26 #include <iconv.h>
27
28 template<typename T>
29 static inline size_t
30 iconv_adapter (size_t (*iconv_f) (iconv_t, T, size_t *, char **, size_t *),
31                iconv_t handle, char **inbuf, size_t *inavail,
32                char **outbuf, size_t *outavail)
33 {
34   return (*iconv_f) (handle, (T) inbuf, inavail, outbuf, outavail);
35 }
36
37 #endif
38
39 void
40 gnu::gcj::convert::Input_iconv::init (jstring encoding)
41 {
42 #ifdef HAVE_ICONV
43   jsize len = _Jv_GetStringUTFLength (encoding);
44   char buffer[len + 1];
45   _Jv_GetStringUTFRegion (encoding, 0, len, buffer);
46   buffer[len] = '\0';
47
48   iconv_t h = iconv_open ("UCS-2", buffer);
49   if (h == (iconv_t) -1)
50     throw new java::io::UnsupportedEncodingException (encoding);
51
52   JvAssert (h != NULL);
53   handle = reinterpret_cast<gnu::gcj::RawData *> (h);
54 #else /* HAVE_ICONV */
55   // If no iconv, just throw an exception.
56   throw new java::io::UnsupportedEncodingException (encoding);
57 #endif /* HAVE_ICONV */
58 }
59
60 void
61 gnu::gcj::convert::Input_iconv::finalize (void)
62 {
63 #ifdef HAVE_ICONV
64   if (handle != NULL)
65     {
66       iconv_close ((iconv_t) handle);
67       handle = NULL;
68     }
69 #endif /* HAVE_ICONV */
70 }
71
72 jint
73 gnu::gcj::convert::Input_iconv::read (jcharArray outbuffer,
74                                       jint outpos, jint count)
75 {
76 #ifdef HAVE_ICONV
77   jbyte *bytes = elements (inbuffer);
78   jchar *out = elements (outbuffer);
79   size_t inavail = inlength - inpos;
80   size_t old_in = inavail;
81   size_t outavail = count * sizeof (jchar);
82   size_t old_out = outavail;
83
84   char *inbuf = (char *) &bytes[inpos];
85   char *outbuf = (char *) &out[outpos];
86
87   size_t r = iconv_adapter (iconv, (iconv_t) handle,
88                             &inbuf, &inavail,
89                             &outbuf, &outavail);
90
91   if (r == (size_t) -1)
92     {
93       // Incomplete character.
94       if (errno == EINVAL || errno == E2BIG)
95         return 0;
96       throw new java::io::CharConversionException ();
97     }
98
99   if (iconv_byte_swap)
100     {
101       size_t max = (old_out - outavail) / sizeof (jchar);
102       for (size_t i = 0; i < max; ++i)
103         {
104           // Byte swap.
105           jchar c = (((out[outpos + i] & 0xff) << 8)
106                      | ((out[outpos + i] >> 8) & 0xff));
107           outbuf[i] = c;
108         }
109     }
110
111   inpos += old_in - inavail;
112   return (old_out - outavail) / sizeof (jchar);
113 #else /* HAVE_ICONV */
114   return -1;
115 #endif /* HAVE_ICONV */
116 }
117
118 void
119 gnu::gcj::convert::Input_iconv::done ()
120 {
121   // 50 bytes should be enough for any reset sequence.
122   size_t avail = 50;
123   char tmp[avail];
124   char *p = tmp;
125   // Calling iconv() with a NULL INBUF pointer will cause iconv() to
126   // switch to its initial state.  We don't care about the output that
127   // might be generated in that situation.
128   iconv_adapter (iconv, (iconv_t) handle, NULL, NULL, &p, &avail);
129   BytesToUnicode::done ();
130 }
131
132 void
133 gnu::gcj::convert::Output_iconv::init (jstring encoding)
134 {
135 #ifdef HAVE_ICONV
136   jsize len = _Jv_GetStringUTFLength (encoding);
137   char buffer[len + 1];
138   _Jv_GetStringUTFRegion (encoding, 0, len, buffer);
139   buffer[len] = '\0';
140
141   iconv_t h = iconv_open (buffer, "UCS-2");
142   if (h == (iconv_t) -1)
143     throw new java::io::UnsupportedEncodingException (encoding);
144
145   JvAssert (h != NULL);
146   handle = reinterpret_cast<gnu::gcj::RawData *> (h);
147 #else /* HAVE_ICONV */
148   // If no iconv, just throw an exception.
149   throw new java::io::UnsupportedEncodingException (encoding);
150 #endif /* HAVE_ICONV */
151 }
152
153 void
154 gnu::gcj::convert::Output_iconv::finalize (void)
155 {
156 #ifdef HAVE_ICONV
157   if (handle != NULL)
158     {
159       iconv_close ((iconv_t) handle);
160       handle = NULL;
161     }
162 #endif /* HAVE_ICONV */
163 }
164
165 jint
166 gnu::gcj::convert::Output_iconv::write (jcharArray inbuffer,
167                                         jint inpos, jint inlength)
168 {
169 #ifdef HAVE_ICONV
170   jchar *chars = elements (inbuffer);
171   jbyte *out = elements (buf);
172   jchar *temp_buffer = NULL;
173
174   size_t inavail = inlength * sizeof (jchar);
175   size_t old_in = inavail;
176
177   size_t outavail = buf->length - count;
178   size_t old_out = outavail;
179
180   char *inbuf = (char *) &chars[inpos];
181   char *outbuf = (char *) &out[count];
182
183   if (iconv_byte_swap)
184     {
185       // Ugly performance penalty -- don't use losing systems!
186       temp_buffer = (jchar *) _Jv_Malloc (inlength * sizeof (jchar));
187       for (int i = 0; i < inlength; ++i)
188         {
189           // Byte swap.
190           jchar c = (((chars[inpos + i] & 0xff) << 8)
191                      | ((chars[inpos + i] >> 8) & 0xff));
192           temp_buffer[i] = c;
193         }
194       inbuf = (char *) temp_buffer;
195     }
196
197   // If the conversion fails on the very first character, then we
198   // assume that the character can't be represented in the output
199   // encoding.  There's nothing useful we can do here, so we simply
200   // omit that character.  Note that we can't check `errno' because
201   // glibc 2.1.3 doesn't set it correctly.  We could check it if we
202   // really needed to, but we'd have to disable support for 2.1.3.
203   size_t loop_old_in = old_in;
204   while (1)
205     {
206       size_t r = iconv_adapter (iconv, (iconv_t) handle,
207                                 &inbuf, &inavail,
208                                 &outbuf, &outavail);
209       if (r == -1 && inavail == loop_old_in)
210         {
211           inavail -= 2;
212           if (inavail == 0)
213             break;
214           loop_old_in -= 2;
215           inbuf += 2;
216         }
217       else
218         break;
219     }
220
221   if (temp_buffer != NULL)
222     _Jv_Free (temp_buffer);
223
224   count += old_out - outavail;
225   return (old_in - inavail) / sizeof (jchar);
226 #else /* HAVE_ICONV */
227   return -1;
228 #endif /* HAVE_ICONV */
229 }
230
231 jboolean
232 gnu::gcj::convert::IOConverter::iconv_init (void)
233 {
234   // Some versions of iconv() always return their UCS-2 results in
235   // big-endian order, and they also require UCS-2 inputs to be in
236   // big-endian order.  For instance, glibc 2.1.3 does this.  If the
237   // UTF-8=>UCS-2 iconv converter has this feature, then we assume
238   // that all UCS-2 converters do.  (This might not be the best
239   // heuristic, but is is all we've got.)
240   jboolean result = false;
241 #ifdef HAVE_ICONV
242   iconv_t handle = iconv_open ("UCS-2", "UTF-8");
243   if (handle != (iconv_t) -1)
244     {
245       jchar c;
246       unsigned char in[3];
247       char *inp, *outp;
248       size_t inc, outc, r;
249
250       // This is the UTF-8 encoding of \ufeff.
251       in[0] = 0xef;
252       in[1] = 0xbb;
253       in[2] = 0xbf;
254
255       inp = (char *) in;
256       inc = 3;
257       outp = (char *) &c;
258       outc = 2;
259
260       r = iconv_adapter (iconv, handle, &inp, &inc, &outp, &outc);
261       // Conversion must be complete for us to use the result.
262       if (r != (size_t) -1 && inc == 0 && outc == 0)
263         result = (c != 0xfeff);
264     }
265 #endif /* HAVE_ICONV */
266   return result;
267 }
268
269 void
270 gnu::gcj::convert::Output_iconv::done ()
271 {
272   // 50 bytes should be enough for any reset sequence.
273   size_t avail = 50;
274   char tmp[avail];
275   char *p = tmp;
276   // Calling iconv() with a NULL INBUF pointer will cause iconv() to
277   // switch to its initial state.  We don't care about the output that
278   // might be generated in that situation.
279   iconv_adapter (iconv, (iconv_t) handle, NULL, NULL, &p, &avail);
280   UnicodeToBytes::done ();
281 }