OSDN Git Service

d346b1488f25f036b6c69bcfd48d1276f08ea553
[pf3gnuchains/gcc-fork.git] / libjava / gnu / gcj / convert / natIconv.cc
1 // Input_iconv.java -- Java side of iconv() reader.
2
3 /* Copyright (C) 2000  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/UnsupportedEncodingException.h>
21
22 #ifdef HAVE_ICONV
23 #include <iconv.h>
24
25 template<typename T>
26 static inline size_t
27 iconv_adapter (size_t (*iconv_f) (iconv_t, T, size_t *, char **, size_t *),
28                iconv_t handle, char **inbuf, size_t *inavail,
29                char **outbuf, size_t *outavail)
30 {
31   return (*iconv_f) (handle, (T) inbuf, inavail, outbuf, outavail);
32 }
33
34 #endif
35
36 void
37 gnu::gcj::convert::Input_iconv::init (jstring encoding)
38 {
39 #ifdef HAVE_ICONV
40   jsize len = _Jv_GetStringUTFLength (encoding);
41   char buffer[len + 1];
42   _Jv_GetStringUTFRegion (encoding, 0, len, buffer);
43   buffer[len] = '\0';
44
45   iconv_t h = iconv_open ("UCS-2", buffer);
46   if (h == (iconv_t) -1)
47     throw new java::io::UnsupportedEncodingException (encoding);
48
49   JvAssert (h != NULL);
50   handle = reinterpret_cast<gnu::gcj::RawData *> (h);
51 #else /* HAVE_ICONV */
52   // If no iconv, just throw an exception.
53   throw new java::io::UnsupportedEncodingException (encoding);
54 #endif /* HAVE_ICONV */
55 }
56
57 void
58 gnu::gcj::convert::Input_iconv::finalize (void)
59 {
60 #ifdef HAVE_ICONV
61   if (handle != NULL)
62     {
63       iconv_close ((iconv_t) handle);
64       handle = NULL;
65     }
66 #endif /* HAVE_ICONV */
67 }
68
69 jint
70 gnu::gcj::convert::Input_iconv::read (jcharArray outbuffer,
71                                       jint outpos, jint count)
72 {
73 #ifdef HAVE_ICONV
74   jbyte *bytes = elements (inbuffer);
75   jchar *out = elements (outbuffer);
76   size_t inavail = inlength - inpos;
77   size_t old_in = inavail;
78   size_t outavail = count * sizeof (jchar);
79   size_t old_out = outavail;
80
81   char *inbuf = (char *) &bytes[inpos];
82   char *outbuf = (char *) &out[outpos];
83
84   size_t r = iconv_adapter (iconv, (iconv_t) handle,
85                             &inbuf, &inavail,
86                             &outbuf, &outavail);
87   // FIXME: what if R==-1?
88
89   if (iconv_byte_swap)
90     {
91       size_t max = (old_out - outavail) / sizeof (jchar);
92       for (size_t i = 0; i < max; ++i)
93         {
94           // Byte swap.
95           jchar c = (((out[outpos + i] & 0xff) << 8)
96                      | ((out[outpos + i] >> 8) & 0xff));
97           outbuf[i] = c;
98         }
99     }
100
101   inpos += old_in - inavail;
102   return (old_out - outavail) / sizeof (jchar);
103 #else /* HAVE_ICONV */
104   return -1;
105 #endif /* HAVE_ICONV */
106 }
107
108 void
109 gnu::gcj::convert::Output_iconv::init (jstring encoding)
110 {
111 #ifdef HAVE_ICONV
112   jsize len = _Jv_GetStringUTFLength (encoding);
113   char buffer[len + 1];
114   _Jv_GetStringUTFRegion (encoding, 0, len, buffer);
115   buffer[len] = '\0';
116
117   iconv_t h = iconv_open (buffer, "UCS-2");
118   if (h == (iconv_t) -1)
119     throw new java::io::UnsupportedEncodingException (encoding);
120
121   JvAssert (h != NULL);
122   handle = reinterpret_cast<gnu::gcj::RawData *> (h);
123 #else /* HAVE_ICONV */
124   // If no iconv, just throw an exception.
125   throw new java::io::UnsupportedEncodingException (encoding);
126 #endif /* HAVE_ICONV */
127 }
128
129 void
130 gnu::gcj::convert::Output_iconv::finalize (void)
131 {
132 #ifdef HAVE_ICONV
133   if (handle != NULL)
134     {
135       iconv_close ((iconv_t) handle);
136       handle = NULL;
137     }
138 #endif /* HAVE_ICONV */
139 }
140
141 jint
142 gnu::gcj::convert::Output_iconv::write (jcharArray inbuffer,
143                                         jint inpos, jint inlength)
144 {
145 #ifdef HAVE_ICONV
146   jchar *chars = elements (inbuffer);
147   jbyte *out = elements (buf);
148   jchar *temp_buffer = NULL;
149
150   size_t inavail = inlength * sizeof (jchar);
151   size_t old_in = inavail;
152
153   size_t outavail = buf->length - count;
154   size_t old_out = outavail;
155
156   char *inbuf = (char *) &chars[inpos];
157   char *outbuf = (char *) &out[count];
158
159   if (iconv_byte_swap)
160     {
161       // Ugly performance penalty -- don't use losing systems!
162       temp_buffer = (jchar *) _Jv_Malloc (inlength * sizeof (jchar));
163       for (int i = 0; i < inlength; ++i)
164         {
165           // Byte swap.
166           jchar c = (((chars[inpos + i] & 0xff) << 8)
167                      | ((chars[inpos + i] >> 8) & 0xff));
168           temp_buffer[i] = c;
169         }
170       inbuf = (char *) temp_buffer;
171     }
172
173   // If the conversion fails on the very first character, then we
174   // assume that the character can't be represented in the output
175   // encoding.  There's nothing useful we can do here, so we simply
176   // omit that character.  Note that we can't check `errno' because
177   // glibc 2.1.3 doesn't set it correctly.  We could check it if we
178   // really needed to, but we'd have to disable support for 2.1.3.
179   size_t loop_old_in = old_in;
180   while (1)
181     {
182       size_t r = iconv_adapter (iconv, (iconv_t) handle,
183                                 &inbuf, &inavail,
184                                 &outbuf, &outavail);
185       if (r == -1 && inavail == loop_old_in)
186         {
187           inavail -= 2;
188           if (inavail == 0)
189             break;
190           loop_old_in -= 2;
191           inbuf += 2;
192         }
193       else
194         break;
195     }
196
197   if (temp_buffer != NULL)
198     _Jv_Free (temp_buffer);
199
200   count += old_out - outavail;
201   return (old_in - inavail) / sizeof (jchar);
202 #else /* HAVE_ICONV */
203   return -1;
204 #endif /* HAVE_ICONV */
205 }
206
207 jboolean
208 gnu::gcj::convert::IOConverter::iconv_init (void)
209 {
210   // Some versions of iconv() always return their UCS-2 results in
211   // big-endian order, and they also require UCS-2 inputs to be in
212   // big-endian order.  For instance, glibc 2.1.3 does this.  If the
213   // UTF-8=>UCS-2 iconv converter has this feature, then we assume
214   // that all UCS-2 converters do.  (This might not be the best
215   // heuristic, but is is all we've got.)
216   jboolean result = false;
217 #ifdef HAVE_ICONV
218   iconv_t handle = iconv_open ("UCS-2", "UTF-8");
219   if (handle != (iconv_t) -1)
220     {
221       jchar c;
222       unsigned char in[3];
223       char *inp, *outp;
224       size_t inc, outc, r;
225
226       // This is the UTF-8 encoding of \ufeff.
227       in[0] = 0xef;
228       in[1] = 0xbb;
229       in[2] = 0xbf;
230
231       inp = (char *) in;
232       inc = 3;
233       outp = (char *) &c;
234       outc = 2;
235
236       r = iconv_adapter (iconv, handle, &inp, &inc, &outp, &outc);
237       // Conversion must be complete for us to use the result.
238       if (r != (size_t) -1 && inc == 0 && outc == 0)
239         result = (c != 0xfeff);
240     }
241 #endif /* HAVE_ICONV */
242   return result;
243 }