Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/java.base/share/classes/java/util/zip/ZipEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -564,20 +564,20 @@ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) {
// be the magic value and it "accidentally" has some
// bytes in extra match the id.
if (sz >= 16) {
size = get64(extra, off);
csize = get64(extra, off + 8);
size = get64S(extra, off);
csize = get64S(extra, off + 8);
}
} else {
// CEN extra zip64
if (size == ZIP64_MAGICVAL) {
if (off + 8 > len) // invalid zip64 extra
break; // fields, just skip
size = get64(extra, off);
size = get64S(extra, off);
}
if (csize == ZIP64_MAGICVAL) {
if (off + 16 > len) // invalid zip64 extra
break; // fields, just skip
csize = get64(extra, off + 8);
csize = get64S(extra, off + 8);
}
}
}
Expand All @@ -588,15 +588,15 @@ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) {
int pos = off + 4; // reserved 4 bytes
if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
break;
long wtime = get64(extra, pos + 4);
long wtime = get64S(extra, pos + 4);
if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
mtime = winTimeToFileTime(wtime);
}
wtime = get64(extra, pos + 12);
wtime = get64S(extra, pos + 12);
if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
atime = winTimeToFileTime(wtime);
}
wtime = get64(extra, pos + 20);
wtime = get64S(extra, pos + 20);
if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
ctime = winTimeToFileTime(wtime);
}
Expand Down
25 changes: 11 additions & 14 deletions src/java.base/share/classes/java/util/zip/ZipFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -906,21 +906,21 @@ private void checkZIP64(byte[] cen, int cenpos) {
if (size == ZIP64_MAGICVAL) {
if (sz < 8 || (off + 8) > end)
break;
size = get64(cen, off);
size = get64S(cen, off);
sz -= 8;
off += 8;
}
if (rem == ZIP64_MAGICVAL) {
if (sz < 8 || (off + 8) > end)
break;
rem = get64(cen, off);
rem = get64S(cen, off);
sz -= 8;
off += 8;
}
if (pos == ZIP64_MAGICVAL) {
if (sz < 8 || (off + 8) > end)
break;
pos = get64(cen, off);
pos = get64S(cen, off);
sz -= 8;
off += 8;
}
Expand Down Expand Up @@ -1376,7 +1376,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize,
// Check the uncompressed size is not negative
if (size == ZIP64_MAGICVAL) {
if ( blockSize >= Long.BYTES) {
if (get64(cen, off) < 0) {
if (get64S(cen, off) < 0) {
zerror("Invalid zip64 extra block size value");
}
off += Long.BYTES;
Expand All @@ -1388,7 +1388,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize,
// Check the compressed size is not negative
if (csize == ZIP64_MAGICVAL) {
if (blockSize >= Long.BYTES) {
if (get64(cen, off) < 0) {
if (get64S(cen, off) < 0) {
zerror("Invalid zip64 extra block compressed size value");
}
off += Long.BYTES;
Expand All @@ -1400,7 +1400,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize,
// Check the LOC offset is not negative
if (locoff == ZIP64_MAGICVAL) {
if (blockSize >= Long.BYTES) {
if (get64(cen, off) < 0) {
if (get64S(cen, off) < 0) {
zerror("Invalid zip64 extra block LOC OFFSET value");
}
// Note: We do not need to adjust the following fields as
Expand Down Expand Up @@ -1641,10 +1641,7 @@ private End findEND() throws IOException {
}
// Now scan the block backwards for END header signature
for (int i = buf.length - ENDHDR; i >= 0; i--) {
if (buf[i+0] == (byte)'P' &&
buf[i+1] == (byte)'K' &&
buf[i+2] == (byte)'\005' &&
buf[i+3] == (byte)'\006') {
if (get32(buf, i) == ENDSIG) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a matter of personal preference, but I think GETSIG(buf, i) == ENDSIG reads better than get32(buf, i) == ENDSIG.

The fact that it's 32 bits is kind of a detail and it doesn't reveal as well that we intend to read a signature.

So could we keep GETSIG, but add an index? There are places in ZipInputStream as well which could make use of that for signature checking. (But maybe not for this PR)

Alternatively, ENDSIG(buf, i) == ENDSIG would be consistent with CENSIG(buf, i) uses.

Same applies to the other GETSIG replacements in this file.

Copy link
Member Author

@cl4es cl4es Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all the GETSIG(byte[]) methods are quite nasty, and it's all used very inconsistently. I wouldn't mind going the other way and removing all the CENSIG, LOCNAM etc methods and just call get16/32/32S/64(buf, ZipConstants.FOO) as appropriate. Add a comment at each ZipConstants entry about exactly how many bytes are in the field, if it's unsigned or signed. Then at least there's a reference to something that looks like a specification, not a mix of constants and single-purpose offset getters scattered around (which doesn't even reference the constants in ZipConstants).

// Found ENDSIG header
byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR);
end.centot = ENDTOT(endbuf);
Expand All @@ -1664,9 +1661,9 @@ private End findEND() throws IOException {
if (cenpos < 0 ||
locpos < 0 ||
readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 ||
GETSIG(sbuf) != CENSIG ||
get32(sbuf, 0) != CENSIG ||
readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 ||
GETSIG(sbuf) != LOCSIG) {
get32(sbuf, 0) != LOCSIG) {
continue;
}
}
Expand All @@ -1681,13 +1678,13 @@ private End findEND() throws IOException {
byte[] loc64 = new byte[ZIP64_LOCHDR];
if (end.endpos < ZIP64_LOCHDR ||
readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
!= loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) {
!= loc64.length || get32(loc64, 0) != ZIP64_LOCSIG) {
return end;
}
long end64pos = ZIP64_LOCOFF(loc64);
byte[] end64buf = new byte[ZIP64_ENDHDR];
if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
!= end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) {
!= end64buf.length || get32(end64buf, 0) != ZIP64_ENDSIG) {
return end;
}
// end64 candidate found,
Expand Down
8 changes: 4 additions & 4 deletions src/java.base/share/classes/java/util/zip/ZipInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -603,14 +603,14 @@ private void readEnd(ZipEntry e) throws IOException {
long sig = get32(tmpbuf, 0);
if (sig != EXTSIG) { // no EXTSIG present
e.crc = sig;
e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
e.csize = get64S(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
e.size = get64S(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
((PushbackInputStream)in).unread(
tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC, ZIP64_EXTCRC);
} else {
e.crc = get32(tmpbuf, ZIP64_EXTCRC);
e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
e.size = get64(tmpbuf, ZIP64_EXTLEN);
e.csize = get64S(tmpbuf, ZIP64_EXTSIZ);
e.size = get64S(tmpbuf, ZIP64_EXTLEN);
}
} else {
readFully(tmpbuf, 0, EXTHDR);
Expand Down
140 changes: 65 additions & 75 deletions src/java.base/share/classes/java/util/zip/ZipUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;

class ZipUtils {

Expand Down Expand Up @@ -170,23 +171,31 @@ static LocalDateTime javaEpochToLocalDateTime(long time) {
* The bytes are assumed to be in Intel (little-endian) byte order.
*/
public static final int get16(byte[] b, int off) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can JIT automatically perform MergeStore here? If JIT can automatically perform MergeStore without using Unsafe, many scenarios will benefit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately we only have merge store for writing to arrays; no merge load when we are reading from arrays.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. As I wrote in the PR description: "An appealing alternative would be to implement a merge load analogue to the merge store optimizations in C2." So it's the opposite of a merge store. I recall @eme64 mentioning somewhere that such an optimization would be possible, if we could show it to be profitable. I think it's reasonable to do this enhancement now, then revert the Unsafe stuff if/when there's a merge load optimization that beats it.

return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8);
Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER);
Preconditions.checkIndex(off + 1, b.length, Preconditions.AIOOBE_FORMATTER);
Comment on lines +174 to +175
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use Preconditions.checkFromIndexSize, which should be less overhead:

Suggested change
Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER);
Preconditions.checkIndex(off + 1, b.length, Preconditions.AIOOBE_FORMATTER);
Preconditions.checkFromIndexSize(off, 2, b.length, Preconditions.AIOOBE_FORMATTER);

Similarly for other methods.

Copy link
Member Author

@cl4es cl4es Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually not less overhead in my tests, since checkIndex is intrinsic and mostly disappears, while with checkFromIndexSize performance gets significantly worse (on par with baseline). It's on my todo to investigate this in-depth but I think checkFromIndexSize needs to be given similar intrinsification treatment as checkIndex.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually if we trust the input index to be nonnegative, we can just check our end index for out of bounds too.

Copy link
Member Author

@cl4es cl4es Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I think the JIT is pretty good at eliminating the (intrinsic) checkIndex calls when they are redundant though. Performance with and without these checkIndexes are the same in my testing, so we can eat and have the cake on this one.

FWIW I wouldn't mind giving similar treatment to ByteArray(-LittleEndian) and avoid the VarHandles dependency in those utility classes, but I have no urge to get into the sort of discussions that were spawned in #19616

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually if we trust the input index to be nonnegative, we can just check our end index for out of bounds too.

I would not trust that. Perhaps for well-formed ZIP files, but trust me, not all ZIPs are well-formed ;-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that requires you to pre-validate the input argument as a fixed position after already-read content, which is not always the case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like @eirbjo suggests we'd have to put a lot of validation in other places if we went down this route. Regardless this is an academic discussion since the PR suggests the safe route and we don't pay much of a cost for that in microbenchmarks.

return Short.toUnsignedInt(
UNSAFE.getShortUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false));
}

/**
* Fetches unsigned 32-bit value from byte array at specified offset.
* The bytes are assumed to be in Intel (little-endian) byte order.
*/
public static final long get32(byte[] b, int off) {
return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER);
Preconditions.checkIndex(off + 3, b.length, Preconditions.AIOOBE_FORMATTER);
return Integer.toUnsignedLong(
UNSAFE.getIntUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false));
}

/**
* Fetches signed 64-bit value from byte array at specified offset.
* The bytes are assumed to be in Intel (little-endian) byte order.
*/
public static final long get64(byte[] b, int off) {
return get32(b, off) | (get32(b, off+4) << 32);
public static final long get64S(byte[] b, int off) {
Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER);
Preconditions.checkIndex(off + 7, b.length, Preconditions.AIOOBE_FORMATTER);
return UNSAFE.getLongUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false);
}

/**
Expand All @@ -195,28 +204,9 @@ public static final long get64(byte[] b, int off) {
*
*/
public static final int get32S(byte[] b, int off) {
return (get16(b, off) | (get16(b, off+2) << 16));
}

// fields access methods
static final int CH(byte[] b, int n) {
return b[n] & 0xff ;
}

static final int SH(byte[] b, int n) {
return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8);
}

static final long LG(byte[] b, int n) {
return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL;
}

static final long LL(byte[] b, int n) {
return (LG(b, n)) | (LG(b, n + 4) << 32);
}

static final long GETSIG(byte[] b) {
return LG(b, 0);
Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER);
Preconditions.checkIndex(off + 3, b.length, Preconditions.AIOOBE_FORMATTER);
return UNSAFE.getIntUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false);
}

/*
Expand All @@ -231,56 +221,56 @@ static final long GETSIG(byte[] b) {


// local file (LOC) header fields
static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature
static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract
static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags
static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method
static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time
static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data
static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size
static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size
static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length
static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length
static final long LOCSIG(byte[] b) { return get32(b, 0); } // signature
static final int LOCVER(byte[] b) { return get16(b, 4); } // version needed to extract
static final int LOCFLG(byte[] b) { return get16(b, 6); } // general purpose bit flags
static final int LOCHOW(byte[] b) { return get16(b, 8); } // compression method
static final long LOCTIM(byte[] b) { return get32(b, 10);} // modification time
static final long LOCCRC(byte[] b) { return get32(b, 14);} // crc of uncompressed data
static final long LOCSIZ(byte[] b) { return get32(b, 18);} // compressed data size
static final long LOCLEN(byte[] b) { return get32(b, 22);} // uncompressed data size
static final int LOCNAM(byte[] b) { return get16(b, 26);} // filename length
static final int LOCEXT(byte[] b) { return get16(b, 28);} // extra field length

// extra local (EXT) header fields
static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data
static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size
static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size
static final long EXTCRC(byte[] b) { return get32(b, 4);} // crc of uncompressed data
static final long EXTSIZ(byte[] b) { return get32(b, 8);} // compressed size
static final long EXTLEN(byte[] b) { return get32(b, 12);} // uncompressed size

// end of central directory header (END) fields
static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk
static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries
static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size
static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset
static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of ZIP file comment
static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);}

// zip64 end of central directory recoder fields
static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk
static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries
static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size
static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset
static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset
static final int ENDSUB(byte[] b) { return get16(b, 8); } // number of entries on this disk
static final int ENDTOT(byte[] b) { return get16(b, 10);} // total number of entries
static final long ENDSIZ(byte[] b) { return get32(b, 12);} // central directory size
static final long ENDOFF(byte[] b) { return get32(b, 16);} // central directory offset
static final int ENDCOM(byte[] b) { return get16(b, 20);} // size of ZIP file comment
static final int ENDCOM(byte[] b, int off) { return get16(b, off + 20);}

// zip64 end of central directory record fields
static final long ZIP64_ENDTOD(byte[] b) { return get64S(b, 24);} // total number of entries on disk
static final long ZIP64_ENDTOT(byte[] b) { return get64S(b, 32);} // total number of entries
static final long ZIP64_ENDSIZ(byte[] b) { return get64S(b, 40);} // central directory size
static final long ZIP64_ENDOFF(byte[] b) { return get64S(b, 48);} // central directory offset
static final long ZIP64_LOCOFF(byte[] b) { return get64S(b, 8);} // zip64 end offset

// central directory header (CEN) fields
static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); }
static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); }
static final int CENVEM_FA(byte[] b, int pos) { return CH(b, pos + 5); } // file attribute compatibility
static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); }
static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); }
static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);}
static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);}
static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);}
static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);}
static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);}
static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);}
static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);}
static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);}
static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);}
static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);}
static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);}
static final int CENATX_PERMS(byte[] b, int pos) { return SH(b, pos + 40);} // posix permission data
static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);}
static final long CENSIG(byte[] b, int pos) { return get32(b, pos + 0); }
static final int CENVEM(byte[] b, int pos) { return get16(b, pos + 4); }
static final int CENVEM_FA(byte[] b, int pos) { return Byte.toUnsignedInt(b[pos + 5]); } // file attribute compatibility
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider introducing get8 for consistency here? As it stands, this looks like the odd one out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered it, but since get8 would basically just delegate to or do exactly what Byte.toUnsignedInt does I opted to cut out the middle man.

static final int CENVER(byte[] b, int pos) { return get16(b, pos + 6); }
static final int CENFLG(byte[] b, int pos) { return get16(b, pos + 8); }
static final int CENHOW(byte[] b, int pos) { return get16(b, pos + 10);}
static final long CENTIM(byte[] b, int pos) { return get32(b, pos + 12);}
static final long CENCRC(byte[] b, int pos) { return get32(b, pos + 16);}
static final long CENSIZ(byte[] b, int pos) { return get32(b, pos + 20);}
static final long CENLEN(byte[] b, int pos) { return get32(b, pos + 24);}
static final int CENNAM(byte[] b, int pos) { return get16(b, pos + 28);}
static final int CENEXT(byte[] b, int pos) { return get16(b, pos + 30);}
static final int CENCOM(byte[] b, int pos) { return get16(b, pos + 32);}
static final int CENDSK(byte[] b, int pos) { return get16(b, pos + 34);}
static final int CENATT(byte[] b, int pos) { return get16(b, pos + 36);}
static final long CENATX(byte[] b, int pos) { return get32(b, pos + 38);}
static final int CENATX_PERMS(byte[] b, int pos) { return get16(b, pos + 40);} // posix permission data
static final long CENOFF(byte[] b, int pos) { return get32(b, pos + 42);}

// The END header is followed by a variable length comment of size < 64k.
static final long END_MAXLEN = 0xFFFF + ENDHDR;
Expand All @@ -293,16 +283,16 @@ static void loadLibrary() {
jdk.internal.loader.BootLoader.loadLibrary("zip");
}

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final Unsafe UNSAFE = Unsafe.getUnsafe();

private static final long byteBufferArrayOffset = unsafe.objectFieldOffset(ByteBuffer.class, "hb");
private static final long byteBufferOffsetOffset = unsafe.objectFieldOffset(ByteBuffer.class, "offset");
private static final long byteBufferArrayOffset = UNSAFE.objectFieldOffset(ByteBuffer.class, "hb");
private static final long byteBufferOffsetOffset = UNSAFE.objectFieldOffset(ByteBuffer.class, "offset");

static byte[] getBufferArray(ByteBuffer byteBuffer) {
return (byte[]) unsafe.getReference(byteBuffer, byteBufferArrayOffset);
return (byte[]) UNSAFE.getReference(byteBuffer, byteBufferArrayOffset);
}

static int getBufferOffset(ByteBuffer byteBuffer) {
return unsafe.getInt(byteBuffer, byteBufferOffsetOffset);
return UNSAFE.getInt(byteBuffer, byteBufferOffsetOffset);
}
}
Loading