1 // Input_iconv.java -- Java side of iconv() reader.
3 /* Copyright (C) 2000, 2001 Free Software Foundation
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
11 /* Author: Tom Tromey <tromey@redhat.com>. */
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>
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)
34 return (*iconv_f) (handle, (T) inbuf, inavail, outbuf, outavail);
40 gnu::gcj::convert::Input_iconv::init (jstring encoding)
43 jsize len = _Jv_GetStringUTFLength (encoding);
45 _Jv_GetStringUTFRegion (encoding, 0, len, buffer);
48 iconv_t h = iconv_open ("UCS-2", buffer);
49 if (h == (iconv_t) -1)
50 throw new java::io::UnsupportedEncodingException (encoding);
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 */
61 gnu::gcj::convert::Input_iconv::finalize (void)
66 iconv_close ((iconv_t) handle);
69 #endif /* HAVE_ICONV */
73 gnu::gcj::convert::Input_iconv::read (jcharArray outbuffer,
74 jint outpos, jint count)
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;
84 char *inbuf = (char *) &bytes[inpos];
85 char *outbuf = (char *) &out[outpos];
87 size_t r = iconv_adapter (iconv, (iconv_t) handle,
93 // Incomplete character.
94 if (errno == EINVAL || errno == E2BIG)
96 throw new java::io::CharConversionException ();
101 size_t max = (old_out - outavail) / sizeof (jchar);
102 for (size_t i = 0; i < max; ++i)
105 jchar c = (((out[outpos + i] & 0xff) << 8)
106 | ((out[outpos + i] >> 8) & 0xff));
111 inpos += old_in - inavail;
112 return (old_out - outavail) / sizeof (jchar);
113 #else /* HAVE_ICONV */
115 #endif /* HAVE_ICONV */
119 gnu::gcj::convert::Input_iconv::done ()
121 // 50 bytes should be enough for any reset sequence.
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 ();
133 gnu::gcj::convert::Output_iconv::init (jstring encoding)
136 jsize len = _Jv_GetStringUTFLength (encoding);
137 char buffer[len + 1];
138 _Jv_GetStringUTFRegion (encoding, 0, len, buffer);
141 iconv_t h = iconv_open (buffer, "UCS-2");
142 if (h == (iconv_t) -1)
143 throw new java::io::UnsupportedEncodingException (encoding);
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 */
154 gnu::gcj::convert::Output_iconv::finalize (void)
159 iconv_close ((iconv_t) handle);
162 #endif /* HAVE_ICONV */
166 gnu::gcj::convert::Output_iconv::write (jcharArray inbuffer,
167 jint inpos, jint inlength)
170 jchar *chars = elements (inbuffer);
171 jbyte *out = elements (buf);
172 jchar *temp_buffer = NULL;
174 size_t inavail = inlength * sizeof (jchar);
175 size_t old_in = inavail;
177 size_t outavail = buf->length - count;
178 size_t old_out = outavail;
180 char *inbuf = (char *) &chars[inpos];
181 char *outbuf = (char *) &out[count];
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)
190 jchar c = (((chars[inpos + i] & 0xff) << 8)
191 | ((chars[inpos + i] >> 8) & 0xff));
194 inbuf = (char *) temp_buffer;
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;
206 size_t r = iconv_adapter (iconv, (iconv_t) handle,
209 if (r == -1 && inavail == loop_old_in)
221 if (temp_buffer != NULL)
222 _Jv_Free (temp_buffer);
224 count += old_out - outavail;
225 return (old_in - inavail) / sizeof (jchar);
226 #else /* HAVE_ICONV */
228 #endif /* HAVE_ICONV */
232 gnu::gcj::convert::IOConverter::iconv_init (void)
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;
242 iconv_t handle = iconv_open ("UCS-2", "UTF-8");
243 if (handle != (iconv_t) -1)
250 // This is the UTF-8 encoding of \ufeff.
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);
265 #endif /* HAVE_ICONV */
270 gnu::gcj::convert::Output_iconv::done ()
272 // 50 bytes should be enough for any reset sequence.
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 ();