Skip to content

Commit 969af09

Browse files
committed
Add PyFloat_Pack8()
1 parent 179e534 commit 969af09

File tree

5 files changed

+134
-5
lines changed

5 files changed

+134
-5
lines changed

docs/api.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,42 @@ Python 3.11
3939
4040
Not available on PyPy
4141
42+
.. c:function:: int PyFloat_Pack2(double x, unsigned char *p, int le)
43+
44+
Pack a C double as 16-bit.
45+
46+
Availability: Python 3.6 and newer. Not available on PyPy
47+
48+
.. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le)
49+
50+
Pack a C double as 32-bit.
51+
52+
Not available on PyPy
53+
54+
.. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le)
55+
56+
Pack a C double as 64-bit.
57+
58+
Not available on PyPy
59+
60+
.. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le)
61+
62+
Unpack 16-bit as a C double.
63+
64+
Availability: Python 3.6 and newer. Not available on PyPy
65+
66+
.. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le)
67+
68+
Unpack 32-bit as a C double.
69+
70+
Not available on PyPy
71+
72+
.. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le)
73+
74+
Unpack 64-bit as a C double.
75+
76+
Not available on PyPy
77+
4278
Python 3.10
4379
-----------
4480

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changelog
22
=========
33

4+
* 2022-03-03: Add functions ``PyFloat_Pack2()``, ``PyFloat_Pack4()``,
5+
``PyFloat_Pack8()``, ``PyFloat_Unpack2()``, ``PyFloat_Unpack4()`` and
6+
``PyFloat_Unpack8()``.
47
* 2022-02-11: The project license changes from the MIT license to the Zero
58
Clause BSD (0BSD) license. Projects copying ``pythoncapi_compat.h`` no longer
69
have to include the MIT license and the copyright notice.

pythoncapi_compat.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,37 @@ _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
391391
#endif
392392

393393

394+
// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1
395+
#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)
396+
PYCAPI_COMPAT_STATIC_INLINE(int)
397+
PyFloat_Pack2(double x, char *p, int le)
398+
{ return _PyFloat_Pack2(x, (unsigned char*)p, le); }
399+
400+
PYCAPI_COMPAT_STATIC_INLINE(double)
401+
PyFloat_Unpack2(const char *p, int le)
402+
{ return _PyFloat_Unpack2((const unsigned char *)p, le); }
403+
#endif
404+
405+
406+
#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)
407+
PYCAPI_COMPAT_STATIC_INLINE(int)
408+
PyFloat_Pack4(double x, char *p, int le)
409+
{ return _PyFloat_Pack4(x, (unsigned char*)p, le); }
410+
411+
PYCAPI_COMPAT_STATIC_INLINE(int)
412+
PyFloat_Pack8(double x, char *p, int le)
413+
{ return _PyFloat_Pack8(x, (unsigned char*)p, le); }
414+
415+
PYCAPI_COMPAT_STATIC_INLINE(double)
416+
PyFloat_Unpack4(const char *p, int le)
417+
{ return _PyFloat_Unpack4((const unsigned char *)p, le); }
418+
419+
PYCAPI_COMPAT_STATIC_INLINE(double)
420+
PyFloat_Unpack8(const char *p, int le)
421+
{ return _PyFloat_Unpack8((const unsigned char *)p, le); }
422+
#endif
423+
424+
394425
// Py_UNUSED() was added to Python 3.4.0b2.
395426
#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED)
396427
# if defined(__GNUC__) || defined(__clang__)

tests/test_pythoncapi_compat.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ def _check_refleak(test_func, verbose):
9999

100100

101101
def run_tests(module_name):
102-
title = "Test %s" % module_name
103-
if "cppext" in title:
104-
title += " (C++)"
102+
if "cppext" in module_name:
103+
lang = "C++"
105104
else:
106-
title += " (C)"
105+
lang = "C"
106+
title = "Test %s (%s)" % (module_name, lang)
107107
display_title(title)
108108

