ソースを参照

putil: Switch to new Python 3.13+ public long APIs for BitArray

rdb 4 週間 前
コミット
b806035c40
2 ファイル変更53 行追加0 行削除
  1. 39 0
      panda/src/putil/bitArray_ext.cxx
  2. 14 0
      tests/putil/test_bitarray.py

+ 39 - 0
panda/src/putil/bitArray_ext.cxx

@@ -20,6 +20,29 @@
  */
 void Extension<BitArray>::
 __init__(PyObject *init_value) {
+#if PY_VERSION_HEX >= 0x030e0000  // Python 3.14
+  BitArray::WordType word = 0;
+  Py_ssize_t result = PyLong_AsNativeBytes(init_value, &word, sizeof(word), Py_ASNATIVEBYTES_LITTLE_ENDIAN | Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE);
+  if (result < 0) {
+    return;
+  }
+
+  if ((size_t)result <= sizeof(BitArray::WordType)) {
+    // It fit in a single word
+    _this->_array.clear();
+    if (word != 0) {
+      _this->_array.push_back(word);
+    }
+  } else {
+    // Requires multiple words.
+    size_t num_words = ((size_t)result + sizeof(BitArray::WordType) - 1) / sizeof(BitArray::WordType);
+    _this->_array.resize(num_words);
+
+    if (PyLong_AsNativeBytes(init_value, &_this->_array[0], num_words * sizeof(BitArray::WordType), Py_ASNATIVEBYTES_LITTLE_ENDIAN | Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE) <= 0) {
+      return;
+    }
+  }
+#else
   if (!PyLong_Check(init_value) || !PyLong_IsNonNegative(init_value)) {
     PyErr_SetString(PyExc_ValueError, "BitArray constructor requires a positive integer");
     return;
@@ -38,6 +61,7 @@ __init__(PyObject *init_value) {
 #endif
       );
   }
+#endif
 }
 
 /**
@@ -55,19 +79,34 @@ __getstate__() const {
   }
 
   if (_this->_highest_bits == 0) {
+#if PY_VERSION_HEX >= 0x030e0000  // Python 3.14
+    return PyLong_FromUnsignedNativeBytes(
+      &_this->_array[0], _this->_array.size() * sizeof(BitArray::WordType),
+      Py_ASNATIVEBYTES_LITTLE_ENDIAN);
+#else
     return _PyLong_FromByteArray(
       (const unsigned char *)&_this->_array[0],
       _this->_array.size() * sizeof(BitArray::WordType),
       1, 0);
+#endif
   } else {
     // This is an infinite array, so we invert it to make it a finite array and
     // store it as an inverted long.
     BitArray copy(*_this);
     copy.invert_in_place();
+#if PY_VERSION_HEX >= 0x030e0000  // Python 3.14
+    PyObject *state = PyLong_FromUnsignedNativeBytes(
+      &copy._array[0], copy._array.size() * sizeof(BitArray::WordType),
+      Py_ASNATIVEBYTES_LITTLE_ENDIAN);
+#else
     PyObject *state = _PyLong_FromByteArray(
       (const unsigned char *)&copy._array[0],
       copy._array.size() * sizeof(BitArray::WordType),
       1, 0);
+#endif
+    if (state == nullptr) {
+      return nullptr;
+    }
     PyObject *inverted = PyNumber_Invert(state);
     Py_DECREF(state);
     return inverted;

+ 14 - 0
tests/putil/test_bitarray.py

@@ -11,6 +11,20 @@ def test_bitarray_constructor():
     assert BitArray().is_zero()
     assert BitArray(0).is_zero()
 
+    one = BitArray(1)
+    assert not one.is_zero()
+    assert one.get_lowest_on_bit() == 0
+    assert one.get_highest_on_bit() == 0
+    assert one.get_lowest_off_bit() == 1
+    assert one.get_highest_off_bit() == -1
+
+    two = BitArray(2)
+    assert not two.is_zero()
+    assert two.get_lowest_on_bit() == 1
+    assert two.get_highest_on_bit() == 1
+    assert two.get_lowest_off_bit() == 0
+    assert two.get_highest_off_bit() == -1
+
     ba = BitArray(0x10000000000000000000000000)
     assert ba.get_lowest_on_bit() == 100
     assert ba.get_highest_on_bit() == 100