Source code: com/mysql/jdbc/SingleByteCharsetConverter.java
1 /*
2 Copyright (C) 2002-2004 MySQL AB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of version 2 of the GNU General Public License as
6 published by the Free Software Foundation.
7
8
9 There are special exceptions to the terms and conditions of the GPL
10 as it is applied to this software. View the full text of the
11 exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
12 software distribution.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 */
24 package com.mysql.jdbc;
25
26 import java.io.UnsupportedEncodingException;
27 import java.util.HashMap;
28 import java.util.Map;
29
30
31 /**
32 * Converter for char[]->byte[] and byte[]->char[] for single-byte character
33 * sets. Much faster (5-6x) than the built-in solution that ships with the
34 * JVM, even with JDK-1.4.x and NewIo.
35 *
36 * @author Mark Matthews
37 */
38 public class SingleByteCharsetConverter {
39 // The initial charToByteMap, with all char mappings mapped
40 // to (byte) '?', so that unknown characters are mapped to '?'
41 // instead of '\0' (which means end-of-string to MySQL).
42 private static byte[] unknownCharsMap = new byte[65536];
43 private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE;
44 private static final Map CONVERTER_MAP = new HashMap();
45 private static byte[] allBytes = new byte[BYTE_RANGE];
46
47 static {
48 for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
49 allBytes[i - Byte.MIN_VALUE] = (byte) i;
50 }
51
52 for (int i = 0; i < unknownCharsMap.length; i++) {
53 unknownCharsMap[i] = (byte) '?'; // use something 'sane' for unknown chars
54 }
55 }
56
57 private char[] byteToChars = new char[BYTE_RANGE];
58 private byte[] charToByteMap = new byte[65536];
59
60 /**
61 * Prevent instantiation, called out of static method initCharset().
62 *
63 * @param encodingName a JVM character encoding
64 *
65 * @throws UnsupportedEncodingException if the JVM does not support the
66 * encoding
67 */
68 private SingleByteCharsetConverter(String encodingName)
69 throws UnsupportedEncodingException {
70 String allBytesString = new String(allBytes, 0, BYTE_RANGE, encodingName);
71 int allBytesLen = allBytesString.length();
72
73 System.arraycopy(unknownCharsMap, 0, charToByteMap, 0,
74 charToByteMap.length);
75
76 for (int i = 0; (i < BYTE_RANGE) && (i < allBytesLen); i++) {
77 char c = allBytesString.charAt(i);
78 byteToChars[i] = c;
79 charToByteMap[c] = allBytes[i];
80 }
81 }
82
83 /**
84 * Get a converter for the given encoding name
85 *
86 * @param encodingName the Java character encoding name
87 *
88 * @return a converter for the given encoding name
89 *
90 * @throws UnsupportedEncodingException if the character encoding is not
91 * supported
92 */
93 public static synchronized SingleByteCharsetConverter getInstance(
94 String encodingName) throws UnsupportedEncodingException {
95 SingleByteCharsetConverter instance = (SingleByteCharsetConverter) CONVERTER_MAP
96 .get(encodingName);
97
98 if (instance == null) {
99 instance = initCharset(encodingName);
100 }
101
102 return instance;
103 }
104
105 /**
106 * Initialize the shared instance of a converter for the given character
107 * encoding.
108 *
109 * @param javaEncodingName the Java name for the character set to
110 * initialize
111 *
112 * @return a converter for the given character set
113 *
114 * @throws UnsupportedEncodingException if the character encoding is not
115 * supported
116 */
117 public static SingleByteCharsetConverter initCharset(
118 String javaEncodingName) throws UnsupportedEncodingException {
119 String mysqlEncodingName = (String) CharsetMapping.JAVA_TO_MYSQL_CHARSET_MAP
120 .get(javaEncodingName);
121
122 if (mysqlEncodingName == null) {
123 return null;
124 }
125
126 if (CharsetMapping.MULTIBYTE_CHARSETS.containsKey(mysqlEncodingName)) {
127 return null;
128 }
129
130 SingleByteCharsetConverter converter = new SingleByteCharsetConverter(javaEncodingName);
131
132 CONVERTER_MAP.put(javaEncodingName, converter);
133
134 return converter;
135 }
136
137 /**
138 * Convert the byte buffer from startPos to a length of length to a string
139 * using the default platform encoding.
140 *
141 * @param buffer the bytes to convert
142 * @param startPos the index to start at
143 * @param length the number of bytes to convert
144 *
145 * @return the String representation of the given bytes
146 */
147 public static String toStringDefaultEncoding(byte[] buffer, int startPos,
148 int length) {
149 return new String(buffer, startPos, length);
150 }
151
152 /**
153 * Convert the given string to an array of bytes.
154 *
155 * @param s the String to convert
156 *
157 * @return the bytes that make up the String
158 */
159 public final byte[] toBytes(String s) {
160 if (s == null) {
161 return null;
162 }
163
164 int length = s.length();
165 byte[] bytes = new byte[length];
166
167 for (int i = 0; i < length; i++) {
168 char c = s.charAt(i);
169 bytes[i] = charToByteMap[c];
170 }
171
172 return bytes;
173 }
174
175 private final static byte[] EMPTY_BYTE_ARRAY = new byte[0];
176
177 /**
178 * Convert the given string to an array of bytes.
179 *
180 * @param s the String to convert
181 * @param offset the offset to start at
182 * @param length length (max) to convert
183 *
184 * @return the bytes that make up the String
185 */
186 public final byte[] toBytes(String s, int offset, int length) {
187 if (s == null) {
188 return null;
189 }
190
191 if (length == 0) {
192 return EMPTY_BYTE_ARRAY;
193 }
194
195 int stringLength = s.length();
196 byte[] bytes = new byte[length];
197
198
199 for (int i = 0; (i < length); i++) {
200 char c = s.charAt(i + offset);
201 bytes[i] = charToByteMap[c];
202 }
203
204 return bytes;
205 }
206
207 /**
208 * Convert the byte buffer to a string using this instance's character
209 * encoding.
210 *
211 * @param buffer the bytes to convert to a String
212 *
213 * @return the converted String
214 */
215 public final String toString(byte[] buffer) {
216 return toString(buffer, 0, buffer.length);
217 }
218
219 /**
220 * Convert the byte buffer from startPos to a length of length to a string
221 * using this instance's character encoding.
222 *
223 * @param buffer the bytes to convert
224 * @param startPos the index to start at
225 * @param length the number of bytes to convert
226 *
227 * @return the String representation of the given bytes
228 */
229 public final String toString(byte[] buffer, int startPos, int length) {
230 char[] charArray = new char[length];
231 int readpoint = startPos;
232
233 for (int i = 0; i < length; i++) {
234 charArray[i] = byteToChars[(int) buffer[readpoint] - Byte.MIN_VALUE];
235 readpoint++;
236 }
237
238 return new String(charArray);
239 }
240 }