|
1 | 1 | import unittest |
2 | 2 | import sys |
| 3 | +import test.support as support |
3 | 4 |
|
4 | 5 | from test.support import import_helper |
5 | 6 |
|
@@ -423,6 +424,150 @@ def test_long_asvoidptr(self): |
423 | 424 | self.assertRaises(OverflowError, asvoidptr, -2**1000) |
424 | 425 | # CRASHES asvoidptr(NULL) |
425 | 426 |
|
| 427 | + def test_long_asnativebytes(self): |
| 428 | + import math |
| 429 | + from _testcapi import ( |
| 430 | + pylong_asnativebytes as asnativebytes, |
| 431 | + SIZE_MAX, |
| 432 | + ) |
| 433 | + |
| 434 | + # Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot |
| 435 | + SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8) |
| 436 | + MAX_SSIZE = 2 ** (SZ * 8 - 1) - 1 |
| 437 | + MAX_USIZE = 2 ** (SZ * 8) - 1 |
| 438 | + if support.verbose: |
| 439 | + print(f"SIZEOF_SIZE={SZ}\n{MAX_SSIZE=:016X}\n{MAX_USIZE=:016X}") |
| 440 | + |
| 441 | + # These tests check that the requested buffer size is correct |
| 442 | + for v, expect in [ |
| 443 | + (0, SZ), |
| 444 | + (512, SZ), |
| 445 | + (-512, SZ), |
| 446 | + (MAX_SSIZE, SZ), |
| 447 | + (MAX_USIZE, SZ + 1), |
| 448 | + (-MAX_SSIZE, SZ), |
| 449 | + (-MAX_USIZE, SZ + 1), |
| 450 | + (2**255-1, 32), |
| 451 | + (-(2**255-1), 32), |
| 452 | + (2**256-1, 33), |
| 453 | + (-(2**256-1), 33), |
| 454 | + ]: |
| 455 | + with self.subTest(f"sizeof-{v:X}"): |
| 456 | + buffer = bytearray(1) |
| 457 | + self.assertEqual(expect, asnativebytes(v, buffer, 0, -1), |
| 458 | + "PyLong_AsNativeBytes(v, NULL, 0, -1)") |
| 459 | + # Also check via the __index__ path |
| 460 | + self.assertEqual(expect, asnativebytes(Index(v), buffer, 0, -1), |
| 461 | + "PyLong_AsNativeBytes(Index(v), NULL, 0, -1)") |
| 462 | + |
| 463 | + # We request as many bytes as `expect_be` contains, and always check |
| 464 | + # the result (both big and little endian). We check the return value |
| 465 | + # independently, since the buffer should always be filled correctly even |
| 466 | + # if we need more bytes |
| 467 | + for v, expect_be, expect_n in [ |
| 468 | + (0, b'\x00', 1), |
| 469 | + (0, b'\x00' * 2, 2), |
| 470 | + (0, b'\x00' * 8, min(8, SZ)), |
| 471 | + (1, b'\x01', 1), |
| 472 | + (1, b'\x00' * 10 + b'\x01', min(11, SZ)), |
| 473 | + (42, b'\x2a', 1), |
| 474 | + (42, b'\x00' * 10 + b'\x2a', min(11, SZ)), |
| 475 | + (-1, b'\xff', 1), |
| 476 | + (-1, b'\xff' * 10, min(11, SZ)), |
| 477 | + (-42, b'\xd6', 1), |
| 478 | + (-42, b'\xff' * 10 + b'\xd6', min(11, SZ)), |
| 479 | + # Extracts 255 into a single byte, but requests sizeof(Py_ssize_t) |
| 480 | + (255, b'\xff', SZ), |
| 481 | + (255, b'\x00\xff', 2), |
| 482 | + (256, b'\x01\x00', 2), |
| 483 | + # Extracts successfully (unsigned), but requests 9 bytes |
| 484 | + (2**63, b'\x80' + b'\x00' * 7, 9), |
| 485 | + # "Extracts", but requests 9 bytes |
| 486 | + (-2**63, b'\x80' + b'\x00' * 7, 9), |
| 487 | + (2**63, b'\x00\x80' + b'\x00' * 7, 9), |
| 488 | + (-2**63, b'\xff\x80' + b'\x00' * 7, 9), |
| 489 | + |
| 490 | + (2**255-1, b'\x7f' + b'\xff' * 31, 32), |
| 491 | + (-(2**255-1), b'\x80' + b'\x00' * 30 + b'\x01', 32), |
| 492 | + # Request extra bytes, but result says we only needed 32 |
| 493 | + (-(2**255-1), b'\xff\x80' + b'\x00' * 30 + b'\x01', 32), |
| 494 | + (-(2**255-1), b'\xff\xff\x80' + b'\x00' * 30 + b'\x01', 32), |
| 495 | + |
| 496 | + # Extracting 256 bits of integer will request 33 bytes, but still |
| 497 | + # copy as many bits as possible into the buffer. So we *can* copy |
| 498 | + # into a 32-byte buffer, though negative number may be unrecoverable |
| 499 | + (2**256-1, b'\xff' * 32, 33), |
| 500 | + (2**256-1, b'\x00' + b'\xff' * 32, 33), |
| 501 | + (-(2**256-1), b'\x00' * 31 + b'\x01', 33), |
| 502 | + (-(2**256-1), b'\xff' + b'\x00' * 31 + b'\x01', 33), |
| 503 | + (-(2**256-1), b'\xff\xff' + b'\x00' * 31 + b'\x01', 33), |
| 504 | + |
| 505 | + # The classic "Windows HRESULT as negative number" case |
| 506 | + # HRESULT hr; |
| 507 | + # PyLong_CopyBits(<-2147467259>, &hr, sizeof(HRESULT)) |
| 508 | + # assert(hr == E_FAIL) |
| 509 | + (-2147467259, b'\x80\x00\x40\x05', 4), |
| 510 | + ]: |
| 511 | + with self.subTest(f"{v:X}-{len(expect_be)}bytes"): |
| 512 | + n = len(expect_be) |
| 513 | + buffer = bytearray(n) |
| 514 | + expect_le = expect_be[::-1] |
| 515 | + |
| 516 | + self.assertEqual(expect_n, asnativebytes(v, buffer, n, 0), |
| 517 | + f"PyLong_AsNativeBytes(v, buffer, {n}, <big>)") |
| 518 | + self.assertEqual(expect_be, buffer[:n], "<big>") |
| 519 | + self.assertEqual(expect_n, asnativebytes(v, buffer, n, 1), |
| 520 | + f"PyLong_AsNativeBytes(v, buffer, {n}, <little>)") |
| 521 | + self.assertEqual(expect_le, buffer[:n], "<little>") |
| 522 | + |
| 523 | + # Check a few error conditions. These are validated in code, but are |
| 524 | + # unspecified in docs, so if we make changes to the implementation, it's |
| 525 | + # fine to just update these tests rather than preserve the behaviour. |
| 526 | + with self.assertRaises(SystemError): |
| 527 | + asnativebytes(1, buffer, 0, 2) |
| 528 | + with self.assertRaises(TypeError): |
| 529 | + asnativebytes('not a number', buffer, 0, -1) |
| 530 | + |
| 531 | + def test_long_fromnativebytes(self): |
| 532 | + import math |
| 533 | + from _testcapi import ( |
| 534 | + pylong_fromnativebytes as fromnativebytes, |
| 535 | + SIZE_MAX, |
| 536 | + ) |
| 537 | + |
| 538 | + # Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot |
| 539 | + SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8) |
| 540 | + MAX_SSIZE = 2 ** (SZ * 8 - 1) - 1 |
| 541 | + MAX_USIZE = 2 ** (SZ * 8) - 1 |
| 542 | + |
| 543 | + for v_be, expect_s, expect_u in [ |
| 544 | + (b'\x00', 0, 0), |
| 545 | + (b'\x01', 1, 1), |
| 546 | + (b'\xff', -1, 255), |
| 547 | + (b'\x00\xff', 255, 255), |
| 548 | + (b'\xff\xff', -1, 65535), |
| 549 | + ]: |
| 550 | + with self.subTest(f"{expect_s}-{expect_u:X}-{len(v_be)}bytes"): |
| 551 | + n = len(v_be) |
| 552 | + v_le = v_be[::-1] |
| 553 | + |
| 554 | + self.assertEqual(expect_s, fromnativebytes(v_be, n, 0, 1), |
| 555 | + f"PyLong_FromNativeBytes(buffer, {n}, <big>)") |
| 556 | + self.assertEqual(expect_s, fromnativebytes(v_le, n, 1, 1), |
| 557 | + f"PyLong_FromNativeBytes(buffer, {n}, <little>)") |
| 558 | + self.assertEqual(expect_u, fromnativebytes(v_be, n, 0, 0), |
| 559 | + f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <big>)") |
| 560 | + self.assertEqual(expect_u, fromnativebytes(v_le, n, 1, 0), |
| 561 | + f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <little>)") |
| 562 | + |
| 563 | + # Check native endian when the result would be the same either |
| 564 | + # way and we can test it. |
| 565 | + if v_be == v_le: |
| 566 | + self.assertEqual(expect_s, fromnativebytes(v_be, n, -1, 1), |
| 567 | + f"PyLong_FromNativeBytes(buffer, {n}, <native>)") |
| 568 | + self.assertEqual(expect_u, fromnativebytes(v_be, n, -1, 0), |
| 569 | + f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <native>)") |
| 570 | + |
426 | 571 |
|
427 | 572 | if __name__ == "__main__": |
428 | 573 | unittest.main() |
0 commit comments