|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
******************************************************************************* |
|
* Copyright (C) 1996-2014, International Business Machines Corporation and |
|
* others. All Rights Reserved. |
|
******************************************************************************* |
|
*/ |
|
|
|
package sun.text.normalizer; |
|
|
|
import java.io.BufferedInputStream; |
|
import java.io.DataInputStream; |
|
import java.io.InputStream; |
|
import java.io.IOException; |
|
import java.io.UncheckedIOException; |
|
import java.nio.ByteBuffer; |
|
import java.nio.ByteOrder; |
|
import java.util.Arrays; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
|
|
public final class ICUBinary { |
|
|
|
private static final class IsAcceptable implements Authenticate { |
|
@Override |
|
public boolean isDataVersionAcceptable(byte version[]) { |
|
return version[0] == 1; |
|
} |
|
} |
|
|
|
// public inner interface ------------------------------------------------ |
|
|
|
|
|
|
|
*/ |
|
public static interface Authenticate |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isDataVersionAcceptable(byte version[]); |
|
} |
|
|
|
// public methods -------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static ByteBuffer getRequiredData(String itemPath) { |
|
final Class<ICUBinary> root = ICUBinary.class; |
|
|
|
try (InputStream is = AccessController.doPrivileged(new PrivilegedAction<InputStream>() { |
|
public InputStream run() { |
|
return root.getResourceAsStream(itemPath); |
|
} |
|
})) { |
|
|
|
BufferedInputStream b=new BufferedInputStream(is, 4096 /* data buffer size */); |
|
DataInputStream inputStream = new DataInputStream(b); |
|
byte[] bb = new byte[130000]; |
|
int n = inputStream.read(bb); |
|
ByteBuffer bytes = ByteBuffer.wrap(bb, 0, n); |
|
return bytes; |
|
} |
|
catch (IOException e) { |
|
throw new UncheckedIOException(e); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static VersionInfo readHeaderAndDataVersion(ByteBuffer bytes, |
|
int dataFormat, |
|
Authenticate authenticate) |
|
throws IOException { |
|
return getVersionInfoFromCompactInt(readHeader(bytes, dataFormat, authenticate)); |
|
} |
|
|
|
private static final byte BIG_ENDIAN_ = 1; |
|
public static final byte[] readHeader(InputStream inputStream, |
|
byte dataFormatIDExpected[], |
|
Authenticate authenticate) |
|
throws IOException |
|
{ |
|
DataInputStream input = new DataInputStream(inputStream); |
|
char headersize = input.readChar(); |
|
int readcount = 2; |
|
|
|
byte magic1 = input.readByte(); |
|
readcount ++; |
|
byte magic2 = input.readByte(); |
|
readcount ++; |
|
if (magic1 != MAGIC1 || magic2 != MAGIC2) { |
|
throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); |
|
} |
|
|
|
input.readChar(); |
|
readcount += 2; |
|
input.readChar(); |
|
readcount += 2; |
|
byte bigendian = input.readByte(); |
|
readcount ++; |
|
byte charset = input.readByte(); |
|
readcount ++; |
|
byte charsize = input.readByte(); |
|
readcount ++; |
|
input.readByte(); |
|
readcount ++; |
|
|
|
byte dataFormatID[] = new byte[4]; |
|
input.readFully(dataFormatID); |
|
readcount += 4; |
|
byte dataVersion[] = new byte[4]; |
|
input.readFully(dataVersion); |
|
readcount += 4; |
|
byte unicodeVersion[] = new byte[4]; |
|
input.readFully(unicodeVersion); |
|
readcount += 4; |
|
if (headersize < readcount) { |
|
throw new IOException("Internal Error: Header size error"); |
|
} |
|
input.skipBytes(headersize - readcount); |
|
|
|
if (bigendian != BIG_ENDIAN_ || charset != CHAR_SET_ |
|
|| charsize != CHAR_SIZE_ |
|
|| !Arrays.equals(dataFormatIDExpected, dataFormatID) |
|
|| (authenticate != null |
|
&& !authenticate.isDataVersionAcceptable(dataVersion))) { |
|
throw new IOException(HEADER_AUTHENTICATION_FAILED_); |
|
} |
|
return unicodeVersion; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static int readHeader(ByteBuffer bytes, int dataFormat, Authenticate authenticate) |
|
throws IOException { |
|
assert bytes.position() == 0; |
|
byte magic1 = bytes.get(2); |
|
byte magic2 = bytes.get(3); |
|
if (magic1 != MAGIC1 || magic2 != MAGIC2) { |
|
throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_); |
|
} |
|
|
|
byte isBigEndian = bytes.get(8); |
|
byte charsetFamily = bytes.get(9); |
|
byte sizeofUChar = bytes.get(10); |
|
if (isBigEndian < 0 || 1 < isBigEndian || |
|
charsetFamily != CHAR_SET_ || sizeofUChar != CHAR_SIZE_) { |
|
throw new IOException(HEADER_AUTHENTICATION_FAILED_); |
|
} |
|
bytes.order(isBigEndian != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); |
|
|
|
int headerSize = bytes.getChar(0); |
|
int sizeofUDataInfo = bytes.getChar(4); |
|
if (sizeofUDataInfo < 20 || headerSize < (sizeofUDataInfo + 4)) { |
|
throw new IOException("Internal Error: Header size error"); |
|
} |
|
// TODO: Change Authenticate to take int major, int minor, int milli, int micro |
|
|
|
byte[] formatVersion = new byte[] { |
|
bytes.get(16), bytes.get(17), bytes.get(18), bytes.get(19) |
|
}; |
|
if (bytes.get(12) != (byte)(dataFormat >> 24) || |
|
bytes.get(13) != (byte)(dataFormat >> 16) || |
|
bytes.get(14) != (byte)(dataFormat >> 8) || |
|
bytes.get(15) != (byte)dataFormat || |
|
(authenticate != null && !authenticate.isDataVersionAcceptable(formatVersion))) { |
|
throw new IOException(HEADER_AUTHENTICATION_FAILED_ + |
|
String.format("; data format %02x%02x%02x%02x, format version %d.%d.%d.%d", |
|
bytes.get(12), bytes.get(13), bytes.get(14), bytes.get(15), |
|
formatVersion[0] & 0xff, formatVersion[1] & 0xff, |
|
formatVersion[2] & 0xff, formatVersion[3] & 0xff)); |
|
} |
|
|
|
bytes.position(headerSize); |
|
return |
|
((int)bytes.get(20) << 24) | |
|
((bytes.get(21) & 0xff) << 16) | |
|
((bytes.get(22) & 0xff) << 8) | |
|
(bytes.get(23) & 0xff); |
|
} |
|
|
|
public static void skipBytes(ByteBuffer bytes, int skipLength) { |
|
if (skipLength > 0) { |
|
bytes.position(bytes.position() + skipLength); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static VersionInfo getVersionInfoFromCompactInt(int version) { |
|
return VersionInfo.getInstance( |
|
version >>> 24, (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); |
|
} |
|
|
|
// private variables ------------------------------------------------- |
|
|
|
|
|
|
|
*/ |
|
private static final byte MAGIC1 = (byte)0xda; |
|
private static final byte MAGIC2 = (byte)0x27; |
|
|
|
|
|
|
|
*/ |
|
private static final byte CHAR_SET_ = 0; |
|
private static final byte CHAR_SIZE_ = 2; |
|
|
|
|
|
|
|
*/ |
|
private static final String MAGIC_NUMBER_AUTHENTICATION_FAILED_ = |
|
"ICUBinary data file error: Magic number authentication failed"; |
|
private static final String HEADER_AUTHENTICATION_FAILED_ = |
|
"ICUBinary data file error: Header authentication failed"; |
|
} |