| 573 | } |
| 574 | |
| 575 | static bool can_use_list_sequence_fastpath(PyObject **items, Py_ssize_t size, |
| 576 | uint8_t type_id) { |
| 577 | // This gate is not only about type checks: |
| 578 | // it enforces "no Python callback during conversion" for the raw ob_item |
| 579 | // path. If conversion can invoke user code (e.g. __int__/__float__/subclass |
| 580 | // hooks), a list may be mutated while iterating raw pointers, which is |
| 581 | // unsafe. |
| 582 | switch (static_cast<TypeId>(type_id)) { |
| 583 | case TypeId::STRING: |
| 584 | for (Py_ssize_t i = 0; i < size; ++i) { |
| 585 | if (!PyUnicode_CheckExact(items[i])) { |
| 586 | return false; |
| 587 | } |
| 588 | } |
| 589 | return true; |
| 590 | case TypeId::VARINT64: |
| 591 | case TypeId::INT64: |
| 592 | case TypeId::TAGGED_INT64: |
| 593 | case TypeId::VARINT32: |
| 594 | case TypeId::INT32: |
| 595 | case TypeId::VAR_UINT64: |
| 596 | case TypeId::UINT64: |
| 597 | case TypeId::TAGGED_UINT64: |
| 598 | case TypeId::VAR_UINT32: |
| 599 | case TypeId::UINT32: |
| 600 | case TypeId::UINT8: |
| 601 | case TypeId::INT8: |
| 602 | case TypeId::UINT16: |
| 603 | case TypeId::INT16: |
| 604 | for (Py_ssize_t i = 0; i < size; ++i) { |
| 605 | if (!PyLong_CheckExact(items[i])) { |
| 606 | return false; |
| 607 | } |
| 608 | } |
| 609 | return true; |
| 610 | case TypeId::BOOL: |
| 611 | for (Py_ssize_t i = 0; i < size; ++i) { |
| 612 | if (items[i] != Py_True && items[i] != Py_False) { |
| 613 | return false; |
| 614 | } |
| 615 | } |
| 616 | return true; |
| 617 | case TypeId::FLOAT32: |
| 618 | case TypeId::FLOAT64: |
| 619 | for (Py_ssize_t i = 0; i < size; ++i) { |
| 620 | if (!PyFloat_CheckExact(items[i])) { |
| 621 | return false; |
| 622 | } |
| 623 | } |
| 624 | return true; |
| 625 | default: |
| 626 | return false; |
| 627 | } |
| 628 | } |
| 629 | |
| 630 | template <typename T> |
| 631 | static bool py_long_to_integral_range(PyObject *value, const char *type_name, |
no outgoing calls
no test coverage detected