From d21f29eb1cc08ffc54af0edf5298a8019fb9566d Mon Sep 17 00:00:00 2001 From: MatrixEditor <58256046+MatrixEditor@users.noreply.github.com> Date: Sun, 29 Sep 2024 08:44:46 +0200 Subject: [PATCH] New feature: global type handlers for capi structs --- + Fixed refcount issue in _CpPack_EvalLength and _CpUnpack_EvalLength + Added Py_TPFLAGS_BASETYPE to all C types + Struct model classes can now be used in pack() and unpack() calls + Added missing operations to CpBinaryExpr and CpUnaryExpr --- docs/sphinx/source/index.rst | 35 ++-- .../capi/objects/atoms/fieldatom.rst | 108 ----------- .../reference/capi/objects/atoms/index.rst | 11 +- .../reference/capi/objects/fieldobj.rst | 59 ------ .../source/reference/capi/objects/objimpl.rst | 1 - src/capi.dat | 2 + src/caterpillar/_C.pyi | 8 +- .../include/caterpillar/atoms/const.h | 11 +- .../include/caterpillar/caterpillarapi.h | 4 + src/caterpillar/include/caterpillar/module.h | 3 + src/ccaterpillar/atomimpl/builtins/repeated.c | 5 + src/ccaterpillar/atomimpl/float.c | 2 +- src/ccaterpillar/atomimpl/int.c | 2 +- src/ccaterpillar/atomimpl/lazy.c | 2 +- src/ccaterpillar/atomimpl/pad.c | 2 +- src/ccaterpillar/atomimpl/string.c | 2 +- src/ccaterpillar/atomimpl/varint.c | 1 - src/ccaterpillar/atomobj.c | 7 + src/ccaterpillar/caterpillarapi.c | 4 +- src/ccaterpillar/context.c | 181 ++++++++++++++---- src/ccaterpillar/module.c | 52 ++++- src/ccaterpillar/option.c | 2 +- src/ccaterpillar/parsing_pack.c | 3 - src/ccaterpillar/parsing_unpack.c | 2 +- src/ccaterpillar/struct.c | 101 +++++++++- test/_C/test_parsing.py | 2 +- test/_C/test_struct.py | 13 +- 27 files changed, 371 insertions(+), 254 deletions(-) delete mode 100644 docs/sphinx/source/reference/capi/objects/atoms/fieldatom.rst delete mode 100644 docs/sphinx/source/reference/capi/objects/fieldobj.rst diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst index fb8595d..97b0893 100644 --- a/docs/sphinx/source/index.rst +++ b/docs/sphinx/source/index.rst @@ -39,10 +39,11 @@ to write complex structures in a compact and readable manner. @struct class Format: - magic: const(b"Foo", octetstring(3)) # constant values + magic: b"Foo" # constant values name: cstring(...) # C-String without a fixed length value: u16 # little endian encoding entries: cstring(...)[be + u32::] # arrays with big-endian prefixed length + value2: bool # python type mapping configurable .. admonition:: Hold up, wait a minute! @@ -52,27 +53,39 @@ to write complex structures in a compact and readable manner. Working with defined classes is as straightforward as working with normal classes. *All constant values are created automatically!* + >>> obj = Format(name="Hello, World!", value=10, entries=["Bar", "Baz"]) >>> print(obj) Format(magic=b'Foo', name='Hello, World!', value=10, entries=['Bar', 'Baz']) Packing and unpacking have never been easier: ->>> pack(obj) -b'FooHello, World!\x00\n\x00\x00\x00\x00\x02Bar\x00Baz\x00' ->>> unpack(Format, _) -Format(magic=b'Foo', name='Hello, World!', value=10, entries=['Bar', 'Baz']) -- What about documentation? +.. tab-set:: + + .. tab-item:: Python - There are specialized options created only for documentation purposes, so you don't - have to worry about documenting fields. Just apply the documentation as usual. + >>> pack(obj) + b'FooHello, World!\x00\n\x00\x00\x00\x00\x02Bar\x00Baz\x00' + >>> unpack(Format, _) + Format(magic=b'Foo', name='Hello, World!', value=10, entries=['Bar', 'Baz']) + + .. tab-item:: Caterpillar C + + >>> pack(obj, Format) + b'FooHello, World!\x00\n\x00\x00\x00\x00\x02Bar\x00Baz\x00\x01' + >>> unpack(_, Format) + + +- What about documentation? + There are specialized options created only for documentation purposes, so you don't + have to worry about documenting fields. Just apply the documentation as usual. - You want to optimize memory space? + No problem! It is possible to shrink the memory space occupied by unpacked objects up + to 4 times. More information are provided when discussing available configuration + :ref:`options`. - No problem! It is possible to shrink the memory space occupied by unpacked objects up - to 4 times. More information are provided when discussing available configuration - :ref:`options`. Where to start? --------------- diff --git a/docs/sphinx/source/reference/capi/objects/atoms/fieldatom.rst b/docs/sphinx/source/reference/capi/objects/atoms/fieldatom.rst deleted file mode 100644 index 1f1fdfe..0000000 --- a/docs/sphinx/source/reference/capi/objects/atoms/fieldatom.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. _reference-capi_fieldatom: - -****************** -Field Atom Objects -****************** - -Python Field Atoms ------------------- - -.. c:var:: PyTypeObject CpFieldAtom_Type - - The type object for the :c:type:`CpFieldAtomObject` class. - - -.. c:type:: CpFieldAtomObject - - C implementation of the Python equivalent (:class:`~caterpillar.fields.FieldMixin`). - - A simple mixin to support operators used to create `Field` instances. - - -.. c:function:: int CpFieldAtom_CheckExact(PyObject *op) - - Checks if the given object is an :c:type:`CpFieldAtomObject`. - - -.. c:function:: int CpFieldAtom_Check(PyObject *op) - - Checks if the given object is instance of an :c:type:`CpFieldAtomObject` - - -.. c:macro:: CpFieldAtom_HEAD - - Inspired by :code:`PyObject_HEAD`, places the :c:type:`CpFieldAtomObject` at the - beginning of the object's memory block. For instance the following code: - - .. code-block:: c - - struct FooAtom { - CpFieldAtom_HEAD - - // other fields - }; - - extern PyTypeObject FooAtom_Type; - - The defined class is now a subclass of :c:type:`CpFieldAtomObject` and inherits - all methods. Make sure you don't forget to initialize the type correctly in your - module code: - - .. code-block:: c - - // ... - FooAtom_Type.tp_base = &CpFieldAtom_Type; - CpModule_SetupType(&FooAtom_Type); - // ... - - -C Field Atoms -------------- - -.. c:var:: PyTypeObject CpFieldCAtom_Type - - The type object for the :c:type:`CpFieldCAtomObject` class. - - -.. c:type:: CpFieldCAtomObject - - Field atom for C fields. Inherits from :c:type:`CpCAtomObject`. - - -.. c:function:: int CpFieldCAtom_CheckExact(PyObject *op) - - Checks if the given object is an :c:type:`CpFieldCAtomObject`. - - -.. c:function:: int CpFieldCAtom_Check(PyObject *op) - - Checks if the given object is instance of an :c:type:`CpFieldCAtomObject` - - -.. c:macro:: CpFieldCAtom_HEAD - - Inspired by :code:`PyObject_HEAD`, places the :c:type:`CpFieldCAtomObject` at the - beginning of the object's memory block. - - -.. c:macro:: CpFieldCAtom_CATOM(self) - - Returns the :c:type:`CpCAtomObject` from the given object which must be a - :c:type:`CpFieldCAtomObject`. This macro can be used to set the packing and - unpacking implementations: - - .. code-block:: c - - static PyObject* - fooatom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) - { - FooAtom *self = (FooAtom *)type->tp_alloc(type, 0); - if (self != NULL) { - // initialize the fields - CpFieldCAtom_CATOM(self).ob_pack = (packfunc)fooatom_pack; - CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)fooatom_unpack; - // ... - } - return (PyObject*)self; - } - diff --git a/docs/sphinx/source/reference/capi/objects/atoms/index.rst b/docs/sphinx/source/reference/capi/objects/atoms/index.rst index d2d1d36..38fe60b 100644 --- a/docs/sphinx/source/reference/capi/objects/atoms/index.rst +++ b/docs/sphinx/source/reference/capi/objects/atoms/index.rst @@ -7,9 +7,8 @@ Atom Objects .. toctree:: :maxdepth: 1 - fieldatom.rst - intatom.rst - charatom.rst - floatatom.rst - boolatom.rst - paddingatom.rst + int + char + float + bool + padding diff --git a/docs/sphinx/source/reference/capi/objects/fieldobj.rst b/docs/sphinx/source/reference/capi/objects/fieldobj.rst deleted file mode 100644 index a4b8df9..0000000 --- a/docs/sphinx/source/reference/capi/objects/fieldobj.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. _reference-capi_field: - -************* -Field Objects -************* - -.. c:var:: PyTypeObject CpField_Type - - The type object for the :c:type:`CpFieldObject` class. - - -.. c:type:: CpFieldObject - - C implementation of the Python equivalent. Represents a field in a data structure. - - -.. c:function:: int CpField_CheckExact(PyObject *op) - - Checks if the given object is an :c:type:`CpFieldObject`. - - -.. c:function:: int CpField_Check(PyObject *op) - - Checks if the given object is instance of an :c:type:`CpFieldObject` - - -.. c:function:: CpFieldObject *CpField_New(PyObject *atom) - - Creates a new field and returns it. Returns *NULL* if an error occurs. - - -.. c:function:: int CpField_HasCondition(CpFieldObject *field) - - Returns whether the field has a condition. This function always succeeds. - - -.. c:function:: int CpField_IsEnabled(CpFieldObject *field, PyObject *context) - - Returns whether the field is enabled. The context object must conform to - the context protocol.Returns :code:`1` if the field is enabled, :code:`0` - otherwise and returns :code:`-1` if an error occurs. - - -.. c:function:: Py_ssize_t CpField_GetOffset(CpFieldObject *field, PyObject *context) - - Returns the offset of the field. The context object must conform to the - context protocol. Returns :code:`-1` if an error occurs. - - -.. c:function:: PyObject *CpField_EvalSwitch(CpFieldObject *field, PyObject *value, PyObject *context) - - Evaluates a switch field (if configured to do so). The context object must conform to the - context protocol. Returns *NULL* if an error occurs. - - -.. c:function:: PyObject *CpField_GetLength(CpFieldObject *field, PyObject *context) - - Returns the length of the field. The context object must conform to the - context protocol. Returns *NULL* if an error occurs. \ No newline at end of file diff --git a/docs/sphinx/source/reference/capi/objects/objimpl.rst b/docs/sphinx/source/reference/capi/objects/objimpl.rst index f9edabf..45d1de0 100644 --- a/docs/sphinx/source/reference/capi/objects/objimpl.rst +++ b/docs/sphinx/source/reference/capi/objects/objimpl.rst @@ -14,6 +14,5 @@ Concrete Objects Layer contextpath state layer - fieldobj struct atoms/index.rst \ No newline at end of file diff --git a/src/capi.dat b/src/capi.dat index fc5c63e..6e213c9 100644 --- a/src/capi.dat +++ b/src/capi.dat @@ -114,6 +114,8 @@ func:54:CpUnaryExpr_New:CpUnaryExprObject*:+1 func:55:CpBinaryExpr_New:CpBinaryExprObject*:+1 func:56:CpContextPath_New:CpContextPathObject*:+1 func:57:CpContextPath_FromString:CpContextPathObject*:+1 +func:58:CpTypeMap_Lookup:PyObject*:+1 +func:59:CpTypeMap_Register:int:null # func:58:CpField_New:CpFieldObject*:null # func:59:CpField_HasCondition:int:null # func:60:CpField_IsEnabled:int:null diff --git a/src/caterpillar/_C.pyi b/src/caterpillar/_C.pyi index 9e6703a..813ee8c 100644 --- a/src/caterpillar/_C.pyi +++ b/src/caterpillar/_C.pyi @@ -194,7 +194,9 @@ class fieldinfo: def __init__(self, field: atom, excluded: bool = ...) -> None: ... class lengthinfo: - def __init__(self, *args, **kwargs) -> None: ... + length: int + greedy: bool + def __init__(self, length: int = ..., greedy: bool = ...) -> None: ... class Struct(builtinatom): members: dict[str, fieldinfo] @@ -279,10 +281,6 @@ class atoffset(builtinatom): def get_offset(self, layer: layer) -> int: ... def __set_byteorder__(self, byteorder: Endian) -> atoffset: ... -class objlayer(layer): - obj: Context - def __init__(self, *args, **kwargs) -> None: ... - def __context_getattr__(self, *args, **kwargs): ... class repeated(builtinatom): atom: Any diff --git a/src/caterpillar/include/caterpillar/atoms/const.h b/src/caterpillar/include/caterpillar/atoms/const.h index e311d88..f7afc70 100644 --- a/src/caterpillar/include/caterpillar/atoms/const.h +++ b/src/caterpillar/include/caterpillar/atoms/const.h @@ -23,11 +23,18 @@ struct _constatomobj { CpBuiltinAtom_HEAD - PyObject *m_value; - PyObject *m_atom; + PyObject* m_value; + PyObject* m_atom; }; #define CpConstAtom_CheckExact(op) Py_IS_TYPE((op), &CpConstAtom_Type) #define CpConstAtom_Check(op) PyObject_TypeCheck((op), &CpConstAtom_Type) +static inline CpConstAtomObject* +CpConstAtom_New(PyObject* value, PyObject* atom) +{ + return (CpConstAtomObject*)CpObject_Create( + &CpConstAtom_Type, "OO", atom, value); +} + #endif \ No newline at end of file diff --git a/src/caterpillar/include/caterpillar/caterpillarapi.h b/src/caterpillar/include/caterpillar/caterpillarapi.h index 38ef90c..916e0e0 100644 --- a/src/caterpillar/include/caterpillar/caterpillarapi.h +++ b/src/caterpillar/include/caterpillar/caterpillarapi.h @@ -191,6 +191,8 @@ CpUnaryExprObject* CpUnaryExpr_New(int op, PyObject* value); CpBinaryExprObject* CpBinaryExpr_New(int op, PyObject* left, PyObject* right); CpContextPathObject* CpContextPath_New(PyObject* path); CpContextPathObject* CpContextPath_FromString(const char* path); +PyObject* CpTypeMap_Lookup(PyObject* annotation, _modulestate* mod); +int CpTypeMap_Register(PyObject* annotation, PyObject* handler, _modulestate* mod); PyObject* CpTypeOf(PyObject* op); PyObject* CpTypeOf_Common(PyObject* op); int CpPack(PyObject* op, PyObject* atom, PyObject* io, PyObject* globals); @@ -332,6 +334,8 @@ caterpillar_api.py #define CpBinaryExpr_New (*((CpBinaryExprObject* (*)(int op, PyObject* left, PyObject* right)))Cp_API[55]) #define CpContextPath_New (*((CpContextPathObject* (*)(PyObject* path)))Cp_API[56]) #define CpContextPath_FromString (*((CpContextPathObject* (*)(const char* path)))Cp_API[57]) +#define CpTypeMap_Lookup (*((PyObject* (*)(PyObject* annotation, _modulestate* mod)))Cp_API[58]) +#define CpTypeMap_Register (*((int (*)(PyObject* annotation, PyObject* handler, _modulestate* mod)))Cp_API[59]) #define CpTypeOf (*((PyObject* (*)(PyObject* op)))Cp_API[64]) #define CpTypeOf_Common (*((PyObject* (*)(PyObject* op)))Cp_API[66]) #define CpPack (*((int (*)(PyObject* op, PyObject* atom, PyObject* io, PyObject* globals)))Cp_API[67]) diff --git a/src/caterpillar/include/caterpillar/module.h b/src/caterpillar/include/caterpillar/module.h index bdb5b75..70bb7b9 100644 --- a/src/caterpillar/include/caterpillar/module.h +++ b/src/caterpillar/include/caterpillar/module.h @@ -99,6 +99,9 @@ struct _modulestate // cached objects PyObject* cp_bytes__true; PyObject* cp_bytes__false; + + // type handler map + PyObject* cp_typehandler_map; }; /** diff --git a/src/ccaterpillar/atomimpl/builtins/repeated.c b/src/ccaterpillar/atomimpl/builtins/repeated.c index 21c5ffd..bf09cdc 100644 --- a/src/ccaterpillar/atomimpl/builtins/repeated.c +++ b/src/ccaterpillar/atomimpl/builtins/repeated.c @@ -56,6 +56,8 @@ cp_repeatedatomobj_new(PyTypeObject* type, PyObject* args, PyObject* kw) CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL; CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_repeatedatomobj_size; CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_repeatedatomobj_type; + self->m_length = NULL; + self->m_atom = NULL; return (PyObject*)self; } @@ -229,6 +231,9 @@ CpRepeatedAtom_Unpack(CpRepeatedAtomObject* self, CpLayerObject* layer) _modulestate* mod = layer->m_state->mod; CpLengthInfoObject* lengthinfo = (CpLengthInfoObject*)CpObject_CreateNoArgs(&CpLengthInfo_Type); + if (!lengthinfo) { + return NULL; + } bool unpack_many_attr = PyObject_HasAttr(self->m_atom, mod->str___unpack_many__); diff --git a/src/ccaterpillar/atomimpl/float.c b/src/ccaterpillar/atomimpl/float.c index 7f8d8cd..575052c 100644 --- a/src/ccaterpillar/atomimpl/float.c +++ b/src/ccaterpillar/atomimpl/float.c @@ -199,7 +199,7 @@ PyTypeObject CpFloatAtom_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpFloatAtom_NAME), .tp_basicsize = sizeof(CpFloatAtomObject), .tp_dealloc = (destructor)cp_floatatom_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = NULL, .tp_members = CpFloatAtom_Members, .tp_methods = CpFloatAtom_Methods, diff --git a/src/ccaterpillar/atomimpl/int.c b/src/ccaterpillar/atomimpl/int.c index 461fb4b..7714cd2 100644 --- a/src/ccaterpillar/atomimpl/int.c +++ b/src/ccaterpillar/atomimpl/int.c @@ -230,7 +230,7 @@ static PyMethodDef CpIntAtom_Methods[] = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpIntAtom_NAME), .tp_basicsize = sizeof(CpIntAtomObject), .tp_dealloc = (destructor)cp_intatom_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = cp_intatom__doc, .tp_members = CpIntAtom_Members, .tp_new = (newfunc)cp_intatom_new, diff --git a/src/ccaterpillar/atomimpl/lazy.c b/src/ccaterpillar/atomimpl/lazy.c index 1eb0dee..564f27f 100644 --- a/src/ccaterpillar/atomimpl/lazy.c +++ b/src/ccaterpillar/atomimpl/lazy.c @@ -153,7 +153,7 @@ PyTypeObject CpLazyAtom_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpLazyAtom_NAME), .tp_basicsize = sizeof(CpLazyAtomObject), .tp_dealloc = (destructor)cp_lazyatom_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = NULL, .tp_init = (initproc)cp_lazyatom_init, .tp_new = (newfunc)cp_lazyatom_new, diff --git a/src/ccaterpillar/atomimpl/pad.c b/src/ccaterpillar/atomimpl/pad.c index a9fa402..6793354 100644 --- a/src/ccaterpillar/atomimpl/pad.c +++ b/src/ccaterpillar/atomimpl/pad.c @@ -183,7 +183,7 @@ PyTypeObject CpPaddingAtom_Type = { .tp_basicsize = sizeof(CpPaddingAtomObject), .tp_dealloc = (destructor)cp_paddingatom_dealloc, .tp_repr = (reprfunc)cp_paddingatom_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = NULL, .tp_init = (initproc)cp_paddingatom_init, .tp_new = (newfunc)cp_paddingatom_new, diff --git a/src/ccaterpillar/atomimpl/string.c b/src/ccaterpillar/atomimpl/string.c index 7a98a94..56589c5 100644 --- a/src/ccaterpillar/atomimpl/string.c +++ b/src/ccaterpillar/atomimpl/string.c @@ -140,7 +140,7 @@ PyTypeObject CpStringAtom_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpStringAtom_NAME), .tp_basicsize = sizeof(CpStringAtomObject), .tp_dealloc = (destructor)cp_stringatom_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = NULL, .tp_members = CpStringAtom_Members, .tp_init = (initproc)cp_stringatom_init, diff --git a/src/ccaterpillar/atomimpl/varint.c b/src/ccaterpillar/atomimpl/varint.c index c8f05a2..6f47fd1 100644 --- a/src/ccaterpillar/atomimpl/varint.c +++ b/src/ccaterpillar/atomimpl/varint.c @@ -115,7 +115,6 @@ cp_varintatom_set_byteorder(CpVarIntAtomObject* self, PyObject* new_varint = (PyObject*)CpVarIntAtom_New(little_endian, self->_m_lsb); - Py_DECREF(byteorder); return new_varint; } diff --git a/src/ccaterpillar/atomobj.c b/src/ccaterpillar/atomobj.c index a563fa5..3948553 100644 --- a/src/ccaterpillar/atomobj.c +++ b/src/ccaterpillar/atomobj.c @@ -3,6 +3,7 @@ #include "caterpillar/atomobj.h" #include "caterpillar/macros.h" +#include #include static PyObject* @@ -361,6 +362,12 @@ cp_lengthinfo_repr(CpLengthInfoObject* self) return PyUnicode_FromFormat("", self->m_length); } +static PyMemberDef CpLengthInfo_Members[] = { + { "length", T_PYSSIZET, offsetof(CpLengthInfoObject, m_length), 0, NULL }, + { "greedy", T_BOOL, offsetof(CpLengthInfoObject, m_greedy), 0, NULL }, + { NULL } /* Sentinel */ +}; + PyTypeObject CpLengthInfo_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpLengthInfo_NAME), .tp_basicsize = sizeof(CpLengthInfoObject), diff --git a/src/ccaterpillar/caterpillarapi.c b/src/ccaterpillar/caterpillarapi.c index b3cae8a..e7b5ef5 100644 --- a/src/ccaterpillar/caterpillarapi.c +++ b/src/ccaterpillar/caterpillarapi.c @@ -61,8 +61,8 @@ void *Cp_API[] = { (void *) &CpBinaryExpr_New, (void *) &CpContextPath_New, (void *) &CpContextPath_FromString, - NULL, - NULL, + (void *) &CpTypeMap_Lookup, + (void *) &CpTypeMap_Register, NULL, NULL, NULL, diff --git a/src/ccaterpillar/context.c b/src/ccaterpillar/context.c index 214f38b..f23dd8d 100644 --- a/src/ccaterpillar/context.c +++ b/src/ccaterpillar/context.c @@ -262,6 +262,49 @@ cp_unaryexpr__call__(CpUnaryExprObject* self, PyObject* args, PyObject* kw) return result; } +static PyObject* +cp_unaryexpr_as_number_neg(PyObject* self) +{ + return (PyObject*)CpUnaryExpr_New(CpUnaryExpr_OpNeg, self); +} + +static PyObject* +cp_unaryexpr_as_number_pos(PyObject* self) +{ + return (PyObject*)CpUnaryExpr_New(CpUnaryExpr_OpPos, self); +} + +static PyObject* +cp_unaryexpr_as_number_not(PyObject* self) +{ + return (PyObject*)CpUnaryExpr_New(CpUnaryExpr_OpNot, self); +} + +/* operations */ +#define _CpUnaryExpr_BinaryNumberMethod(name, op) \ + static PyObject* cp_unaryexpr_as_number_##name(PyObject* self, \ + PyObject* other) \ + { \ + return (PyObject*)CpBinaryExpr_New(op, self, other); \ + } + +_CpUnaryExpr_BinaryNumberMethod(add, CpBinaryExpr_OpAdd); +_CpUnaryExpr_BinaryNumberMethod(sub, CpBinaryExpr_OpSub); +_CpUnaryExpr_BinaryNumberMethod(mul, CpBinaryExpr_OpMul); +_CpUnaryExpr_BinaryNumberMethod(div, CpBinaryExpr_OpTrueDiv); +_CpUnaryExpr_BinaryNumberMethod(truediv, CpBinaryExpr_OpTrueDiv); +_CpUnaryExpr_BinaryNumberMethod(floordiv, CpBinaryExpr_OpFloorDiv); +_CpUnaryExpr_BinaryNumberMethod(mod, CpBinaryExpr_OpMod); +_CpUnaryExpr_BinaryNumberMethod(lshift, CpBinaryExpr_OpLShift); +_CpUnaryExpr_BinaryNumberMethod(rshift, CpBinaryExpr_OpRShift); +_CpUnaryExpr_BinaryNumberMethod(and, CpBinaryExpr_OpBitAnd); +_CpUnaryExpr_BinaryNumberMethod(xor, CpBinaryExpr_OpBitXor); +_CpUnaryExpr_BinaryNumberMethod(or, CpBinaryExpr_OpBitOr); +_CpUnaryExpr_BinaryNumberMethod(pow, CpBinaryExpr_OpPow); +_CpUnaryExpr_BinaryNumberMethod(matmul, CpBinaryExpr_OpMatMul); + +#undef _CpUnaryExpr_BinaryNumberMethod + /*CpAPI*/ CpUnaryExprObject* CpUnaryExpr_New(int op, PyObject* value) @@ -285,6 +328,27 @@ static PyMemberDef CpUnaryExpr_Members[] = { { NULL } /* Sentinel */ }; +static PyNumberMethods CpBinaryExpr_NumberMethods = { + // unary + .nb_negative = (unaryfunc)cp_unaryexpr_as_number_neg, + .nb_positive = (unaryfunc)cp_unaryexpr_as_number_pos, + .nb_invert = (unaryfunc)cp_unaryexpr_as_number_not, + // binary + .nb_add = (binaryfunc)cp_unaryexpr_as_number_add, + .nb_subtract = (binaryfunc)cp_unaryexpr_as_number_sub, + .nb_multiply = (binaryfunc)cp_unaryexpr_as_number_mul, + .nb_true_divide = (binaryfunc)cp_unaryexpr_as_number_truediv, + .nb_floor_divide = (binaryfunc)cp_unaryexpr_as_number_floordiv, + .nb_remainder = (binaryfunc)cp_unaryexpr_as_number_mod, + .nb_power = (ternaryfunc)cp_unaryexpr_as_number_pow, + .nb_lshift = (binaryfunc)cp_unaryexpr_as_number_lshift, + .nb_rshift = (binaryfunc)cp_unaryexpr_as_number_rshift, + .nb_and = (binaryfunc)cp_unaryexpr_as_number_and, + .nb_xor = (binaryfunc)cp_unaryexpr_as_number_xor, + .nb_or = (binaryfunc)cp_unaryexpr_as_number_or, + .nb_matrix_multiply = (binaryfunc)cp_unaryexpr_as_number_matmul, +}; + PyTypeObject CpUnaryExpr_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(UnaryExpr), .tp_basicsize = sizeof(CpUnaryExprObject), @@ -297,6 +361,7 @@ PyTypeObject CpUnaryExpr_Type = { .tp_members = CpUnaryExpr_Members, .tp_init = (initproc)cp_unaryexpr_init, .tp_new = (newfunc)cp_unaryexpr_new, + .tp_as_number = &CpBinaryExpr_NumberMethods, }; //------------------------------------------------------------------------------ @@ -542,30 +607,48 @@ cp_binaryexpr__call__(CpBinaryExprObject* self, PyObject* args, PyObject* kw) return result; } +static PyObject* +cp_binaryexpr_as_number_neg(PyObject* self) +{ + return (PyObject*)CpUnaryExpr_New(CpUnaryExpr_OpNeg, self); +} + +static PyObject* +cp_binaryexpr_as_number_pos(PyObject* self) +{ + return (PyObject*)CpUnaryExpr_New(CpUnaryExpr_OpPos, self); +} + +static PyObject* +cp_binaryexpr_as_number_not(PyObject* self) +{ + return (PyObject*)CpUnaryExpr_New(CpUnaryExpr_OpNot, self); +} + /* operations */ -#define _CpBinaryExpr_BinaryNumberMethod(name, op) \ - static PyObject* cp_binaryexpr_as_number_##name(PyObject* self, \ - PyObject* other) \ +#define _CpUnaryExpr_BinaryNumberMethod(name, op) \ + static PyObject* cp_binaryexpr_as_number_##name(PyObject* self, \ + PyObject* other) \ { \ return (PyObject*)CpBinaryExpr_New(op, self, other); \ } -_CpBinaryExpr_BinaryNumberMethod(add, CpBinaryExpr_OpAdd); -_CpBinaryExpr_BinaryNumberMethod(sub, CpBinaryExpr_OpSub); -_CpBinaryExpr_BinaryNumberMethod(mul, CpBinaryExpr_OpMul); -_CpBinaryExpr_BinaryNumberMethod(div, CpBinaryExpr_OpTrueDiv); -_CpBinaryExpr_BinaryNumberMethod(truediv, CpBinaryExpr_OpTrueDiv); -_CpBinaryExpr_BinaryNumberMethod(floordiv, CpBinaryExpr_OpFloorDiv); -_CpBinaryExpr_BinaryNumberMethod(mod, CpBinaryExpr_OpMod); -_CpBinaryExpr_BinaryNumberMethod(lshift, CpBinaryExpr_OpLShift); -_CpBinaryExpr_BinaryNumberMethod(rshift, CpBinaryExpr_OpRShift); -_CpBinaryExpr_BinaryNumberMethod(and, CpBinaryExpr_OpBitAnd); -_CpBinaryExpr_BinaryNumberMethod(xor, CpBinaryExpr_OpBitXor); -_CpBinaryExpr_BinaryNumberMethod(or, CpBinaryExpr_OpBitOr); -_CpBinaryExpr_BinaryNumberMethod(pow, CpBinaryExpr_OpPow); -_CpBinaryExpr_BinaryNumberMethod(matmul, CpBinaryExpr_OpMatMul); - -#undef _CpContextPath_BinaryNumberMethod +_CpUnaryExpr_BinaryNumberMethod(add, CpBinaryExpr_OpAdd); +_CpUnaryExpr_BinaryNumberMethod(sub, CpBinaryExpr_OpSub); +_CpUnaryExpr_BinaryNumberMethod(mul, CpBinaryExpr_OpMul); +_CpUnaryExpr_BinaryNumberMethod(div, CpBinaryExpr_OpTrueDiv); +_CpUnaryExpr_BinaryNumberMethod(truediv, CpBinaryExpr_OpTrueDiv); +_CpUnaryExpr_BinaryNumberMethod(floordiv, CpBinaryExpr_OpFloorDiv); +_CpUnaryExpr_BinaryNumberMethod(mod, CpBinaryExpr_OpMod); +_CpUnaryExpr_BinaryNumberMethod(lshift, CpBinaryExpr_OpLShift); +_CpUnaryExpr_BinaryNumberMethod(rshift, CpBinaryExpr_OpRShift); +_CpUnaryExpr_BinaryNumberMethod(and, CpBinaryExpr_OpBitAnd); +_CpUnaryExpr_BinaryNumberMethod(xor, CpBinaryExpr_OpBitXor); +_CpUnaryExpr_BinaryNumberMethod(or, CpBinaryExpr_OpBitOr); +_CpUnaryExpr_BinaryNumberMethod(pow, CpBinaryExpr_OpPow); +_CpUnaryExpr_BinaryNumberMethod(matmul, CpBinaryExpr_OpMatMul); + +#undef _CpUnaryExpr_BinaryNumberMethod /*CpAPI*/ CpBinaryExprObject* @@ -598,6 +681,27 @@ static PyMemberDef CpBinaryExpr_Members[] = { { NULL } /* Sentinel */ }; +static PyNumberMethods CpUnaryExpr_NumberMethods = { + // unary + .nb_negative = (unaryfunc)cp_binaryexpr_as_number_neg, + .nb_positive = (unaryfunc)cp_binaryexpr_as_number_pos, + .nb_invert = (unaryfunc)cp_binaryexpr_as_number_not, + // binary + .nb_add = (binaryfunc)cp_binaryexpr_as_number_add, + .nb_subtract = (binaryfunc)cp_binaryexpr_as_number_sub, + .nb_multiply = (binaryfunc)cp_binaryexpr_as_number_mul, + .nb_true_divide = (binaryfunc)cp_binaryexpr_as_number_truediv, + .nb_floor_divide = (binaryfunc)cp_binaryexpr_as_number_floordiv, + .nb_remainder = (binaryfunc)cp_binaryexpr_as_number_mod, + .nb_power = (ternaryfunc)cp_binaryexpr_as_number_pow, + .nb_lshift = (binaryfunc)cp_binaryexpr_as_number_lshift, + .nb_rshift = (binaryfunc)cp_binaryexpr_as_number_rshift, + .nb_and = (binaryfunc)cp_binaryexpr_as_number_and, + .nb_xor = (binaryfunc)cp_binaryexpr_as_number_xor, + .nb_or = (binaryfunc)cp_binaryexpr_as_number_or, + .nb_matrix_multiply = (binaryfunc)cp_binaryexpr_as_number_matmul, +}; + PyTypeObject CpBinaryExpr_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(BinaryExpr), .tp_basicsize = sizeof(CpBinaryExprObject), @@ -609,6 +713,7 @@ PyTypeObject CpBinaryExpr_Type = { .tp_members = CpBinaryExpr_Members, .tp_init = (initproc)cp_binaryexpr_init, .tp_new = (newfunc)cp_binaryexpr_new, + .tp_as_number = &CpUnaryExpr_NumberMethods, }; //------------------------------------------------------------------------------ @@ -655,7 +760,13 @@ cp_contextpath_init(CpContextPathObject* self, PyObject* args, PyObject* kw) static PyObject* cp_contextpath_repr(CpContextPathObject* self) { - return PyUnicode_FromFormat("CpPath(%R)", self->m_path); + return PyUnicode_FromFormat("", self->m_path); +} + +static PyObject* +cp_contextpath_str(CpContextPathObject* self) +{ + return Py_XNewRef(self->m_path); } static Py_hash_t @@ -784,7 +895,6 @@ CpContextPath_FromString(const char* path) return (CpContextPathObject*)CpObject_Create(&CpContextPath_Type, "s", path); } - /* docs */ PyDoc_STRVAR(cp_contextpath__doc__, "\ ContextPath(path)\n\ @@ -832,18 +942,19 @@ static PyNumberMethods CpContextPath_NumberMethods = { /* type */ PyTypeObject CpContextPath_Type = { PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(ContextPath), - .tp_basicsize =sizeof(CpContextPathObject), - .tp_dealloc =(destructor)cp_contextpath_dealloc, - .tp_getattr =(getattrfunc)cp_contextpath__getattr__, - .tp_repr =(reprfunc)cp_contextpath_repr, - .tp_as_number =&CpContextPath_NumberMethods, - .tp_hash =(hashfunc)cp_contextpath_hash, - .tp_call =(ternaryfunc)cp_contextpath__call__, - .tp_flags =Py_TPFLAGS_DEFAULT, - .tp_doc =cp_contextpath__doc__, - .tp_richcompare =(richcmpfunc)cp_contextpath_richcmp, - .tp_methods =CpContextPath_Methods, - .tp_members =CpContextPath_Members, - .tp_init =(initproc)cp_contextpath_init, - .tp_new =(newfunc)cp_contextpath_new, + .tp_basicsize = sizeof(CpContextPathObject), + .tp_dealloc = (destructor)cp_contextpath_dealloc, + .tp_getattr = (getattrfunc)cp_contextpath__getattr__, + .tp_repr = (reprfunc)cp_contextpath_repr, + .tp_as_number = &CpContextPath_NumberMethods, + .tp_hash = (hashfunc)cp_contextpath_hash, + .tp_call = (ternaryfunc)cp_contextpath__call__, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = cp_contextpath__doc__, + .tp_richcompare = (richcmpfunc)cp_contextpath_richcmp, + .tp_methods = CpContextPath_Methods, + .tp_members = CpContextPath_Members, + .tp_init = (initproc)cp_contextpath_init, + .tp_new = (newfunc)cp_contextpath_new, + .tp_str = (reprfunc)cp_contextpath_str, }; \ No newline at end of file diff --git a/src/ccaterpillar/module.c b/src/ccaterpillar/module.c index 1ce6c26..69847d8 100644 --- a/src/ccaterpillar/module.c +++ b/src/ccaterpillar/module.c @@ -75,7 +75,19 @@ cp_core_pack_into(PyObject* m, PyObject* args, PyObject* kw) PyErr_SetString(PyExc_ValueError, "output stream not set!"); goto finish; } - res = CpPack(op, atom, io, globals); + + _modulestate* state = get_module_state(m); + if (CpStructModel_Check(op, state)) { + atom = CpStructModel_GetStruct(op, state); + if (!atom) { + goto finish; + } + res = CpPack(op, atom, io, globals); + Py_DECREF(atom); + } else { + res = CpPack(op, atom, io, globals); + } + finish: Py_XDECREF(globals); return res < 0 ? NULL : Py_None; @@ -112,7 +124,17 @@ cp_core_pack(PyObject* m, PyObject* args, PyObject* kw) goto finish; } - res = CpPack(op, atom, io, globals); + if (CpStructModel_Check(op, state)) { + atom = CpStructModel_GetStruct(op, state); + if (!atom) { + goto finish; + } + res = CpPack(op, atom, io, globals); + Py_DECREF(atom); + } else { + res = CpPack(op, atom, io, globals); + } + finish: Py_XDECREF(globals); if (res < 0) { @@ -132,6 +154,16 @@ cp_core_sizeof(PyObject* m, PyObject* args, PyObject* kw) if (!PyArg_ParseTupleAndKeywords(args, kw, "O|O", kwlist, &op, &globals)) { return NULL; } + _modulestate* state = get_module_state(m); + if (CpStructModel_Check(op, state)) { + PyObject* atom = CpStructModel_GetStruct(op, state); + if (!atom) { + return NULL; + } + PyObject* res = CpSizeOf(atom, globals); + Py_DECREF(atom); + return res; + } return CpSizeOf(op, globals); } @@ -174,7 +206,17 @@ cp_core_unpack(PyObject* m, PyObject* args, PyObject* kw) goto finish; } - res = CpUnpack(atom, io, globals); + if (CpStructModel_Check(atom, state)) { + atom = CpStructModel_GetStruct(atom, state); + if (!atom) { + goto finish; + } + res = CpUnpack(atom, io, globals); + Py_DECREF(atom); + } else { + res = CpUnpack(atom, io, globals); + } + finish: Py_XDECREF(globals); if (wrapped_io) { @@ -253,6 +295,7 @@ cp_module_clear(PyObject* m) Py_CLEAR(state->cp_bytes__false); Py_CLEAR(state->cp_bytes__true); + Py_CLEAR(state->cp_typehandler_map); } return 0; } @@ -508,6 +551,9 @@ PyInit__C(void) CpModuleState_AddObject( cp_option__global_field_options, "FIELD_OPTIONS", PySet_New(NULL)); + /* typehandler map */ + CpModuleState_AddObject(cp_typehandler_map, "TYPE_MAP", PyDict_New()); + /* setup arch and endian */ CpModuleState_AddObject(cp_endian__native, "NATIVE_ENDIAN", diff --git a/src/ccaterpillar/option.c b/src/ccaterpillar/option.c index f39665c..1c8e1c6 100644 --- a/src/ccaterpillar/option.c +++ b/src/ccaterpillar/option.c @@ -37,7 +37,7 @@ static PyObject* cp_option_repr(CpOptionObject* self) { return PyUnicode_FromFormat( - "CpOption(name=%R value=%R)", self->name, self->value); + "