Skip to content

Commit

Permalink
[#469] Bind C++ BitStreamWriter to python runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
Mi-La committed Feb 2, 2023
1 parent 046aeab commit ef091d1
Show file tree
Hide file tree
Showing 13 changed files with 437 additions and 162 deletions.
13 changes: 4 additions & 9 deletions compiler/extensions/cpp/runtime/src/zserio/BitStreamWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,7 @@ void BitStreamWriter::writeBool(bool data)
void BitStreamWriter::setBitPosition(BitPosType position)
{
if (hasWriteBuffer())
{
if (!checkCapacity(position))
throw CppRuntimeException("BitStreamWriter: Reached eof(), setting of bit position failed!");
}
checkCapacity(position);

m_bitIndex = position;
}
Expand Down Expand Up @@ -294,8 +291,7 @@ void BitStreamWriter::writeUnsignedBits(uint32_t data, uint8_t numBits)
return;
}

if (!checkCapacity(m_bitIndex + numBits))
throw CppRuntimeException("BitStreamWriter: Reached eof(), writing to stream failed!");
checkCapacity(m_bitIndex + numBits);

uint8_t restNumBits = numBits;
const uint8_t bitsUsed = m_bitIndex & 0x07;
Expand Down Expand Up @@ -392,11 +388,10 @@ inline void BitStreamWriter::writeVarNum(uint64_t value, bool isSigned, bool isN
}
}

inline bool BitStreamWriter::checkCapacity(size_t bitSize) const
inline void BitStreamWriter::checkCapacity(size_t bitSize) const
{
if (bitSize > m_bufferBitSize)
return false;
return true;
throw InsufficientCapacityException("BitStreamWriter: Reached end of bit buffer!");
}

} // namespace zserio
17 changes: 12 additions & 5 deletions compiler/extensions/cpp/runtime/src/zserio/BitStreamWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
#include <cstddef>
#include <cstring>

#include "zserio/Types.h"
#include "zserio/Span.h"
#include "zserio/BitBuffer.h"
#include "zserio/VarSizeUtil.h"
#include "zserio/StringView.h"
#include "zserio/CppRuntimeException.h"
#include "zserio/Span.h"
#include "zserio/StringView.h"
#include "zserio/Types.h"
#include "zserio/VarSizeUtil.h"