109109
testmod = import_tests(module_name)
@@ -127,7 +127,7 @@ def test_func():
127127

128128
ver = sys.version_info
129129
build = 'debug' if hasattr(sys, 'gettotalrefcount') else 'release'
130-
msg = "%s tests succeeded!" % len(tests)
130+
msg = "%s %s tests succeeded!" % (len(tests), lang)
131131
if hasattr(sys, 'implementation'):
132132
python_impl = sys.implementation.name
133133
if python_impl == 'cpython':

tests/test_pythoncapi_compat_cext.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,62 @@ test_module(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
406406
}
407407

408408

409+
#ifndef PYPY_VERSION
410+
static PyObject *
411+
test_float_pack(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
412+
{
413+
const int big_endian = 0;
414+
const int little_endian = 1;
415+
char data[8];
416+
const double d = 1.5;
417+
418+
#if PY_VERSION_HEX >= 0x030600B1
419+
# define HAVE_FLOAT_PACK2
420+
#endif
421+
422+
// Test Pack (big endian)
423+
#ifdef HAVE_FLOAT_PACK2
424+
assert(PyFloat_Pack2(d, data, big_endian) == 0);
425+
assert(memcmp(data, ">\x00", 2) == 0);
426+
#endif
427+
428+
assert(PyFloat_Pack4(d, data, big_endian) == 0);
429+
assert(memcmp(data, "?\xc0\x00\x00", 2) == 0);
430+
431+
assert(PyFloat_Pack8(d, data, big_endian) == 0);
432+
assert(memcmp(data, "?\xf8\x00\x00\x00\x00\x00\x00", 2) == 0);
433+
434+
// Test Pack (little endian)
435+
#ifdef HAVE_FLOAT_PACK2
436+
assert(PyFloat_Pack2(d, data, little_endian) == 0);
437+
assert(memcmp(data, "\x00>", 2) == 0);
438+
#endif
439+
440+
assert(PyFloat_Pack4(d, data, little_endian) == 0);
441+
assert(memcmp(data, "\x00\x00\xc0?", 2) == 0);
442+
443+
assert(PyFloat_Pack8(d, data, little_endian) == 0);
444+
assert(memcmp(data, "\x00\x00\x00\x00\x00\x00\xf8?", 2) == 0);
445+
446+
// Test Unpack (big endian)
447+
#ifdef HAVE_FLOAT_PACK2
448+
assert(PyFloat_Unpack2(">\x00", big_endian) == d);
449+
#endif
450+
assert(PyFloat_Unpack4("?\xc0\x00\x00", big_endian) == d);
451+
assert(PyFloat_Unpack8("?\xf8\x00\x00\x00\x00\x00\x00", big_endian) == d);
452+
453+
// Test Unpack (little endian)
454+
#ifdef HAVE_FLOAT_PACK2
455+
assert(PyFloat_Unpack2("\x00>", little_endian) == d);
456+
#endif
457+
assert(PyFloat_Unpack4("\x00\x00\xc0?", little_endian) == d);
458+
assert(PyFloat_Unpack8("\x00\x00\x00\x00\x00\x00\xf8?", little_endian) == d);
459+
460+
Py_RETURN_NONE;
461+
}
462+
#endif // !PYPY_VERSION
463+
464+
409465
static struct PyMethodDef methods[] = {
410466
{"test_object", test_object, METH_NOARGS, NULL},
411467
{"test_py_is", test_py_is, METH_NOARGS, NULL},
@@ -418,6 +474,9 @@ static struct PyMethodDef methods[] = {
418474
{"test_calls", test_calls, METH_NOARGS, NULL},
419475
{"test_gc", test_gc, METH_NOARGS, NULL},
420476
{"test_module", test_module, METH_NOARGS, NULL},
477+
#ifndef PYPY_VERSION
478+
{"test_float_pack", test_float_pack, METH_NOARGS, NULL},
479+
#endif
421480
{NULL, NULL, 0, NULL}
422481
};
423482

0 commit comments

Comments
 (0)