Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.wildfly.common.archive;
import static java.lang.Math.min;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.wildfly.common.Assert;
/**
*/
public final class Archive implements Closeable {
public static final int GP_ENCRYPTED = 1 << 0;
// only if method == implode
public static final int GP_IMPLODE_8K_DICTIONARY = 1 << 1;
public static final int GP_IMPLODE_3_TREES = 1 << 2;
// only if method == deflate
public static final int GP_DEFLATE_COMP_OPT_MASK = 0b11 << 1;
public static final int GP_DEFLATE_COMP_OPT_NORMAL = 0b00 << 1;
public static final int GP_DEFLATE_COMP_OPT_MAXIMUM = 0b01 << 1;
public static final int GP_DEFLATE_COMP_OPT_FAST = 0b10 << 1;
public static final int GP_DEFLATE_COMP_OPT_SUPER_FAST = 0b11 << 1;
// only if method == lzma
public static final int GP_LZMA_EOS_USED = 1 << 1;
public static final int GP_LATE_SIZES = 1 << 3;
// reserved 1 << 4
public static final int GP_COMPRESSED_PATCHED = 1 << 5;
public static final int GP_STRONG_ENCRYPTION = 1 << 6;
// reserved 1 << 7
// reserved 1 << 8
// reserved 1 << 9
// reserved 1 << 10
public static final int GP_UTF_8 = 1 << 11;
// reserved 1 << 12
public static final int GP_CD_MASKED = 1 << 13;
// reserved 1 << 14
// reserved 1 << 15
public static final int METHOD_STORED = 0;
public static final int METHOD_SHRINK = 1;
public static final int METHOD_REDUCE_1 = 2;
public static final int METHOD_REDUCE_2 = 3;
public static final int METHOD_REDUCE_3 = 4;
public static final int METHOD_REDUCE_4 = 5;
public static final int METHOD_IMPLODE = 6;
public static final int METHOD_DEFLATE = 8;
public static final int METHOD_DEFLATE64 = 9;
public static final int METHOD_BZIP2 = 12;
public static final int METHOD_LZMA = 14;
public static final int MADE_BY_MS_DOS = 0;
public static final int MADE_BY_UNIX = 3;
public static final int MADE_BY_NTFS = 10;
public static final int MADE_BY_OS_X = 19;
public static final int SIG_LH = 0x04034b50;
public static final int LH_SIGNATURE = 0;
public static final int LH_MIN_VERSION = 4;
public static final int LH_GP_BITS = 6;
public static final int LH_COMP_METHOD = 8;
public static final int LH_MOD_TIME = 10;
public static final int LH_MOD_DATE = 12;
public static final int LH_CRC_32 = 14;
public static final int LH_COMPRESSED_SIZE = 18;
public static final int LH_UNCOMPRESSED_SIZE = 22;
public static final int LH_FILE_NAME_LENGTH = 26;
public static final int LH_EXTRA_LENGTH = 28;
public static final int LH_END = 30;
public static final int SIG_DD = 0x08074b50;
public static final int DD_SIGNATURE = 0;
public static final int DD_CRC_32 = 4;
public static final int DD_COMPRESSED_SIZE = 8;
public static final int DD_UNCOMPRESSED_SIZE = 12;
public static final int DD_END = 16;
public static final int DD_ZIP64_COMPRESSED_SIZE = 8;
public static final int DD_ZIP64_UNCOMPRESSED_SIZE = 16;
public static final int DD_ZIP64_END = 24;
public static final int SIG_CDE = 0x02014b50;
public static final int CDE_SIGNATURE = 0;
public static final int CDE_VERSION_MADE_BY = 4;
public static final int CDE_VERSION_NEEDED = 6;
public static final int CDE_GP_BITS = 8;
public static final int CDE_COMP_METHOD = 10;
public static final int CDE_MOD_TIME = 12;
public static final int CDE_MOD_DATE = 14;
public static final int CDE_CRC_32 = 16;
public static final int CDE_COMPRESSED_SIZE = 20;
public static final int CDE_UNCOMPRESSED_SIZE = 24;
public static final int CDE_FILE_NAME_LENGTH = 28;
public static final int CDE_EXTRA_LENGTH = 30;
public static final int CDE_COMMENT_LENGTH = 32;
public static final int CDE_FIRST_DISK_NUMBER = 34;
public static final int CDE_INTERNAL_ATTRIBUTES = 36;
public static final int CDE_EXTERNAL_ATTRIBUTES = 38;
public static final int CDE_LOCAL_HEADER_OFFSET = 42; // relative to the start of the above first disk number
public static final int CDE_END = 46;
public static final int SIG_EOCD = 0x06054b50;
public static final int EOCD_SIGNATURE = 0;
public static final int EOCD_DISK_NUMBER = 4;
public static final int EOCD_CD_FIRST_DISK_NUMBER = 6;
public static final int EOCD_CDE_COUNT_THIS_DISK = 8;
public static final int EOCD_CDE_COUNT_ALL = 10;
public static final int EOCD_CD_SIZE = 12;
public static final int EOCD_CD_START_OFFSET = 16;
public static final int EOCD_COMMENT_LENGTH = 20;
public static final int EOCD_END = 22;
public static final int EXT_ID_ZIP64 = 0x0001;
public static final int ZIP64_UNCOMPRESSED_SIZE = 0;
public static final int ZIP64_COMPRESSED_SIZE = 8;
public static final int ZIP64_LOCAL_HEADER_OFFSET = 16;
public static final int ZIP64_FIRST_DISK_NUMBER = 24; // 4 bytes
public static final int ZIP64_END = 28;
public static final int EXT_ID_UNIX = 0x000d;
public static final int UNIX_ACCESS_TIME = 0;
public static final int UNIX_MODIFIED_TIME = 4;
public static final int UNIX_UID = 8;
public static final int UNIX_GID = 10;
public static final int UNIX_END = 12; // symlink target
public static final int UNIX_DEV_MAJOR = 12; // if it's a device node
public static final int UNIX_DEV_MINOR = 16;
public static final int UNIX_DEV_END = 20;
public static final int SIG_EOCD_ZIP64 = 0x06064b50;
public static final int EOCD_ZIP64_SIGNATURE = 0;
public static final int EOCD_ZIP64_SIZE = 4;
public static final int EOCD_ZIP64_VERSION_MADE_BY = 12;
public static final int EOCD_ZIP64_VERSION_NEEDED = 14;
public static final int EOCD_ZIP64_DISK_NUMBER = 16;
public static final int EOCD_ZIP64_CD_FIRST_DISK_NUMBER = 20;
public static final int EOCD_ZIP64_CDE_COUNT_THIS_DISK = 24;
public static final int EOCD_ZIP64_CDE_COUNT_ALL = 28;
public static final int EOCD_ZIP64_CD_SIZE = 36;
public static final int EOCD_ZIP64_CD_START_OFFSET = 44;
public static final int EOCD_ZIP64_END = 52;
public static final int SIG_EOCDL_ZIP64 = 0x07064b50;
public static final int EOCDL_ZIP64_SIGNATURE = 0;
public static final int EOCDL_ZIP64_EOCD_DISK_NUMBER = 4;
public static final int EOCDL_ZIP64_EOCD_OFFSET = 8;
public static final int EOCDL_ZIP64_DISK_COUNT = 16;
public static final int EOCDL_ZIP64_END = 20;
/**
* Maximum size of a single buffer.
*/
private static final int BUF_SIZE_MAX = 0x4000_0000;
private static final int BUF_SHIFT = Integer.numberOfTrailingZeros(BUF_SIZE_MAX);
private static final int BUF_SIZE_MASK = BUF_SIZE_MAX - 1;
private final ByteBuffer[] bufs;
private final long offset;
private final long length;
private final long cd;
private final Index index;
private Archive(final ByteBuffer[] bufs, final long offset, final long length, final long cd, final Index index) {
this.bufs = bufs;
this.offset = offset;
this.length = length;
this.cd = cd;
this.index = index;
}
public static Archive open(Path path) throws IOException {
try (FileChannel fc = FileChannel.open(path, StandardOpenOption.READ)) {
long size = fc.size();
final int bufCnt = Math.toIntExact((size + BUF_SIZE_MASK) >> BUF_SHIFT);
final ByteBuffer[] array = new ByteBuffer[bufCnt];
long offs = 0;
int idx = 0;
while (size > BUF_SIZE_MASK) {
array[idx++] = fc.map(FileChannel.MapMode.READ_ONLY, offs, BUF_SIZE_MAX).order(ByteOrder.LITTLE_ENDIAN);
size -= BUF_SIZE_MAX;
offs += BUF_SIZE_MAX;
}
array[idx] = fc.map(FileChannel.MapMode.READ_ONLY, offs, size).order(ByteOrder.LITTLE_ENDIAN);
return open(array);
}
}
public static Archive open(ByteBuffer buf) throws IOException {
Assert.checkNotNullParam("buf", buf);
if (buf.order() == ByteOrder.BIG_ENDIAN) {
buf = buf.duplicate().order(ByteOrder.LITTLE_ENDIAN);
}
return open(new ByteBuffer[] { buf });
}
static Archive open(ByteBuffer[] bufs) throws IOException {
return open(bufs, 0, capacity(bufs));
}
static Archive open(ByteBuffer[] bufs, long offset, long length) throws IOException {
// find the directory by looking first at its expected location and working our way backwards
long eocd = length - EOCD_END;
// todo: this could be optimized a bit (boyer-moore for example)
while (getUnsignedInt(bufs, offset + eocd) != SIG_EOCD) {
if (eocd == 0) {
throw new IOException("Invalid archive");
}
eocd--;
}
int entries = getUnsignedShort(bufs, offset + eocd + EOCD_CDE_COUNT_ALL);
// validate the EOCD record
if (getUnsignedShort(bufs, offset + eocd + EOCD_CD_FIRST_DISK_NUMBER) != 0 || getUnsignedShort(bufs, offset + eocd + EOCD_DISK_NUMBER) != 0 || entries != getUnsignedShort(bufs, offset + eocd + EOCD_CDE_COUNT_THIS_DISK)) {
throw new IOException("Multi-disk archives are not supported");
}
// check now for zip64
long eocdLocZip64 = eocd - EOCDL_ZIP64_END;
long eocdZip64 = -1;
if (getInt(bufs, offset + eocdLocZip64 + EOCDL_ZIP64_SIGNATURE) == SIG_EOCDL_ZIP64) {
// probably zip64
// validate the EOCDL_ZIP64
if (getInt(bufs, offset + eocdLocZip64 + EOCDL_ZIP64_DISK_COUNT) != 1 || getInt(bufs, offset + eocdLocZip64 + EOCDL_ZIP64_EOCD_DISK_NUMBER) != 0) {
throw new IOException("Multi-disk archives are not supported");
}
eocdZip64 = getLong(bufs, offset + eocdLocZip64 + EOCDL_ZIP64_EOCD_OFFSET);
if (getUnsignedInt(bufs, offset + eocdZip64 + EOCD_ZIP64_SIGNATURE) != SIG_EOCD_ZIP64) {
// oops, zip64 isn't really present, just bad luck
eocdZip64 = -1;
}
}
long cd;
cd = getUnsignedInt(bufs, offset + eocd + EOCD_CD_START_OFFSET);
if (cd == 0xffffffffL && eocdZip64 != -1) {
cd = getLong(bufs, offset + eocdZip64 + EOCD_ZIP64_CD_START_OFFSET);
}
if (entries == 0xffff && eocdZip64 != -1) {
final long cnt = getUnsignedInt(bufs, offset + eocdZip64 + EOCD_ZIP64_CDE_COUNT_ALL);
if (cnt > 0x07ff_ffffL) {
throw new IOException("Archive has too many entries");
}
entries = (int) cnt;
}
// generate the index
Index index;
if (length <= 0xfffe) {
index = new TinyIndex(entries);
} else if (length <= 0xffff_ffffeL) {
index = new LargeIndex(entries);
} else {
index = new HugeIndex(entries);
}
// iterate the directory
final int mask = index.getMask();
long cde = cd;
for (int i = 0; i < entries; i ++) {
if (getInt(bufs, offset + cde + CDE_SIGNATURE) != SIG_CDE) {
throw new IOException("Archive appears to be corrupted");
}
int hc = getHashCodeOfEntry(bufs, offset + cde);
index.put(hc & mask, cde);
cde = cde + CDE_END + getUnsignedShort(bufs, offset + cde + CDE_FILE_NAME_LENGTH) + getUnsignedShort(bufs, offset + cde + CDE_EXTRA_LENGTH) + getUnsignedShort(bufs, offset + cde + CDE_COMMENT_LENGTH);
}
return new Archive(bufs, offset, length, cd, index);
}
private static String getNameOfEntry(ByteBuffer[] bufs, long cde) {
long name = cde + CDE_END;
int nameLen = getUnsignedShort(bufs, cde + CDE_FILE_NAME_LENGTH);
boolean utf8 = (getUnsignedShort(bufs, cde + CDE_GP_BITS) & GP_UTF_8) != 0;
if (utf8) {
return new String(getBytes(bufs, name, nameLen), StandardCharsets.UTF_8);
} else {
char[] nameChars = new char[nameLen];
for (int i = 0; i < nameLen; i ++) {
nameChars[i] = Cp437.charFor(getUnsignedByte(bufs, name + i));
}
return new String(nameChars);
}
}
private static int getHashCodeOfEntry(ByteBuffer[] bufs, long cde) {
long name = cde + CDE_END;
int nameLen = getUnsignedShort(bufs, cde + CDE_FILE_NAME_LENGTH);
boolean utf8 = (getUnsignedShort(bufs, cde + CDE_GP_BITS) & GP_UTF_8) != 0;
int hc = 0;
if (utf8) {
int cp;
for (int i = 0; i < nameLen; i += Utf8.getByteCount(getUnsignedByte(bufs, name + i))) {
cp = Utf8.codePointAt(bufs, name + i);
if (Character.isSupplementaryCodePoint(cp)) {
hc = hc * 31 + Character.highSurrogate(cp);
hc = hc * 31 + Character.lowSurrogate(cp);
} else {
hc = hc * 31 + cp;
}
}
} else {
for (int i = 0; i < nameLen; i ++) {
hc = hc * 31 + Cp437.charFor(getUnsignedByte(bufs, name + i));
}
}
return hc;
}
public long getFirstEntryHandle() {
return cd;
}
public long getNextEntryHandle(long entryHandle) {
final long next = entryHandle + CDE_END + getUnsignedShort(bufs, offset + entryHandle + CDE_FILE_NAME_LENGTH) + getUnsignedShort(bufs, offset + entryHandle + CDE_EXTRA_LENGTH);
if (next >= length || getInt(bufs, offset + next + CDE_SIGNATURE) != SIG_CDE) {
return -1;
}
return next;
}
public long getEntryHandle(String fileName) {
final int mask = index.getMask();
final int base = fileName.hashCode();
long entryHandle;
for (int i = 0; i < mask; i ++) {
entryHandle = index.get(base + i & mask);
if (entryHandle == -1) {
return -1;
}
if (entryNameEquals(entryHandle, fileName)) {
return entryHandle;
}
}
return -1;
}
public boolean entryNameEquals(final long entryHandle, final String fileName) {
long name = entryHandle + CDE_END;
int nameLen = getUnsignedShort(bufs, offset + entryHandle + CDE_FILE_NAME_LENGTH);
boolean utf8 = (getUnsignedShort(bufs, offset + entryHandle + CDE_GP_BITS) & GP_UTF_8) != 0;
final int length = fileName.length();
if (utf8) {
long i;
int j;
for (i = 0, j = 0; i < nameLen && j < length; i += Utf8.getByteCount(getUnsignedByte(bufs, offset + name + i)), j = fileName.offsetByCodePoints(j, 1)) {
if (Utf8.codePointAt(bufs, offset + name + i) != fileName.codePointAt(j)) {
return false;
}
}
return i == nameLen && j == length;
} else {
int i, j;
for (i = 0, j = 0; i < nameLen && j < length; i++, j = fileName.offsetByCodePoints(j, 1)) {
if (Cp437.charFor(getUnsignedByte(bufs, offset + i + entryHandle + CDE_END)) != fileName.codePointAt(j)) {
return false;
}
}
return i == nameLen && j == length;
}
}
private long getLocalHeader(long entryHandle) {
long lh = getUnsignedInt(bufs, offset + entryHandle + CDE_LOCAL_HEADER_OFFSET);
if (lh == 0xffff_ffffL) {
long zip64 = getExtraRecord(entryHandle, EXT_ID_ZIP64);
if (zip64 != -1) {
lh = getLong(bufs, offset + zip64 + ZIP64_LOCAL_HEADER_OFFSET);
}
}
return lh;
}
public String getEntryName(long entryHandle) {
return getNameOfEntry(bufs, entryHandle);
}
public ByteBuffer getEntryContents(long entryHandle) throws IOException {
long size = getUncompressedSize(entryHandle);
long compSize = getCompressedSize(entryHandle);
if (size > 0x1000_0000 || compSize > 0x1000_0000) {
throw new IOException("Entry is too large to read into RAM");
}
long localHeader = getLocalHeader(entryHandle);
if ((getUnsignedShort(bufs, offset + localHeader + LH_GP_BITS) & (GP_ENCRYPTED | GP_STRONG_ENCRYPTION)) != 0) {
throw new IOException("Cannot read encrypted entries");
}
final long offset = getDataOffset(localHeader);
final int method = getCompressionMethod(entryHandle);
switch (method) {
case METHOD_STORED: {
return bufferOf(bufs, this.offset + offset, (int) size);
}
case METHOD_DEFLATE: {
final Inflater inflater = new Inflater(true);
try {
return JDKSpecific.inflate(inflater, bufs, this.offset + offset, (int) compSize, (int) size);
} catch (DataFormatException e) {
throw new IOException(e);
} finally {
inflater.end();
}
}
default: {
throw new IOException("Unsupported compression scheme");
}
}
}
private long getDataOffset(final long localHeader) {
return localHeader + LH_END + getUnsignedShort(bufs, offset + localHeader + LH_FILE_NAME_LENGTH) + getUnsignedShort(bufs, offset + localHeader + LH_EXTRA_LENGTH);
}
public InputStream getEntryStream(final long entryHandle) throws IOException {
long size = getCompressedSize(entryHandle);
long localHeader = getLocalHeader(entryHandle);
if ((getUnsignedShort(bufs, offset + localHeader + LH_GP_BITS) & (GP_ENCRYPTED | GP_STRONG_ENCRYPTION)) != 0) {
throw new IOException("Cannot read encrypted entries");
}
final long offset = getDataOffset(localHeader);
final int method = getCompressionMethod(entryHandle);
switch (method) {
case METHOD_STORED: {
return new ByteBufferInputStream(bufs, this.offset + offset, size);
}
case METHOD_DEFLATE: {
return new InflaterInputStream(new ByteBufferInputStream(bufs, this.offset + offset, size));
}
default: {
throw new IOException("Unsupported compression scheme");
}
}
}
public Archive getNestedArchive(long entryHandle) throws IOException {
long localHeader = getLocalHeader(entryHandle);
if ((getUnsignedShort(bufs, this.offset + localHeader + LH_GP_BITS) & (GP_ENCRYPTED | GP_STRONG_ENCRYPTION)) != 0) {
throw new IOException("Cannot read encrypted entries");
}
final long offset = getDataOffset(localHeader);
final int method = getCompressionMethod(entryHandle);
if (method != METHOD_STORED) {
throw new IOException("Cannot open compressed nested archive");
}
long size = getUncompressedSize(entryHandle);
if (size < Integer.MAX_VALUE) {
final ByteBuffer slice = sliceOf(bufs, this.offset + offset, (int) size);
if (slice != null) {
return Archive.open(slice);
}
}
return Archive.open(bufs, this.offset + offset, size);
}
public boolean isCompressed(long entryHandle) {
return getCompressionMethod(entryHandle) != METHOD_STORED;
}
private int getCompressionMethod(final long entryHandle) {
return getUnsignedShort(bufs, offset + entryHandle + CDE_COMP_METHOD);
}
private long getExtraRecord(final long entryHandle, final int headerId) {
long extra = entryHandle + CDE_END + getUnsignedShort(bufs, offset + entryHandle + CDE_FILE_NAME_LENGTH) + getUnsignedShort(bufs, offset + entryHandle + CDE_COMMENT_LENGTH);
int extraLen = getUnsignedShort(bufs, offset + entryHandle + CDE_EXTRA_LENGTH);
for (int i = 0; i < extraLen; i = i + getUnsignedShort(bufs, offset + extra + i + 2)) {
if (getUnsignedShort(bufs, offset + extra + i) == headerId) {
return extra + i + 4;
}
}
return -1;
}
public long getUncompressedSize(long entryHandle) {
long size = getUnsignedInt(bufs, offset + entryHandle + CDE_UNCOMPRESSED_SIZE);
if (size == 0xffffffff) {
long zip64 = getExtraRecord(entryHandle, EXT_ID_ZIP64);
if (zip64 != -1) {
size = getLong(bufs, offset + zip64 + ZIP64_UNCOMPRESSED_SIZE);
}
}
return size;
}
public long getCompressedSize(long entryHandle) {
long size = getUnsignedInt(bufs, offset + entryHandle + CDE_COMPRESSED_SIZE);
if (size == 0xffffffff) {
long zip64 = getExtraRecord(entryHandle, EXT_ID_ZIP64);
if (zip64 != -1) {
size = getLong(bufs, offset + zip64 + ZIP64_COMPRESSED_SIZE);
}
}
return size;
}
public long getModifiedTime(long entryHandle) {
long unix = getExtraRecord(entryHandle, EXT_ID_UNIX);
if (unix != -1) {
long unixTime = getUnsignedInt(bufs, offset + unix + UNIX_MODIFIED_TIME);
if (unixTime != 0) {
return unixTime * 1000;
}
}
// todo: NTFS (0x000a) sub-tag ID 0x0001 contains an 8-byte mtime
return dosTimeStamp(getUnsignedShort(bufs, offset + entryHandle + CDE_MOD_TIME), getUnsignedShort(bufs, offset + entryHandle + CDE_MOD_DATE));
}
public void close() {
}
private static long dosTimeStamp(int modTime, int modDate) {
int year = 1980 + (modDate >> 9);
int month = 1 + ((modDate >> 5) & 0b1111);
int day = modDate & 0b11111;
int hour = modTime >> 11;
int minute = (modTime >> 5) & 0b111111;
int second = (modTime & 0b11111) << 1;
return LocalDateTime.of(year, month, day, hour, minute, second).toInstant(ZoneOffset.UTC).toEpochMilli();
}
public boolean isDirectory(final long entryHandle) {
final int madeBy = getUnsignedShort(bufs, offset + entryHandle + CDE_VERSION_MADE_BY);
final int extAttr = getInt(bufs, entryHandle + CDE_EXTERNAL_ATTRIBUTES);
switch (madeBy) {
case MADE_BY_UNIX: {
//noinspection OctalInteger
return (extAttr & 0170000) == 0040000;
}
default: {
return (extAttr & 0b10000) != 0;
}
}
}
/**
* Only loaded if CP-437 zip entries exist, which is unlikely but allowed.
*/
static final class Cp437 {
static final char[] codePoints = {
'\0','☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '◙', '♂', '♀', '♪', '♫', '☼',
'►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼',
' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '⌂',
'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å',
'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ',
'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»',
'░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐',
'└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
'╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
'≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', 0xA0
};
private Cp437() {}
static char charFor(int c) {
return codePoints[c];
}
}
static final class Utf8 {
private Utf8() {}
static int getByteCount(final int a) {
if (a <= 0b0111_1111) {
return 1;
} else if (a <= 0b1011_1111) {
// invalid garbage (1 byte)
return 1;
} else if (a <= 0b1101_1111) {
return 2;
} else if (a <= 0b1110_1111) {
return 3;
} else if (a <= 0b1111_0111) {
return 4;
} else {
// invalid garbage (1 byte)
return 1;
}
}
static int codePointAt(final ByteBuffer[] bufs, final long i) {
final int a = getUnsignedByte(bufs, i);
if (a <= 0b0111_1111) {
return a;
} else if (a <= 0b1011_1111) {
// invalid garbage (1 byte)
return '�';
}
int b = getUnsignedByte(bufs, i + 1);
if ((b & 0b11_000000) != 0b10_000000) {
// second byte is invalid; return � instead
return '�';
}
if (a <= 0b1101_1111) {
// two bytes
return (a & 0b000_11111) << 6 | b & 0b00_111111;
}
int c = getUnsignedByte(bufs, i + 2);
if ((c & 0b11_000000) != 0b10_000000) {
// third byte is invalid; return � instead
return '�';
}
if (a <= 0b1110_1111) {
// three bytes
return (a & 0b0000_1111) << 12 | (b & 0b00_111111) << 6 | c & 0b00_111111;
}
int d = getUnsignedByte(bufs, i + 3);
if ((d & 0b11_000000) != 0b10_000000) {
// fourth byte is invalid; return � instead
return '�';
}
if (a <= 0b1111_0111) {
// four bytes
return (a & 0b00000_111) << 18 | (b & 0b00_111111) << 12 | (c & 0b00_111111) << 6 | d & 0b00_111111;
}
// invalid garbage (1 byte)
return '�';
}
}
static int bufIdx(long idx) {
return (int) (idx >>> BUF_SHIFT);
}
static int bufOffs(long idx) {
return ((int)idx) & BUF_SIZE_MASK;
}
static byte getByte(ByteBuffer[] bufs, long idx) {
return bufs[bufIdx(idx)].get(bufOffs(idx));
}
static int getUnsignedByte(ByteBuffer[] bufs, long idx) {
return getByte(bufs, idx) & 0xff;
}
static int getUnsignedByte(ByteBuffer buf, int idx) {
return buf.get(idx) & 0xff;
}
static short getShort(ByteBuffer[] bufs, long idx) {
final int bi = bufIdx(idx);
return bi == bufIdx(idx + 1) ? bufs[bi].getShort(bufOffs(idx)) : (short) (getUnsignedByte(bufs, idx) | getByte(bufs, idx + 1) << 8);
}
static int getUnsignedShort(ByteBuffer[] bufs, long idx) {
return getShort(bufs, idx) & 0xffff;
}
static int getMedium(ByteBuffer[] bufs, long idx) {
return getUnsignedByte(bufs, idx) | getUnsignedShort(bufs, idx + 1) << 8;
}
static long getUnsignedMedium(ByteBuffer[] bufs, long idx) {
return getUnsignedByte(bufs, idx) | getUnsignedShort(bufs, idx + 1) << 8;
}
static int getInt(ByteBuffer[] bufs, long idx) {
final int bi = bufIdx(idx);
return bi == bufIdx(idx + 3) ? bufs[bi].getInt(bufOffs(idx)) : getUnsignedShort(bufs, idx) | getShort(bufs, idx + 2) << 16;
}
static long getUnsignedInt(ByteBuffer[] bufs, long idx) {
return getInt(bufs, idx) & 0xffff_ffffL;
}
static long getLong(ByteBuffer[] bufs, long idx) {
final int bi = bufIdx(idx);
return bi == bufIdx(idx + 7) ? bufs[bi].getLong(bufOffs(idx)) : getUnsignedInt(bufs, idx) | (long)getInt(bufs, idx + 4) << 32;
}
static void readBytes(ByteBuffer[] bufs, long idx, byte[] dest, int off, int len) {
while (len > 0) {
final int bi = bufIdx(idx);
final int bo = bufOffs(idx);
ByteBuffer buf = bufs[bi].duplicate();
buf.position(bo);
final int cnt = min(len, buf.remaining());
buf.get(dest, 0, cnt);
len -= cnt;
off += cnt;
idx += cnt;
}
}
static byte[] getBytes(ByteBuffer[] bufs, long idx, int len) {
final byte[] bytes = new byte[len];
readBytes(bufs, idx, bytes, 0, len);
return bytes;
}
private static final ByteBuffer EMPTY_BUF = ByteBuffer.allocateDirect(0);
static ByteBuffer sliceOf(ByteBuffer[] bufs, long idx, int len) {
if (len == 0) return EMPTY_BUF;
final int biStart = bufIdx(idx);
final int biEnd = bufIdx(idx + len - 1);
if (biStart == biEnd) {
final ByteBuffer buf = bufs[biStart].duplicate();
buf.position(bufOffs(idx));
buf.limit(buf.position() + len);
return buf.slice();
} else {
return null;
}
}
static ByteBuffer bufferOf(ByteBuffer[] bufs, long idx, int len) {
ByteBuffer buf = sliceOf(bufs, idx, len);
if (buf == null) {
buf = ByteBuffer.wrap(getBytes(bufs, idx, len));
}
return buf;
}
static long capacity(ByteBuffer[] bufs) {
final int lastIdx = bufs.length - 1;
return ((long) lastIdx) * BUF_SIZE_MAX + bufs[lastIdx].capacity();
}
}