namespace zserio
{
Expand All @@ -20,6 +20,13 @@ namespace zserio
class BitStreamWriter
{
public:
/** Exception throw in case of insufficient capacity of the given buffer. */
class InsufficientCapacityException : public CppRuntimeException
{
public:
using CppRuntimeException::CppRuntimeException;
};

/** Type for bit position. */
typedef size_t BitPosType;

Expand Down Expand Up @@ -299,7 +306,7 @@ class BitStreamWriter
void writeUnsignedVarNum(uint64_t value, size_t maxVarBytes, size_t numVarBytes);
void writeVarNum(uint64_t value, bool hasSign, bool isNegative, size_t maxVarBytes, size_t numVarBytes);

bool checkCapacity(size_t bitSize) const;
void checkCapacity(size_t bitSize) const;

uint8_t* m_buffer;
size_t m_bitIndex;
Expand Down
3 changes: 3 additions & 0 deletions compiler/extensions/python/runtime/src/zserio/bitwriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from zserio.exception import PythonRuntimeException
from zserio.float import float_to_uint16, float_to_uint32, float_to_uint64
from zserio.limits import INT64_MIN
from zserio.cppbind import import_cpp_class

class BitStreamWriter:
"""
Expand Down Expand Up @@ -359,3 +360,5 @@ def _write_varnum(self, value: int, max_var_bytes: int, num_var_bytes: int, *, i
self.write_bits_unchecked(byte, 8)

VAR_NUM_BIT_MASKS = [0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff]

BitStreamWriter = import_cpp_class("BitStreamWriter") or BitStreamWriter # type: ignore
44 changes: 44 additions & 0 deletions compiler/extensions/python/runtime/src/zserio_cpp/BitBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "BitBuffer.h"

namespace
{

zserio::BitBuffer bitBufferInit(const py::buffer_info& bufferInfo, std::optional<py::int_> optionalBitSize)
{
uint8_t* buffer = static_cast<uint8_t*>(bufferInfo.ptr);
if (optionalBitSize)
{
const size_t bitSize = optionalBitSize->cast<size_t>();
if ((bitSize + 7) / 8 > static_cast<size_t>(bufferInfo.size))
throw zserio::CppRuntimeException("BitBuffer: Bit size out of range for given buffer!");

return zserio::BitBuffer(buffer, bitSize);
}
else
{
return zserio::BitBuffer(buffer, bufferInfo.size * 8);
}
}

} // namespace

namespace zserio_cpp
{

void pybindBitBuffer(py::module_ module)
{
py::class_<zserio::BitBuffer>(module, "BitBuffer")
.def(py::init([](const py::object& buffer, std::optional<py::int_> bitsize) {
return bitBufferInit(py::buffer(buffer).request(), bitsize);
}), py::arg("buffer"), py::arg("bitsize") = py::none())
.def_property_readonly("buffer", py::cpp_function([](zserio::BitBuffer& self){
constexpr bool readOnly = false;
return py::memoryview::from_memory(self.getBuffer(), self.getByteSize(), readOnly);
}, py::keep_alive<0, 1>()))
.def_property_readonly("bitsize", &zserio::BitBuffer::getBitSize)
.def(py::self == py::self)
.def("__hash__", &zserio::BitBuffer::hashCode)
;
}

} // namespace zserio_cpp
19 changes: 19 additions & 0 deletions compiler/extensions/python/runtime/src/zserio_cpp/BitBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef ZSERIO_CPP_BIT_BUFFER_H_INC
#define ZSERIO_CPP_BIT_BUFFER_H_INC

#include "ZserioCpp.h"
#include "zserio/BitBuffer.h"

namespace zserio_cpp
{

/**
* Binds C++ bit buffer to python runtime.
*
* \param module Module where to place the binding.
*/
void pybindBitBuffer(py::module_ module);

} // namespace zserio_cpp

#endif // ZSERIO_CPP_BIT_BUFFER_H_INC
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "BitStreamReader.h"

namespace
{

std::unique_ptr<zserio::BitStreamReader> bitStreamReaderInit(const py::buffer_info& bufferInfo,
std::optional<py::int_> optionalBitSize)
{
const uint8_t* buffer = static_cast<const uint8_t*>(bufferInfo.ptr);
if (optionalBitSize)
{
const size_t bitSize = optionalBitSize->cast<size_t>();
if ((bitSize + 7) / 8 > static_cast<size_t>(bufferInfo.size))
throw zserio::CppRuntimeException("BitStreamReader: Bit size out of range for given buffer!");

return std::make_unique<zserio::BitStreamReader>(buffer, bitSize, zserio::BitsTag());
}
else
{
return std::make_unique<zserio::BitStreamReader>(buffer, bufferInfo.size);
}
}

} // namespace

namespace zserio_cpp
{

void pybindBitStreamReader(py::module_ module)
{
py::class_<zserio::BitStreamReader>(module, "BitStreamReader")
.def(py::init([](const py::object& buffer, std::optional<py::int_> bitsize) {
return bitStreamReaderInit(py::buffer(buffer).request(), bitsize);
}), py::arg("buffer"), py::arg("bitsize") = py::none(), py::keep_alive<1, 2>())
.def_static("from_bitbuffer", [](const zserio::BitBuffer& bitBuffer){
return std::make_unique<zserio::BitStreamReader>(bitBuffer);
}, py::keep_alive<0, 1>())
.def("read_bits", [](zserio::BitStreamReader& self, py::int_ numbits){
return self.readBits64(numbits.cast<uint8_t>());
})
.def("read_signed_bits", [](zserio::BitStreamReader& self, py::int_ numbits){
return self.readSignedBits64(numbits.cast<uint8_t>());
})
.def("read_bits_unchecked", [](zserio::BitStreamReader& self, py::int_ numbits){
return self.readBits64(numbits.cast<uint8_t>());
})
.def("read_signed_bits_unchecked", [](zserio::BitStreamReader& self, py::int_ numbits){
return self.readSignedBits64(numbits.cast<uint8_t>());
})
.def("read_varint16", &zserio::BitStreamReader::readVarInt16)
.def("read_varint32", &zserio::BitStreamReader::readVarInt32)
.def("read_varint64", &zserio::BitStreamReader::readVarInt64)
.def("read_varint", &zserio::BitStreamReader::readVarInt)
.def("read_varuint16", &zserio::BitStreamReader::readVarUInt16)
.def("read_varuint32", &zserio::BitStreamReader::readVarUInt32)
.def("read_varuint64", &zserio::BitStreamReader::readVarUInt64)
.def("read_varuint", &zserio::BitStreamReader::readVarUInt)
.def("read_varsize", &zserio::BitStreamReader::readVarSize)
.def("read_float16", &zserio::BitStreamReader::readFloat16)
.def("read_float32", &zserio::BitStreamReader::readFloat32)
.def("read_float64", &zserio::BitStreamReader::readFloat64)
.def("read_bytes", [](zserio::BitStreamReader& self){
auto bytes = self.readBytes();
return py::bytearray(reinterpret_cast<char*>(bytes.data()), bytes.size());
})
.def("read_string", [](zserio::BitStreamReader& self){
return self.readString();
})
.def("read_bool", &zserio::BitStreamReader::readBool)
.def("read_bitbuffer", [](zserio::BitStreamReader& self) {
return self.readBitBuffer();
})
.def("alignto", [](zserio::BitStreamReader& self, py::int_ alignment) {
self.alignTo(alignment.cast<size_t>());
})
.def_property("bitposition",
&zserio::BitStreamReader::getBitPosition,
[](zserio::BitStreamReader& self, py::int_ bitposition) {
self.setBitPosition(bitposition.cast<zserio::BitStreamReader::BitPosType>());
})
.def_property_readonly("buffer_bitsize",
&zserio::BitStreamReader::getBufferBitSize)
;
}

} // namespace zserio_cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef ZSERIO_CPP_BIT_STREAM_READER_H_INC
#define ZSERIO_CPP_BIT_STREAM_READER_H_INC

#include "ZserioCpp.h"
#include "zserio/BitStreamReader.h"

namespace zserio_cpp
{

/**
* Binds C++ bit stream reader to python runtime.
*
* \param module Module where to place the binding.
*/
void pybindBitStreamReader(py::module_ module);

} // namespace zserio_cpp

#endif // ZSERIO_CPP_BIT_STREAM_READER_H_INC
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "BitStreamWriter.h"

namespace zserio_cpp
{

void pybindBitStreamWriter(py::module_ module)
{
py::class_<zserio_cpp::BitStreamWriter>(module, "BitStreamWriter")
.def(py::init())
.def("write_bits", [](zserio_cpp::BitStreamWriter& self, py::int_ value, py::int_ numbits) {
self.write(&zserio::BitStreamWriter::writeBits64,
value.cast<uint64_t>(), numbits.cast<uint8_t>());
})
.def("write_bits_unchecked", [](zserio_cpp::BitStreamWriter& self,
py::int_ value, py::int_ numbits) {
self.write(&zserio::BitStreamWriter::writeBits64,
value.cast<uint64_t>(), numbits.cast<uint8_t>());
})
.def("write_signed_bits", [](zserio_cpp::BitStreamWriter& self, py::int_ value, py::int_ numbits) {
self.write(&zserio::BitStreamWriter::writeSignedBits64,
value.cast<int64_t>(), numbits.cast<uint8_t>());
})
.def("write_signed_bits_unchecked", [](zserio_cpp::BitStreamWriter& self,
py::int_ value, py::int_ numbits) {
self.write(&zserio::BitStreamWriter::writeSignedBits64,
value.cast<int64_t>(), numbits.cast<uint8_t>());
})
.def("write_varint16", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarInt16, value.cast<int16_t>());
})
.def("write_varint32", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarInt32, value.cast<int32_t>());
})
.def("write_varint64", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarInt64, value.cast<int64_t>());
})
.def("write_varint", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarInt, value.cast<int64_t>());
})
.def("write_varuint16", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarUInt16, value.cast<uint16_t>());
})
.def("write_varuint32", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarUInt32, value.cast<uint32_t>());
})
.def("write_varuint64", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarUInt64, value.cast<uint64_t>());
})
.def("write_varuint", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarUInt, value.cast<uint64_t>());
})
.def("write_varsize", [](zserio_cpp::BitStreamWriter& self, py::int_ value) {
self.write(&zserio::BitStreamWriter::writeVarSize, value.cast<uint32_t>());
})
.def("write_float16", [](zserio_cpp::BitStreamWriter& self, py::object value) {
self.write(&zserio::BitStreamWriter::writeFloat16, value.cast<float>());
})
.def("write_float32", [](zserio_cpp::BitStreamWriter& self, py::object value) {
self.write(&zserio::BitStreamWriter::writeFloat32, value.cast<float>());
})
.def("write_float64", [](zserio_cpp::BitStreamWriter& self, py::object value) {
self.write(&zserio::BitStreamWriter::writeFloat64, value.cast<double>());
})
.def("write_bytes", [](zserio_cpp::BitStreamWriter& self, const py::object& value) {
py::buffer_info bufferInfo = py::buffer(value).request();
self.write(&zserio::BitStreamWriter::writeBytes,
zserio::Span(static_cast<const uint8_t*>(bufferInfo.ptr), bufferInfo.size));
})
.def("write_string", [](zserio_cpp::BitStreamWriter& self, const std::string& value) {
self.write(&zserio::BitStreamWriter::writeString, value);
})
.def("write_bool", [](zserio_cpp::BitStreamWriter& self, py::bool_ value) {
self.write(&zserio::BitStreamWriter::writeBool, value.cast<bool>());
})
.def("write_bitbuffer", [](zserio_cpp::BitStreamWriter& self, const zserio::BitBuffer& value) {
self.write(&zserio::BitStreamWriter::writeBitBuffer<std::allocator<uint8_t>>, value);
})
.def_property_readonly("byte_array", py::cpp_function([](zserio_cpp::BitStreamWriter& self) {
constexpr bool readOnly = true;
return py::memoryview::from_memory(self.getWriteBuffer(), self.getBufferByteSize(), readOnly);
}, py::keep_alive<0, 1>()))
.def_property_readonly("bitposition", &zserio_cpp::BitStreamWriter::getBitPosition)
.def("alignto", [](zserio_cpp::BitStreamWriter& self, py::int_ alignment) {
self.write(&zserio::BitStreamWriter::alignTo, alignment.cast<size_t>());
})
.def("to_file", &zserio_cpp::BitStreamWriter::toFile)
;
}

} // namespace zserio_cpp
Loading

0 comments on commit ef091d1

Please sign in to comment.