Merge "Migration of ZoneInfo to 64-bit times from 32-bit"
diff --git a/luni/src/main/java/libcore/io/BufferIterator.java b/luni/src/main/java/libcore/io/BufferIterator.java
index b7c5b38..97890c3 100644
--- a/luni/src/main/java/libcore/io/BufferIterator.java
+++ b/luni/src/main/java/libcore/io/BufferIterator.java
@@ -44,13 +44,13 @@
public abstract int pos();
/**
- * Copies {@code byteCount} bytes from the current position into {@code dst}, starting at
- * {@code dstOffset}, and advances the current position {@code byteCount} bytes.
+ * Copies {@code byteCount} bytes from the current position into {@code bytes}, starting at
+ * {@code arrayOffset}, and advances the current position {@code byteCount} bytes.
*
* @throws IndexOutOfBoundsException if the read / write would be outside of the buffer / array
*/
@UnsupportedAppUsage
- public abstract void readByteArray(byte[] dst, int dstOffset, int byteCount);
+ public abstract void readByteArray(byte[] bytes, int arrayOffset, int byteCount);
/**
* Returns the byte at the current position, and advances the current position one byte.
@@ -69,13 +69,21 @@
public abstract int readInt();
/**
- * Copies {@code intCount} 32-bit ints from the current position into {@code dst}, starting at
- * {@code dstOffset}, and advances the current position {@code 4 * intCount} bytes.
+ * Copies {@code intCount} 32-bit ints from the current position into {@code ints}, starting at
+ * {@code arrayOffset}, and advances the current position {@code 4 * intCount} bytes.
*
* @throws IndexOutOfBoundsException if the read / write would be outside of the buffer / array
*/
@UnsupportedAppUsage
- public abstract void readIntArray(int[] dst, int dstOffset, int intCount);
+ public abstract void readIntArray(int[] ints, int arrayOffset, int intCount);
+
+ /**
+ * Copies {@code longCount} 64-bit ints from the current position into {@code longs}, starting
+ * at {@code arrayOffset}, and advances the current position {@code 8 * longCount} bytes.
+ *
+ * @throws IndexOutOfBoundsException if the read / write would be outside of the buffer / array
+ */
+ public abstract void readLongArray(long[] longs, int arrayOffset, int longCount);
/**
* Returns the 16-bit short at the current position, and advances the current position two bytes.
diff --git a/luni/src/main/java/libcore/io/NioBufferIterator.java b/luni/src/main/java/libcore/io/NioBufferIterator.java
index 263666d..1554519 100644
--- a/luni/src/main/java/libcore/io/NioBufferIterator.java
+++ b/luni/src/main/java/libcore/io/NioBufferIterator.java
@@ -50,10 +50,12 @@
this.swap = swap;
}
+ @Override
public void seek(int offset) {
position = offset;
}
+ @Override
public void skip(int byteCount) {
position += byteCount;
}
@@ -63,14 +65,16 @@
return position;
}
- public void readByteArray(byte[] dst, int dstOffset, int byteCount) {
- checkDstBounds(dstOffset, dst.length, byteCount);
+ @Override
+ public void readByteArray(byte[] bytes, int arrayOffset, int byteCount) {
+ checkArrayBounds(arrayOffset, bytes.length, byteCount);
file.checkNotClosed();
checkReadBounds(position, length, byteCount);
- Memory.peekByteArray(address + position, dst, dstOffset, byteCount);
+ Memory.peekByteArray(address + position, bytes, arrayOffset, byteCount);
position += byteCount;
}
+ @Override
public byte readByte() {
file.checkNotClosed();
checkReadBounds(position, length, 1);
@@ -79,6 +83,7 @@
return result;
}
+ @Override
public int readInt() {
file.checkNotClosed();
checkReadBounds(position, length, Integer.BYTES);
@@ -87,15 +92,27 @@
return result;
}
- public void readIntArray(int[] dst, int dstOffset, int intCount) {
- checkDstBounds(dstOffset, dst.length, intCount);
+ @Override
+ public void readIntArray(int[] ints, int arrayOffset, int intCount) {
+ checkArrayBounds(arrayOffset, ints.length, intCount);
file.checkNotClosed();
final int byteCount = Integer.BYTES * intCount;
checkReadBounds(position, length, byteCount);
- Memory.peekIntArray(address + position, dst, dstOffset, intCount, swap);
+ Memory.peekIntArray(address + position, ints, arrayOffset, intCount, swap);
position += byteCount;
}
+ @Override
+ public void readLongArray(long[] longs, int arrayOffset, int longCount) {
+ checkArrayBounds(arrayOffset, longs.length, longCount);
+ file.checkNotClosed();
+ final int byteCount = Long.BYTES * longCount;
+ checkReadBounds(position, length, byteCount);
+ Memory.peekLongArray(address + position, longs, arrayOffset, longCount, swap);
+ position += byteCount;
+ }
+
+ @Override
public short readShort() {
file.checkNotClosed();
checkReadBounds(position, length, Short.BYTES);
@@ -118,18 +135,18 @@
}
}
- private static void checkDstBounds(int dstOffset, int dstLength, int count) {
- if (dstOffset < 0 || count < 0) {
+ private static void checkArrayBounds(int arrayOffset, int arrayLength, int count) {
+ if (arrayOffset < 0 || count < 0) {
throw new IndexOutOfBoundsException(
- "Invalid dst args: offset=" + dstLength + ", count=" + count);
+ "Invalid args: arrayOffset=" + arrayOffset + ", count=" + count);
}
- // Use of int here relies on dstLength being an int <= Integer.MAX_VALUE, which it has to
+ // Use of int here relies on arrayLength being an int <= Integer.MAX_VALUE, which it has to
// be because it's an array length.
- final int targetPos = dstOffset + count;
- if (targetPos < 0 || targetPos > dstLength) {
+ final int targetPos = arrayOffset + count;
+ if (targetPos < 0 || targetPos > arrayLength) {
throw new IndexOutOfBoundsException(
- "Write outside range: dst.length=" + dstLength + ", offset="
- + dstOffset + ", count=" + count);
+ "Write outside range: arrayLength=" + arrayLength + ", arrayOffset="
+ + arrayOffset + ", count=" + count);
}
}
}
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index abd98b1..48665ca 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -48,20 +48,9 @@
* and storing it a representation to support the {@link TimeZone} and {@link GregorianCalendar}
* implementations. See {@link ZoneInfo#readTimeZone(String, BufferIterator, long)}.
*
- * <p>The main difference between {@code tzfile} and the compacted form is that the
- * {@code struct ttinfo} only uses a single byte for {@code tt_isdst} and {@code tt_abbrind}.
- *
* <p>This class does not use all the information from the {@code tzfile}; it uses:
* {@code tzh_timecnt} and the associated transition times and type information. For each type
- * (described by {@code struct ttinfo}) it uses {@code tt_gmtoff} and {@code tt_isdst}. Note, that
- * the definition of {@code struct ttinfo} uses {@code long}, and {@code int} but they do not have
- * the same meaning as Java. The prose following the definition makes it clear that the {@code long}
- * is 4 bytes and the {@code int} fields are 1 byte.
- *
- * <p>As the data uses 32 bits to store the time in seconds the time range is limited to roughly
- * 69 years either side of the epoch (1st Jan 1970 00:00:00) that means that it cannot handle any
- * dates before 1900 and after 2038. There is an extended version of the table that uses 64 bits
- * to store the data but that information is not used by this.
+ * (described by {@code struct ttinfo}) it uses {@code tt_gmtoff} and {@code tt_isdst}.
*
* <p>This class should be in libcore.timezone but this class is Serializable so cannot
* be moved there without breaking apps that have (for some reason) serialized TimeZone objects.
@@ -197,6 +186,19 @@
public static ZoneInfo readTimeZone(String id, BufferIterator it, long currentTimeMillis)
throws IOException {
+
+ // Skip over the superseded 32-bit header and data.
+ skipOver32BitData(id, it);
+
+ // Read the v2+ 64-bit header and data.
+ return read64BitData(id, it, currentTimeMillis);
+ }
+
+ /**
+ * Skip over the 32-bit data with some minimal validation to make sure sure we reading a valid
+ * and supported file.
+ */
+ private static void skipOver32BitData(String id, BufferIterator it) throws IOException {
// Variable names beginning tzh_ correspond to those in "tzfile.h".
// Check tzh_magic.
@@ -205,24 +207,82 @@
throw new IOException("Timezone id=" + id + " has an invalid header=" + tzh_magic);
}
- // Skip the uninteresting part of the header.
- it.skip(28);
+ byte tzh_version = it.readByte();
+ checkTzifVersionAcceptable(id, tzh_version);
+
+ // Skip the unused bytes.
+ it.skip(15);
+
+ // Read the header values necessary to read through all the 32-bit data.
+ int tzh_ttisgmtcnt = it.readInt();
+ int tzh_ttisstdcnt = it.readInt();
+ int tzh_leapcnt = it.readInt();
+ int tzh_timecnt = it.readInt();
+ int tzh_typecnt = it.readInt();
+ int tzh_charcnt = it.readInt();
+
+ // Skip transitions data, 4 bytes for each 32-bit time + 1 byte for isDst.
+ final int transitionInfoSize = 4 + 1;
+ it.skip(tzh_timecnt * transitionInfoSize);
+
+ // Skip ttinfos.
+ // struct ttinfo {
+ // int32_t tt_gmtoff;
+ // unsigned char tt_isdst;
+ // unsigned char tt_abbrind;
+ // };
+ final int ttinfoSize = 4 + 1 + 1;
+ it.skip(tzh_typecnt * ttinfoSize);
+
+ // Skip tzh_charcnt time zone abbreviations.
+ it.skip(tzh_charcnt);
+
+ // Skip tzh_leapcnt repetitions of a 32-bit time + a 32-bit correction.
+ int leapInfoSize = 4 + 4;
+ it.skip(tzh_leapcnt * leapInfoSize);
+
+ // Skip ttisstds and ttisgmts information. These can be ignored for our usecases as per
+ // https://mm.icann.org/pipermail/tz/2006-February/013359.html
+ it.skip(tzh_ttisstdcnt + tzh_ttisgmtcnt);
+ }
+
+ /**
+ * Read the 64-bit header and data for {@code id} from the current position of {@code it} and
+ * return a ZoneInfo.
+ */
+ private static ZoneInfo read64BitData(String id, BufferIterator it, long currentTimeMillis)
+ throws IOException {
+ // Variable names beginning tzh_ correspond to those in "tzfile.h".
+
+ // Check tzh_magic.
+ int tzh_magic = it.readInt();
+ if (tzh_magic != 0x545a6966) { // "TZif"
+ throw new IOException("Timezone id=" + id + " has an invalid header=" + tzh_magic);
+ }
+
+ byte tzh_version = it.readByte();
+ checkTzifVersionAcceptable(id, tzh_version);
+
+ // Skip the uninteresting parts of the header.
+ it.skip(27);
// Read the sizes of the arrays we're about to read.
int tzh_timecnt = it.readInt();
+
// Arbitrary ceiling to prevent allocating memory for corrupt data.
- // 2 per year with 2^32 seconds would give ~272 transitions.
final int MAX_TRANSITIONS = 2000;
if (tzh_timecnt < 0 || tzh_timecnt > MAX_TRANSITIONS) {
throw new IOException(
- "Timezone id=" + id + " has an invalid number of transitions=" + tzh_timecnt);
+ "Timezone id=" + id + " has an invalid number of transitions="
+ + tzh_timecnt);
}
int tzh_typecnt = it.readInt();
final int MAX_TYPES = 256;
if (tzh_typecnt < 1) {
throw new IOException("ZoneInfo requires at least one type "
- + "to be provided for each timezone but could not find one for '" + id + "'");
+ + "to be provided for each timezone but could not find one for '" + id
+ + "'");
} else if (tzh_typecnt > MAX_TYPES) {
throw new IOException(
"Timezone with id " + id + " has too many types=" + tzh_typecnt);
@@ -230,18 +290,9 @@
it.skip(4); // Skip tzh_charcnt.
- // Transitions are signed 32 bit integers, but we store them as signed 64 bit
- // integers since it's easier to compare them against 64 bit inputs (see getOffset
- // and isDaylightTime) with much less risk of an overflow in our calculations.
- //
- // The alternative of checking the input against the first and last transition in
- // the array is far more awkward and error prone.
- int[] transitions32 = new int[tzh_timecnt];
- it.readIntArray(transitions32, 0, transitions32.length);
-
long[] transitions64 = new long[tzh_timecnt];
+ it.readLongArray(transitions64, 0, transitions64.length);
for (int i = 0; i < tzh_timecnt; ++i) {
- transitions64[i] = transitions32[i];
if (i > 0 && transitions64[i] <= transitions64[i - 1]) {
throw new IOException(
id + " transition at " + i + " is not sorted correctly, is "
@@ -249,13 +300,14 @@
}
}
- byte[] type = new byte[tzh_timecnt];
- it.readByteArray(type, 0, type.length);
- for (int i = 0; i < type.length; i++) {
- int typeIndex = type[i] & 0xff;
+ byte[] types = new byte[tzh_timecnt];
+ it.readByteArray(types, 0, types.length);
+ for (int i = 0; i < types.length; i++) {
+ int typeIndex = types[i] & 0xff;
if (typeIndex >= tzh_typecnt) {
throw new IOException(
- id + " type at " + i + " is not < " + tzh_typecnt + ", is " + typeIndex);
+ id + " type at " + i + " is not < " + tzh_typecnt + ", is "
+ + typeIndex);
}
}
@@ -277,8 +329,19 @@
// for any locale. (The RI doesn't do any better than us here either.)
it.skip(1);
}
+ return new ZoneInfo(id, transitions64, types, gmtOffsets, isDsts, currentTimeMillis);
+ }
- return new ZoneInfo(id, transitions64, type, gmtOffsets, isDsts, currentTimeMillis);
+ private static void checkTzifVersionAcceptable(String id, byte tzh_version) throws IOException {
+ char tzh_version_char = (char) tzh_version;
+ // Version >= 2 is required because the 64-bit time section is required. v3 is the latest
+ // version known at the time of writing and is identical to v2 in the parts used by this
+ // class.
+ if (tzh_version_char != '2' && tzh_version_char != '3') {
+ throw new IOException(
+ "Timezone id=" + id + " has an invalid format version=\'" + tzh_version_char
+ + "\' (" + tzh_version + ")");
+ }
}
private ZoneInfo(String name, long[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts,
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 481766c..da4a04f 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -59,7 +59,7 @@
// http://code.google.com/p/android/issues/detail?id=14395
public void testPreHistoricInDaylightTime() {
- // A replacement for testPreHistoricInDaylightTime_old() using a zone that still lacks an
+ // A replacement for testPreHistoricInDaylightTime_old() using a zone that lacks an
// explicit transition at Integer.MIN_VALUE with zic 2019a and 2019a data.
TimeZone tz = TimeZone.getTimeZone("CET");
@@ -76,9 +76,7 @@
public void testPreHistoricInDaylightTime_old() throws Exception {
// Originally this test was intended to assert what happens when the first transition for a
// time zone was a "to DST" transition. i.e. that the (implicit) offset / DST state before
- // the first was treated as a non-DST state. Since zic version 2014c some zones have an
- // explicit non-DST transition at time -2^31 seconds so it is no longer possible to test
- // this with America/Los_Angeles.
+ // the first was treated as a non-DST state. With the latest data this is no longer true.
// This regression test has been kept in case that changes again in future and to prove the
// behavior has remained consistent.
@@ -92,7 +90,7 @@
assertFalse(tz.inDaylightTime(date));
assertEquals("Fri Oct 31 08:00:00 PST 1902", date.toString());
assertEquals("31 Oct 1902 16:00:00 GMT", date.toGMTString());
- // For zic versions <= 2014b, this would be before the first transition.
+ // For zic versions <= 2014b with 32-bit data, this would be before the first transition.
date = sdf.parse("1902-06-01T00:00:00.000+0800");
assertEquals(-28800000, tz.getOffset(date.getTime()));
assertFalse(tz.inDaylightTime(date));
@@ -109,7 +107,7 @@
// prehistoric offsets. http://b/118835133
// "Africa/Bissau" has just a few known transitions:
// Transition time : Offset : DST / non-DST
- // <Integer.MIN_VALUE secs>[1] : -01:02:20 : non-DST
+ // <Before first transition>[1]: -01:02:20 : non-DST
// 1912-01-01 01:00:00 GMT : -01:00:00 : non-DST
// 1975-01-01 01:00:00 GMT : 00:00:00 : non-DST
//
@@ -117,7 +115,8 @@
// generate the data. When implicit, the first non-DST type defn should be used.
TimeZone tz = TimeZone.getTimeZone("Africa/Bissau");
- // Times before Integer.MIN_VALUE should assume we're using the first non-DST type.
+ // Integer.MIN_VALUE seconds should not be significant for TimeZone on Android since it
+ // switched to using 64-bit data but we try a time before to make sure that is true.
assertNonDaylightOffset(-3740, parseIsoTime("1900-01-01T00:00:00.0+0000"), tz);
// Time before 1912-01-01 01:00:00 but after Integer.MIN_VALUE.
@@ -351,11 +350,6 @@
* calculations. A bug (http://b/18839557) was reported when someone noticed that Android's
* TimeZone didn't produce the same answers as other libraries at times just outside the range
* of Integer seconds. The reason was because of int overflow / underflow which has been fixed.
- * At the time of writing, Android's java.util.TimeZone implementation only supports reading
- * TZif version 1 data (32-bit times) and provides one additional "before first transition"
- * type. This makes Android's time zone information outside of the Integer range unreliable and
- * unlikely to match libraries that use 64-bit times for transitions and/or calculate times
- * outside of the range using rules (e.g. like ICU4J does).
*/
public void testOverflowing32BitUnixDates() {
final TimeZone tz = TimeZone.getTimeZone("America/New_York");
@@ -370,13 +364,8 @@
// This timezone didn't have any daylight savings prior to 1917 and this date is in 1900.
assertFalse(tz.inDaylightTime(new Date(lowerTimeMillis)));
- // http://b/118835133:
- // zic <= 2014b produces data that suggests before -1633280400 seconds (Sun, 31 Mar 1918
- // 07:00:00 GMT) the offset was -18000000.
- // zic > 2014b produces data that suggests before Integer.MIN_VALUE seconds the offset was
- // -17762000 and between Integer.MIN_VALUE and -1633280400 it was -18000000.
int actualOffset = tz.getOffset(lowerTimeMillis);
- assertEquals(-17762000, actualOffset);
+ assertEquals(-18000000, actualOffset);
// Nov 30th 2039, no daylight savings as per current rules.
assertFalse(tz.inDaylightTime(new Date(upperTimeMillis)));
diff --git a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
index 6cf4a00..fba0b47 100644
--- a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
+++ b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
@@ -315,24 +315,17 @@
}
/**
- * TimeZone APIs use long times in millis. Android uses TZif version 1 format data which
- * uses 32-bit time values for transitions so it only gives accurate results for times in that
- * range.
+ * TimeZone APIs use Java long times in millis.
*
- * <p>Newer versions of zic after 2014b introduce an explicit transition at the earliest
- * representable time, which is Integer.MIN_VALUE for TZif version 1 files. Previously the type
- * used was left implicit and readers were expected to use the first non-DST type in the file.
- * This extra transition mostly went away again with zic 2018f.
- *
- * <p>Testing newer zic versions demonstrated that Android had been mishandling the lookup of
- * offset for times before the first transition. The logic has been corrected. This test would
- * fail on versions of Android <= P.
+ * <p>Android has historically mishandled the lookup of offset for times before Integer.MIN_VALUE
+ * seconds for various reasons. The logic has been corrected. This test would fail on versions of
+ * Android <= P.
*/
public void testReadTimeZone_Bug118835133_extraFirstTransition() throws Exception {
- // A time before the first representable time in a TZif version 1 file.
+ // A time before the first representable time in seconds with a java int.
Instant before32BitTime = timeFromSeconds(Integer.MIN_VALUE).minusMillis(1);
- // Times between the start of the 32-bit time range and the first "official" transition.
+ // Times around the 32-bit seconds minimum.
Instant[] earlyTimes = {
timeFromSeconds(Integer.MIN_VALUE),
timeFromSeconds(Integer.MIN_VALUE).plusMillis(1),
@@ -358,8 +351,7 @@
{ offsetToSeconds(type2Offset), 0 },
};
- // Creates a simulation of zic version <= 2014b or zic version >= 2018f where there is often
- // no explicit transition at Integer.MIN_VALUE seconds in TZif version 1 data.
+ // Simulates a zone with a single transition.
{
long[][] transitions = {
{ timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
@@ -375,11 +367,11 @@
assertOffsetAt(oldZoneInfo, type2Offset, afterFirstRealTransitionTimes);
}
- // Creates a simulation of zic version > 2014b and zic version < 2018f where there is usually an
- // explicit transition at Integer.MIN_VALUE seconds for TZif version 1 data.
+ // Simulation a zone where there is an explicit transition at Integer.MIN_VALUE seconds. This
+ // used to be common when Android used 32-bit data from the TZif file.
{
long[][] transitions = {
- { Integer.MIN_VALUE, 1 /* type 1 */ }, // The extra transition added by zic.
+ { Integer.MIN_VALUE, 1 /* type 1 */ },
{ timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
};
ZoneInfo newZoneInfo = createZoneInfo(transitions, types, currentTime);
@@ -398,7 +390,7 @@
/**
* Newer versions of zic after 2014b sometime introduce an explicit transition at
- * Integer.MAX_VALUE.
+ * Integer.MAX_VALUE seconds.
*/
public void testReadTimeZone_Bug118835133_extraLastTransition() throws Exception {
// An arbitrary time to use as currentTime. Not important for this test.
@@ -419,8 +411,8 @@
};
Duration expectedLateOffset = offsetFromSeconds(latestOffsetSeconds);
- // Create a simulation of zic version <= 2014b where there is usually no explicit transition at
- // Integer.MAX_VALUE seconds.
+ // Create a simulation of a zone where there is no explicit transition at Integer.MAX_VALUE
+ // seconds.
{
long[][] transitions = {
{ 1000, 0 },
@@ -430,8 +422,8 @@
assertOffsetAt(oldZoneInfo, expectedLateOffset, timesToCheck);
}
- // Create a simulation of zic version > 2014b where there is sometimes an explicit transition at
- // Integer.MAX_VALUE seconds.
+ // Create a simulation of a zone where there is an explicit transition at Integer.MAX_VALUE
+ // seconds.
{
long[][] transitions = {
{ 1000, 0 },
@@ -500,7 +492,7 @@
assertNotNull(createZoneInfo(getName(), Instant.now(), builder.build()));
}
- public void testReadTimeZone_BadMagic() throws Exception {
+ public void testReadTimeZone_BadMagic() {
ZoneInfoTestHelper.ZicDataBuilder builder =
new ZoneInfoTestHelper.ZicDataBuilder()
.initializeToValid()
@@ -683,16 +675,12 @@
}
}
- private static Instant timeFromSeconds(int timeInSeconds) {
+ private static Instant timeFromSeconds(long timeInSeconds) {
return Instant.ofEpochSecond(timeInSeconds);
}
- private static int timeToSeconds(Instant time) {
- long seconds = time.getEpochSecond();
- if (seconds < Integer.MIN_VALUE || seconds > Integer.MAX_VALUE) {
- fail("Time out of seconds range: " + time);
- }
- return (int) seconds;
+ private static long timeToSeconds(Instant time) {
+ return time.getEpochSecond();
}
private static Duration offsetFromSeconds(int offsetSeconds) {
@@ -792,8 +780,8 @@
}
@Override
- public void readByteArray(byte[] dst, int dstOffset, int byteCount) {
- buffer.get(dst, dstOffset, byteCount);
+ public void readByteArray(byte[] bytes, int arrayOffset, int byteCount) {
+ buffer.get(bytes, arrayOffset, byteCount);
}
@Override
@@ -806,16 +794,24 @@
int value = buffer.asIntBuffer().get();
// Using a separate view does not update the position of this buffer so do it
// explicitly.
- skip(4);
+ skip(Integer.BYTES);
return value;
}
@Override
- public void readIntArray(int[] dst, int dstOffset, int intCount) {
- buffer.asIntBuffer().get(dst, dstOffset, intCount);
+ public void readIntArray(int[] ints, int arrayOffset, int intCount) {
+ buffer.asIntBuffer().get(ints, arrayOffset, intCount);
// Using a separate view does not update the position of this buffer so do it
// explicitly.
- skip(4 * intCount);
+ skip(Integer.BYTES * intCount);
+ }
+
+ @Override
+ public void readLongArray(long[] longs, int arrayOffset, int longCount) {
+ buffer.asLongBuffer().get(longs, arrayOffset, longCount);
+ // Using a separate view does not update the position of this buffer so do it
+ // explicitly.
+ skip(Long.BYTES * longCount);
}
@Override
@@ -823,7 +819,7 @@
short value = buffer.asShortBuffer().get();
// Using a separate view does not update the position of this buffer so do it
// explicitly.
- skip(2);
+ skip(Short.BYTES);
return value;
}
}