| #ifndef LIBRT_STRINGS_COMMON_H |
| #define LIBRT_STRINGS_COMMON_H |
| |
| #include <Python.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| // Byte-swap functions for endianness conversion (needed for both LE and BE operations) |
| #if defined(_MSC_VER) |
| # include <stdlib.h> |
| # define BSWAP16(x) _byteswap_ushort(x) |
| # define BSWAP32(x) _byteswap_ulong(x) |
| # define BSWAP64(x) _byteswap_uint64(x) |
| #elif defined(__GNUC__) || defined(__clang__) |
| # define BSWAP16(x) __builtin_bswap16(x) |
| # define BSWAP32(x) __builtin_bswap32(x) |
| # define BSWAP64(x) __builtin_bswap64(x) |
| #else |
| // Fallback for other compilers (slower but portable) |
| static inline uint16_t BSWAP16(uint16_t x) { |
| return (uint16_t)((x >> 8) | (x << 8)); |
| } |
| static inline uint32_t BSWAP32(uint32_t x) { |
| return ((x >> 24) & 0xFFU) | |
| ((x >> 8) & 0xFF00U) | |
| ((x << 8) & 0xFF0000U) | |
| ((x << 24) & 0xFF000000U); |
| } |
| static inline uint64_t BSWAP64(uint64_t x) { |
| return ((x >> 56) & 0xFFULL) | |
| ((x >> 40) & 0xFF00ULL) | |
| ((x >> 24) & 0xFF0000ULL) | |
| ((x >> 8) & 0xFF000000ULL) | |
| ((x << 8) & 0xFF00000000ULL) | |
| ((x << 24) & 0xFF0000000000ULL) | |
| ((x << 40) & 0xFF000000000000ULL) | |
| ((x << 56) & 0xFF00000000000000ULL); |
| } |
| #endif |
| |
| // Length of the default buffer embedded directly in a BytesWriter object |
| #define WRITER_EMBEDDED_BUF_LEN 256 |
| |
| typedef struct { |
| PyObject_HEAD |
| char *buf; // Beginning of the buffer |
| Py_ssize_t len; // Current length (number of bytes written) |
| Py_ssize_t capacity; // Total capacity of the buffer |
| char data[WRITER_EMBEDDED_BUF_LEN]; // Default buffer |
| } BytesWriterObject; |
| |
| // Write a 16-bit signed integer in little-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteI16LEUnsafe(BytesWriterObject *self, int16_t value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| uint16_t swapped = BSWAP16((uint16_t)value); |
| memcpy(self->buf + self->len, &swapped, 2); |
| #else |
| memcpy(self->buf + self->len, &value, 2); |
| #endif |
| self->len += 2; |
| } |
| |
| // Write a 16-bit signed integer in big-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteI16BEUnsafe(BytesWriterObject *self, int16_t value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| memcpy(self->buf + self->len, &value, 2); |
| #else |
| uint16_t swapped = BSWAP16((uint16_t)value); |
| memcpy(self->buf + self->len, &swapped, 2); |
| #endif |
| self->len += 2; |
| } |
| |
| // Read a 16-bit signed integer in little-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline int16_t |
| CPyBytes_ReadI16LEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| uint16_t value; |
| memcpy(&value, data, 2); |
| #if PY_BIG_ENDIAN |
| value = BSWAP16(value); |
| #endif |
| return (int16_t)value; |
| } |
| |
| // Read a 16-bit signed integer in big-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline int16_t |
| CPyBytes_ReadI16BEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| uint16_t value; |
| memcpy(&value, data, 2); |
| #if PY_BIG_ENDIAN |
| // Already in big-endian format, no swap needed |
| #else |
| value = BSWAP16(value); |
| #endif |
| return (int16_t)value; |
| } |
| |
| // Write a 32-bit signed integer in little-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteI32LEUnsafe(BytesWriterObject *self, int32_t value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| uint32_t swapped = BSWAP32((uint32_t)value); |
| memcpy(self->buf + self->len, &swapped, 4); |
| #else |
| memcpy(self->buf + self->len, &value, 4); |
| #endif |
| self->len += 4; |
| } |
| |
| // Write a 32-bit signed integer in big-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteI32BEUnsafe(BytesWriterObject *self, int32_t value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| memcpy(self->buf + self->len, &value, 4); |
| #else |
| uint32_t swapped = BSWAP32((uint32_t)value); |
| memcpy(self->buf + self->len, &swapped, 4); |
| #endif |
| self->len += 4; |
| } |
| |
| // Read a 32-bit signed integer in little-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline int32_t |
| CPyBytes_ReadI32LEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| uint32_t value; |
| memcpy(&value, data, 4); |
| #if PY_BIG_ENDIAN |
| value = BSWAP32(value); |
| #endif |
| return (int32_t)value; |
| } |
| |
| // Read a 32-bit signed integer in big-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline int32_t |
| CPyBytes_ReadI32BEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| uint32_t value; |
| memcpy(&value, data, 4); |
| #if PY_BIG_ENDIAN |
| // Already in big-endian format, no swap needed |
| #else |
| value = BSWAP32(value); |
| #endif |
| return (int32_t)value; |
| } |
| |
| // Write a 64-bit signed integer in little-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteI64LEUnsafe(BytesWriterObject *self, int64_t value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| uint64_t swapped = BSWAP64((uint64_t)value); |
| memcpy(self->buf + self->len, &swapped, 8); |
| #else |
| memcpy(self->buf + self->len, &value, 8); |
| #endif |
| self->len += 8; |
| } |
| |
| // Write a 64-bit signed integer in big-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteI64BEUnsafe(BytesWriterObject *self, int64_t value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| memcpy(self->buf + self->len, &value, 8); |
| #else |
| uint64_t swapped = BSWAP64((uint64_t)value); |
| memcpy(self->buf + self->len, &swapped, 8); |
| #endif |
| self->len += 8; |
| } |
| |
| // Read a 64-bit signed integer in little-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline int64_t |
| CPyBytes_ReadI64LEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| uint64_t value; |
| memcpy(&value, data, 8); |
| #if PY_BIG_ENDIAN |
| value = BSWAP64(value); |
| #endif |
| return (int64_t)value; |
| } |
| |
| // Read a 64-bit signed integer in big-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline int64_t |
| CPyBytes_ReadI64BEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| uint64_t value; |
| memcpy(&value, data, 8); |
| #if PY_BIG_ENDIAN |
| // Already in big-endian format, no swap needed |
| #else |
| value = BSWAP64(value); |
| #endif |
| return (int64_t)value; |
| } |
| |
| // Write a 32-bit float in little-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteF32LEUnsafe(BytesWriterObject *self, float value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| uint32_t bits; |
| memcpy(&bits, &value, 4); |
| bits = BSWAP32(bits); |
| memcpy(self->buf + self->len, &bits, 4); |
| #else |
| memcpy(self->buf + self->len, &value, 4); |
| #endif |
| self->len += 4; |
| } |
| |
| // Write a 32-bit float in big-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteF32BEUnsafe(BytesWriterObject *self, float value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| memcpy(self->buf + self->len, &value, 4); |
| #else |
| uint32_t bits; |
| memcpy(&bits, &value, 4); |
| bits = BSWAP32(bits); |
| memcpy(self->buf + self->len, &bits, 4); |
| #endif |
| self->len += 4; |
| } |
| |
| // Read a 32-bit float in little-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline float |
| CPyBytes_ReadF32LEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| float value; |
| #if PY_BIG_ENDIAN |
| uint32_t bits; |
| memcpy(&bits, data, 4); |
| bits = BSWAP32(bits); |
| memcpy(&value, &bits, 4); |
| #else |
| memcpy(&value, data, 4); |
| #endif |
| return value; |
| } |
| |
| // Read a 32-bit float in big-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline float |
| CPyBytes_ReadF32BEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| float value; |
| #if PY_BIG_ENDIAN |
| memcpy(&value, data, 4); |
| #else |
| uint32_t bits; |
| memcpy(&bits, data, 4); |
| bits = BSWAP32(bits); |
| memcpy(&value, &bits, 4); |
| #endif |
| return value; |
| } |
| |
| // Write a 64-bit float (double) in little-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteF64LEUnsafe(BytesWriterObject *self, double value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| uint64_t bits; |
| memcpy(&bits, &value, 8); |
| bits = BSWAP64(bits); |
| memcpy(self->buf + self->len, &bits, 8); |
| #else |
| memcpy(self->buf + self->len, &value, 8); |
| #endif |
| self->len += 8; |
| } |
| |
| // Write a 64-bit float (double) in big-endian format to BytesWriter. |
| // NOTE: This does NOT check buffer capacity - caller must ensure space is available. |
| static inline void |
| BytesWriter_WriteF64BEUnsafe(BytesWriterObject *self, double value) { |
| // memcpy is reliably optimized to a single store by GCC, Clang, and MSVC |
| #if PY_BIG_ENDIAN |
| memcpy(self->buf + self->len, &value, 8); |
| #else |
| uint64_t bits; |
| memcpy(&bits, &value, 8); |
| bits = BSWAP64(bits); |
| memcpy(self->buf + self->len, &bits, 8); |
| #endif |
| self->len += 8; |
| } |
| |
| // Read a 64-bit float (double) in little-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline double |
| CPyBytes_ReadF64LEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| double value; |
| #if PY_BIG_ENDIAN |
| uint64_t bits; |
| memcpy(&bits, data, 8); |
| bits = BSWAP64(bits); |
| memcpy(&value, &bits, 8); |
| #else |
| memcpy(&value, data, 8); |
| #endif |
| return value; |
| } |
| |
| // Read a 64-bit float (double) in big-endian format from bytes. |
| // NOTE: This does NOT check bounds - caller must ensure valid index. |
| static inline double |
| CPyBytes_ReadF64BEUnsafe(const unsigned char *data) { |
| // memcpy is reliably optimized to a single load by GCC, Clang, and MSVC |
| double value; |
| #if PY_BIG_ENDIAN |
| memcpy(&value, data, 8); |
| #else |
| uint64_t bits; |
| memcpy(&bits, data, 8); |
| bits = BSWAP64(bits); |
| memcpy(&value, &bits, 8); |
| #endif |
| return value; |
| } |
| |
| #endif // LIBRT_STRINGS_COMMON_H |