Skip to content
Permalink
Browse files

putil: Assorted improvements to BitArray, SparseArray, BitMask:

* Support for pickling (except DoubleBitMask)
* All of them now properly define __nonzero__()
* BitArray constructor accepts a Python long of arbitrary size
* More unit tests

Fixes #886
  • Loading branch information
rdb committed Mar 21, 2020
1 parent 867a5c0 commit 6ca19cd5b4b994a283535a08b2306e851509ed11
@@ -125,9 +125,15 @@ set(P3PUTIL_SOURCES
set(P3PUTIL_IGATEEXT
bamReader_ext.cxx
bamReader_ext.h
bitArray_ext.cxx
bitArray_ext.h
bitArray_ext.I
callbackObject_ext.h
pythonCallbackObject.cxx
pythonCallbackObject.h
sparseArray_ext.cxx
sparseArray_ext.h
sparseArray_ext.I
typedWritable_ext.cxx
typedWritable_ext.h
)
@@ -21,6 +21,7 @@
#include "typedObject.h"
#include "indent.h"
#include "pointerToArray.h"
#include "extension.h"

#include "checksumHashGenerator.h"

@@ -41,12 +42,14 @@ class EXPCL_PANDA_PUTIL BitArray {
typedef BitMaskNative MaskType;
typedef MaskType::WordType WordType;

INLINE BitArray(WordType init_value);

PUBLISHED:
enum { num_bits_per_word = MaskType::num_bits };

INLINE BitArray();
INLINE BitArray(WordType init_value);
BitArray(const SparseArray &from);
EXTENSION(BitArray(PyObject *init_value));

INLINE static BitArray all_on();
INLINE static BitArray all_off();
@@ -125,6 +128,10 @@ class EXPCL_PANDA_PUTIL BitArray {
void operator <<= (int shift);
void operator >>= (int shift);

EXTENSION(bool __nonzero__() const);
EXTENSION(PyObject *__getstate__() const);
EXTENSION(void __setstate__(PyObject *state));

public:
void generate_hash(ChecksumHashGenerator &hashgen) const;

@@ -138,6 +145,8 @@ class EXPCL_PANDA_PUTIL BitArray {
Array _array;
int _highest_bits; // Either 0 or 1.

friend class Extension<BitArray>;

public:
void write_datagram(BamWriter *manager, Datagram &dg) const;
void read_datagram(DatagramIterator &scan, BamReader *manager);
@@ -0,0 +1,20 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file bitArray_ext.I
* @author rdb
* @date 2020-03-21
*/

/**
* Returns true if the value is not zero.
*/
INLINE bool Extension<BitArray>::
__nonzero__() const {
return !_this->is_zero();
}
@@ -0,0 +1,101 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file bitArray_ext.cxx
* @author rdb
* @date 2020-03-21
*/

#include "bitArray_ext.h"

#ifdef HAVE_PYTHON

/**
* Creates a BitArray from a Python long object.
*/
void Extension<BitArray>::
__init__(PyObject *init_value) {
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(init_value)) {
long value = PyInt_AS_LONG(init_value);
if (value >= 0) {
_this->set_word(0, value);
} else {
PyErr_SetString(PyExc_ValueError, "BitArray constructor requires a positive integer");
}
return;
}
#endif

if (!PyLong_Check(init_value) || Py_SIZE(init_value) < 0) {
PyErr_SetString(PyExc_ValueError, "BitArray constructor requires a positive integer");
return;
}

int n = _PyLong_NumBits(init_value);
if (n > 0) {
size_t num_words = (n + BitArray::num_bits_per_word - 1) / BitArray::num_bits_per_word;
_this->_array.resize(num_words);
_PyLong_AsByteArray((PyLongObject *)init_value,
(unsigned char *)&_this->_array[0],
num_words * sizeof(BitArray::WordType),
1, 0);
}
}

/**
* Returns the value of the BitArray as a picklable Python object.
*
* We could just return a list of words. However, different builds of Panda3D
* may have different sizes for the WordType, so we'd also need to add code to
* convert between different WordTypes. Instead, we'll just encode the whole
* array as a Python long, with infinite arrays stored as inverted longs.
*/
PyObject *Extension<BitArray>::
__getstate__() const {
if (_this->_array.empty()) {
return PyLong_FromLong(-_this->_highest_bits);
}

if (_this->_highest_bits == 0) {
return _PyLong_FromByteArray(
(const unsigned char *)&_this->_array[0],
_this->_array.size() * sizeof(BitArray::WordType),
1, 0);
} 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();
PyObject *state = _PyLong_FromByteArray(
(const unsigned char *)&copy._array[0],
copy._array.size() * sizeof(BitArray::WordType),
1, 0);
PyObject *inverted = PyNumber_Invert(state);
Py_DECREF(state);
return inverted;
}
}

/**
* Takes the value returned by __getstate__ and uses it to freshly initialize
* this BitArray object.
*/
void Extension<BitArray>::
__setstate__(PyObject *state) {
if (Py_SIZE(state) >= 0) {
__init__(state);
} else {
PyObject *inverted = PyNumber_Invert(state);
__init__(inverted);
Py_DECREF(inverted);
_this->invert_in_place();
}
}

#endif
@@ -0,0 +1,44 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file bitArray_ext.h
* @author rdb
* @date 2020-03-21
*/

#ifndef BITARRAY_EXT_H
#define BITARRAY_EXT_H

#include "dtoolbase.h"

#ifdef HAVE_PYTHON

#include "extension.h"
#include "bitArray.h"
#include "py_panda.h"

/**
* This class defines the extension methods for BitArray, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<BitArray> : public ExtensionBase<BitArray> {
public:
void __init__(PyObject *init_value);

INLINE bool __nonzero__() const;

PyObject *__getstate__() const;
void __setstate__(PyObject *state);
};

#include "bitArray_ext.I"

#endif // HAVE_PYTHON

#endif // BITARRAY_EXT_H
@@ -632,6 +632,24 @@ __nonzero__() const {
return _word != 0;
}

/**
* Returns the picklable state.
*/
template<class WType, int nbits>
INLINE typename BitMask<WType, nbits>::WordType BitMask<WType, nbits>::
__getstate__() const {
return _word;
}

/**
* Sets the picklable state.
*/
template<class WType, int nbits>
INLINE void BitMask<WType, nbits>::
__setstate__(WordType word) {
_word = word;
}

/**
* Adds the bitmask to the indicated hash generator.
*/
@@ -126,6 +126,8 @@ class BitMask {
INLINE int get_key() const;

INLINE bool __nonzero__() const;
INLINE WordType __getstate__() const;
INLINE void __setstate__(WordType word);

public:
INLINE void generate_hash(ChecksumHashGenerator &hashgen) const;
@@ -647,6 +647,15 @@ operator >>= (int shift) {
_hi >>= shift;
}

/**
* Returns true if the bitmask is not zero.
*/
template<class BMType>
INLINE bool DoubleBitMask<BMType>::
__nonzero__() const {
return !is_zero();
}

/**
* Adds the doubleBitMask to the indicated hash generator.
*/
@@ -110,6 +110,8 @@ class DoubleBitMask {
INLINE void operator <<= (int shift);
INLINE void operator >>= (int shift);

INLINE bool __nonzero__() const;

public:
INLINE void generate_hash(ChecksumHashGenerator &hashgen) const;

@@ -1,3 +1,5 @@
#include "bamReader_ext.cxx"
#include "bitArray_ext.cxx"
#include "pythonCallbackObject.cxx"
#include "sparseArray_ext.cxx"
#include "typedWritable_ext.cxx"
@@ -16,6 +16,7 @@

#include "pandabase.h"
#include "ordered_vector.h"
#include "extension.h"

class BitArray;
class BamWriter;
@@ -116,6 +117,10 @@ class EXPCL_PANDA_PUTIL SparseArray {
INLINE int get_subrange_begin(size_t n) const;
INLINE int get_subrange_end(size_t n) const;

EXTENSION(bool __nonzero__() const);
EXTENSION(PyObject *__getstate__() const);
EXTENSION(void __setstate__(PyObject *state));

private:
void do_add_range(int begin, int end);
void do_remove_range(int begin, int end);
@@ -140,6 +145,8 @@ class EXPCL_PANDA_PUTIL SparseArray {
Subranges _subranges;
bool _inverse;

friend class Extension<SparseArray>;

public:
void write_datagram(BamWriter *manager, Datagram &dg) const;
void read_datagram(DatagramIterator &scan, BamReader *manager);
@@ -0,0 +1,20 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file sparseArray_ext.I
* @author rdb
* @date 2020-03-21
*/

/**
* Returns true if the value is not zero.
*/
INLINE bool Extension<SparseArray>::
__nonzero__() const {
return !_this->is_zero();
}

0 comments on commit 6ca19cd

Please sign in to comment.
You can’t perform that action at this time.