1/*
2 * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.cs;
27
28import java.nio.Buffer;
29import java.nio.ByteBuffer;
30import java.nio.CharBuffer;
31import java.nio.charset.Charset;
32import java.nio.charset.CharsetDecoder;
33import java.nio.charset.CharsetEncoder;
34import java.nio.charset.CoderResult;
35import java.util.Arrays;
36import static sun.nio.cs.CharsetMapping.*;
37
38public class SingleByte
39{
40    private static final CoderResult withResult(CoderResult cr,
41                                                Buffer src, int sp,
42                                                Buffer dst, int dp)
43    {
44        src.position(sp - src.arrayOffset());
45        dst.position(dp - dst.arrayOffset());
46        return cr;
47    }
48
49    public static final class Decoder extends CharsetDecoder
50                                      implements ArrayDecoder {
51        private final char[] b2c;
52        private final boolean isASCIICompatible;
53
54        public Decoder(Charset cs, char[] b2c) {
55            super(cs, 1.0f, 1.0f);
56            this.b2c = b2c;
57            this.isASCIICompatible = false;
58        }
59
60        public Decoder(Charset cs, char[] b2c, boolean isASCIICompatible) {
61            super(cs, 1.0f, 1.0f);
62            this.b2c = b2c;
63            this.isASCIICompatible = isASCIICompatible;
64        }
65
66        private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
67            byte[] sa = src.array();
68            int sp = src.arrayOffset() + src.position();
69            int sl = src.arrayOffset() + src.limit();
70
71            char[] da = dst.array();
72            int dp = dst.arrayOffset() + dst.position();
73            int dl = dst.arrayOffset() + dst.limit();
74
75            CoderResult cr = CoderResult.UNDERFLOW;
76            if ((dl - dp) < (sl - sp)) {
77                sl = sp + (dl - dp);
78                cr = CoderResult.OVERFLOW;
79            }
80
81            while (sp < sl) {
82                char c = decode(sa[sp]);
83                if (c == UNMAPPABLE_DECODING) {
84                    return withResult(CoderResult.unmappableForLength(1),
85                               src, sp, dst, dp);
86                }
87                da[dp++] = c;
88                sp++;
89            }
90            return withResult(cr, src, sp, dst, dp);
91        }
92
93        private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
94            int mark = src.position();
95            try {
96                while (src.hasRemaining()) {
97                    char c = decode(src.get());
98                    if (c == UNMAPPABLE_DECODING)
99                        return CoderResult.unmappableForLength(1);
100                    if (!dst.hasRemaining())
101                        return CoderResult.OVERFLOW;
102                    dst.put(c);
103                    mark++;
104                }
105                return CoderResult.UNDERFLOW;
106            } finally {
107                src.position(mark);
108            }
109        }
110
111        protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
112            if (src.hasArray() && dst.hasArray())
113                return decodeArrayLoop(src, dst);
114            else
115                return decodeBufferLoop(src, dst);
116        }
117
118        public final char decode(int b) {
119            return b2c[b + 128];
120        }
121
122        private char repl = '\uFFFD';
123        protected void implReplaceWith(String newReplacement) {
124            repl = newReplacement.charAt(0);
125        }
126
127        @Override
128        public int decode(byte[] src, int sp, int len, char[] dst) {
129            if (len > dst.length)
130                len = dst.length;
131            int dp = 0;
132            while (dp < len) {
133                dst[dp] = decode(src[sp++]);
134                if (dst[dp] == UNMAPPABLE_DECODING) {
135                    dst[dp] = repl;
136                }
137                dp++;
138            }
139            return dp;
140        }
141
142        @Override
143        public boolean isASCIICompatible() {
144            return isASCIICompatible;
145        }
146    }
147
148    public static final class Encoder extends CharsetEncoder
149                                      implements ArrayEncoder {
150        private Surrogate.Parser sgp;
151        private final char[] c2b;
152        private final char[] c2bIndex;
153        private final boolean isASCIICompatible;
154
155        public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) {
156            super(cs, 1.0f, 1.0f);
157            this.c2b = c2b;
158            this.c2bIndex = c2bIndex;
159            this.isASCIICompatible = isASCIICompatible;
160        }
161
162        public boolean canEncode(char c) {
163            return encode(c) != UNMAPPABLE_ENCODING;
164        }
165
166        public boolean isLegalReplacement(byte[] repl) {
167            return ((repl.length == 1 && repl[0] == (byte)'?') ||
168                    super.isLegalReplacement(repl));
169        }
170
171        private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
172            char[] sa = src.array();
173            int sp = src.arrayOffset() + src.position();
174            int sl = src.arrayOffset() + src.limit();
175
176            byte[] da = dst.array();
177            int dp = dst.arrayOffset() + dst.position();
178            int dl = dst.arrayOffset() + dst.limit();
179            int len  = Math.min(dl - dp, sl - sp);
180
181            while (len-- > 0) {
182                char c = sa[sp];
183                int b = encode(c);
184                if (b == UNMAPPABLE_ENCODING) {
185                    if (Character.isSurrogate(c)) {
186                        if (sgp == null)
187                            sgp = new Surrogate.Parser();
188                        if (sgp.parse(c, sa, sp, sl) < 0) {
189                            return withResult(sgp.error(), src, sp, dst, dp);
190                        }
191                        return withResult(sgp.unmappableResult(), src, sp, dst, dp);
192                    }
193                    return withResult(CoderResult.unmappableForLength(1),
194                               src, sp, dst, dp);
195                }
196                da[dp++] = (byte)b;
197                sp++;
198            }
199            return withResult(sp < sl ? CoderResult.OVERFLOW : CoderResult.UNDERFLOW,
200                              src, sp, dst, dp);
201        }
202
203        private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
204            int mark = src.position();
205            try {
206                while (src.hasRemaining()) {
207                    char c = src.get();
208                    int b = encode(c);
209                    if (b == UNMAPPABLE_ENCODING) {
210                        if (Character.isSurrogate(c)) {
211                            if (sgp == null)
212                                sgp = new Surrogate.Parser();
213                            if (sgp.parse(c, src) < 0)
214                                return sgp.error();
215                            return sgp.unmappableResult();
216                        }
217                        return CoderResult.unmappableForLength(1);
218                    }
219                    if (!dst.hasRemaining())
220                        return CoderResult.OVERFLOW;
221                    dst.put((byte)b);
222                    mark++;
223                }
224                return CoderResult.UNDERFLOW;
225            } finally {
226                src.position(mark);
227            }
228        }
229
230        protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
231            if (src.hasArray() && dst.hasArray())
232                return encodeArrayLoop(src, dst);
233            else
234                return encodeBufferLoop(src, dst);
235        }
236
237        public final int encode(char ch) {
238            char index = c2bIndex[ch >> 8];
239            if (index == UNMAPPABLE_ENCODING)
240                return UNMAPPABLE_ENCODING;
241            return c2b[index + (ch & 0xff)];
242        }
243
244        private byte repl = (byte)'?';
245        protected void implReplaceWith(byte[] newReplacement) {
246            repl = newReplacement[0];
247        }
248
249        public int encode(char[] src, int sp, int len, byte[] dst) {
250            int dp = 0;
251            int sl = sp + Math.min(len, dst.length);
252            while (sp < sl) {
253                char c = src[sp++];
254                int b = encode(c);
255                if (b != UNMAPPABLE_ENCODING) {
256                    dst[dp++] = (byte)b;
257                    continue;
258                }
259                if (Character.isHighSurrogate(c) && sp < sl &&
260                    Character.isLowSurrogate(src[sp])) {
261                    if (len > dst.length) {
262                        sl++;
263                        len--;
264                    }
265                    sp++;
266                }
267                dst[dp++] = repl;
268            }
269            return dp;
270        }
271
272        @Override
273        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
274            int dp = 0;
275            int sl = sp + Math.min(len, dst.length);
276            while (sp < sl) {
277                char c = (char)(src[sp++] & 0xff);
278                int b = encode(c);
279                if (b == UNMAPPABLE_ENCODING) {
280                    dst[dp++] = repl;
281                } else {
282                    dst[dp++] = (byte)b;
283                }
284            }
285            return dp;
286        }
287
288        @Override
289        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
290            int dp = 0;
291            int sl = sp + Math.min(len, dst.length);
292            while (sp < sl) {
293                char c = StringUTF16.getChar(src, sp++);
294                int b = encode(c);
295                if (b != UNMAPPABLE_ENCODING) {
296                    dst[dp++] = (byte)b;
297                    continue;
298                }
299                if (Character.isHighSurrogate(c) && sp < sl &&
300                    Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
301                    if (len > dst.length) {
302                        sl++;
303                        len--;
304                    }
305                    sp++;
306                }
307                dst[dp++] = repl;
308            }
309            return dp;
310        }
311
312        @Override
313        public boolean isASCIICompatible() {
314            return isASCIICompatible;
315        }
316    }
317
318    // init the c2b and c2bIndex tables from b2c.
319    public static void initC2B(char[] b2c, char[] c2bNR,
320                               char[] c2b, char[] c2bIndex) {
321        for (int i = 0; i < c2bIndex.length; i++)
322            c2bIndex[i] = UNMAPPABLE_ENCODING;
323        for (int i = 0; i < c2b.length; i++)
324            c2b[i] = UNMAPPABLE_ENCODING;
325        int off = 0;
326        for (int i = 0; i < b2c.length; i++) {
327            char c = b2c[i];
328            if (c == UNMAPPABLE_DECODING)
329                continue;
330            int index = (c >> 8);
331            if (c2bIndex[index] == UNMAPPABLE_ENCODING) {
332                c2bIndex[index] = (char)off;
333                off += 0x100;
334            }
335            index = c2bIndex[index] + (c & 0xff);
336            c2b[index] = (char)((i>=0x80)?(i-0x80):(i+0x80));
337        }
338        if (c2bNR != null) {
339            // c-->b nr entries
340            int i = 0;
341            while (i < c2bNR.length) {
342                char b = c2bNR[i++];
343                char c = c2bNR[i++];
344                int index = (c >> 8);
345                if (c2bIndex[index] == UNMAPPABLE_ENCODING) {
346                    c2bIndex[index] = (char)off;
347                    off += 0x100;
348                }
349                index = c2bIndex[index] + (c & 0xff);
350                c2b[index] = b;
351            }
352        }
353    }
354}
355