1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package libcore.libcore.io; 18 19 import junit.framework.TestCase; 20 21 import android.system.ErrnoException; 22 import android.system.OsConstants; 23 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.nio.ByteBuffer; 27 import java.nio.ByteOrder; 28 import java.nio.IntBuffer; 29 import java.util.Arrays; 30 import java.util.function.Function; 31 import libcore.io.BufferIterator; 32 import libcore.io.IoUtils; 33 import libcore.io.MemoryMappedFile; 34 import libcore.io.SizeOf; 35 36 public class MemoryMappedFileTest extends TestCase { 37 38 private File tempDir; 39 40 @Override 41 public void setUp() throws Exception { 42 super.setUp(); 43 tempDir = IoUtils.createTemporaryDirectory("MemoryMappedFileTest"); 44 } 45 46 public void testMmapRo_missingFile() throws Exception { 47 try { 48 MemoryMappedFile.mmapRO("doesNotExist"); 49 fail(); 50 } catch (ErrnoException e) { 51 assertEquals(OsConstants.ENOENT, e.errno); 52 } 53 } 54 55 public void testMmapRo_emptyFile() throws Exception { 56 byte[] bytes = new byte[0]; 57 File file = createFile(bytes); 58 try { 59 MemoryMappedFile.mmapRO(file.getPath()); 60 fail(); 61 } catch (ErrnoException e) { 62 assertEquals(OsConstants.EINVAL, e.errno); 63 } finally { 64 file.delete(); 65 } 66 } 67 68 public void testMmapRo() throws Exception { 69 byte[] bytes = createBytes(10); 70 File file = createFile(bytes); 71 try (MemoryMappedFile memoryMappedFile = MemoryMappedFile.mmapRO(file.getPath())) { 72 assertEquals(10, memoryMappedFile.size()); 73 } finally { 74 file.delete(); 75 } 76 } 77 78 public void testMmapRo_close() throws Exception { 79 byte[] bytes = createBytes(10); 80 File file = createFile(bytes); 81 MemoryMappedFile memoryMappedFile = MemoryMappedFile.mmapRO(file.getPath()); 82 memoryMappedFile.close(); 83 84 try { 85 memoryMappedFile.bigEndianIterator(); 86 fail(); 87 } catch (IllegalStateException expected) { 88 } 89 90 try { 91 memoryMappedFile.littleEndianIterator(); 92 fail(); 93 } catch (IllegalStateException expected) { 94 } 95 96 // Should not have any effect. 97 memoryMappedFile.close(); 98 } 99 100 public void testReadAfterCloseFails() throws Exception { 101 byte[] bytes = createBytes(10); 102 File file = createFile(bytes); 103 MemoryMappedFile memoryMappedFile = MemoryMappedFile.mmapRO(file.getPath()); 104 BufferIterator iterator = memoryMappedFile.bigEndianIterator(); 105 memoryMappedFile.close(); 106 107 try { 108 iterator.readByte(); 109 fail(); 110 } catch (IllegalStateException expected) {} 111 } 112 113 public void testReadByte() throws Exception { 114 checkReadByte(MemoryMappedFile::bigEndianIterator); 115 checkReadByte(MemoryMappedFile::littleEndianIterator); 116 } 117 118 private void checkReadByte( 119 Function<MemoryMappedFile, BufferIterator> iteratorFactory) throws Exception { 120 121 byte[] bytes = createBytes(10); 122 File file = createFile(bytes); 123 try { 124 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 125 BufferIterator iterator = iteratorFactory.apply(mappedFile); 126 for (int i = 0; i < bytes.length; i++) { 127 assertReadByteSucceeds(iterator, bytes[i]); 128 } 129 130 // Check skip. 131 iterator.seek(0); 132 for (int i = 0; i < bytes.length; i += 2) { 133 assertReadByteSucceeds(iterator, bytes[i]); 134 iterator.skip(1); 135 } 136 } finally { 137 file.delete(); 138 } 139 } 140 141 public void testSeek() throws Exception { 142 checkSeek(MemoryMappedFile::bigEndianIterator); 143 checkSeek(MemoryMappedFile::littleEndianIterator); 144 } 145 146 private void checkSeek( 147 Function<MemoryMappedFile, BufferIterator> iteratorFactory) throws Exception { 148 149 byte[] bytes = createBytes(10); 150 File file = createFile(bytes); 151 try { 152 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 153 BufferIterator iterator = iteratorFactory.apply(mappedFile); 154 seekRead(bytes, iterator, 2); 155 156 seekRead(bytes, iterator, 0); 157 158 seekRead(bytes, iterator, 1); 159 160 seekRead(bytes, iterator, 9); 161 162 seekReadExpectFailure(iterator, -1); 163 164 seekRead(bytes, iterator, 1); 165 166 seekReadExpectFailure(iterator, 10); 167 seekReadExpectFailure(iterator, Integer.MAX_VALUE); 168 seekReadExpectFailure(iterator, Integer.MIN_VALUE); 169 } finally { 170 file.delete(); 171 } 172 } 173 174 private static void seekRead(byte[] bytes, BufferIterator iterator, int offset) { 175 iterator.seek(offset); 176 assertEquals(offset, iterator.pos()); 177 assertReadByteSucceeds(iterator, bytes[offset]); 178 } 179 180 private static void seekReadExpectFailure(BufferIterator iterator, int offset) { 181 iterator.seek(offset); 182 assertReadByteFails(iterator); 183 } 184 185 public void testSkip() throws Exception { 186 checkSkip(MemoryMappedFile::bigEndianIterator); 187 checkSkip(MemoryMappedFile::littleEndianIterator); 188 } 189 190 private void checkSkip( 191 Function<MemoryMappedFile, BufferIterator> iteratorFactory) throws Exception { 192 193 byte[] bytes = createBytes(10); 194 File file = createFile(bytes); 195 try { 196 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 197 BufferIterator iterator = iteratorFactory.apply(mappedFile); 198 iterator.skip(1); 199 assertEquals(1, iterator.pos()); 200 assertReadByteSucceeds(iterator, bytes[1]); 201 202 iterator.skip(-1); 203 assertEquals(1, iterator.pos()); 204 assertReadByteSucceeds(iterator, bytes[1]); 205 206 iterator.skip(2); 207 assertEquals(4, iterator.pos()); 208 assertReadByteSucceeds(iterator, bytes[4]); 209 210 iterator.skip(-2); 211 assertEquals(3, iterator.pos()); 212 assertReadByteSucceeds(iterator, bytes[3]); 213 214 iterator.skip(3); 215 assertEquals(7, iterator.pos()); 216 assertReadByteSucceeds(iterator, bytes[7]); 217 218 iterator.skip(-3); 219 assertEquals(5, iterator.pos()); 220 assertReadByteSucceeds(iterator, bytes[5]); 221 222 iterator.skip(4); 223 assertEquals(10, iterator.pos()); 224 assertReadByteFails(iterator); 225 226 iterator.skip(-1); 227 assertEquals(9, iterator.pos()); 228 assertReadByteSucceeds(iterator, bytes[9]); 229 } finally { 230 file.delete(); 231 } 232 } 233 234 public void testReadShort_bigEndian() throws Exception { 235 byte[] bytes = createBytes(10); 236 File file = createFile(bytes); 237 try { 238 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 239 BufferIterator iterator = mappedFile.bigEndianIterator(); 240 241 // Even offset 242 short expectedValue = (short) ((bytes[0] << 8) | bytes[1]); 243 assertReadShortSucceeds(iterator, expectedValue); 244 245 checkShortFailureCases(iterator); 246 247 // Odd offset. 248 iterator.seek(1); 249 expectedValue = (short) ((bytes[1] << 8) | bytes[2]); 250 assertReadShortSucceeds(iterator, expectedValue); 251 } finally { 252 file.delete(); 253 } 254 } 255 256 public void testReadShort_littleEndian() throws Exception { 257 byte[] bytes = createBytes(10); 258 File file = createFile(bytes); 259 try { 260 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 261 BufferIterator iterator = mappedFile.littleEndianIterator(); 262 263 // Even offset 264 short expectedValue = (short) ((bytes[1] << 8) | bytes[0]); 265 assertReadShortSucceeds(iterator, expectedValue); 266 267 checkShortFailureCases(iterator); 268 269 // Odd offset. 270 iterator.seek(1); 271 expectedValue = (short) ((bytes[2] << 8) | bytes[1]); 272 assertReadShortSucceeds(iterator, expectedValue); 273 } finally { 274 file.delete(); 275 } 276 } 277 278 private static void checkShortFailureCases(BufferIterator iterator) { 279 // Partly before bounds. 280 iterator.seek(-1); 281 assertReadShortFails(iterator); 282 283 // Entirely before bounds. 284 iterator.seek(-2); 285 assertReadShortFails(iterator); 286 287 // Partly after bounds. 288 iterator.seek(9); 289 assertReadShortFails(iterator); 290 291 // Entirely after bounds. 292 iterator.seek(10); 293 assertReadShortFails(iterator); 294 } 295 296 public void testReadInt_bigEndian() throws Exception { 297 byte[] bytes = createBytes(10); 298 File file = createFile(bytes); 299 try { 300 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 301 BufferIterator iterator = mappedFile.bigEndianIterator(); 302 303 // Even offset 304 int expectedValue = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; 305 assertReadIntSucceeds(iterator, expectedValue); 306 307 checkIntFailureCases(iterator); 308 309 // Odd offset. 310 iterator.seek(1); 311 expectedValue = (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4]; 312 assertReadIntSucceeds(iterator, expectedValue); 313 } finally { 314 file.delete(); 315 } 316 } 317 318 public void testReadInt_littleEndian() throws Exception { 319 byte[] bytes = createBytes(10); 320 File file = createFile(bytes); 321 try { 322 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 323 BufferIterator iterator = mappedFile.littleEndianIterator(); 324 325 // Even offset 326 int expectedValue = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; 327 assertReadIntSucceeds(iterator, expectedValue); 328 329 checkIntFailureCases(iterator); 330 331 // Odd offset. 332 iterator.seek(1); 333 expectedValue = (bytes[4] << 24) | (bytes[3] << 16) | (bytes[2] << 8) | bytes[1]; 334 assertReadIntSucceeds(iterator, expectedValue); 335 } finally { 336 file.delete(); 337 } 338 } 339 340 private static void checkIntFailureCases(BufferIterator iterator) { 341 // Partly before bounds. 342 iterator.seek(-1); 343 assertReadIntFails(iterator); 344 345 // Entirely before bounds. 346 iterator.seek(-4); 347 assertReadIntFails(iterator); 348 349 // Partly after bounds. 350 iterator.seek(7); 351 assertReadIntFails(iterator); 352 353 // Entirely after bounds. 354 iterator.seek(10); 355 assertReadIntFails(iterator); 356 } 357 358 public void testReadIntArray() throws Exception { 359 checkReadIntArray(MemoryMappedFile::bigEndianIterator, ByteOrder.BIG_ENDIAN); 360 checkReadIntArray(MemoryMappedFile::littleEndianIterator, ByteOrder.LITTLE_ENDIAN); 361 } 362 363 private void checkReadIntArray( 364 Function<MemoryMappedFile, BufferIterator> iteratorFactory, 365 ByteOrder byteOrdering) throws Exception { 366 367 byte[] testBytes = createBytes(12); 368 File file = createFile(testBytes); 369 try { 370 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 371 BufferIterator iterator = iteratorFactory.apply(mappedFile); 372 373 // Even offsets. 374 iterator.seek(4); 375 assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 2 /* intCount */); 376 377 iterator.seek(0); 378 assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 3 /* intCount */); 379 380 checkIntArrayZeroReadCases(iterator); 381 382 // Odd offsets. 383 iterator.seek(1); 384 assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 2 /* intCount */); 385 iterator.seek(3); 386 assertReadIntArraySucceeds(iterator, testBytes, byteOrdering, 2 /* intCount */); 387 } finally { 388 file.delete(); 389 } 390 } 391 392 private static void checkIntArrayZeroReadCases(BufferIterator iterator) { 393 // Zero length reads do nothing. 394 int posBeforeRead = iterator.pos(); 395 int[] dstWithExistingValues = new int[] { 111, 222 }; 396 iterator.readIntArray(dstWithExistingValues, 0, 0); 397 assertEquals(posBeforeRead, iterator.pos()); 398 assertArrayEquals(new int[] { 111, 222 }, dstWithExistingValues); 399 400 try { 401 iterator.readIntArray(null, 0, 0); 402 fail(); 403 } catch (NullPointerException expected) { 404 } 405 assertEquals(posBeforeRead, iterator.pos()); 406 407 int[] dst = new int[2]; 408 409 // Partly before bounds. 410 iterator.seek(-1); 411 assertReadIntArrayFails(iterator, dst, 0, 1); 412 413 // Entirely before bounds. 414 iterator.seek(-2); 415 assertReadIntArrayFails(iterator, dst, 0, 1); 416 417 // Partly after bounds. 418 iterator.seek(9); 419 assertReadIntArrayFails(iterator, dst, 0, 1); 420 421 // Entirely after bounds. 422 iterator.seek(12); 423 assertReadIntArrayFails(iterator, dst, 0, 1); 424 425 // dst too small. 426 assertReadIntArrayFails(iterator, dst, 0, 3); // dst can only hold 2 ints 427 428 // offset leaves dst too small. 429 assertReadIntArrayFails(iterator, dst, 1, 2); 430 431 // Invalid offset 432 assertReadIntArrayFails(iterator, dst, -1, 2); 433 assertReadIntArrayFails(iterator, dst, 2, 2); 434 435 // Null dst 436 try { 437 iterator.readIntArray(null, 0, 1); 438 fail(); 439 } catch (NullPointerException expected) { 440 } 441 } 442 443 public void testReadByteArray() throws Exception { 444 checkReadByteArray(MemoryMappedFile::bigEndianIterator); 445 checkReadByteArray(MemoryMappedFile::littleEndianIterator); 446 } 447 448 private void checkReadByteArray( 449 Function<MemoryMappedFile, BufferIterator> iteratorFactory) throws Exception { 450 451 byte[] testBytes = createBytes(12); 452 File file = createFile(testBytes); 453 try { 454 MemoryMappedFile mappedFile = MemoryMappedFile.mmapRO(file.getPath()); 455 BufferIterator iterator = iteratorFactory.apply(mappedFile); 456 457 // Even offsets. 458 iterator.seek(4); 459 assertReadByteArraySucceeds(iterator, testBytes, 2 /* intCount */); 460 461 iterator.seek(0); 462 assertReadByteArraySucceeds(iterator, testBytes, 3 /* intCount */); 463 464 checkByteArrayZeroReadCases(iterator); 465 466 // Odd offsets. 467 iterator.seek(1); 468 assertReadByteArraySucceeds(iterator, testBytes, 2 /* intCount */); 469 iterator.seek(3); 470 assertReadByteArraySucceeds(iterator, testBytes, 2 /* intCount */); 471 } finally { 472 file.delete(); 473 } 474 } 475 476 private static void checkByteArrayZeroReadCases(BufferIterator iterator) { 477 // Zero length reads do nothing. 478 int posBeforeRead = iterator.pos(); 479 byte[] dstWithExistingValues = new byte[] { 11, 22, 33, 44, 55, 66, 77, 88 }; 480 iterator.readByteArray(dstWithExistingValues, 0, 0); 481 assertEquals(posBeforeRead, iterator.pos()); 482 assertArrayEquals(new byte[] { 11, 22, 33, 44, 55, 66, 77, 88 }, dstWithExistingValues); 483 484 try { 485 iterator.readByteArray(null, 0, 0); 486 fail(); 487 } catch (NullPointerException expected) { 488 } 489 assertEquals(posBeforeRead, iterator.pos()); 490 491 byte[] dst = new byte[10]; 492 493 // Before bounds. 494 iterator.seek(-1); 495 assertReadByteArrayFails(iterator, dst, 0, 1); 496 497 // After bounds. 498 iterator.seek(12); 499 assertReadByteArrayFails(iterator, dst, 0, 1); 500 501 // dst too small. 502 assertReadByteArrayFails(iterator, dst, 0, 11); // dst can only hold 10 bytes 503 504 // offset leaves dst too small. 505 assertReadByteArrayFails(iterator, dst, 1, 10); 506 507 // Invalid offset 508 assertReadByteArrayFails(iterator, dst, -1, 2); 509 assertReadByteArrayFails(iterator, dst, 2, 2); 510 511 // Null dst 512 try { 513 iterator.readByteArray(null, 0, 1); 514 fail(); 515 } catch (NullPointerException expected) { 516 } 517 } 518 519 private static void assertReadByteArrayFails( 520 BufferIterator iterator, byte[] dst, int offset, int intCount) { 521 522 int posBefore = iterator.pos(); 523 try { 524 iterator.readByteArray(dst, offset, intCount); 525 fail(); 526 } catch (IndexOutOfBoundsException expected) { 527 } 528 assertEquals(posBefore, iterator.pos()); 529 } 530 531 private static void assertReadByteArraySucceeds( 532 BufferIterator iterator, byte[] underlyingData, int byteCount) { 533 534 int posBefore = iterator.pos(); 535 536 // Create a byte[] containing book-end bytes we don't expect to be touched: 537 // [Byte.MAX_VALUE, {the bytes we expect from underlyingData from posBefore onward}, 538 // Byte.MIN_VALUE]. 539 byte[] expectedBytes = new byte[byteCount + 2]; 540 expectedBytes[0] = Byte.MAX_VALUE; 541 expectedBytes[byteCount - 1] = Byte.MIN_VALUE; 542 System.arraycopy(underlyingData, posBefore, expectedBytes, 1, byteCount); 543 544 // Get the true data. 545 byte[] dst = new byte[byteCount + 2]; 546 // Copy the two bytes we expect to be untouched. 547 dst[0] = expectedBytes[0]; 548 dst[byteCount - 1] = expectedBytes[byteCount - 1]; 549 // Do the read. 550 iterator.readByteArray(dst, 1, byteCount); 551 552 assertArrayEquals(expectedBytes, dst); 553 assertEquals(posBefore + byteCount, iterator.pos()); 554 } 555 556 private static void assertReadIntArrayFails( 557 BufferIterator iterator, int[] dst, int offset, int intCount) { 558 559 int posBefore = iterator.pos(); 560 try { 561 iterator.readIntArray(dst, offset, intCount); 562 fail(); 563 } catch (IndexOutOfBoundsException expected) { 564 } 565 assertEquals(posBefore, iterator.pos()); 566 } 567 568 private static void assertReadIntArraySucceeds( 569 BufferIterator iterator, byte[] underlyingData, ByteOrder byteOrder, int intCount) { 570 571 int posBefore = iterator.pos(); 572 573 // Create an int[] containing book-end ints we don't expect to be touched: 574 // [Integer.MAX_VALUE, {the ints we expect from underlyingData from posBefore onward}, 575 // Integer.MIN_VALUE]. 576 577 // Create an IntBuffer containing the ints we'd expect from underlyingData from posBefore 578 // onward. 579 ByteBuffer byteBuffer = ByteBuffer.wrap(underlyingData); 580 byteBuffer.position(posBefore); 581 IntBuffer expectedIntsBuffer = byteBuffer.slice().order(byteOrder).asIntBuffer(); 582 assertEquals(byteOrder, expectedIntsBuffer.order()); 583 584 // Copy the ints we expect. 585 int[] expectedInts = new int[intCount + 2]; 586 expectedInts[0] = Integer.MAX_VALUE; 587 expectedInts[intCount - 1] = Integer.MIN_VALUE; 588 expectedIntsBuffer.get(expectedInts, 1, intCount); 589 590 // Get the true data. 591 int[] dst = new int[intCount + 2]; 592 dst[0] = expectedInts[0]; 593 dst[intCount - 1] = expectedInts[intCount - 1]; 594 iterator.readIntArray(dst, 1, intCount); 595 596 assertArrayEquals(expectedInts, dst); 597 assertEquals(posBefore + (intCount * SizeOf.INT), iterator.pos()); 598 } 599 600 private static void assertReadIntFails(BufferIterator iterator) { 601 int posBefore = iterator.pos(); 602 try { 603 iterator.readInt(); 604 fail(); 605 } catch (IndexOutOfBoundsException expected) { 606 } 607 assertEquals(posBefore, iterator.pos()); 608 } 609 610 private static void assertReadIntSucceeds(BufferIterator iterator, int expectedValue) { 611 int posBefore = iterator.pos(); 612 assertEquals(expectedValue, iterator.readInt()); 613 assertEquals(posBefore + SizeOf.INT, iterator.pos()); 614 } 615 616 private static void assertReadShortFails(BufferIterator iterator) { 617 int posBefore = iterator.pos(); 618 try { 619 iterator.readShort(); 620 fail(); 621 } catch (IndexOutOfBoundsException expected) { 622 } 623 assertEquals(posBefore, iterator.pos()); 624 } 625 626 private static void assertReadShortSucceeds(BufferIterator iterator, short expectedValue) { 627 int posBefore = iterator.pos(); 628 assertEquals(expectedValue, iterator.readShort()); 629 assertEquals(posBefore + SizeOf.SHORT, iterator.pos()); 630 } 631 632 private static void assertReadByteFails(BufferIterator iterator) { 633 int posBefore = iterator.pos(); 634 try { 635 iterator.readByte(); 636 fail(); 637 } catch (IndexOutOfBoundsException expected) { 638 } 639 // Must not advance pos. 640 assertEquals(posBefore, iterator.pos()); 641 } 642 643 private static void assertReadByteSucceeds(BufferIterator iterator, byte expectedValue) { 644 int posBefore = iterator.pos(); 645 assertEquals(expectedValue, iterator.readByte()); 646 assertEquals(posBefore + 1, iterator.pos()); 647 } 648 649 private static void assertArrayEquals(int[] expected, int[] actual) { 650 assertEquals(Arrays.toString(expected), Arrays.toString(actual)); 651 } 652 653 private static void assertArrayEquals(byte[] expected, byte[] actual) { 654 assertEquals(Arrays.toString(expected), Arrays.toString(actual)); 655 } 656 657 private static byte[] createBytes(int byteCount) { 658 byte[] bytes = new byte[byteCount]; 659 for (int i = 0; i < byteCount; i++) { 660 bytes[i] = (byte) i; 661 } 662 return bytes; 663 } 664 665 private File createFile(byte[] bytes) throws Exception { 666 File file = File.createTempFile("bytes", null, tempDir); 667 try (FileOutputStream fos = new FileOutputStream(file)) { 668 fos.write(bytes); 669 } 670 return file; 671 } 672 } 673