diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml
index 61d57f2..0156440 100644
--- a/.github/workflows/python-test.yml
+++ b/.github/workflows/python-test.yml
@@ -13,17 +13,17 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Install
run: |
- pip install -e .
+ pip install -ve ./src/ccaterpillar
pip install -r requirements.txt
pip install -r test/requirements.txt
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2a80ac3..f179498 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,33 +8,51 @@ find_package(
add_compile_definitions(_CPMODULE)
+# TODO: document this change
+set(CP_C_CONFIGURED DEFINED ENV{CP_ENABLE_NATIVE} OR DEFINED CP_ENABLE_NATIVE)
-if (DEFINED ENV{CP_ENABLE_NATIVE})
+if (CP_C_CONFIGURED)
python_add_library(
_C
MODULE
- src/arch.c
- src/atomobj.c
- src/context.c
- src/field.c
- src/option.c
- src/module.c
- src/context.c
- src/state.c
- src/struct.c
-
- src/parsing_typeof.c
- src/parsing_sizeof.c
- src/parsing_pack.c
- src/parsing_unpack.c
-
- src/atomimpl/intatomobj.c
- src/atomimpl/floatatomobj.c
- src/atomimpl/boolatomobj.c
- src/atomimpl/charatomobj.c
- src/atomimpl/padatomobj.c
- src/atomimpl/stringatomobj.c
+ src/ccaterpillar/arch.c
+ src/ccaterpillar/atomobj.c
+ src/ccaterpillar/context.c
+ src/ccaterpillar/option.c
+ src/ccaterpillar/module.c
+ src/ccaterpillar/context.c
+ src/ccaterpillar/state.c
+ src/ccaterpillar/struct.c
+ src/ccaterpillar/layer.c
+ src/ccaterpillar/default.c
+
+ src/ccaterpillar/parsing_typeof.c
+ src/ccaterpillar/parsing_sizeof.c
+ src/ccaterpillar/parsing_pack.c
+ src/ccaterpillar/parsing_unpack.c
+
+ src/ccaterpillar/atomimpl/int.c
+ src/ccaterpillar/atomimpl/float.c
+ src/ccaterpillar/atomimpl/bool.c
+ src/ccaterpillar/atomimpl/char.c
+ src/ccaterpillar/atomimpl/pad.c
+ src/ccaterpillar/atomimpl/string.c
+ src/ccaterpillar/atomimpl/const.c
+ src/ccaterpillar/atomimpl/bytes.c
+ src/ccaterpillar/atomimpl/pstring.c
+ src/ccaterpillar/atomimpl/enum.c
+ src/ccaterpillar/atomimpl/varint.c
+ src/ccaterpillar/atomimpl/computed.c
+ src/ccaterpillar/atomimpl/lazy.c
+ src/ccaterpillar/atomimpl/cstring.c
+
+ src/ccaterpillar/atomimpl/builtins/builtin.c
+ src/ccaterpillar/atomimpl/builtins/repeated.c
+ src/ccaterpillar/atomimpl/builtins/condition.c
+ src/ccaterpillar/atomimpl/builtins/switch.c
+ src/ccaterpillar/atomimpl/builtins/atoffset.c
+ src/ccaterpillar/atomimpl/builtins/primitive.c
WITH_SOABI
)
@@ -44,10 +62,18 @@ target_include_directories(_C PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpilla
message(STATUS "CMake ${CMAKE_VERSION}")
message(STATUS "Python ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}")
+set (CP_GENAPI_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/src/code_gen/genapi.py)
+set (CP_CAPI_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpillar/include/caterpillar/caterpillarapi.h.in)
+set (CP_CAPI_CSRC ${CMAKE_CURRENT_SOURCE_DIR}/src/ccaterpillar/caterpillarapi.c.in)
+set (CP_CAPI_HEADER_OUT ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpillar/include/caterpillar/caterpillarapi.h)
+set (CP_CAPI_CSRC_OUT ${CMAKE_CURRENT_SOURCE_DIR}/src/ccaterpillar/caterpillarapi.c)
+
add_custom_target(
genapi ALL
- COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/src/code_gen/genapi.py ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpillar/include/caterpillar/caterpillarapi.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpillarapi.c.in
- BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpillar/include/caterpillar/caterpillarapi.h ${CMAKE_CURRENT_SOURCE_DIR}/src/caterpillarapi.c
+ # execute python ./src/code_gen/genapi.py ./src/caterpillar/include/caterpillar/caterpillarapi.h.in ./src/ccaterpillar/caterpillarapi.c.in
+ # in the root directory
+ COMMAND ${PYTHON_EXECUTABLE} ${CP_GENAPI_SCRIPT} ${CP_CAPI_HEADER} ${CP_CAPI_CSRC}
+ BYPRODUCTS ${CP_CAPI_HEADER_OUT} ${CP_CAPI_CSRC_OUT}
COMMENT "Generating Public Caterpillar API"
)
diff --git a/README.md b/README.md
index dc1eb65..8d45d45 100644
--- a/README.md
+++ b/README.md
@@ -28,10 +28,10 @@ options will be added in the future. Documentation is [here >](https://matrixedi
* it helps you to create cleaner and more compact code.
* You can even extend Caterpillar and write your parsing logic in C or C++!!
-## What does it look like?
+## Give me some code!
```python
-from caterpillar._Py import *
+from caterpillar.py import *
@struct(order=LittleEndian)
class Format:
@@ -61,24 +61,19 @@ on its powerful features, explore the official [documentation](https://matrixedi
> As of Caterpillar v2.1.2 it is possible to install the library without the need of
> compiling the C extension.
-Simply use pip to install the package with all tools, all extra packages and the C extension:
+### Python-only installation
+
```bash
-pip install "caterpillar[all]@git+https://github.com/MatrixEditor/caterpillar.git"
+pip install "caterpillar[all]@git+https://github.com/MatrixEditor/caterpillar"
```
-If you want to use the native C extension you can specify the following environment variables:
-
-* `CP_ENABLE_NATIVE` - Enables installation of the native C extension
-* `CP_ENABLE_TOOLS` - Enables installation of extra tools (proposed)
+### C-extension installation
-For instance, the following command will install the `caterpillar` package with
-a native interface and with extra tools:
```bash
-CP_ENABLE_NATIVE=1 \
-CP_ENABLE_TOOLS=1 \
-pip install git+https://github.com/MatrixEditor/caterpillar
+pip install "caterpillar[all]@git+https://github.com/MatrixEditor/caterpillar/#subdirectory=src/ccaterpillar"
```
+
## Starting Point
Please visit the [Documentation](https://matrixeditor.github.io/caterpillar/), it contains a complete tutorial on how to use this library.
diff --git a/docs/requirements.txt b/docs/requirements.txt
index c1500b1..ad16558 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,5 @@
sphinx
pydata-sphinx-theme
sphinx-design
-breathe
\ No newline at end of file
+breathe
+sphinx-copybutton
\ No newline at end of file
diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py
index aa9a9ce..efcc88a 100644
--- a/docs/sphinx/source/conf.py
+++ b/docs/sphinx/source/conf.py
@@ -27,7 +27,8 @@
"sphinx.ext.viewcode",
"sphinx_design",
"breathe",
- "c_annotations"
+ "c_annotations",
+ "sphinx_copybutton"
]
templates_path = ["_templates"]
@@ -61,7 +62,7 @@
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
-refcount_file = 'extensions/refcounts.dat'
+refcount_file = '../../../src/capi.dat'
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
diff --git a/docs/sphinx/source/development/index.rst b/docs/sphinx/source/development/index.rst
index 5e0c89d..8a5a340 100644
--- a/docs/sphinx/source/development/index.rst
+++ b/docs/sphinx/source/development/index.rst
@@ -10,5 +10,6 @@ Development
:numbered:
:maxdepth: 2
+ roadmap.rst
contribution.rst
changelog.rst
\ No newline at end of file
diff --git a/docs/sphinx/source/development/roadmap.rst b/docs/sphinx/source/development/roadmap.rst
new file mode 100644
index 0000000..a2ad41a
--- /dev/null
+++ b/docs/sphinx/source/development/roadmap.rst
@@ -0,0 +1,111 @@
+.. _dev-roadmap:
+
+********
+Roadmap
+********
+
+.. |check_| raw:: html
+
+
+
+.. |uncheck_| raw:: html
+
+
+
+.. role:: text-danger
+
+.. role:: text-warning
+
+Python API
+----------
+
+- |check_| Implementation of parsing process (unpack, pack)
+- |check_| Struct class (:class:`Struct`) with wrapper function (:code:`@struct`)
+- |uncheck_| Python docs and examples
+- |uncheck_| Python tests
+
+C API
+-----
+
+- |check_| Implementation of parsing process (unpack, pack)
+- |uncheck_| Struct class (:c:type:`CpStructObject`)
+- |uncheck_| Struct wrapper function
+- |uncheck_| Python docs
+
+Struct Objects:
+^^^^^^^^^^^^^^^
+
+- |check_| Struct (C type: :c:type:`CpStructObject`, Py type: :class:`Struct`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |uncheck_| Bitfield
+
+Atom Objects:
+^^^^^^^^^^^^^
+
+- |check_| Integer (uint8-128 and int8-128) (C type: :c:type:`CpIntAtomObject`, Py type: :class:`int_t`)
+ [:text-danger:`missing docs`]
+
+- |check_| Float, Double (C type: :c:type:`CpFloatAtomObject`, Py type: :class:`float_t`)
+ [:text-danger:`missing docs`]
+
+- |check_| Boolean (C type: :c:type:`CpBoolAtomObject`, Py type: :class:`bool_t`)
+ Global instance: :code:`boolean`,
+ [:text-danger:`missing docs`]
+
+- |check_| Char (C type: :c:type:`CpCharAtomObject`, Py type: :class:`char_t`)
+ Global instance: :code:`char`,
+ [:text-danger:`missing docs`]
+
+- |check_| Padding (C type: :c:type:`CpPaddingAtomObject`, Py type: :class:`padding_t`)
+ Global instance: :code:`padding`,
+ [:text-danger:`missing docs`]
+
+- |check_| String (C type: :c:type:`CpStringAtomObject`, Py type: :class:`string`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |check_| Const (C type: :c:type:`CpConstAtomObject`, Py type: :class:`const_t`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |uncheck_| CString (C type: :c:type:`CpCStringAtomObject`, Py type: :class:`cstring`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |check_| Bytes (C type: :c:type:`CpBytesAtomObject`, Py type: :class:`octetstring`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |check_| Enum (C type: :c:type:`CpEnumAtomObject`, Py type: :class:`enumeration`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |check_| Computed (C type: :c:type:`CpComputedAtomObject`, Py type: :class:`computed`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |check_| PString (C type: :c:type:`CpPStringAtomObject`, Py type: :class:`pstring`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |uncheck_| Prefixed
+ .. seealso:: *link issue here*
+
+- |check_| Lazy
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |uncheck_| uuid
+ .. seealso:: *link issue here*
+
+- |uncheck_| Conditional: If, Else, ElseIf
+ .. seealso:: *link issue here*
+
+- |check_| VarInt (C type: :c:type:`CpVarIntAtomObject`, Py type: :class:`varint_t`)
+ [:text-danger:`missing docs`],
+ [:text-warning:`missing perftest`]
+
+- |uncheck_| While
+ .. seealso:: *link issue here*
\ No newline at end of file
diff --git a/docs/sphinx/source/extensions/c_annotations.py b/docs/sphinx/source/extensions/c_annotations.py
index 4d01d2c..9ef9c98 100644
--- a/docs/sphinx/source/extensions/c_annotations.py
+++ b/docs/sphinx/source/extensions/c_annotations.py
@@ -6,9 +6,10 @@
from os import path
import docutils
from docutils import nodes
+from sphinx.addnodes import pending_xref
from sphinx.locale import _ as sphinx_gettext
-from sphinx import addnodes
+from sphinx import addnodes, application
class RCEntry:
@@ -21,56 +22,90 @@ def __init__(self, name):
class Annotations:
def __init__(self, refcount_filename):
self.refcount_data = {}
+ self.typedef_data = {}
with open(refcount_filename, "r", encoding="utf-8") as fp:
for line in fp:
line = line.strip()
if line[:1] in ("", "#"):
# blank lines and comments
continue
- parts = line.split(":", 2)
- if len(parts) != 3:
- raise ValueError("Wrong field count in %r" % line)
- function, type, refcount = parts
- # Get the entry, creating it if needed:
- try:
- entry = self.refcount_data[function]
- except KeyError:
- entry = self.refcount_data[function] = RCEntry(function)
- if not refcount or refcount == "null":
- refcount = None
- else:
- refcount = int(refcount)
- entry.result_type = type
- entry.result_refs = refcount
+ def_type, *parts = line.split(":")
+ match def_type:
+ case "func":
+ index, name, rtype, refcount = parts
+ # Get the entry, creating it if needed:
+ entry = self.refcount_data.get(name)
+ if not entry:
+ entry = self.refcount_data[name] = RCEntry(name)
+ if not refcount or refcount == "null":
+ refcount = None
+ else:
+ refcount = int(refcount)
+ entry.result_type = rtype
+ entry.result_refs = refcount
+
+ case "type":
+ index, struct_name, typedef_name, py_type = parts
+ if py_type != "-":
+ self.typedef_data[py_type] = typedef_name
+
+ def add_py_annotations(
+ self, app: application.Sphinx, node: docutils.nodes.Element, par
+ ):
+
+ if not par[0].has_key("ids") or not par[0]["ids"]:
+ return
+
+ name = par[0]["ids"][0]
+ if not name.startswith("caterpillar.c."):
+ return
+
+ if par["objtype"] != "class":
+ return
+
+ name = name[14:]
+ if name in self.typedef_data:
+ typedef_name = self.typedef_data[name]
+
+ rc = sphinx_gettext(f"C API Type: {typedef_name}")
+ node.insert(0, nodes.emphasis(rc, rc, classes=["refcount", "text-info"]))
+ node.insert(1, nodes.line())
+
+ def add_c_annotation(self, app, node, par):
+ if not par[0].has_key("ids") or not par[0]["ids"]:
+ return
+
+ name = par[0]["ids"][0]
+ if name.startswith("c."):
+ name = name[2:]
+
+ objtype = par["objtype"]
+ if objtype != "function":
+ return
+ entry = self.refcount_data.get(name)
+ if not entry:
+ return
+
+ if not entry.result_type.endswith("Object*"):
+ return
+
+ if entry.result_refs is None:
+ rc = sphinx_gettext("Return value: Always NULL.")
+ elif entry.result_refs:
+ rc = sphinx_gettext("Return value: New reference.")
+ else:
+ rc = sphinx_gettext("Return value: Borrowed reference.")
+ node.insert(0, nodes.emphasis(rc, rc, classes=["refcount", "text-info"]))
def add_annotations(self, app, doctree):
for node in doctree.findall(addnodes.desc_content):
par = node.parent
- if par["domain"] != "c":
- continue
- if not par[0].has_key("ids") or not par[0]["ids"]:
- continue
- name = par[0]["ids"][0]
- if name.startswith("c."):
- name = name[2:]
-
- objtype = par["objtype"]
- if objtype != "function":
- continue
- entry = self.refcount_data.get(name)
- if not entry:
- continue
-
- if not entry.result_type.endswith("Object*"):
- continue
-
- if entry.result_refs is None:
- rc = sphinx_gettext("Return value: Always NULL.")
- elif entry.result_refs:
- rc = sphinx_gettext("Return value: New reference.")
- else:
- rc = sphinx_gettext("Return value: Borrowed reference.")
- node.insert(0, nodes.emphasis(rc, rc, classes=["refcount", "text-info"]))
+ match par["domain"]:
+ case "c":
+ self.add_c_annotation(app, node, par)
+
+ case "py":
+ self.add_py_annotations(app, node, par)
def init_annotations(app):
diff --git a/docs/sphinx/source/extensions/refcounts.dat b/docs/sphinx/source/extensions/refcounts.dat
index 36aa46f..ec563fe 100644
--- a/docs/sphinx/source/extensions/refcounts.dat
+++ b/docs/sphinx/source/extensions/refcounts.dat
@@ -10,9 +10,6 @@ CpStruct_GetAnnotations:PyObject*:+1
CpStructFieldInfo_New:CpStructFieldInfoObject*:+1
CpCharAtom_Unpack:PyObject*:+1
-
-CpContext_GetAttr:PyObject*:+1
-CpContext_GetAttrString:PyObject*:+1
CpContext_New:CpContextObject*:+1
CpUnaryExpr_New:CpUnaryExprObject*:+1
@@ -39,4 +36,11 @@ CpState_Read:PyObject*:+1
CpState_ReadFully:PyObject*:+1
CpState_Write:PyObject*:+1
-CpLayer_New:CpLayerObject*:+1
\ No newline at end of file
+CpLayer_New:CpLayerObject*:+1
+
+CpIntAtom_Unpack:PyObject*:+1
+
+CpFloatAtom_Unpack:PyObject*:+1
+
+CpRepeatedAtom_GetLength:PyObject*:+1
+CpRepeatedAtom_Unpack:PyObject*:+1
\ No newline at end of file
diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst
index f9a0d10..97b0893 100644
--- a/docs/sphinx/source/index.rst
+++ b/docs/sphinx/source/index.rst
@@ -16,15 +16,34 @@ efficient.
structs that adjust their size based on the current context. This framework enables you
to write complex structures in a compact and readable manner.
-.. code-block::
- :caption: Simple example of a custom struct
+.. tab-set::
- @struct
- class Format:
- magic: b"Foo" # constant values
- name: CString(...) # C-String without a fixed length
- value: le + uint16 # little endian encoding
- entries: be + CString[uint32::] # arrays with big-endian prefixed length
+ .. tab-item:: Python
+
+ .. code-block::
+ :caption: Simple example of a custom struct
+
+ @struct
+ class Format:
+ magic: b"Foo" # constant values
+ name: CString(...) # C-String without a fixed length
+ value: le + uint16 # little endian encoding
+ entries: be + CString[uint32::] # arrays with big-endian prefixed length
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: Simple example of a custom struct
+
+ from caterpillar.c import BIG_ENDIAN as be
+
+ @struct
+ class Format:
+ 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!
@@ -34,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
+
+ >>> 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'])
- 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.
+ .. 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/installing/index.rst b/docs/sphinx/source/installing/index.rst
index b93a1a1..3930ee8 100644
--- a/docs/sphinx/source/installing/index.rst
+++ b/docs/sphinx/source/installing/index.rst
@@ -9,9 +9,8 @@ to install it by providing the Git link. This library has no fixed dependencies,
out of the box.
There are several installation options you can use to install a desired part of the library.
-*caterpillar* ships with a native C extension and additional tools, that are using native
-extensions too. As not everyone wants to use C extensions, it's default installation candidate
-does not include the native extension.
+*caterpillar* ships with a native C extension and additional tools. As not everyone wants to
+use C extensions, it's default installation candidate does not include the native extension.
.. code-block:: bash
@@ -28,6 +27,11 @@ If you want to use the native C extension you can specify the following environm
* `CP_ENABLE_NATIVE` - Enables installation of the native C extension
* `CP_ENABLE_TOOLS` - Enables installation of extra tools (proposed)
+**or** you can use the following command to install it directly:
+
+.. code-block:: bash
+
+ pip install git+https://github.com/MatrixEditor/caterpillar/#subdirectory=src/ccaterpillar
This project comes with packaging extras. Therefore, if you want to enable specific structs, you
have to install the corresponding extra. It can be done using pip again:
diff --git a/docs/sphinx/source/library/ctypes/int.rst b/docs/sphinx/source/library/ctypes/int.rst
new file mode 100644
index 0000000..ebbd881
--- /dev/null
+++ b/docs/sphinx/source/library/ctypes/int.rst
@@ -0,0 +1,192 @@
+.. _api-ctypes_int:
+
+*************
+Integer Types
+*************
+
+This document provides detailed API documentation for integer types in the :code:`caterpillar.c` module. These
+integer types are low-level representations of integers with customizable bit-length, signedness, and byte
+order. They are particularly useful when interacting with binary data or when a fixed-width integer
+representation is required.
+
+The integer types offered here are optimized for performance, making them more efficient than their Python
+equivalents (:class:`FormatField`) in almost every context. The global predefined integer types (e.g.,
+:data:`u8`, :data:`i32`) are convenient for most common cases, but you can also define custom integer types
+with specific bit-widths, signedness, and endianness using the :class:`Int` class.
+
+.. py:class:: caterpillar.c.Int(bits, signed=True, little_endian=True)
+
+ This class implements an integer type with a variable number of bits,
+ supporting signed and unsigned integers, and little-endian or big-endian
+ byte orders.
+
+ Unlike its Python counterpart (:class:`FormatField`), this C-based class
+ focuses solely on integer operations, optimizing performance through direct
+ use of the `int.from_bytes` and `int.to_bytes` methods. Although you can
+ instantiate the class directly, it is recommended to use the global predefined
+ instances for common integer types (see below for more details).
+
+ .. note::
+ For most use cases, utilize the pre-defined global instances like :data:`u8`,
+ :data:`i16`, etc., for simplicity and efficiency.
+
+ .. csv-table:: Supported Operations
+ :header: "Action", "Operator", "Result"
+ :widths: 15, 30, 30
+
+ "Byteorder", ":code:` + `", "new :class:`Int` object with corresponding byteorder"
+ "Sequence", ":code:`[]`", ":class:`repeated` object"
+ "Offset", ":code:` @ `", ":class:`atoffset` object"
+ "Condition", ":code:` // `", ":class:`condition` object"
+ "Switch", ":code:` >> `", ":class:`switch` object"
+
+ .. versionadded:: 2.2.0
+
+
+ .. py:method:: __init__(self, bits, signed=True, little_endian=True)
+
+ Initializes the integer type with the given number of bits, sign specification,
+ and byte order.
+
+ :param int bits: The number of bits for the integer. Must be a multiple of 8.
+ :param bool signed: Optional; whether the integer is signed. Defaults to True.
+ :param bool little_endian: Optional; whether the integer should be stored in little-endian byte order. Defaults to True.
+
+ .. code-block:: python
+ :caption: Examples of using :class:`Int`
+
+ from caterpillar.c import *
+
+ # Unsigned 8-bit integer
+ assert unpack(b"\x01", u8) == 1
+
+ # Signed 8-bit integer
+ assert pack(1, i8) == b"\x01"
+
+ # Unsigned 16-bit little-endian integer
+ assert unpack(b"\x01\x00", u16) == 1
+
+ # Signed 16-bit big-endian integer
+ assert pack(-1, Int(16, signed=True, little_endian=False)) == b"\xff\xff"
+
+ .. py:method:: __repr__(self) -> str
+
+ Returns a string representation of the integer type. The format of the returned string
+ is as follows:
+
+ .. code-block::
+
+ := '<' ('le' | 'be') ['u'] 'int' '>'
+
+ >>> assert repr(u32) == ""
+
+ :return: A string representation of the integer type, indicating the byte order, sign, and number of bits.
+
+ .. py:method:: __type__(self) -> type
+
+ Returns the Python type for this class. This is typically :code:`int`.
+
+ :return: The Python type for this class.
+
+ .. py:method:: __size__(self, ctx) -> int
+
+ Returns the size in bytes of the integer type.
+
+ >>> assert sizeof(u64) == 8
+
+ :param ctx: The context object. (must not be null)
+ :return: The size in bytes of the integer type.
+
+ .. py:attribute:: nbytes
+ :type: int
+
+ The number of bytes in the integer, calculated from :attr:`nbits`.
+
+ .. py:attribute:: nbits
+ :type: int
+
+ The number of bits in the integer.
+
+ >>> assert u32.nbits == 32
+
+ .. py:attribute:: signed
+ :type: bool
+
+ Indicates whether the integer is signed (True) or unsigned (False).
+
+ >>> assert i8.signed is True
+
+ .. py:attribute:: little_endian
+ :type: bool
+
+ Indicates whether the integer is stored in little-endian byte order (True) or big-endian byte order (False).
+
+ >>> assert Int(32, little_endian=False).little_endian is False
+ >>> assert (BIG_ENDIAN + u32).little_endian is False
+
+
+Pre-Defined Integer Types (global)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. raw:: html
+
+
+
+The following pre-defined integer types are globally available, optimized for
+common use cases. By default, they use little-endian byte order. To switch to
+big-endian, use the byteorder operation (e.g., :code:`BIG_ENDIAN + u16`).
+
+Unsigned Integer Types:
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. py:data:: caterpillar.c.u8
+
+ Unsigned 8-bit integer type.
+
+.. py:data:: caterpillar.c.u16
+
+ Unsigned 16-bit integer type.
+
+.. py:data:: caterpillar.c.u24
+
+ Unsigned 24-bit integer type.
+
+.. py:data:: caterpillar.c.u32
+
+ Unsigned 32-bit integer type.
+
+.. py:data:: caterpillar.c.u64
+
+ Unsigned 64-bit integer type.
+
+.. py:data:: caterpillar.c.u128
+
+ Unsigned 128-bit integer type.
+
+
+Signed Integer Types:
+~~~~~~~~~~~~~~~~~~~~~
+
+.. py:data:: caterpillar.c.i8
+
+ Signed 8-bit integer type.
+
+.. py:data:: caterpillar.c.i16
+
+ Signed 16-bit integer type.
+
+.. py:data:: caterpillar.c.i24
+
+ Signed 24-bit integer type.
+
+.. py:data:: caterpillar.c.i32
+
+ Signed 32-bit integer type.
+
+.. py:data:: caterpillar.c.i64
+
+ Signed 64-bit integer type.
+
+.. py:data:: caterpillar.c.i128
+
+ Signed 128-bit integer type.
diff --git a/docs/sphinx/source/library/fields/common.rst b/docs/sphinx/source/library/fields/common.rst
index 6a4c45b..7642d40 100644
--- a/docs/sphinx/source/library/fields/common.rst
+++ b/docs/sphinx/source/library/fields/common.rst
@@ -7,93 +7,93 @@ Common Structs
Numeric Structs
---------------
-.. autoclass:: caterpillar.fields.FormatField
+.. autoclass:: caterpillar.py.FormatField
:members:
-.. autoattribute:: caterpillar.fields.uint8
+.. autoattribute:: caterpillar.py.uint8
-.. autoattribute:: caterpillar.fields.int8
+.. autoattribute:: caterpillar.py.int8
-.. autoattribute:: caterpillar.fields.uint16
+.. autoattribute:: caterpillar.py.uint16
-.. autoattribute:: caterpillar.fields.int16
+.. autoattribute:: caterpillar.py.int16
-.. autoattribute:: caterpillar.fields.uint32
+.. autoattribute:: caterpillar.py.uint32
-.. autoattribute:: caterpillar.fields.int32
+.. autoattribute:: caterpillar.py.int32
-.. autoattribute:: caterpillar.fields.uint64
+.. autoattribute:: caterpillar.py.uint64
-.. autoattribute:: caterpillar.fields.int64
+.. autoattribute:: caterpillar.py.int64
-.. autoattribute:: caterpillar.fields.size_t
+.. autoattribute:: caterpillar.py.size_t
-.. autoattribute:: caterpillar.fields.ssize_t
+.. autoattribute:: caterpillar.py.ssize_t
-.. autoattribute:: caterpillar.fields.float16
+.. autoattribute:: caterpillar.py.float16
-.. autoattribute:: caterpillar.fields.float32
+.. autoattribute:: caterpillar.py.float32
-.. autoattribute:: caterpillar.fields.float64
+.. autoattribute:: caterpillar.py.float64
-.. autoattribute:: caterpillar.fields.void_ptr
+.. autoattribute:: caterpillar.py.void_ptr
-.. autoattribute:: caterpillar.fields.char
+.. autoattribute:: caterpillar.py.char
-.. autoattribute:: caterpillar.fields.boolean
+.. autoattribute:: caterpillar.py.boolean
-.. autoattribute:: caterpillar.fields.padding
+.. autoattribute:: caterpillar.py.padding
-.. autoclass:: caterpillar.fields.Int
+.. autoclass:: caterpillar.py.Int
:members:
-.. autoclass:: caterpillar.fields.UInt
+.. autoclass:: caterpillar.py.UInt
:members:
-.. autoattribute:: caterpillar.fields.vint
+.. autoattribute:: caterpillar.py.vint
-.. autoclass:: caterpillar.fields.VarInt
+.. autoclass:: caterpillar.py.VarInt
:members:
Bytes, Strings
--------------
-.. autoclass:: caterpillar.fields.Memory
+.. autoclass:: caterpillar.py.Memory
:members:
-.. autoclass:: caterpillar.fields.Bytes
+.. autoclass:: caterpillar.py.Bytes
:members:
-.. autoclass:: caterpillar.fields.String
+.. autoclass:: caterpillar.py.String
:members:
-.. autoclass:: caterpillar.fields.Prefixed
+.. autoclass:: caterpillar.py.Prefixed
:members:
-.. autoclass:: caterpillar.fields.CString
+.. autoclass:: caterpillar.py.CString
:members:
-.. autoclass:: caterpillar.fields.ConstString
+.. autoclass:: caterpillar.py.ConstString
:members:
-.. autoclass:: caterpillar.fields.ConstBytes
+.. autoclass:: caterpillar.py.ConstBytes
:members:
Special Structs
---------------
-.. autoattribute:: caterpillar.fields.Pass
+.. autoattribute:: caterpillar.py.Pass
-.. autoclass:: caterpillar.fields.Computed
+.. autoclass:: caterpillar.py.Computed
:members:
-.. autoclass:: caterpillar.fields.Transformer
+.. autoclass:: caterpillar.py.Transformer
:members:
-.. autoclass:: caterpillar.fields.Enum
+.. autoclass:: caterpillar.py.Enum
:members:
-.. autoclass:: caterpillar.fields.Const
+.. autoclass:: caterpillar.py.Const
:members:
\ No newline at end of file
diff --git a/docs/sphinx/source/library/fields/compression.rst b/docs/sphinx/source/library/fields/compression.rst
index 6fbbaf4..cf7296a 100644
--- a/docs/sphinx/source/library/fields/compression.rst
+++ b/docs/sphinx/source/library/fields/compression.rst
@@ -7,16 +7,16 @@ Compression Structs
Basic structs
-------------
-.. autoclass:: caterpillar.fields.Compressed
+.. autoclass:: caterpillar.py.Compressed
:members:
Supported compression types
---------------------------
-.. autofunction:: caterpillar.fields.ZLibCompressed
+.. autofunction:: caterpillar.py.ZLibCompressed
-.. autofunction:: caterpillar.fields.Bz2Compressed
+.. autofunction:: caterpillar.py.Bz2Compressed
-.. autofunction:: caterpillar.fields.LZMACompressed
+.. autofunction:: caterpillar.py.LZMACompressed
-.. autofunction:: caterpillar.fields.LZOCompressed
+.. autofunction:: caterpillar.py.LZOCompressed
diff --git a/docs/sphinx/source/library/fields/field_model.rst b/docs/sphinx/source/library/fields/field_model.rst
index f0b3c68..e737cc0 100644
--- a/docs/sphinx/source/library/fields/field_model.rst
+++ b/docs/sphinx/source/library/fields/field_model.rst
@@ -4,24 +4,24 @@
Field Model
***********
-.. autoclass:: caterpillar.fields.Field()
+.. autoclass:: caterpillar.py.Field()
:members:
:private-members:
:no-undoc-members:
-.. autoclass:: caterpillar.fields.FieldMixin
+.. autoclass:: caterpillar.py.FieldMixin
:special-members:
-.. autoclass:: caterpillar.fields.FieldStruct
+.. autoclass:: caterpillar.py.FieldStruct
:members:
:special-members:
-.. autoclass:: caterpillar.fields.Chain
+.. autoclass:: caterpillar.py.Chain
:members:
:special-members:
-.. autoclass:: caterpillar.fields.If
+.. autoclass:: caterpillar.py.If
:members:
-.. autoclass:: caterpillar.fields.ElseIf
+.. autoclass:: caterpillar.py.ElseIf
:members:
\ No newline at end of file
diff --git a/docs/sphinx/source/library/index.rst b/docs/sphinx/source/library/index.rst
index 7e1c14c..cada89b 100644
--- a/docs/sphinx/source/library/index.rst
+++ b/docs/sphinx/source/library/index.rst
@@ -8,25 +8,19 @@ Library
.. toctree::
:maxdepth: 2
- :caption: Basic Core classes
+ :caption: Python API
byteorder.rst
options.rst
abc.rst
-
-
-.. toctree::
- :maxdepth: 2
- :caption: Context and Exceptions
-
context.rst
exceptions.rst
+ model.rst
+ fields/index.rst
.. toctree::
:maxdepth: 2
- :caption: Struct model
-
- model.rst
- fields/index.rst
+ :caption: C API Python Types
+ ctypes/int.rst
diff --git a/docs/sphinx/source/reference/capi/objects/atoms/boolatom.rst b/docs/sphinx/source/reference/capi/objects/atoms/bool.rst
similarity index 100%
rename from docs/sphinx/source/reference/capi/objects/atoms/boolatom.rst
rename to docs/sphinx/source/reference/capi/objects/atoms/bool.rst
diff --git a/docs/sphinx/source/reference/capi/objects/atoms/charatom.rst b/docs/sphinx/source/reference/capi/objects/atoms/char.rst
similarity index 100%
rename from docs/sphinx/source/reference/capi/objects/atoms/charatom.rst
rename to docs/sphinx/source/reference/capi/objects/atoms/char.rst
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/floatatom.rst b/docs/sphinx/source/reference/capi/objects/atoms/float.rst
similarity index 85%
rename from docs/sphinx/source/reference/capi/objects/atoms/floatatom.rst
rename to docs/sphinx/source/reference/capi/objects/atoms/float.rst
index 02cb855..31bcfaa 100644
--- a/docs/sphinx/source/reference/capi/objects/atoms/floatatom.rst
+++ b/docs/sphinx/source/reference/capi/objects/atoms/float.rst
@@ -8,7 +8,7 @@ Float Atoms
.. c:var:: PyTypeObject CpFloatAtom_Type
- The type object for the :c:type:`floatatom` class.
+ The type object for the :c:type:`CpFloatAtomObject` class.
.. c:type:: CpFloatAtomObject
@@ -20,6 +20,11 @@ Float Atoms
.. c:function:: PyObject* CpFloatAtom_Unpack(CpFloatAtomObject* self, CpLayerObject* layer)
+.. c:function:: int CpFloatAtom_Check(PyObject *op)
+
+
+.. c:function:: int CpFloatAtom_CheckExact(PyObject *op)
+
Runtime Performance
-------------------
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/atoms/int.rst b/docs/sphinx/source/reference/capi/objects/atoms/int.rst
new file mode 100644
index 0000000..94423d0
--- /dev/null
+++ b/docs/sphinx/source/reference/capi/objects/atoms/int.rst
@@ -0,0 +1,108 @@
+.. _reference-capi_intatom:
+
+*************
+Integer Atoms
+*************
+
+The :c:type:`CpIntAtomObject` is a specialized implementation for handling integers,
+providing efficient mechanisms to pack and unpack sized integers. Unlike its Python
+counterpart (:class:`FormatField`), this C-based class focuses solely on integer operations.
+
+
+.. c:var:: PyTypeObject CpIntAtom_Type
+
+ The type object for the :class:`~caterpillar.c.Int` class.
+
+This implementation utilizes :code:`int.from_bytes` and :code:`int.to_bytes`. Direct C
+calls are optimized, reducing runtime overhead compared to Python.
+
+.. c:type:: CpIntAtomObject
+
+ A fundamental class for managing fixed-size integers. Further recommendations for optimal
+ use are detailed below.
+
+ .. c:var:: PyObject * m_byte_count
+
+ Represents the number of bytes in the integer. This can be accessed in Python using the attribute :code:`nbytes`.
+
+ .. c:var:: int _m_signed
+
+ Indicates whether the integer is signed. A value of ``1`` means signed, and ``0`` means unsigned.
+
+ .. c:var:: int _m_little_endian
+
+ Indicates the endianness of the integer. A value of ``1`` signifies little-endian, while ``0`` signifies big-endian.
+
+
+.. c:function:: int CpIntAtom_Pack(CpIntAtomObject* self, PyObject* value, CpLayerObject* layer)
+
+ Packs the given value into the given layer. Returns ``-1`` if an error occurs.
+
+
+.. c:function:: PyObject* CpIntAtom_Unpack(CpIntAtomObject* self, CpLayerObject* layer)
+
+ Unpacks the value from the given layer. Returns *NULL* if an error occurs.
+
+
+.. c:function:: int CpIntAtom_Check(PyObject *op)
+
+ Checks if the given object is an :c:type:`CpIntAtomObject`
+
+
+.. c:function:: int CpIntAtom_CheckExact(PyObject *op)
+
+ Checks if the given object is an instance of an :c:type:`CpIntAtomObject`
+
+
+.. rubric:: Runtime Performance
+
+Measurements represent the accumulated runtime of one million calls to
+:code:`unpack` or :code:`pack` using the corresponding implementation
+in seconds.
+
+.. list-table::
+ :header-rows: 1
+ :widths: 20 20 20 20 20
+
+ * - Function
+ - Caterpillar [0]_
+ - Caterpillar G [1]_
+ - Caterpillar C [2]_
+ - Construct [3]_
+ * - :code:`unpack`
+ - 4.002179
+ - 2.782663
+ - 0.815902
+ - 1.581962
+ * - :code:`pack`
+ - 3.866999
+ - 2.707753
+ - 0.926041
+ - 1.587046
+
+
+.. [0] local field instance
+.. [1] global field instance
+.. [2] C implementation
+.. [3] Construct :code:`Int32sn`
+
+The *benchmark* has been performed using the following code snippets:
+
+.. code-block:: python
+ :linenos:
+
+ from caterpillar import _C, model, fields
+ from construct import Int32sn
+
+ # Caterpillar
+ model.unpack(fields.Field(fields.int32), b"\x00\xFF\x00\xFF")
+
+ # Caterpillar (Global)
+ I32_G = fields.Field(fields.int32)
+ model.unpack(I32_G, b"\x00\xFF\x00\xFF")
+
+ # Caterpillar (C)
+ _C.unpack(b"\x00\xFF\x00\xFF", _C.i32)
+
+ # Construct
+ Int32sn.parse(b"\x00\xFF\x00\xFF")
diff --git a/docs/sphinx/source/reference/capi/objects/atoms/intatom.rst b/docs/sphinx/source/reference/capi/objects/atoms/intatom.rst
deleted file mode 100644
index 5398106..0000000
--- a/docs/sphinx/source/reference/capi/objects/atoms/intatom.rst
+++ /dev/null
@@ -1,59 +0,0 @@
-.. _reference-capi_intatom:
-
-*************
-Integer Atoms
-*************
-
-The :c:type:`CpIntAtomObject` is a specialized implementation for handling integers,
-providing efficient mechanisms to pack and unpack sized integers. Unlike its Python
-counterpart (:class:`FormatField`), this C-based class focuses solely on integer operations.
-
-
-.. c:var:: PyTypeObject CpIntAtom_Type
-
- The type object for the :c:type:`intatom` class.
-
-This implementation utilizes :code:`int.from_bytes` and :code:`int.to_bytes`. Direct C
-calls are optimized, reducing runtime overhead compared to Python.
-
-.. c:type:: CpIntAtomObject
-
- A fundamental class for managing fixed-size integers. Further recommendations for optimal
- use are detailed below.
-
- .. c:var:: PyObject * m_byte_count
-
- Represents the number of bytes in the integer. This can be accessed in Python using the attribute :code:`nbytes`.
-
- .. c:var:: int _m_signed
-
- Indicates whether the integer is signed. A value of ``1`` means signed, and ``0`` means unsigned.
-
- .. c:var:: int _m_little_endian
-
- Indicates the endianness of the integer. A value of ``1`` signifies little-endian, while ``0`` signifies big-endian.
-
-
-Recommendations
----------------
-
-The following examples illustrate how to effectively utilize the :c:type:`intatom` class
-and the associated methods:
-
-.. code-block:: python
- :linenos:
-
- from caterpillar._C import LITTLE_ENDIAN as le
- from caterpillar._C import intatom, unpack, i16
-
- # Define a global 16-bit little-endian signed integer atom
- I16_LE = le + i16
- _I16_LE = intatom(16, signed=True, little_endian=True)
-
- unpack(b"\x01\x02", _I16_LE)
- unpack(b"\x01\x02", intatom(16, signed=True, little_endian=True))
- unpack(b"\x01\x02", I16_LE)
- unpack(b"\x01\x02", le + i16)
-
-*TODO: describe the impact on runtime overhead of these four methods*
-
diff --git a/docs/sphinx/source/reference/capi/objects/atoms/paddingatom.rst b/docs/sphinx/source/reference/capi/objects/atoms/padding.rst
similarity index 100%
rename from docs/sphinx/source/reference/capi/objects/atoms/paddingatom.rst
rename to docs/sphinx/source/reference/capi/objects/atoms/padding.rst
diff --git a/docs/sphinx/source/reference/capi/objects/contextobj.rst b/docs/sphinx/source/reference/capi/objects/contextobj.rst
index 20cc060..897b7d7 100644
--- a/docs/sphinx/source/reference/capi/objects/contextobj.rst
+++ b/docs/sphinx/source/reference/capi/objects/contextobj.rst
@@ -26,17 +26,6 @@ Context Objects
Checks if the given object is instance of an :c:type:`CpContextObject`.
-.. c:function:: PyObject* CpContext_GetAttr(PyObject* context, PyObject* key, _coremodulestate* state)
-
- Get an attribute from a context by using :code:`__context_getattr__`. Returns
- *NULL* if the attribute is not found.
-
-
-.. c:function:: PyObject *CpContext_GetAttrString(PyObject* context, const char* key)
-
- Get an attribute from a context by using :code:`__context_getattr__`. Returns
- *NULL* if the attribute is not found.
-
.. c:function:: CpContextObject *CpContext_New(void)
Creates a new context and returns it. Returns *NULL* if an error occurs.
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/docs/sphinx/source/reference/capi/objects/state.rst b/docs/sphinx/source/reference/capi/objects/state.rst
index 755978d..e8220b0 100644
--- a/docs/sphinx/source/reference/capi/objects/state.rst
+++ b/docs/sphinx/source/reference/capi/objects/state.rst
@@ -41,7 +41,7 @@ State Objects
Returns the current position of the state. Returns *NULL* if an error occurs.
-.. c:function:: PyObject *CpState_Seek(CpStateObject *state, Py_ssize_t pos, int whence)
+.. c:function:: PyObject *CpState_Seek(CpStateObject *state, Py_ssize_t pos, PyObject whence)
Seeks to the given position. Returns :code:`0` if successful. Returns :code:`-1`
if an error occurs.
diff --git a/docs/sphinx/source/reference/capi/parsing.rst b/docs/sphinx/source/reference/capi/parsing.rst
index 131666a..04a4271 100644
--- a/docs/sphinx/source/reference/capi/parsing.rst
+++ b/docs/sphinx/source/reference/capi/parsing.rst
@@ -11,64 +11,20 @@ Packing and Unpacking
Returns the type of the given atom object.
+
.. c:function:: PyObject *CpSizeOf(PyObject *atom, PyObject *globals)
- PyObject *CpSizeOf_Field(CpFieldObject *, CpLayerObject *)
- PyObject *CpSizeOf_Struct(CpStructObject *, CpLayerObject *)
- PyObject *CpSizeOf_Common(PyObject *, CpLayerObject *)
Returns the size of the given atom object.
+
.. c:function:: int CpPack(PyObject *op, PyObject *atom, PyObject *io, PyObject *globals)
Packs the given value using the given atom object and returns :code::`0` if
successful. Returns :code:`-1` if an error occurs. The *globals* parameter may
be *NULL*.
- Serializing objects follows specific rules when it comes to CpLayerObject
- creation. The first layer (root layer) will be assigned with the path
- ''. By default, cp_pack will automatically create a new layer object
- when called. Therefore, it is recommended to utilize cp_pack_internal for
- C calls to avoid unnecessary layer creation.
-
- Currently, the following conditions allow the creation of a new layer:
-
- - Start packing an object
- - Serializing a sequence object that requires to iterate over all elements individually. Note that atoms that support :code:`__unpack_many__` won't be called with the newly created sequence layer.
- - Struct object or named sequences of fields in general will always introduce a new layer object.
-
- There are some built-in types that are designed to fasten calls by providing
- a native C implementation. It is recommended to implement new classes in C
- to leverage its speed rather than define new Python atom classes.
-
- A full workflow of cp_pack with all layers displayed would be the following:
- .. code-block:: text
-
- 1. cp_pack: create new state and root layer | l:
- 2. cp_pack_internal: measure atom type and call | <--+-+-+
- according C method | l: | | |
- 3. One of the following methods will be called: | | |
- a. cp_pack_catom: calls native C method | l: | | |
- b. cp_pack_atom: call python pack method | l: | | |
- c. cp_pack_field: inserts the field instance into | | |
- the current layer and calls cp_pack_internal | | |
- again. | l: -+ | |
- d. cp_pack_struct: creates a new object layer | |
- that addresses only the current struct's | |
- values. Calls cp_pack_internal on each field | l: ---+ |
- e. cp_pack_common: createsa a new layer if |
- multiple objects should be packed AND |
- __[un]pack_many__ is not implemented. | l: -+
-
- The following functions are defined and called within the packing process:
-
- .. c:function:: int CpPack_Field(PyObject *, CpFieldObject *, CpLayerObject *)
- int CpPack_Struct(PyObject *, CpStructObject *, CpLayerObject *)
- int CpPack_CAtom(PyObject *, CpCAtomObject *, CpLayerObject *)
- int CpPack_Common(PyObject *, PyObject *, CpLayerObject *)
- int _Cp_Pack(PyObject*, PyObject*, CpLayerObject* )
-
- .. c:function:: int _CpPack_EvalLength(CpLayerObject* layer, PyObject* length, Py_ssize_t size, bool* seq_greedy, Py_ssize_t* seq_length)
+.. c:function:: int _CpPack_EvalLength(CpLayerObject* layer, PyObject* length, Py_ssize_t size, bool* seq_greedy, Py_ssize_t* seq_length)
Evaluates the length of the sequence to pack.
@@ -83,16 +39,8 @@ Packing and Unpacking
:param seq_length: destination pointer to store the length of the sequence
:return: :code:`0` if successful, :code:`-1` if an error occurs
-.. c:function:: PyObject *CpUnpack(PyObject *atom, PyObject *io, PyObject *globals)
- Unlike serializing objects, unpacking returns fully qualified objects as a
- result. In addition, the internal 'obj' within a struct layer will be a
- :c:type:`CpContextObject` instance.
+.. c:function:: PyObject *CpUnpack(PyObject *atom, PyObject *io, PyObject *globals)
- The following functions are defined and called within the unpacking process:
+ *TODO*
- .. c:function:: PyObject *_Cp_Unpack(PyObject*, CpLayerObject*)
- PyObject *CpUnpack_Field(CpFieldObject*, CpLayerObject*)
- PyObject *CpUnpack_Struct(CpStructObject*, CpLayerObject*)
- PyObject *CpUnpack_CAtom(CpCAtomObject*, CpLayerObject*)
- PyObject *CpUnpack_Common(PyObject *, CpLayerObject*)
diff --git a/docs/sphinx/source/reference/index.rst b/docs/sphinx/source/reference/index.rst
index 0b9875a..5fdf64a 100644
--- a/docs/sphinx/source/reference/index.rst
+++ b/docs/sphinx/source/reference/index.rst
@@ -23,14 +23,15 @@ documentation.
If you want to switch to the C API instead, please refer to the sections described in the CAPI reference.
.. toctree::
- :maxdepth: 3
+ :maxdepth: 2
:numbered:
:caption: C API Reference
+ capi/extension
capi/abstract
capi/objects/objimpl
capi/parsing
capi/macros
capi/module
- capi/extension
+
diff --git a/docs/sphinx/source/tutorial/basics.rst b/docs/sphinx/source/tutorial/basics.rst
index 82b1511..d7b5cd6 100644
--- a/docs/sphinx/source/tutorial/basics.rst
+++ b/docs/sphinx/source/tutorial/basics.rst
@@ -27,24 +27,52 @@ When dealing with binary data, numbers play a crucial role. Besides the default
(e.g., uint8, uint16, etc.), *caterpillar* introduces some special integer formats. The default
types include:
-* Unsigned (:code:`u...`) and signed:
- :attr:`~caterpillar.fields.int8`, :attr:`~caterpillar.fields.uint8`,
- :attr:`~caterpillar.fields.int16`, :attr:`~caterpillar.fields.uint16`,
- :attr:`~caterpillar.fields.int24`, :attr:`~caterpillar.fields.uint24`,
- :attr:`~caterpillar.fields.int32`, :attr:`~caterpillar.fields.uint32`,
- :attr:`~caterpillar.fields.int64`, :attr:`~caterpillar.fields.uint64`,
- :attr:`~caterpillar.fields.ssize_t`, :attr:`~caterpillar.fields.size_t`,
+.. tab-set::
-* Floating point:
+ .. tab-item:: Python
- :attr:`~caterpillar.fields.float16`, :attr:`~caterpillar.fields.float32`,
- :attr:`~caterpillar.fields.float64`
+ * Unsigned (:code:`u...`) and signed:
+
+ :attr:`~caterpillar.fields.int8`, :attr:`~caterpillar.fields.uint8`,
+ :attr:`~caterpillar.fields.int16`, :attr:`~caterpillar.fields.uint16`,
+ :attr:`~caterpillar.fields.int24`, :attr:`~caterpillar.fields.uint24`,
+ :attr:`~caterpillar.fields.int32`, :attr:`~caterpillar.fields.uint32`,
+ :attr:`~caterpillar.fields.int64`, :attr:`~caterpillar.fields.uint64`,
+ :attr:`~caterpillar.fields.ssize_t`, :attr:`~caterpillar.fields.size_t`,
+
+ * Floating point:
+
+ :attr:`~caterpillar.fields.float16`, :attr:`~caterpillar.fields.float32`,
+ :attr:`~caterpillar.fields.float64`
+
+ * Special primitives:
+
+ :attr:`~caterpillar.fields.boolean`, :attr:`~caterpillar.fields.char`,
+ :attr:`~caterpillar.fields.void_ptr`
+
+
+ .. tab-item:: Caterpillar C
+
+ * Unsigned (:code:`u...`) and signed:
+
+ :attr:`~caterpillar.c.i8`, :attr:`~caterpillar.c.u8`,
+ :attr:`~caterpillar.c.i16`, :attr:`~caterpillar.c.u16`,
+ :attr:`~caterpillar.c.i24`, :attr:`~caterpillar.c.u24`,
+ :attr:`~caterpillar.c.i32`, :attr:`~caterpillar.c.u32`,
+ :attr:`~caterpillar.c.i64`, :attr:`~caterpillar.c.u64`,
+ :attr:`~caterpillar.c.i128`, :attr:`~caterpillar.c.u128`,
+
+ * Floating point:
+
+ :attr:`~caterpillar.c.f16`, :attr:`~caterpillar.c.f32`,
+ :attr:`~caterpillar.c.f64`
+
+ * Special primitives:
+
+ :attr:`~caterpillar.c.boolean`, :attr:`~caterpillar.c.char`
-* Special primitives:
- :attr:`~caterpillar.fields.boolean`, :attr:`~caterpillar.fields.char`,
- :attr:`~caterpillar.fields.void_ptr`
Custom-sized integer
~~~~~~~~~~~~~~~~~~~~
@@ -53,18 +81,38 @@ It's also possible to use integers with a custom size (in bits). However, it's i
that you have to define the struct with the bit count, and internally, only the occupied bytes
will be used. For example:
->>> field = F(Int(24)) # three-byte signed integer
->>> field = F(UInt(40)) # five-byte unsigned integer
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> field = F(Int(24)) # three-byte signed integer
+ >>> field = F(UInt(40)) # five-byte unsigned integer
+
+
+ .. tab-item:: Caterpillar C
+
+ >>> i48 = Int(48) # six-byte signed integer
+ >>> u40 = Int(40, signed=False) # five-byte unsigned integer
Variable-sized integer
~~~~~~~~~~~~~~~~~~~~~~
-The built-in struct :class:`~caterpillar.fields.VarInt` supports parsing and building integers with variable length. Its
+The built-in struct :class:`.py.VarInt`/:class:`.c.VarInt` supports parsing and building integers with variable length. Its
documentation provides a detailed explanation of all different configurations.
->>> field = F(vint) # or F(VarInt())
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> field = F(vint) # or F(VarInt())
+
+ .. tab-item:: Caterpillar C
+
+ >>> # use 'varint' directly or use VarInt()
+ >>> be_varint = BIG_ENDIAN + varint
+ >>> le_varint = VarInt(little_endian=True)
Enumerations
@@ -76,21 +124,43 @@ standard Python enumerations - classes extending :code:`enum.Enum` - with ease.
Let's revisit `pHYS `_ chunk to add an enum to the
last field.
-.. code-block:: python
- :caption: Simple enumeration in a struct definition
+.. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :caption: Simple enumeration in a struct definition
- import enum
+ import enum
- class PHYSUnit(enum.IntEnum): # <-- the enum value doesn't have to be int
- __struct__ = uint8 # <-- to make the code even more compact, use this
- UNKNOWN = 0
- METRE = 1
+ class PHYSUnit(enum.IntEnum): # <-- the enum value doesn't have to be int
+ __struct__ = uint8 # <-- to make the code even more compact, use this
+ UNKNOWN = 0
+ METRE = 1
- @struct(order=BigEndian) # <-- same as before
- class PHYSChunk:
- pixels_per_unit_x: uint32
- pixels_per_unit_y: uint32
- unit: PHYSUnit # <-- now we have an auto-enumeration
+ @struct(order=BigEndian) # <-- same as before
+ class PHYSChunk:
+ pixels_per_unit_x: uint32
+ pixels_per_unit_y: uint32
+ unit: PHYSUnit # <-- now we have an auto-enumeration
+
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: Simple enumeration in a struct definition
+
+ import enum
+
+ class PHYSUnit(enum.IntEnum): # <-- the enum value doesn't have to be int
+ UNKNOWN = 0
+ METRE = 1
+
+ @struct(endian=BIG_ENDIAN) # <-- same as before
+ class PHYSChunk:
+ pixels_per_unit_x: u32
+ pixels_per_unit_y: u32
+ unit: enumeration(u8, PHYSUnit) # <-- atom is required here
.. important::
It's worth noting that a default value can be specified for the field as a fallback. If
@@ -107,7 +177,19 @@ simplifies this with item access for defining arrays of static or dynamic size.
We started with the `PLTE `_ chunk, which stores three-byte
sequences. We can define an array of RGB objects as follows:
->>> PLTEChunk = RGB[this.length / 3]
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> PLTEChunk = RGB[this.length / 3]
+
+ .. tab-item:: Caterpillar C
+
+ >>> PLTEChunk = RGB.__struct__[ContextPath("obj.length") / 3]
+
+ .. versionadded:: 2.2.0
+ The syntax will be changed once `__class_getitem__` is implemented by
+ any :class:`.c.Struct` instance.
Since this chunk has only one field, the array specifier is used to make it a list type. The
length is calculated based on the chunk's length field divided by three because the RGB class
@@ -123,18 +205,50 @@ CString
The CString in this library extends beyond a mere reference to C strings. It provides
additional functionality, as demonstrated in the structure of the next chunk.
-.. code-block:: python
- :caption: The `tEXt `_ chunk structure
-
- @struct
- class TEXTChunk:
- # dynamic sized string that ends with a null-byte
- keyword: CString(encoding="ISO-8859-1")
- # static sized string based on the current context. some notes:
- # - parent.length is the current chunkt's length
- # - lenof(...) is the runtime length of the context variable
- # - 1 because of the extra null-byte that is stripped from keyword
- text: CString(encoding="ISO-8859-1", length=parent.length - lenof(this.keword) - 1)
+.. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :caption: The `tEXt `_ chunk structure
+
+ from caterpillar.py import *
+ from caterpillar.shortcuts import lenof
+
+ @struct
+ class TEXTChunk:
+ # dynamic sized string that ends with a null-byte
+ keyword: CString(encoding="ISO-8859-1")
+ # static sized string based on the current context. some notes:
+ # - parent.length is the current chunkt's length
+ # - lenof(...) is the runtime length of the context variable
+ # - 1 because of the extra null-byte that is stripped from keyword
+ text: CString(encoding="ISO-8859-1", length=parent.length - lenof(this.keword) - 1)
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: The `tEXt` chunk structure
+
+ from caterpillar.c import * # <-- main difference
+ from caterpillar.shortcuts import lenof
+ # NOTE: lenof works here, because Caterpillar C's Context implements
+ # the 'Context Protocol'.
+
+ parent = ContextPath("parent.obj")
+ this = ContextPath("obj")
+
+ @struct
+ class TEXTChunk:
+ # dynamic sized string that ends with a null-byte
+ keyword: cstring(encoding="ISO-8859-1")
+ # static sized string based on the current context. some notes:
+ # - parent.length is the current chunkt's length
+ # - lenof(...) is the runtime length of the context variable
+ # - 1 because of the extra null-byte that is stripped from keyword
+ text: cstring(encoding="ISO-8859-1", length=parent.length - lenof(this.keword) - 1)
+
+
.. admonition:: Challenge
@@ -145,25 +259,62 @@ additional functionality, as demonstrated in the structure of the next chunk.
This solution serves as an example and isn't the only way to approach it!
- .. code-block:: python
-
- @struct
- class ITXTChunk:
- keyword: CString(encoding="utf-8")
- compression_flag: uint8
- # we actually don't need an Enum here
- compression_method: uint8
- language_tag: CString(encoding="ASCII")
- translated_keyword: CString(encoding="utf-8")
- # length is calculated with parent.length - len(keyword)+len(b"\x00") - ...
- text: CString(
- encoding="utf-8",
- length=parent.length - lenof(this.translated_keyword) - lenof(this.keyword) - 5,
- )
+ .. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :linenos:
+
+ @struct
+ class ITXTChunk:
+ keyword: CString(encoding="utf-8")
+ compression_flag: uint8
+ # we actually don't need an Enum here
+ compression_method: uint8
+ language_tag: CString(encoding="ASCII")
+ translated_keyword: CString(encoding="utf-8")
+ # length is calculated with parent.length - len(keyword)+len(b"\x00") - ...
+ text: CString(
+ encoding="utf-8",
+ length=parent.length - lenof(this.translated_keyword) - lenof(this.keyword) - 5,
+ )
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :linenos:
+
+ from caterpillar.c import * # <-- main difference
+ from caterpillar.shortcuts import lenof
+
+ parent = ContextPath("parent.obj")
+ this = ContextPath("obj")
+
+ @struct
+ class ITXTChunk:
+ keyword: cstring() # default encoding is "utf-8"
+ compression_flag: u8
+ # we actually don't need an Enum here
+ compression_method: u8
+ language_tag: cstring(encoding="ASCII")
+ translated_keyword: cstring(...) # explicit greedy parsing
+ # length is calculated with parent.length - len(keyword)+len(b"\x00") - ...
+ text: cstring(
+ parent.length - lenof(this.translated_keyword) - lenof(this.keyword) - 5,
+ )
You can also apply your own termination character, for example:
->>> struct = CString(pad="\x0A")
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> struct = CString(pad="\x0A")
+
+ .. tab-item:: Caterpillar C
+
+ >>> s = cstring(sep="\x0A")
This struct will use a space as the termination character and strip all trailing
padding bytes.
@@ -174,7 +325,16 @@ String
Besides special the special *c strings* there's a default :class:`~caterpillar.fields.String` class that implements
the basic behaviour of a string. It's crucial to specify the length for this struct.
->>> struct = String(100 or this.length) # static integer or context lambda
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> struct = String(100 or this.length) # static integer or context lambda
+
+ .. tab-item:: Caterpillar C
+
+ >>> # takes static length, context lambda, another atom or ... for greedy parsing
+ >>> s = cstring(100)
Prefixed
@@ -185,11 +345,23 @@ encoding is specified, the returned value will be of type :code:`bytes`. This cl
using the given struct and then retrieves the corresponding number of bytes from the stream returned
by that struct.
->>> field = F(Prefixed(uint8, encoding="utf-8"))
->>> pack("Hello, World!", field)
-b'\rHello, World!'
->>> unpack(field, _)
-'Hello, World!'
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> field = F(Prefixed(uint8, encoding="utf-8"))
+ >>> pack("Hello, World!", field)
+ b'\rHello, World!'
+ >>> unpack(field, _)
+ 'Hello, World!'
+
+ .. tab-item:: Caterpillar C
+
+ >>> s = pstring(u8)
+ >>> pack("Hello, World!", s)
+ b'\rHello, World!'
+ >>> unpack(_, s)
+ 'Hello, World!'
Byte Sequences
@@ -201,11 +373,19 @@ Memory
When dealing with data that can be stored in memory and you intend to print out your
unpacked object, the :class:`~caterpillar.fields.Memory` struct is recommended.
->>> m = F(Memory(5)) # static size; dynamic size is allowed too
->>> pack(bytes([i for i in range(5)], m))
-b'\x00\x01\x02\x03\x04'
->>> unpack(m, _)
-
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> m = F(Memory(5)) # static size; dynamic size is allowed too
+ >>> pack(bytes([i for i in range(5)], m))
+ b'\x00\x01\x02\x03\x04'
+ >>> unpack(m, _)
+
+
+ .. tab-item:: Caterpillar C
+
+ *Not supported yet.*
Bytes
~~~~~
@@ -214,20 +394,48 @@ If direct access to the bytes is what you need, the :class:`~caterpillar.fields.
converts the :code:`memoryview` to :code:`bytes`. Additionally, as mentioned earlier, you can
use the :class:`~caterpillar.fields.Prefixed` class to unpack bytes of a prefixed size.
->>> field = F(Bytes(5)) # static, dynamic and greedy size allowed
+
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> field = F(Bytes(5)) # static, dynamic and greedy size allowed
+
+
+ .. tab-item:: Caterpillar C
+
+ >>> b = octetstring(5) # static, dynamic size allowed
With the gained knowledge, let's implement the struct for the `fDAT `_
chunk of our PNG format. It should look like this:
-.. code-block:: python
- :caption: Implementation for the frame data chunk
- @struct(order=BigEndian) # <-- endianess as usual
- class FDATChunk:
- sequence_number: uint32
- # We rather use a memory instance here instead of Bytes()
- frame_data: Memory(parent.length - 4)
+.. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :caption: Implementation for the frame data chunk
+
+ @struct(order=BigEndian) # <-- endianess as usual
+ class FDATChunk:
+ sequence_number: uint32
+ # We rather use a memory instance here instead of Bytes()
+ frame_data: Memory(parent.length - 4)
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: Implementation for the frame data chunk
+
+ parent = ContextPath("parent.obj")
+
+ @struct(endian=BIG_ENDIAN)
+ class FDATChunk:
+ sequence_number: u32
+ frame_data: octetstring(parent.length - 4)
+
.. admonition:: Challenge
@@ -236,6 +444,8 @@ chunk of our PNG format. It should look like this:
.. dropdown:: Solution
+ Python API only:
+
.. code-block:: python
:caption: Sample implementation of the *zTXt* chunk
@@ -272,14 +482,29 @@ reference in detail.
The current object that is being packed or parsed can be referenced with a shortcut :attr:`~caterpillar.context.this`.
Additionally, the parent object (if any) can be referenced by using :attr:`~caterpillar.context.parent`.
+.. tab-set::
-.. code-block:: python
- :caption: Understanding the *context*
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :caption: Understanding the *context*
+
+ @struct
+ class Format:
+ length: uint8
+ foo: CString(this.length) # <-- just reference the length field
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: Understanding the *context*
+
+ this = ContextPath("obj")
- @struct
- class Format:
- length: uint8
- foo: CString(this.length) # <-- just reference the length field
+ @struct
+ class Format:
+ length: u8
+ foo: cstring(this.length)
.. note::
You can apply any operation on context paths. However, be aware that conditional branches must
diff --git a/docs/sphinx/source/tutorial/first_steps.rst b/docs/sphinx/source/tutorial/first_steps.rst
index dc071ca..12af577 100644
--- a/docs/sphinx/source/tutorial/first_steps.rst
+++ b/docs/sphinx/source/tutorial/first_steps.rst
@@ -31,19 +31,35 @@ Given the important role of structs in this library, let's start by understandin
starting point is the `PLTE `_ chunk, which uses three-byte entries
for its data.
-.. code-block:: python
- :caption: RGB struct for the PLTE chunk
+.. tab-set::
- from caterpillar.model import struct # <-- these imports are important
- from caterpillar.fields import *
+ .. tab-item:: Python
- @struct # <-- just decorate the class with the struct() function
- class RGB:
- r: uint8 # <-- a field can be defined just like this
- g: uint8
- b: uint8
+ .. code-block:: python
+ :caption: RGB struct for the PLTE chunk
+
+ from caterpillar.py import * # <-- just import everything
+
+ @struct # <-- just decorate the class with the struct() function
+ class RGB:
+ r: uint8 # <-- a field can be defined just like this
+ g: uint8
+ b: uint8
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: RGB struct for the PLTE chunk (using Caterpillar C)
+
+ from caterpillar.c import * # <-- just import everything
+
+ @struct # <-- just decorate the class with the struct() function
+ class RGB:
+ r: u8 # <-- a field can be defined just like this
+ g: u8
+ b: u8
+
With this simple annotation, the struct class becomes universally applicable. You can
integrate it into other struct definitions or instantiate objects of the class.
@@ -51,7 +67,8 @@ integrate it into other struct definitions or instantiate objects of the class.
.. note::
To optimize memory space and get faster attribute access times, you have to explicitly
- enable the :attr:`~caterpillar.options.S_SLOTS` option. More information can be taken from :ref:`options`.
+ enable the :attr:`~caterpillar.options.S_SLOTS` option. More information can be taken from
+ :ref:`options`.
Wow, thats it? That was less than expected? Let's move directly to working with the defined class.
@@ -71,9 +88,20 @@ module. When packing data, a struct and an input object are needed.
Thanks to the RGB class encapsulating its struct instance, explicitly stating the struct to use
becomes unnecessary.
->>> obj = RGB(r=1, g=2, b=3)
->>> pack(obj) # equivalent to pack(obj, RGB)
-b'\x01\x02\x03'
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> obj = RGB(r=1, g=2, b=3)
+ >>> pack(obj) # equivalent to pack(obj, RGB)
+ b'\x01\x02\x03'
+
+ .. tab-item:: Caterpillar C
+
+ >>> obj = RGB(r=1, g=2, b=3)
+ >>> pack(obj, RGB) # required as of version 2.2.0
+ b'\x01\x02\x03'
+
Unpacking data
^^^^^^^^^^^^^^
@@ -81,8 +109,17 @@ Unpacking data
Recreating data from binary streams is as easy as serializing objects. Here, providing the struct directly
or our struct class is necessary.
->>> unpack(RGB, b"\x01\x02\x03")
-RGB(r=1, g=2, b=3)
+.. tab-set::
+
+ .. tab-item:: Python
+
+ >>> unpack(RGB, b"\x01\x02\x03")
+ RGB(r=1, g=2, b=3)
+
+ .. tab-item:: Caterpillar C
+
+ >>> unpack(b"\x01\x02\x03", RGB)
+ RGB(r=1, g=2, b=3)
And no, we're not done yet - we've just wrapped up the warm-up!
@@ -100,14 +137,36 @@ configure the struct to correctly decode these integer fields.
`StackOverflow `_ or
`Wikipedia `_
-.. code-block:: python
- :caption: Configuring a struct-wide endianess
- @struct(order=BigEndian) # <-- extra argument to apply the order to all fields.
- class PHYSChunk:
- pixels_per_unit_x: uint32 # <-- same definition as above
- pixels_per_unit_y: uint32
- unit: uint8 # <-- endianess meaningless, only one byte
+.. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :caption: Configuring a struct-wide endianess
+
+ @struct(order=BigEndian) # <-- extra argument to apply the order to all fields.
+ class PHYSChunk:
+ pixels_per_unit_x: uint32 # <-- same definition as above
+ pixels_per_unit_y: uint32
+ unit: uint8 # <-- endianess meaningless, only one byte
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: Configuring a struct-wide endianess
+
+ @struct(endian=BIG_ENDIAN) # <-- extra argument to apply the order to all fields.
+ class PHYSChunk:
+ pixels_per_unit_x: u32 # <-- same definition as above
+ pixels_per_unit_y: u32
+ unit: u8 # <-- endianess meaningless, only one byte
+
+ .. note::
+ Even though, there will be :code:`` visible in the annotations
+ of the class, the created struct stores the modified big endian integer
+ atom.
+
If your structs depend on the architecture associated with the binary, you can also specify a
struct-wide :class:`~caterpillar.byteorder.Arch`.
@@ -120,17 +179,37 @@ struct-wide :class:`~caterpillar.byteorder.Arch`.
.. dropdown:: Solution
:icon: check
- .. code-block:: python
- :caption: Example implementation
+ Example implementation
+
+ .. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :linenos:
+
+ @struct(order=BigEndian)
+ class TIMEChunk:
+ year: uint16 # <-- we could also use: BigEndian + uint16
+ month: uint8
+ day: uint8
+ hour: uint8
+ minute: uint8
+ second: uint8
+
+ .. tab-item:: Caterpillar C
- @struct(order=BigEndian)
- class TIMEChunk:
- year: uint16 # <-- we could also use: BigEndian + uint16
- month: uint8
- day: uint8
- hour: uint8
- minute: uint8
- second: uint8
+ .. code-block:: python
+ :linenos:
+
+ @struct(endian=BIG_ENDIAN)
+ class TIMEChunk:
+ year: u16 # <-- we could also use: BIG_ENDIAN + u16
+ month: u8
+ day: u8
+ hour: u8
+ minute: u8
+ second: u8
Note that we can integrate this struct later on.
@@ -142,12 +221,26 @@ To minimize changes to your codebase or require as little adaptation as possible
this library, there's a documentation feature. By utilizing the ability to globally apply
options, you just need the following code:
-.. code-block:: python
- :caption: Enable documentation feature
+.. tab-set::
+
+ .. tab-item:: Python
+
+ .. code-block:: python
+ :caption: Enable documentation feature
+
+ from caterpillar.shortcuts import opt
+
+ opt.set_struct_flags(opt.S_REPLACE_TYPES)
+
+
+ .. tab-item:: Caterpillar C
+
+ .. code-block:: python
+ :caption: Enable documentation feature
- from caterpillar.shortcuts import opt
+ from caterpillar.c import *
- opt.set_struct_flags(opt.S_REPLACE_TYPES)
+ STRUCT_OPTIONS.add(S_REPLACE_TYPES)
.. tip::
If you are working with `Sphinx `_, you might need
diff --git a/docs/sphinx/source/tutorial/index.rst b/docs/sphinx/source/tutorial/index.rst
index 430552b..8d2a40d 100644
--- a/docs/sphinx/source/tutorial/index.rst
+++ b/docs/sphinx/source/tutorial/index.rst
@@ -22,10 +22,11 @@ For those ready to dig deeper or contribute to the framework, the :ref:`library-
is an important resource to start from.
.. toctree::
- :caption: Roadmap
+ :caption: Python
:numbered:
:maxdepth: 4
first_steps.rst
basics.rst
advanced.rst
+
diff --git a/examples/bitfield_example.py b/examples/bitfield_example.py
index d9fc6b7..039feb0 100644
--- a/examples/bitfield_example.py
+++ b/examples/bitfield_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import bitfield, char, int8, unpack, pack
+from caterpillar.py import bitfield, char, int8, unpack, pack
try:
from rich import print
diff --git a/examples/greedy_example.py b/examples/greedy_example.py
index e194406..4b381cb 100644
--- a/examples/greedy_example.py
+++ b/examples/greedy_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import struct, BigEndian, this, unpack, uint8
+from caterpillar.py import struct, BigEndian, this, unpack, uint8
try:
from rich import print
diff --git a/examples/inheritance_example.py b/examples/inheritance_example.py
index 2715b59..0b6f2b2 100644
--- a/examples/inheritance_example.py
+++ b/examples/inheritance_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import struct, pack, unpack, this, uint8, int32, Bytes
+from caterpillar.py import struct, pack, unpack, this, uint8, int32, Bytes
try:
from rich import print
diff --git a/examples/inner_struct.py b/examples/inner_struct.py
index 9bd2993..270fe3d 100644
--- a/examples/inner_struct.py
+++ b/examples/inner_struct.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import struct, pack, unpack, uint8
+from caterpillar.py import struct, pack, unpack, uint8
try:
from rich import print
diff --git a/examples/operator_example.py b/examples/operator_example.py
index a031452..c999d8d 100644
--- a/examples/operator_example.py
+++ b/examples/operator_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import struct, uint16, Operator, S_REPLACE_TYPES
+from caterpillar.py import struct, uint16, Operator, S_REPLACE_TYPES
# Here, we define a custom operator named 'M' that will multiply
# the second argument by 2.
diff --git a/examples/pointer_example.py b/examples/pointer_example.py
index eef81df..f9d3237 100644
--- a/examples/pointer_example.py
+++ b/examples/pointer_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import (
+from caterpillar.py import (
set_struct_flags,
uintptr,
CString,
diff --git a/examples/template_example.py b/examples/template_example.py
index 52e0c17..9fb0b49 100644
--- a/examples/template_example.py
+++ b/examples/template_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import (
+from caterpillar.py import (
struct,
set_struct_flags,
S_REPLACE_TYPES,
diff --git a/examples/union_example.py b/examples/union_example.py
index 12ad58d..aa56c55 100644
--- a/examples/union_example.py
+++ b/examples/union_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import uint16, uint32, boolean, union
+from caterpillar.py import uint16, uint32, boolean, union
try:
from rich import print
diff --git a/examples/varint_example.py b/examples/varint_example.py
index 78b6796..d8ad9e8 100644
--- a/examples/varint_example.py
+++ b/examples/varint_example.py
@@ -1,5 +1,5 @@
# type: ignore
-from caterpillar._Py import (
+from caterpillar.py import (
vint,
VARINT_LSB,
pack,
diff --git a/pyproject.toml b/pyproject.toml
index 97cde92..e024a7c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,10 +6,11 @@ build-backend = "scikit_build_core.build"
build-dir = "build/{wheel_tag}"
sdist.include = ["*.pyi", "*.h"]
wheel.py-api = "cp312"
+cmake.source-dir = "."
[project]
name = "caterpillar"
-version = "2.1.5"
+version = "2.2.0"
description="Library to pack and unpack structurized binary data."
authors = [
diff --git a/src/atomimpl/intatomobj.c b/src/atomimpl/intatomobj.c
deleted file mode 100644
index 323ef49..0000000
--- a/src/atomimpl/intatomobj.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/* intatom C implementation */
-#include
-
-#include "caterpillar/caterpillar.h"
-#include "caterpillar/atoms/int.h"
-#include "caterpillar/arch.h"
-#include "caterpillar/state.h"
-#include "caterpillar/module.h"
-#include
-
-static PyObject*
-cp_intatom__type__(CpIntAtomObject* self)
-{
- return Py_XNewRef(&PyLong_Type);
-}
-
-static PyObject *
-cp_intatom__size__(CpIntAtomObject* self, PyObject *ctx)
-{
- return Py_XNewRef(self->m_byte_count);
-}
-
-static PyObject*
-cp_intatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
-{
- CpIntAtomObject* self = (CpIntAtomObject*)type->tp_alloc(type, 0);
- if (self != NULL) {
- self->m_byte_count = NULL;
- self->_m_bits = 0;
- self->_m_signed = true;
- self->_m_little_endian = true;
-
- CpFieldCAtom_CATOM(self).ob_pack = (packfunc)CpIntAtom_Pack;
- CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)CpIntAtom_Unpack;
- CpFieldCAtom_CATOM(self).ob_pack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_unpack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_size = (sizefunc)cp_intatom__size__;
- CpFieldCAtom_CATOM(self).ob_type = (typefunc)cp_intatom__type__;
- CpFieldCAtom_CATOM(self).ob_bits = NULL;
- }
- return (PyObject*)self;
-}
-
-static void
-cp_intatom_dealloc(CpIntAtomObject* self)
-{
- Py_XDECREF(self->m_byte_count);
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static int
-cp_intatom_init(CpIntAtomObject* self, PyObject* args, PyObject* kwds)
-{
- static char* kwlist[] = { "nbits", "signed", "little_endian", NULL };
- int _signed = true, little_endian = true;
- size_t bits = 0;
- if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "I|pp", kwlist, &bits, &_signed, &little_endian)) {
- return -1;
- }
- if (bits == 0) {
- PyErr_SetString(PyExc_ValueError, "bytes cannot be zero");
- return -1;
- }
- if (bits % 8) {
- PyErr_SetString(PyExc_ValueError, "nbits must be a multiple of 8");
- return -1;
- }
-
- self->_m_bits = bits;
- self->_m_byte_count = bits / 8;
- self->_m_signed = _signed;
- self->_m_little_endian = little_endian;
- self->m_byte_count = PyLong_FromUnsignedLong(self->_m_byte_count);
- if (!self->m_byte_count) {
- return -1;
- }
- return 0;
-}
-
-
-
-/* Public API */
-int
-CpIntAtom_Pack(CpIntAtomObject* self, PyObject* op, CpLayerObject* layer)
-{
- PyObject* bytes = CpObject_CreateOneArg(&PyBytes_Type, self->m_byte_count);
- if (!bytes) {
- return -1;
- }
-
- if (!PyLong_Check(op)) {
- PyErr_Format(PyExc_TypeError, "op must be an int, got %R", op);
- Py_DECREF(bytes);
- }
-
- int little_endian = self->_m_little_endian;
- if (layer->m_field) {
- _modulestate *mod = layer->m_state->mod;
- PyObject *endian = ((CpFieldObject *)layer->m_field)->m_endian;
-
- if (CpEndian_Check(endian))
- /* If the field has an endian specified, use that. */
- little_endian = CpEndian_IsLittleEndian((CpEndianObject *)endian, mod);
- }
-
- int res = _PyLong_AsByteArray((PyLongObject*)op,
- (unsigned char*)PyBytes_AS_STRING(op),
- self->_m_byte_count,
- little_endian,
- self->_m_signed);
- if (res == -1) {
- return -1;
- }
-
- PyObject* result = CpState_Write(layer->m_state, bytes);
- if (!result) {
- return -1;
- }
- Py_DECREF(result);
- return 0;
-}
-
-PyObject*
-CpIntAtom_Unpack(CpIntAtomObject* self, CpLayerObject* layer)
-{
- PyObject* bytes = CpState_Read(layer->m_state, self->_m_byte_count);
- if (!bytes) {
- return NULL;
- }
-
- int little_endian = self->_m_little_endian;
- if (layer->m_field) {
- _modulestate *mod = layer->m_state->mod;
- PyObject *endian = ((CpFieldObject *)layer->m_field)->m_endian;
-
- if (CpEndian_Check(endian))
- /* If the field has an endian specified, use that. */
- little_endian = CpEndian_IsLittleEndian((CpEndianObject *)endian, mod);
- }
-
- PyObject* obj =
- _PyLong_FromByteArray((unsigned char*)PyBytes_AS_STRING(bytes),
- self->_m_byte_count,
- little_endian,
- self->_m_signed);
- Py_DECREF(bytes);
- return obj;
-}
-
-/* docs */
-
-/* type */
-static PyMemberDef CpIntAtom_Members[] = {
- { "nbytes",
- T_PYSSIZET,
- offsetof(CpIntAtomObject, _m_byte_count),
- READONLY,
- NULL },
- { "nbits", T_PYSSIZET, offsetof(CpIntAtomObject, _m_bits), READONLY, NULL },
- { "signed", T_BOOL, offsetof(CpIntAtomObject, _m_signed), READONLY, NULL },
- { "little_endian",
- T_BOOL,
- offsetof(CpIntAtomObject, _m_little_endian)},
- { NULL } /* Sentinel */
-};
-
-PyTypeObject CpIntAtom_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(intatom),
- .tp_basicsize = sizeof(CpIntAtomObject),
- .tp_dealloc = (destructor)cp_intatom_dealloc,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = NULL,
- .tp_members = CpIntAtom_Members,
- .tp_new = (newfunc)cp_intatom_new,
-};
\ No newline at end of file
diff --git a/src/atomimpl/padatomobj.c b/src/atomimpl/padatomobj.c
deleted file mode 100644
index c411c85..0000000
--- a/src/atomimpl/padatomobj.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/* padding atom implementation */
-
-#include "caterpillar/caterpillar.h"
-#include "caterpillar/atoms/primitive.h"
-#include "caterpillar/parsing.h"
-#include "caterpillar/module.h"
-#include
-
-static PyObject*
-cp_paddingatom__type__(PyObject* self)
-{
- return Py_XNewRef(&_PyNone_Type);
-}
-
-static PyObject*
-cp_paddingatom__size__(PyObject* self, PyObject *ctx)
-{
- /* NOTE:
- We are using the size of one byte here to allow padding atoms to be
- statically sized. It helps size calculation as well as defines the
- default size for padding atoms.
- */
- return PyLong_FromSize_t(1);
-}
-
-static PyObject*
-cp_paddingatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
-{
- CpPaddingAtomObject* self = (CpPaddingAtomObject*)type->tp_alloc(type, 0);
- if (self != NULL) {
- CpFieldCAtom_CATOM(self).ob_pack = (packfunc)CpPaddingAtom_Pack;
- CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)CpPaddingAtom_Unpack;
- CpFieldCAtom_CATOM(self).ob_pack_many =
- (packmanyfunc)CpPaddingAtom_PackMany;
- CpFieldCAtom_CATOM(self).ob_unpack_many =
- (unpackmanyfunc)CpPaddingAtom_UnpackMany;
- CpFieldCAtom_CATOM(self).ob_size = (sizefunc)cp_paddingatom__size__;
- CpFieldCAtom_CATOM(self).ob_type = (typefunc)cp_paddingatom__type__;
- CpFieldCAtom_CATOM(self).ob_bits = NULL;
- }
- return (PyObject*)self;
-}
-
-static void
-cp_paddingatom_dealloc(CpPaddingAtomObject* self)
-{
- self->padding = 0;
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static int
-cp_paddingatom_init(CpPaddingAtomObject* self, PyObject* args, PyObject* kwds)
-{
- static char* kwlist[] = { "pad", NULL };
- char value = 0;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|b", kwlist, &value))
- return -1;
-
- self->padding = value;
- return 0;
-}
-
-/* Public API */
-
-int
-CpPaddingAtom_Pack(CpPaddingAtomObject* self,
- PyObject* value,
- CpLayerObject* layer)
-{
- /* value will be ignored here */
- PyObject* res;
- if (!self->padding) {
- res = CpState_Write(layer->m_state, layer->m_state->mod->cp_bytes__false);
- } else {
- PyObject* bytes = PyBytes_FromStringAndSize((char*)&self->padding, 1);
- if (!bytes) {
- return -1;
- }
- res = CpState_Write(layer->m_state, bytes);
- Py_DECREF(bytes);
- }
-
- if (!res) {
- return -1;
- }
- Py_XDECREF(res);
- return 0;
-}
-
-int
-CpPaddingAtom_PackMany(CpPaddingAtomObject* self,
- PyObject* value,
- CpLayerObject* layer)
-{
- /* value will be ignored here */
- PyObject *res = NULL, *bytes = NULL, *objSize = NULL, *objLengh = NULL;
- bool greedy = false;
- Py_ssize_t length = 0;
-
- if (!layer->m_field) {
- PyErr_SetString(PyExc_TypeError, "layer must have a field");
- return -1;
- }
-
- /* The following call will fail if the length is a prefixed statement
- * (slice)*/
- objLengh =
- CpField_GetLength((CpFieldObject*)layer->m_field, (PyObject*)layer);
- if (!objLengh) {
- return -1;
- }
- if (_CpPack_EvalLength(layer, objLengh, -1, &greedy, &length) < 0) {
- Py_XDECREF(objLengh);
- return -1;
- }
- Py_XDECREF(objLengh);
-
- if (length == 0) {
- return 0;
- } else if (length < 0) {
- PyErr_SetString(PyExc_ValueError, "length must be >= 0");
- return -1;
- } else if (greedy) {
- PyErr_SetString(PyExc_ValueError, "length must be >= 0 and not greedy!");
- return -1;
- }
-
- objSize = PyLong_FromSsize_t(length);
- if (!objSize) {
- return -1;
- }
- bytes = CpObject_CreateOneArg(&PyBytes_Type, objSize);
- if (!bytes) {
- Py_DECREF(objSize);
- return -1;
- }
- /* unsafe { */
- memset(PyBytes_AS_STRING(bytes), self->padding, length);
- /* } */
- res = CpState_Write(layer->m_state, bytes);
- Py_DECREF(bytes);
- Py_DECREF(objSize);
- if (!res) {
- return -1;
- }
- Py_XDECREF(res);
- return 0;
-}
-
-PyObject*
-CpPaddingAtom_Unpack(CpPaddingAtomObject* self, CpLayerObject* layer)
-{
- PyObject* res = CpState_Read(layer->m_state, 1);
- if (!res) {
- return NULL;
- }
- Py_XDECREF(res);
- Py_RETURN_NONE;
-}
-
-PyObject*
-CpPaddingAtom_UnpackMany(CpPaddingAtomObject* self, CpLayerObject* layer)
-{
- PyObject *res = NULL, *bytes = NULL, *objLengh = NULL;
- bool greedy = false;
- Py_ssize_t length = 0, parsedLength = 0;
-
- objLengh =
- CpField_GetLength((CpFieldObject*)layer->m_field, (PyObject*)layer);
- if (!objLengh) {
- return NULL;
- }
- if (_CpPack_EvalLength(layer, objLengh, -1, &greedy, &length) < 0) {
- Py_DECREF(objLengh);
- return NULL;
- }
- Py_DECREF(objLengh);
-
- res = CpState_Read(layer->m_state, length);
- if (!res) {
- return NULL;
- }
-
- Py_XDECREF(res);
- Py_RETURN_NONE;
-}
-
-PyObject*
-cp_paddingatom__repr__(CpPaddingAtomObject* self)
-{
- return PyUnicode_FromFormat("padding(0x%02x)", self->padding);
-}
-
-/* type setup */
-PyTypeObject CpPaddingAtom_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(paddingatom),
- .tp_basicsize = sizeof(CpPaddingAtomObject),
- .tp_dealloc = (destructor)cp_paddingatom_dealloc,
- .tp_repr = (reprfunc)cp_paddingatom__repr__,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = NULL,
- .tp_init = (initproc)cp_paddingatom_init,
- .tp_new = (newfunc)cp_paddingatom_new,
-};
\ No newline at end of file
diff --git a/src/capi.dat b/src/capi.dat
new file mode 100644
index 0000000..6e213c9
--- /dev/null
+++ b/src/capi.dat
@@ -0,0 +1,233 @@
+# Caterpillar C API
+#
+# All types and API functions are described in this file. They are used to
+# generate the Caterpillar C API headers and to build the documentation.
+#
+# modified format from sphinx documentation
+# see https://github.com/python/cpython/blob/main/Doc/data/refcounts.dat
+#
+# There are currently four different types of statements:
+#
+# type:INDEX:STRUCT_NAME:TYPEDEF_NAME:CAPI_TYPE_NAME
+# Defines a C API type for a C structure. The index is optional and
+# the CAPI_TYPE_NAME will add an extra macro to the header file.
+#
+# obj:INDEX:NAME:TYPE
+# Defines a C API object.
+#
+# func:INDEX:NAME:RETURN_TYPE:REFCOUNT
+# Defines a C API function. The function must be present within the
+# source set of this file.
+#
+# src:FILE
+# Defines the source file (relative to this file) that contains the
+# function definitions.
+
+src:ccaterpillar/context.c
+src:ccaterpillar/arch.c
+src:ccaterpillar/atomobj.c
+src:ccaterpillar/option.c
+src:ccaterpillar/struct.c
+src:ccaterpillar/state.c
+src:ccaterpillar/layer.c
+src:ccaterpillar/parsing_pack.c
+src:ccaterpillar/parsing_unpack.c
+src:ccaterpillar/parsing_typeof.c
+src:ccaterpillar/parsing_sizeof.c
+src:ccaterpillar/atomimpl/bool.c
+src:ccaterpillar/atomimpl/float.c
+src:ccaterpillar/atomimpl/char.c
+src:ccaterpillar/atomimpl/int.c
+src:ccaterpillar/atomimpl/pad.c
+src:ccaterpillar/atomimpl/string.c
+src:ccaterpillar/atomimpl/const.c
+src:ccaterpillar/atomimpl/bytes.c
+src:ccaterpillar/atomimpl/pstring.c
+src:ccaterpillar/atomimpl/enum.c
+src:ccaterpillar/atomimpl/varint.c
+src:ccaterpillar/atomimpl/computed.c
+src:ccaterpillar/atomimpl/lazy.c
+src:ccaterpillar/atomimpl/cstring.c
+src:ccaterpillar/atomimpl/builtins/builtin.c
+src:ccaterpillar/atomimpl/builtins/repeated.c
+src:ccaterpillar/atomimpl/builtins/condition.c
+src:ccaterpillar/atomimpl/builtins/switch.c
+src:ccaterpillar/atomimpl/builtins/atoffset.c
+src:ccaterpillar/atomimpl/builtins/primitive.c
+
+# First index is reserved for the global module reference
+obj:0:CpModule:PyModuleDef
+
+type:-:_modulestate:_modulestate:-
+
+type:1:_atomobj:CpAtomObject:atom
+type:2:_catomobj:CpCAtomObject:catom
+type:3:_archobj:CpArchObject:Arch
+type:4:_endianobj:CpEndianObject:Endian
+type:5:_contextobj:CpContextObject:Context
+type:6:_unaryexpr:CpUnaryExprObject:unaryexpr
+type:7:_binaryexpr:CpBinaryExprObject:binaryexpr
+type:8:_contextpath:CpContextPathObject:ContextPath
+# type:9:_fieldobj:CpFieldObject:Field
+# type:10:_fieldatomobj:CpFieldAtomObject:fieldatom
+# type:11:_fieldcatomobj:CpFieldCAtomObject:fieldcatom
+
+obj:12:CpInvalidDefault_Type:-
+obj:13:CpDefaultOption_Type:-
+obj:14:_CpInvalidDefault_Object:PyObject
+obj:15:_CpDefaultOption_Object:PyObject
+
+type:16:_option:CpOptionObject:Option
+type:17:_stateobj:CpStateObject:State
+type:18:_layerobj:CpLayerObject:layer
+
+# REVISIT: maybe rename to _structfieldinfo
+type:19:_fieldinfoobj:CpStructFieldInfoObject:fieldinfo
+type:20:_structobj:CpStructObject:Struct
+type:21:_floatatomobj:CpFloatAtomObject:Float
+type:22:_intatomobj:CpIntAtomObject:Int
+type:23:_boolatomobj:CpBoolAtomObject:Bool
+type:24:_charatomobj:CpCharAtomObject:Char
+type:25:_paddingatomobj:CpPaddingAtomObject:Padding
+type:26:_stringatomobj:CpStringAtomObject:string
+type:27:_constatomobj:CpConstAtomObject:const
+type:28:_builtinatomobj:CpBuiltinAtomObject:builtinatom
+type:29:_repeatedatomobj:CpRepeatedAtomObject:repeated
+type:30:_seqlayerobj:CpSeqLayerObject:seqlayer
+type:31:_objlayerobj:CpObjLayerObject:objlayer
+type:32:_conditionatomobj:CpConditionAtomObject:condition
+type:33:_switchatomobj:CpSwitchAtomObject:switch
+type:34:_offsetatomobj:CpOffsetAtomObject:atoffset
+type:35:_primitiveatomobj:CpPrimitiveAtomObject:patom
+type:36:_lengthinfoobj:CpLengthInfoObject:lengthinfo
+type:37:_bytesatomobj:CpBytesAtomObject:octetstring
+type:38:_pstringatomobj:CpPStringAtomObject:pstring
+type:39:_enumatomobj:CpEnumAtomObject:enumeration
+type:40:_varintatomobj:CpVarIntAtomObject:VarInt
+type:41:_computedatomobj:CpComputedAtomObject:computed
+type:42:_lazyatomobj:CpLazyAtomObject:lazy
+type:43:_cstringatomobj:CpCStringAtomObject:cstring
+
+func:50:CpEndian_IsLittleEndian:int:null
+func:53:CpContext_New:CpContextObject*:+1
+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
+# func:61:CpField_GetOffset:Py_ssize_t:null
+# func:62:CpField_EvalSwitch:PyObject*:+1
+# func:63:CpField_GetLength:PyObject*:+1
+func:64:CpTypeOf:PyObject*:+1
+# func:65:CpTypeOf_Field:PyObject*:+1
+func:66:CpTypeOf_Common:PyObject*:+1
+func:67:CpPack:int:null
+# func:68:CpPack_Field:int:null
+# func:69:CpPack_Common:int:null
+# func:70:CpPack_Struct:int:null
+func:72:_CpPack_EvalLength:int:null
+func:73:CpSizeOf:PyObject*:+1
+# func:74:CpSizeOf_Field:PyObject*:+1
+func:75:CpSizeOf_Struct:PyObject*:+1
+func:76:CpSizeOf_Common:PyObject*:+1
+func:77:_Cp_SizeOf:PyObject*:+1
+func:78:CpUnpack:PyObject*:+1
+# func:79:CpUnpack_Field:PyObject*:+1
+# func:80:CpUnpack_Common:PyObject*:+1
+# func:81:CpUnpack_Struct:PyObject*:+1
+func:83:_CpUnpack_EvalLength:int:null
+func:84:CpUnpack_CAtom:PyObject*:+1
+func:85:CpPack_CAtom:int:null
+func:86:CpSizeOf_CAtom:PyObject*:+1
+func:87:CpTypeOf_CAtom:PyObject*:+1
+func:88:CpState_New:CpStateObject*:null
+func:89:CpState_Tell:PyObject*:+1
+func:90:CpState_Seek:PyObject*:+1
+func:91:CpState_Read:PyObject*:+1
+func:92:CpState_ReadSsize_t:PyObject*:+1
+func:93:CpState_ReadFully:PyObject*:+1
+func:94:CpState_Write:PyObject*:+1
+func:95:CpState_SetGlobals:int:null
+func:96:CpLayer_New:CpLayerObject*:+1
+func:98:CpLayer_Invalidate:int:null
+func:99:CpStructFieldInfo_New:CpStructFieldInfoObject*:+1
+func:100:CpStruct_AddFieldInfo:int:null
+# func:101:CpStruct_AddField:int:null
+func:102:CpStruct_New:CpStructObject*:+1
+func:103:CpStruct_GetAnnotations:PyObject*:+1
+func:104:CpStruct_ReplaceType:int:null
+func:105:CpStruct_HasOption:int:null
+func:106:CpStruct_Pack:int:null
+func:107:CpStruct_Unpack:PyObject*:+1
+func:108:CpStruct_SizeOf:PyObject*:+1
+func:109:CpStructModel_Check:int:null
+func:110:CpStructModel_GetStruct:PyObject*:+1
+func:111:CpSeqLayer_New:CpSeqLayerObject*:+1
+func:112:CpSeqLayer_SetSequence:int:null
+func:113:CpObjLayer_New:CpObjLayerObject*:+1
+
+# atom api
+func:120:CpIntAtom_Pack:int:null
+func:121:CpIntAtom_Unpack:PyObject*:+1
+func:122:CpFloatAtom_Pack:int:null
+func:123:CpFloatAtom_Unpack:PyObject*:+1
+func:124:CpBoolAtom_Pack:int:null
+func:125:CpBoolAtom_Unpack:PyObject*:+1
+func:126:CpCharAtom_Pack:int:null
+func:127:CpCharAtom_Unpack:PyObject*:+1
+func:128:CpPaddingAtom_Pack:int:null
+func:129:CpPaddingAtom_PackMany:int:null
+func:130:CpPaddingAtom_Unpack:PyObject*:+1
+func:131:CpPaddingAtom_UnpackMany:PyObject*:+1
+func:132:CpStringAtom_Pack:int:null
+func:133:CpStringAtom_Unpack:PyObject*:+1
+func:134:CpConstAtom_Pack:int:null
+func:135:CpConstAtom_Unpack:PyObject*:+1
+func:-:CpRepeatedAtom_New:CpRepeatedAtomObject*:+1
+func:136:CpRepeatedAtom_Pack:int:null
+func:137:CpRepeatedAtom_Unpack:PyObject*:+1
+func:138:CpRepeatedAtom_GetLength:PyObject*:+1
+func:-:CpConditionAtom_New:CpConditionAtomObject*:+1
+func:139:CpConditionAtom_Pack:int:null
+func:140:CpConditionAtom_Unpack:PyObject*:+1
+func:141:CpConditionAtom_IsEnabled:int:null
+func:-:CpSwitchAtom_New:CpSwitchAtomObject*:+1
+func:142:CpSwitchAtom_GetNext:PyObject*:+1
+func:143:CpSwitchAtom_Pack:int:null
+func:144:CpSwitchAtom_Unpack:PyObject*:+1
+func:-:CpOffsetAtom_FromSsize_t:CpOffsetAtomObject*:+1
+func:-:CpOffsetAtom_New:CpOffsetAtomObject*:+1
+func:145:CpOffsetAtom_Pack:int:null
+func:146:CpOffsetAtom_Unpack:PyObject*:+1
+func:147:CpOffsetAtom_GetOffset:PyObject*:+1
+func:-:CpBytesAtom_New:CpBytesAtomObject*:+1
+func:148:CpBytesAtom_GetLength:PyObject*:+1
+func:149:CpBytesAtom_Pack:int:null
+func:150:CpBytesAtom_Unpack:PyObject*:+1
+func:-:CpPStringAtom_New:CpPStringAtomObject*:+1
+func:152:CpPStringAtom_Pack:int:null
+func:153:CpPStringAtom_Unpack:PyObject*:+1
+func:-:CpEnumAtom_New:CpEnumAtomObject*:+1
+func:154:CpEnumAtom_Pack:int:null
+func:155:CpEnumAtom_Unpack:PyObject*:+1
+func:-:CpVarIntAtom_New:CpVarIntAtomObject*:+1
+func:156:CpVarIntAtom_Pack:int:null
+func:157:CpVarIntAtom_Unpack:PyObject*:+1
+func:158:CpVarIntAtom_BSwap:PyObject*:+1
+func:159:CpVarIntAtom_BSwapUnsignedLongLong:ulonglong:null
+func:160:CpVarIntAtom_BSwapLongLong:longlong:null
+func:161:CpVarIntAtom_BSwapSsize_t:Py_ssize_t:null
+func:-:CpComputedAtom_New:CpComputedAtomObject*:+1
+func:162:CpComputedAtom_Pack:int:null
+func:163:CpComputedAtom_Unpack:PyObject*:+1
+func:-:CpComputedAtom_Value:PyObject*:+1
+func:-:CpLazyAtom_New:CpLazyAtomObject*:+1
+func:164:CpLazyAtom_Pack:int:null
+func:165:CpLazyAtom_Unpack:PyObject*:+1
+func:-:CpLazyAtom_Atom:PyObject*:+1
+func:166:CpCStringAtom_Pack:int:null
+func:167:CpCStringAtom_Unpack:PyObject*:+1
\ No newline at end of file
diff --git a/src/caterpillar/_C.pyi b/src/caterpillar/_C.pyi
index 0244f9e..813ee8c 100644
--- a/src/caterpillar/_C.pyi
+++ b/src/caterpillar/_C.pyi
@@ -1,69 +1,95 @@
from __future__ import annotations
-from typing import Any, Optional, Collection, Union, Callable, IO
+from typing import Any, Optional, Collection, Union, Callable, IO, TypeVar
+from enum import EnumType
_Length = Union[int, ContextLambda, slice, Ellipsis]
+ContextLambda = Callable[[Context], Any]
+_ConstType = Union[Any, ContextLambda]
-DefaultOption: DefaultOptionType = ...
-FIELD_OPTIONS: set = ...
-F_DYNAMIC: Option = ...
-F_SEQUENTIAL: Option = ...
-HOST_ARCH: Arch = ...
-InvalidDefault: InvalidDefaultType = ...
-NATIVE_ENDIAN: Endian = ...
-LITTLE_ENDIAN: Endian = ...
-BIG_ENDIAN: Endian = ...
-STRUCT_OPTIONS: set = ...
-S_DISCARD_CONST: Option = ...
-S_DISCARD_UNNAMED: Option = ...
-S_EVAL_ANNOTATIONS: Option = ...
-S_REPLACE_TYPES: Option = ...
-S_SLOTS: Option = ...
-S_UNION: Option = ...
+BIG_ENDIAN: Endian
+DefaultOption: DefaultOptionType
+FIELD_OPTIONS: set
+F_DYNAMIC: Option
+F_SEQUENTIAL: Option
+HOST_ARCH: Arch
+InvalidDefault: InvalidDefaultType
+LITTLE_ENDIAN: Endian
+NATIVE_ENDIAN: Endian
+STRUCT_OPTIONS: set
+S_DISCARD_CONST: Option
+S_DISCARD_UNNAMED: Option
+S_EVAL_ANNOTATIONS: Option
+S_REPLACE_TYPES: Option
+S_SLOTS: Option
+S_UNION: Option
+boolean: Bool
+char: Char
+f16: Float
+f32: Float
+f64: Float
+i128: Int
+i16: Int
+i24: Int
+i32: Int
+i64: Int
+i8: Int
+lsbvarint: VarInt
+padding: Padding
+u128: Int
+u16: Int
+u24: Int
+u32: Int
+u64: Int
+u8: Int
+varint: VarInt
class Context(dict):
- def __init__(self, *args, **kwargs) -> None: ...
+ def __init__(self, /, **kwargs) -> None: ...
def __context_getattr__(self, *args, **kwargs): ...
-ContextLambda = Callable[[Context], Any]
-
class Arch:
name: str
ptr_size: int
def __init__(self, name: str, ptr_size: int) -> None: ...
def __hash__(self) -> int: ...
-class BinaryExpr:
+class binaryexpr:
expr: int
lhs: Union[ContextLambda, Any]
rhs: Union[ContextLambda, Any]
- def __init__(self, *args, **kwargs) -> None: ...
- def __call__(self, *args, **kwargs): ...
+ def __init__(
+ self,
+ expr: int,
+ left: Union[ContextLambda, Any],
+ right: Union[ContextLambda, Any],
+ ) -> None: ...
+ def __call__(self, *args, **kwargs) -> Any: ...
class ContextPath:
path: str
def __init__(self, path: str) -> None: ...
- def __add__(self, other) -> BinaryExpr: ...
- def __and__(self, other) -> BinaryExpr: ...
+ def __add__(self, other) -> binaryexpr: ...
+ def __and__(self, other) -> binaryexpr: ...
def __call__(self, context: Context) -> Any: ...
- def __eq__(self, other: object) -> BinaryExpr: ...
- def __floordiv__(self, other) -> BinaryExpr: ...
- def __ge__(self, other: object) -> BinaryExpr: ...
- def __gt__(self, other: object) -> BinaryExpr: ...
+ def __eq__(self, other: object) -> binaryexpr: ...
+ def __floordiv__(self, other) -> binaryexpr: ...
+ def __ge__(self, other: object) -> binaryexpr: ...
+ def __gt__(self, other: object) -> binaryexpr: ...
def __hash__(self) -> int: ...
- def __invert__(self) -> UnaryExpr: ...
- def __le__(self, other: object) -> BinaryExpr: ...
- def __lshift__(self, other) -> BinaryExpr: ...
- def __lt__(self, other: object) -> BinaryExpr: ...
- def __mod__(self, other) -> BinaryExpr: ...
- def __mul__(self, other) -> BinaryExpr: ...
- def __ne__(self, other: object) -> BinaryExpr: ...
- def __neg__(self) -> UnaryExpr: ...
- def __or__(self, other) -> BinaryExpr: ...
- def __sub__(self, other) -> UnaryExpr: ...
- def __truediv__(self, other) -> BinaryExpr: ...
+ def __invert__(self) -> unaryexpr: ...
+ def __le__(self, other: object) -> binaryexpr: ...
+ def __lshift__(self, other) -> binaryexpr: ...
+ def __lt__(self, other: object) -> binaryexpr: ...
+ def __mod__(self, other) -> binaryexpr: ...
+ def __mul__(self, other) -> binaryexpr: ...
+ def __ne__(self, other: object) -> binaryexpr: ...
+ def __neg__(self) -> unaryexpr: ...
+ def __or__(self, other) -> binaryexpr: ...
+ def __sub__(self, other) -> unaryexpr: ...
+ def __truediv__(self, other) -> binaryexpr: ...
def __type__(self) -> type: ...
- def __xor__(self, other) -> BinaryExpr: ...
+ def __xor__(self, other) -> binaryexpr: ...
class DefaultOptionType:
def __init__(self) -> None: ...
@@ -82,85 +108,48 @@ class Endian:
class atom:
def __init__(self) -> None: ...
- def __pack__(self, obj: Any, context: Context) -> None: ...
- def __size__(self, context: Context) -> Any: ...
+ def __pack__(self, obj: Any, context: layer) -> None: ...
+ def __size__(self, context: layer) -> Any: ...
def __type__(self) -> Any: ...
- def __unpack__(self, context: Context) -> Any: ...
+ def __unpack__(self, context: layer) -> Any: ...
class catom(atom):
def __init__(self) -> None: ...
- def __pack__(self, obj: Any, context: Context) -> None: ...
- def __pack_many__(self, obj: Collection[Any], context: Context) -> None: ...
- def __size__(self, context: Context) -> Any: ...
+ def __pack__(self, obj: Any, context: layer) -> None: ...
+ def __pack_many__(self, obj: Collection[Any], layer: layer) -> None: ...
+ def __size__(self, context: layer) -> Any: ...
def __type__(self) -> Any: ...
- def __unpack__(self, context: Context) -> Any: ...
- def __unpack_many__(self, context: Context) -> Collection[Any]: ...
+ def __unpack__(self, context: layer) -> Any: ...
+ def __unpack_many__(self, context: layer, lengthinfo) -> Collection[Any]: ...
-class fieldatom(atom):
- def __init__(self) -> None: ...
- def __add__(self, endian: Endian) -> Field: ...
- def __floordiv__(self, condition: Union[bool, ContextLambda]) -> Field: ...
- def __getitem__(
- self, length: _Length
- ) -> Field: ...
- def __matmul__(self, offset: Union[ContextLambda, int]) -> Field: ...
- def __or__(self, option: Option) -> Field: ...
- def __radd__(self, endian: Endian) -> Field: ...
- def __rfloordiv__(self, condition: Union[bool, ContextLambda]) -> Field: ...
- def __rmatmul__(self, offset: Union[ContextLambda, int]) -> Field: ...
- def __ror__(self, option: Option) -> Field: ...
- def __rrshift__(self, switch: Union[dict, ContextLambda]) -> Field: ...
- def __rshift__(self, switch: Union[dict, ContextLambda]) -> Field: ...
- def __rxor__(self, option: Option) -> Field: ...
- def __xor__(self, option: Option) -> Field: ...
+class builtinatom(catom):
+ def __init__(self, *args, **kwargs) -> None: ...
+ def __floordiv__(self, other) -> condition: ...
+ def __getitem__(self, index) -> repeated: ...
+ def __matmul__(self, *args, **kwargs) -> atoffset: ...
+ def __rfloordiv__(self, other) -> condition: ...
+ def __rmatmul__(self, *args, **kwargs) -> atoffset: ...
+ def __rrshift__(self, other) -> switch: ...
+ def __rshift__(self, other) -> switch: ...
-class fieldcatom(catom):
- def __init__(self) -> None: ...
- def __add__(self, endian: Endian) -> Field: ...
- def __floordiv__(self, condition: Union[bool, ContextLambda]) -> Field: ...
- def __getitem__(
- self, length: _Length
- ) -> Field: ...
- def __matmul__(self, offset: Union[ContextLambda, int]) -> Field: ...
- def __or__(self, option: Option) -> Field: ...
- def __radd__(self, endian: Endian) -> Field: ...
- def __rfloordiv__(self, condition: Union[bool, ContextLambda]) -> Field: ...
- def __rmatmul__(self, offset: Union[ContextLambda, int]) -> Field: ...
- def __ror__(self, option: Option) -> Field: ...
- def __rrshift__(self, switch: Union[dict, ContextLambda]) -> Field: ...
- def __rshift__(self, switch: Union[dict, ContextLambda]) -> Field: ...
- def __rxor__(self, option: Option) -> Field: ...
- def __xor__(self, option: Option) -> Field: ...
+class Char(builtinatom):
+ def __init__(self, *args, **kwargs) -> None: ...
-class Field:
- arch: Arch
- atom: atom
+class condition(builtinatom):
+ atom: Any
condition: Union[bool, ContextLambda]
- default: Union[Any, InvalidDefaultType]
- endian: Endian
- length: _Length
- name: str
- offset: Union[ContextLambda, int]
- options: set[Option]
- switch: Union[dict, ContextLambda]
- def __init__(
- self,
- atom: atom,
- name: str = ...,
- endian: Endian = ...,
- offset: Union[ContextLambda, int] = ...,
- arch: Arch = ...,
- length: _Length = ...,
- default: Union[Any, InvalidDefaultType] = ...,
- switch: Union[dict, ContextLambda] = ...,
- options: set[Option] = ...,
- condition: Union[bool, ContextLambda] = ...,
- ) -> None: ...
+ def __init__(self, condition: Union[bool, ContextLambda], atom: Any) -> None: ...
+ def is_enabled(self, context: layer) -> bool: ...
+ def __set_byteorder__(self, byteorder: Endian) -> condition: ...
+
+class const(builtinatom):
+ def __init__(self, *args, **kwargs) -> None: ...
+
+class builtinatom(catom):
+ def __init__(self) -> None: ...
def __add__(self, endian: Endian) -> Field: ...
def __floordiv__(self, condition: Union[bool, ContextLambda]) -> Field: ...
- def __getitem__(
- self, length: _Length
- ) -> Field: ...
+ def __getitem__(self, length: _Length) -> Field: ...
def __matmul__(self, offset: Union[ContextLambda, int]) -> Field: ...
def __or__(self, option: Option) -> Field: ...
def __radd__(self, endian: Endian) -> Field: ...
@@ -199,10 +188,17 @@ class State:
class fieldinfo:
excluded: bool
- field: Field
- def __init__(self, field: Field, excluded: bool = ...) -> None: ...
+ default: Any
+ field: atom
+ name: str
+ def __init__(self, field: atom, excluded: bool = ...) -> None: ...
+
+class lengthinfo:
+ length: int
+ greedy: bool
+ def __init__(self, length: int = ..., greedy: bool = ...) -> None: ...
-class Struct(fieldatom):
+class Struct(builtinatom):
members: dict[str, fieldinfo]
model: type
options: set[Option]
@@ -215,7 +211,7 @@ class Struct(fieldatom):
alter_model: bool = ...,
) -> None: ...
-class UnaryExpr:
+class unaryexpr:
expr: int
value: Union[ContextLambda, Any]
def __init__(self, expr: int, value: Union[ContextLambda, Any]) -> Any: ...
@@ -252,41 +248,130 @@ def pack(__obj: Any, __struct: atom, **globals) -> bytes: ...
def sizeof(obj: atom, globals: Optional[dict | Context] = ...): ...
def unpack(__io: Any, __struct: atom, **globals) -> Any: ...
-class intatom(fieldcatom):
+class Int(builtinatom):
little_endian: bool
nbits: int
nbytes: int
signed: bool
- def __init__(self, nbits: int, signed: bool = ..., little_endian: bool = ...) -> None: ...
-
-i16: intatom
-i24: intatom
-i32: intatom
-i64: intatom
-i8: intatom
-u16: intatom
-u24: intatom
-u32: intatom
-u64: intatom
-u8: intatom
-
-class floatatom(fieldcatom):
+ def __init__(
+ self, nbits: int, signed: bool = ..., little_endian: bool = ...
+ ) -> None: ...
+
+class Float(builtinatom):
little_endian: bool
nbits: int
nbytes: int
def __init__(self, nbits: int, little_endian: bool = ...) -> None: ...
-f16: floatatom
-f32: floatatom
-f64: floatatom
-
-class paddingatom(fieldcatom):
+class Padding(builtinatom):
def __init__(self, pad: int) -> None: ...
-padding: paddingatom
-
-class stringatom(fieldcatom):
+class string(builtinatom):
encoding: str
errors: str
length: _Length
def __init__(self, length: _Length, encoding: str, errors: str = ...) -> None: ...
+
+class atoffset(builtinatom):
+ offset: Union[int, ContextLambda]
+ whence: int
+ def __init__(
+ self, offset: Union[int, ContextLambda], atom: atom, whence: int = ...
+ ) -> None: ...
+ def get_offset(self, layer: layer) -> int: ...
+ def __set_byteorder__(self, byteorder: Endian) -> atoffset: ...
+
+
+class repeated(builtinatom):
+ atom: Any
+ length: _Length
+ def __init__(self, atom: atom, length: _Length) -> None: ...
+ def __set_byteorder__(self, byteorder: Endian) -> repeated: ...
+
+class seqlayer(layer):
+ index: int
+ length: int
+ sequence: Collection
+ def __init__(self, *args, **kwargs) -> None: ...
+
+class switch(builtinatom):
+ atom: Any
+ cases: Union[dict[Any, Any], ContextLambda]
+ def __init__(
+ self, atom: atom, cases: Union[dict[Any, Any], ContextLambda]
+ ) -> None: ...
+ def get_next(self, obj: Any, context: layer) -> Any: ...
+ def __set_byteorder__(self, byteorder: Endian) -> switch: ...
+
+class cstring(builtinatom):
+ length: Union[_Length, atom]
+ encoding: str
+ errors: str
+ terminator: str
+ keep_terminator: bool
+
+ def __init__(
+ self,
+ length: Union[_Length, atom],
+ encoding: str = ...,
+ errors: str = ...,
+ sep: str = ...,
+ keep_terminator: bool = ...,
+ ) -> None: ...
+
+class octetstring(builtinatom):
+ length: _Length
+ def __init__(self, length: _Length) -> None: ...
+
+class enumeration(builtinatom):
+ atom: Any
+ enum_type: EnumType
+ members: dict[Any, Any]
+ default: Any
+ def __init__(
+ self,
+ atom: atom,
+ enum_type: EnumType,
+ default: Any = ...,
+ ) -> None: ...
+ def __set_byteorder__(self, byteorder: Endian) -> enumeration: ...
+
+class pstring(builtinatom):
+ atom: Any
+ encoding: str
+ errors: str
+ def __init__(self, atom: atom, encoding: str = ..., errors: str = ...) -> None: ...
+ def __set_byteorder__(self, byteorder: Endian) -> pstring: ...
+
+class VarInt(builtinatom):
+ little_endian: bool
+ lsb: bool
+ def __init__(self, little_endian: bool = ..., lsb: bool = ...) -> None: ...
+ def __set_byteorder__(self, byteorder: Endian) -> VarInt: ...
+
+class computed(builtinatom):
+ value: _ConstType
+ def __init__(self, value: _ConstType) -> None: ...
+
+class objlayer(layer):
+ obj: Context
+ def __init__(
+ self, state: State, path: str = ..., parent: layer = ..., obj: Context = ...
+ ) -> None: ...
+ def __context_getattr__(self, path: str) -> Any: ...
+
+class lazy(builtinatom):
+ always_lazy: bool
+ fn: Callable[[], atom]
+ def __init__(self, fn: Callable[[], atom], always_lazy: bool = ...) -> None: ...
+ def __set_byteorder__(self, byteorder: Endian) -> lazy: ...
+
+class patom(atom):
+ def __init__(self) -> None: ...
+ def __floordiv__(self, other): ...
+ def __getitem__(self, index): ...
+ def __matmul__(self, *args, **kwargs): ...
+ def __rfloordiv__(self, other): ...
+ def __rmatmul__(self, *args, **kwargs): ...
+ def __rrshift__(self, other): ...
+ def __rshift__(self, other): ...
diff --git a/src/caterpillar/__init__.py b/src/caterpillar/__init__.py
index df1f0fe..9ee086e 100644
--- a/src/caterpillar/__init__.py
+++ b/src/caterpillar/__init__.py
@@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-__version__ = "2.1.5"
+__version__ = "2.2.0"
__release__ = None
__author__ = "MatrixEditor"
diff --git a/src/caterpillar/c.py b/src/caterpillar/c.py
new file mode 100644
index 0000000..8d79d74
--- /dev/null
+++ b/src/caterpillar/c.py
@@ -0,0 +1,23 @@
+from caterpillar._C import * # noqa
+
+
+def struct(cls: type = None, /, **kwargs) -> type:
+ """Decorator for new struct classes using the C API.
+
+ >>> @struct
+ ... class MyStruct:
+ ... pass
+
+ :param cls: The target class used as the base model.
+ :param options: Additional options specifying what to include in the final class.
+ :param endian: Optional configuration value for the byte order of a field.
+ :param arch: Global architecture definition (will be inferred on all fields).
+ :param field_options: Additional field options.
+ """
+ def wrap(cls) -> type:
+ return Struct(cls, alter_model=True, **kwargs).model
+
+ if cls is not None:
+ return Struct(cls, alter_model=True, **kwargs).model
+
+ return wrap
diff --git a/src/caterpillar/include/caterpillar/arch.h b/src/caterpillar/include/caterpillar/arch.h
index 09fa1bb..a79b6ca 100644
--- a/src/caterpillar/include/caterpillar/arch.h
+++ b/src/caterpillar/include/caterpillar/arch.h
@@ -88,4 +88,56 @@ struct _endianobj
*/
#define CpEndian_Check(op) PyObject_TypeCheck((PyObject*)(op), &CpEndian_Type)
+/**
+ * @brief Set the byteorder of the given object
+ *
+ * @param op the object to set
+ * @param endian the new byteorder
+ * @return 0 on success, -1 on error
+ */
+static inline PyObject*
+CpEndian_SetEndian(PyObject* op, CpEndianObject* endian)
+{
+ PyObject* attr = PyObject_GetAttrString(op, "__set_byteorder__");
+ if (!attr) {
+ return NULL;
+ }
+ PyObject* ret = PyObject_CallOneArg(attr, (PyObject*)endian);
+ if (!ret) {
+ return NULL;
+ }
+ Py_DECREF(attr);
+ return ret;
+}
+
+#define _CpEndian_ImplSetByteorder_MethDef(name, docs) \
+ { \
+ "__set_byteorder__", (PyCFunction)cp_##name##_set_byteorder, \
+ METH_VARARGS | METH_KEYWORDS, (docs) \
+ }
+
+#define _CpEndian_KwArgsGetByteorder(ret) \
+ static char* kwlist[] = { "byteorder", NULL }; \
+ PyObject* byteorder = NULL; \
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &byteorder)) { \
+ return ret; \
+ } \
+ if (!CpEndian_Check(byteorder)) { \
+ PyErr_SetString(PyExc_TypeError, "byteorder must be an Endian object"); \
+ return ret; \
+ }
+
+#define _CpEndian_ImplSetByteorder(typename, name, field) \
+ static PyObject* cp_##name##_set_byteorder( \
+ typename* self, PyObject* args, PyObject* kw) \
+ { \
+ _CpEndian_KwArgsGetByteorder(NULL); \
+ PyObject* ret = CpEndian_SetEndian(field, (CpEndianObject*)byteorder); \
+ if (!ret) { \
+ return NULL; \
+ } \
+ _Cp_SetObj(field, ret); \
+ return Py_NewRef((PyObject*)self); \
+ }
+
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/atomobj.h b/src/caterpillar/include/caterpillar/atomobj.h
index dfafa09..b002961 100644
--- a/src/caterpillar/include/caterpillar/atomobj.h
+++ b/src/caterpillar/include/caterpillar/atomobj.h
@@ -110,6 +110,17 @@ CpAtom_Pack(PyObject* atom, PyObject* attrname, PyObject* op, PyObject* ctx)
return PyObject_CallMethodObjArgs(atom, attrname, op, ctx, NULL);
}
+// ---------------------------------------------------------------------------
+// Length info
+
+struct _lengthinfoobj
+{
+ PyObject_HEAD
+
+ Py_ssize_t m_length;
+ int m_greedy;
+};
+
// ---------------------------------------------------------------------------
// CAtom
@@ -176,8 +187,20 @@ typedef int (*packfunc)(PyObject*, PyObject*, PyObject*);
* This is another specialized version of the `packfunc` function, which
* is used to pack multiple objects to the underlying stream. See `packfunc`
* for more information.
+ *
+ * The signature of this function is as follows:
+ * @code {.cpp}
+ * int packmanyfunc(PyObject* self, PyObject* op, PyObject* ctx,
+ * CpLengthInfo* info);
+ * @endcode
+ *
+ * @param self the atom object (self)
+ * @param op the object to pack
+ * @param ctx the context object
+ * @param info the length information
+ * @return the result of the `__pack_many__` method
*/
-typedef int (*packmanyfunc)(PyObject*, PyObject*, PyObject*);
+typedef int (*packmanyfunc)(PyObject*, PyObject*, PyObject*, PyObject*);
/**
* @brief Unpack-Protocol
@@ -198,8 +221,19 @@ typedef PyObject* (*unpackfunc)(PyObject*, PyObject*);
* This is another specialized version of the `unpackfunc` function, which
* is used to unpack multiple objects from the underlying stream. See
* `unpackfunc` for more information.
+ *
+ * The signature of this function is as follows:
+ * @code {.cpp}
+ * PyObject* unpackmanyfunc(PyObject* self, PyObject* ctx,
+ * CpLengthInfo* info);
+ * @endcode
+ *
+ * @param self the atom object (self)
+ * @param ctx the context object
+ * @param info the length information
+ * @return the result of the `__unpack_many__` method
*/
-typedef PyObject* (*unpackmanyfunc)(PyObject*, PyObject*);
+typedef PyObject* (*unpackmanyfunc)(PyObject*, PyObject*, PyObject*);
/// Experimental API
/**
diff --git a/src/caterpillar/include/caterpillar/atoms/builtins.h b/src/caterpillar/include/caterpillar/atoms/builtins.h
new file mode 100644
index 0000000..60288ca
--- /dev/null
+++ b/src/caterpillar/include/caterpillar/atoms/builtins.h
@@ -0,0 +1,151 @@
+/**
+ * Copyright (C) MatrixEditor 2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef BUILTINS_H
+#define BUILTINS_H
+
+#include "caterpillar/atomobj.h"
+#include "caterpillar/caterpillarapi.h"
+
+// PROPOSAL: Builtin atoms are similar to the Python class FieldMixin
+// but implement all parts of a field separately. For instance, the
+// builtin atom for a sequence only implements the sequence part.
+struct _builtinatomobj
+{
+ CpCAtom_HEAD
+};
+
+#define CpBuiltinAtom_CheckExact(op) Py_IS_TYPE((op), &CpBuiltinAtom_Type)
+#define CpBuiltinAtom_Check(op) PyObject_TypeCheck((op), &CpBuiltinAtom_Type)
+#define CpBuiltinAtom_HEAD CpBuiltinAtomObject ob_base;
+#define CpBuiltinAtom_CATOM(self) (self)->ob_base.ob_base
+
+//------------------------------------------------------------------------------
+// Repeated
+struct _repeatedatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// Stores a reference to the actual parsing struct that will be used
+ /// to parse or build our data. This attribute is never null.
+ PyObject* m_atom;
+
+ /// A constant or dynamic value to represent the amount of structs. Zero
+ /// indicates there are no sequence types associated with this field.
+ PyObject* m_length;
+};
+
+#define CpRepeatedAtom_CheckExact(op) Py_IS_TYPE((op), &CpRepeatedAtom_Type)
+#define CpRepeatedAtom_Check(op) PyObject_TypeCheck((op), &CpRepeatedAtom_Type)
+
+inline CpRepeatedAtomObject*
+CpRepeatedAtom_New(PyObject* atom, PyObject* length)
+{
+ return (CpRepeatedAtomObject*)CpObject_Create(
+ &CpRepeatedAtom_Type, "OO", atom, length);
+}
+
+//------------------------------------------------------------------------------
+// Conditional
+struct _conditionatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// Stores a reference to the actual parsing struct that will be used
+ /// to parse or build our data. This attribute is never null.
+ PyObject* m_atom;
+
+ /// A constant or dynamic value to represent the condition.
+ PyObject* m_condition;
+};
+
+#define CpConditionAtom_CheckExact(op) Py_IS_TYPE((op), &CpConditionAtom_Type)
+#define CpConditionAtom_Check(op) PyObject_TypeCheck((op), &CpConditionAtom_Type)
+
+static inline CpConditionAtomObject*
+CpConditionAtom_New(PyObject* atom, PyObject* condition)
+{
+ return (CpConditionAtomObject*)CpObject_Create(
+ &CpConditionAtom_Type, "OO", condition, atom);
+}
+
+//------------------------------------------------------------------------------
+// Switch
+struct _switchatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// Stores a reference to the actual parsing struct that will be used
+ /// to parse or build our data. This attribute is never null.
+ PyObject* m_atom;
+
+ // A dictionary or dynamic value to represent the cases.
+ PyObject* m_cases;
+
+ // -- internal ---
+ int s_callable;
+};
+
+#define CpSwitchAtom_CheckExact(op) Py_IS_TYPE((op), &CpSwitchAtom_Type)
+#define CpSwitchAtom_Check(op) PyObject_TypeCheck((op), &CpSwitchAtom_Type)
+
+static inline CpSwitchAtomObject*
+CpSwitchAtom_New(PyObject* atom, PyObject* cases)
+{
+ return (CpSwitchAtomObject*)CpObject_Create(
+ &CpSwitchAtom_Type, "OO", atom, cases);
+}
+
+//------------------------------------------------------------------------------
+// Offset
+struct _offsetatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// Stores a reference to the actual parsing struct that will be used
+ /// to parse or build our data. This attribute is never null.
+ PyObject* m_atom;
+
+ PyObject* m_offset;
+ PyObject* m_whence;
+
+ // -- internal ---
+ int s_is_number;
+};
+
+#define CpOffsetAtom_CheckExact(op) Py_IS_TYPE((op), &CpOffsetAtom_Type)
+#define CpOffsetAtom_Check(op) PyObject_TypeCheck((op), &CpOffsetAtom_Type)
+
+static inline CpOffsetAtomObject*
+CpOffsetAtom_FromSsize_t(PyObject* atom, Py_ssize_t offset)
+{
+ PyObject* o = PyLong_FromSsize_t(offset);
+ if (o == NULL)
+ return NULL;
+ CpOffsetAtomObject* a =
+ (CpOffsetAtomObject*)CpObject_Create(&CpOffsetAtom_Type, "OO", atom, o);
+ Py_DECREF(o);
+ return a;
+}
+
+static inline CpOffsetAtomObject*
+CpOffsetAtom_New(PyObject* atom, PyObject* offset)
+{
+ return (CpOffsetAtomObject*)CpObject_Create(
+ &CpOffsetAtom_Type, "OO", atom, offset);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/atoms/const.h b/src/caterpillar/include/caterpillar/atoms/const.h
new file mode 100644
index 0000000..f7afc70
--- /dev/null
+++ b/src/caterpillar/include/caterpillar/atoms/const.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) MatrixEditor 2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef CONSTATOMOBJ_H
+#define CONSTATOMOBJ_H
+
+#include "caterpillar/atoms/builtins.h"
+
+struct _constatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ 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/atoms/enum.h b/src/caterpillar/include/caterpillar/atoms/enum.h
new file mode 100644
index 0000000..d1ea921
--- /dev/null
+++ b/src/caterpillar/include/caterpillar/atoms/enum.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) MatrixEditor 2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef ENUMATOMOBJ_H
+#define ENUMATOMOBJ_H
+
+#include "caterpillar/atoms/builtins.h"
+
+struct _enumatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// Stores the mapping of the underlying enum type.
+ PyObject* m_members;
+
+ PyObject* m_value2member_map;
+ PyObject* m_default;
+
+ /// Reference to the atom that is responsible for
+ /// parsing the enum value
+ PyObject* m_atom;
+
+ /// Reference to the enum type
+ PyObject* m_enum_type;
+};
+
+#define CpEnumAtom_CheckExact(op) Py_IS_TYPE((op), &CpEnumAtom_Type)
+#define CpEnumAtom_Check(op) (PyObject_TypeCheck((op), &CpEnumAtom_Type))
+
+static inline CpEnumAtomObject*
+CpEnumAtom_New(PyObject* members, PyObject* atom)
+{
+ return (CpEnumAtomObject*)CpObject_Create(
+ &CpEnumAtom_Type, "OO", members, atom);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/atoms/float.h b/src/caterpillar/include/caterpillar/atoms/float.h
index 5aac58a..11bf6f7 100644
--- a/src/caterpillar/include/caterpillar/atoms/float.h
+++ b/src/caterpillar/include/caterpillar/atoms/float.h
@@ -17,12 +17,11 @@
#ifndef FLOATATOMOBJ_H
#define FLOATATOMOBJ_H
-#include "caterpillar/caterpillarapi.h"
-#include "caterpillar/field.h"
+#include "caterpillar/atoms/builtins.h"
struct _floatatomobj
{
- CpFieldCAtom_HEAD
+ CpBuiltinAtom_HEAD
/// Stores the amount of bytes this float atom
/// has in total
@@ -35,18 +34,7 @@ struct _floatatomobj
int _m_little_endian;
};
-// PyAPI_DATA(PyTypeObject) CpFloatAtom_Type;
-
-/** @brief Checks if the given object is an integer atom object */
#define CpFloatAtom_CheckExact(op) Py_IS_TYPE((op), &CpFloatAtom_Type)
-/** @brief Checks if the given object is an integer atom object */
-#define CpFloatAtom_Check(op) (PyObject_IsInstance((op), &CpFloatAtom_Type))
-
-PyAPI_FUNC(int) CpFloatAtom_Pack(CpFloatAtomObject* self,
- PyObject* value,
- CpLayerObject* layer);
-
-PyAPI_FUNC(PyObject*)
- CpFloatAtom_Unpack(CpFloatAtomObject* self, CpLayerObject* layer);
+#define CpFloatAtom_Check(op) (PyObject_TypeCheck((op), &CpFloatAtom_Type))
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/atoms/int.h b/src/caterpillar/include/caterpillar/atoms/int.h
index b5a79f9..5776fde 100644
--- a/src/caterpillar/include/caterpillar/atoms/int.h
+++ b/src/caterpillar/include/caterpillar/atoms/int.h
@@ -17,12 +17,11 @@
#ifndef INTATOMOBJ_H
#define INTATOMOBJ_H
-#include "caterpillar/caterpillarapi.h"
-#include "caterpillar/field.h"
+#include "caterpillar/atoms/builtins.h"
struct _intatomobj
{
- CpFieldCAtom_HEAD
+ CpBuiltinAtom_HEAD
/// Stores the amount of bits this integer atom
/// has in total
@@ -40,17 +39,36 @@ struct _intatomobj
int _m_little_endian;
};
-/// Integer atom object type
-// PyAPI_DATA(PyTypeObject) CpIntAtom_Type;
-
/** @brief Checks if the given object is an integer atom object */
#define CpIntAtom_CheckExact(op) Py_IS_TYPE((op), &CpIntAtom_Type)
/** @brief Checks if the given object is an integer atom object */
-#define CpIntAtom_Check(op) (PyObject_IsInstance((op), &CpIntAtom_Type))
+#define CpIntAtom_Check(op) (PyObject_TypeCheck((op), &CpIntAtom_Type))
+
+//------------------------------------------------------------------------------
+// varint atom
+
+struct _varintatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// Stores whether or not the integer is little endian
+ int _m_little_endian;
+
+ /// Specifies that the last significant byte will use a ``1`` to identify
+ /// the end of the varint. Otherwise, zero will be used (which is the
+ /// default setting).
+ int _m_lsb;
+};
+
+#define CpVarIntAtom_CheckExact(op) Py_IS_TYPE((op), &CpVarIntAtom_Type)
+#define CpVarIntAtom_Check(op) (PyObject_TypeCheck((op), &CpVarIntAtom_Type))
+
+static inline CpVarIntAtomObject*
+CpVarIntAtom_New(bool little_endian, bool lsb)
+{
+ return (CpVarIntAtomObject*)CpObject_Create(
+ &CpVarIntAtom_Type, "II", little_endian, lsb);
+}
-PyAPI_FUNC(int)
- CpIntAtom_Pack(CpIntAtomObject* self, PyObject* value, CpLayerObject* layer);
-PyAPI_FUNC(PyObject*)
- CpIntAtom_Unpack(CpIntAtomObject* self, CpLayerObject* layer);
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/atoms/primitive.h b/src/caterpillar/include/caterpillar/atoms/primitive.h
index f5c5668..d030fc1 100644
--- a/src/caterpillar/include/caterpillar/atoms/primitive.h
+++ b/src/caterpillar/include/caterpillar/atoms/primitive.h
@@ -17,8 +17,22 @@
#ifndef PRIMITIVEATOMOBJ_H
#define PRIMITIVEATOMOBJ_H
-#include "caterpillar/caterpillarapi.h"
-#include "caterpillar/field.h"
+#include "caterpillar/atoms/builtins.h"
+
+//------------------------------------------------------------------------------
+// primitive atom
+struct _primitiveatomobj
+{
+ CpAtom_HEAD
+};
+
+#define CpPrimitiveAtom_NAME "patom"
+#define CpPrimitiveAtom_CheckExact(op) Py_IS_TYPE((op), &CpPrimitiveAtom_Type)
+#define CpPrimitiveAtom_Check(op) \
+ (PyObject_TypeCheck((op), &CpPrimitiveAtom_Type))
+
+//------------------------------------------------------------------------------
+// Bool
/// @struct _boolatomobj
/// @brief Struct representing a bool atom object.
@@ -27,7 +41,7 @@
/// family.
struct _boolatomobj
{
- CpFieldCAtom_HEAD
+ CpBuiltinAtom_HEAD
};
/// @brief Bool atom object type
@@ -58,81 +72,103 @@ struct _boolatomobj
* @return True if the object is of type CpBoolAtom_Type or a subtype,
* false otherwise.
*/
-#define CpBoolAtom_Check(op) (PyObject_IsInstance((op), &CpBoolAtom_Type))
-
-/**
- * @brief Packs a value into the underlying stream.
- *
- * @param self The bool atom object
- * @param value The boolean value to be packed.
- * @param layer The layer object providing the context for packing.
- * @return Integer status code (typically 0 for success, non-zero for error).
- */
-PyAPI_FUNC(int) CpBoolAtom_Pack(CpBoolAtomObject* self,
- PyObject* value,
- CpLayerObject* layer);
-
-/**
- * @brief Unpacks a value from the underlying stream.
- *
- * @param self The bool atom object instance.
- * @param layer The layer object providing the context for unpacking.
- * @return The unpacked boolean value as a PyObject.
- */
-PyAPI_FUNC(PyObject*)
- CpBoolAtom_Unpack(CpBoolAtomObject* self, CpLayerObject* layer);
+#define CpBoolAtom_Check(op) (PyObject_TypeCheck((op), &CpBoolAtom_Type))
//------------------------------------------------------------------------------
// Char Atom
struct _charatomobj
{
- CpFieldCAtom_HEAD
+ CpBuiltinAtom_HEAD
};
-/// Char atom object type
-// PyAPI_DATA(PyTypeObject) CpCharAtom_Type;
-
/** @brief Checks if the given object is a char atom object */
#define CpCharAtom_CheckExact(op) Py_IS_TYPE((op), &CpCharAtom_Type)
/** @brief Checks if the given object is a char atom object */
-#define CpCharAtom_Check(op) (PyObject_IsInstance((op), &CpCharAtom_Type))
-
-PyAPI_FUNC(int) CpCharAtom_Pack(CpCharAtomObject* self,
- PyObject* value,
- CpLayerObject* layer);
-
-PyAPI_FUNC(PyObject*)
- CpCharAtom_Unpack(CpCharAtomObject* self, CpLayerObject* layer);
+#define CpCharAtom_Check(op) (PyObject_TypeCheck((op), &CpCharAtom_Type))
//------------------------------------------------------------------------------
// Padding
struct _paddingatomobj
{
- CpFieldCAtom_HEAD
+ CpBuiltinAtom_HEAD
- char padding;
+ char _m_padding;
};
-/// Padding atom object type
-// PyAPI_DATA(PyTypeObject) CpPaddingAtom_Type;
-
-/** @brief Checks if the given object is a padding atom object */
#define CpPaddingAtom_CheckExact(op) Py_IS_TYPE((op), &CpPaddingAtom_Type)
-/** @brief Checks if the given object is a padding atom object */
-#define CpPaddingAtom_Check(op) (PyObject_IsInstance((op), &CpPaddingAtom_Type))
+#define CpPaddingAtom_Check(op) (PyObject_TypeCheck((op), &CpPaddingAtom_Type))
+
+//------------------------------------------------------------------------------
+// Computed
+
+struct _computedatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ PyObject* m_value;
+ int s_callable;
+};
+
+#define CpComputedAtom_CheckExact(op) Py_IS_TYPE((op), &CpComputedAtom_Type)
+#define CpComputedAtom_Check(op) \
+ (PyObject_TypeCheck((op), &CpComputedAtom_Type))
+
+#define CpComputedAtom_VALUE(op) (((CpComputedAtomObject*)(op))->m_value)
+
+/*CpAPI*/
+static inline PyObject*
+CpComputedAtom_Value(CpComputedAtomObject* self, CpLayerObject* layer)
+{
+ return self->s_callable ? PyObject_CallOneArg(self->m_value, (PyObject*)layer)
+ : Py_NewRef(self->m_value);
+}
+
+/*CpAPI*/
+static inline CpComputedAtomObject*
+CpComputedAtom_New(PyObject* value)
+{
+ return (CpComputedAtomObject*)CpObject_Create(
+ &CpComputedAtom_Type, "O", value);
+}
+
+//------------------------------------------------------------------------------
+// Lazy
+
+struct _lazyatomobj
+{
+ CpBuiltinAtom_HEAD
-PyAPI_FUNC(int) CpPaddingAtom_Pack(CpPaddingAtomObject* self,
- PyObject* _,
- CpLayerObject* layer);
+ PyObject* m_fn;
+ PyObject* m_atom;
+ int s_always_lazy;
+};
+
+#define CpLazyAtom_CheckExact(op) Py_IS_TYPE((op), &CpLazyAtom_Type)
+#define CpLazyAtom_Check(op) (PyObject_TypeCheck((op), &CpLazyAtom_Type))
+#define CpLazyAtom_ATOM(op) (((CpLazyAtomObject*)(op))->m_atom)
-PyAPI_FUNC(int) CpPaddingAtom_PackMany(CpPaddingAtomObject* self,
- PyObject* _,
- CpLayerObject* layer);
+/*CpAPI*/
+static inline PyObject*
+CpLazyAtom_Atom(CpLazyAtomObject* self)
+{
+ if (self->s_always_lazy)
+ return PyObject_CallNoArgs(self->m_fn);
-PyAPI_FUNC(PyObject*)
- CpPaddingAtom_Unpack(CpPaddingAtomObject* self, CpLayerObject* layer);
+ if (!self->m_atom) {
+ Py_XSETREF(self->m_atom, PyObject_CallNoArgs(self->m_fn));
+ if (!self->m_atom)
+ return NULL;
+ }
-PyAPI_FUNC(PyObject*)
- CpPaddingAtom_UnpackMany(CpPaddingAtomObject* self, CpLayerObject* layer);
+ return Py_NewRef(self->m_atom);
+}
+
+/*CpAPI*/
+static inline CpLazyAtomObject*
+CpLazyAtom_New(PyObject* fn, int always_lazy)
+{
+ return (CpLazyAtomObject*)CpObject_Create(
+ &CpLazyAtom_Type, "Oi", fn, always_lazy);
+}
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/atoms/string.h b/src/caterpillar/include/caterpillar/atoms/string.h
index 6b9ea98..9fd5b30 100644
--- a/src/caterpillar/include/caterpillar/atoms/string.h
+++ b/src/caterpillar/include/caterpillar/atoms/string.h
@@ -17,28 +17,125 @@
#ifndef STRINGATOMOBJ_H
#define STRINGATOMOBJ_H
-#include "caterpillar/caterpillarapi.h"
-#include "caterpillar/field.h"
+#include "caterpillar/atoms/builtins.h"
+#include "caterpillar/parsing.h"
+// ---------------------------------------------------------------------------
+// Default String
struct _stringatomobj
{
- CpFieldCAtom_HEAD
+ CpBuiltinAtom_HEAD
+ /// The length of this string. It can be a constant integer
+ /// object or a ContextLambda.
PyObject* m_length;
+
+ /// Whether this string should ignore errors when parsing
PyObject* m_errors;
+
+ /// The encoding of this string
PyObject* m_encoding;
};
-// PyAPI_DATA(PyTypeObject) CpStringAtom_Type;
-
#define CpStringAtom_CheckExact(op) Py_IS_TYPE((op), &CpStringAtom_Type)
#define CpStringAtom_Check(op) PyObject_TypeCheck((op), &CpStringAtom_Type)
-PyAPI_FUNC(int) CpStringAtom_Pack(CpStringAtomObject* self,
- PyObject* value,
- CpLayerObject* layer);
+// TODO: CString, PString
+
+// ---------------------------------------------------------------------------
+// Bytes
+struct _bytesatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// The length of this string. It can be a constant integer
+ /// object or a ContextLambda.
+ PyObject* m_length;
+
+ // -- internal ---
+ int s_callable;
+};
+
+// REVISIT: The name of this atom should be something similar to 'bytes'
+#define CpBytesAtom_CheckExact(op) Py_IS_TYPE((op), &CpBytesAtom_Type)
+#define CpBytesAtom_Check(op) PyObject_TypeCheck((op), &CpBytesAtom_Type)
+
+static inline CpBytesAtomObject*
+CpBytesAtom_New(PyObject* length)
+{
+ return (CpBytesAtomObject*)CpObject_CreateOneArg(&CpBytesAtom_Type, length);
+}
+
+// ---------------------------------------------------------------------------
+// PString
+
+struct _pstringatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ /// The atom that parses the length of this string
+ PyObject* m_atom;
+
+ /// Whether this string should ignore errors when parsing
+ PyObject* m_errors;
+
+ /// The encoding of this string
+ PyObject* m_encoding;
+};
+
+#define CpPStringAtom_CheckExact(op) Py_IS_TYPE((op), &CpPStringAtom_Type)
+#define CpPStringAtom_Check(op) PyObject_TypeCheck((op), &CpPStringAtom_Type)
-PyAPI_FUNC(PyObject*)
- CpStringAtom_Unpack(CpStringAtomObject* self, CpLayerObject* layer);
+static inline CpPStringAtomObject*
+CpPStringAtom_New(PyObject* atom, PyObject* encoding)
+{
+ return (CpPStringAtomObject*)CpObject_Create(
+ &CpPStringAtom_Type, "OO", atom, encoding);
+}
+
+// ---------------------------------------------------------------------------
+// CString
+
+struct _cstringatomobj
+{
+ CpBuiltinAtom_HEAD
+
+ PyObject *m_length;
+ PyObject *m_encoding;
+ PyObject *m_errors;
+ PyObject *m_terminator;
+ PyObject *_m_terminator_bytes;
+
+ int s_callable;
+ int s_number;
+ int s_keep_terminator;
+ int s_greedy;
+};
+
+#define CpCStringAtom_CheckExact(op) Py_IS_TYPE((op), &CpCStringAtom_Type)
+#define CpCStringAtom_Check(op) PyObject_TypeCheck((op), &CpCStringAtom_Type)
+
+static inline CpCStringAtomObject*
+CpCStringAtom_New(PyObject* length, PyObject* encoding, PyObject* errors,
+ PyObject* terminator)
+{
+ return (CpCStringAtomObject*)CpObject_Create(
+ &CpCStringAtom_Type, "OOOO", length, encoding, errors, terminator);
+}
+
+static inline PyObject*
+CpCStringAtom_Length(CpCStringAtomObject* self, CpLayerObject *layer)
+{
+ if (self->s_number) {
+ return Py_NewRef(self->m_length);
+ }
+ if (self->s_greedy) {
+ Py_RETURN_NONE;
+ }
+ if (self->s_callable) {
+ return PyObject_CallOneArg(self->m_length, (PyObject *)layer);
+ }
+ return _Cp_Unpack(self->m_length, layer);
+}
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/caterpillar.h b/src/caterpillar/include/caterpillar/caterpillar.h
index c8923c2..1124a9b 100644
--- a/src/caterpillar/include/caterpillar/caterpillar.h
+++ b/src/caterpillar/include/caterpillar/caterpillar.h
@@ -11,13 +11,17 @@
#include "caterpillar/struct.h"
#include "caterpillar/context.h"
#include "caterpillar/atomobj.h"
-#include "caterpillar/field.h"
#include "caterpillar/state.h"
+#include "caterpillar/parsing.h"
+#include "caterpillar/default.h"
// Atom Objects
+#include "caterpillar/atoms/builtins.h"
#include "caterpillar/atoms/int.h"
#include "caterpillar/atoms/float.h"
#include "caterpillar/atoms/primitive.h"
#include "caterpillar/atoms/string.h"
+#include "caterpillar/atoms/const.h"
+#include "caterpillar/atoms/enum.h"
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/caterpillarapi.h b/src/caterpillar/include/caterpillar/caterpillarapi.h
index 06d440d..916e0e0 100644
--- a/src/caterpillar/include/caterpillar/caterpillarapi.h
+++ b/src/caterpillar/include/caterpillar/caterpillarapi.h
@@ -28,14 +28,14 @@
/* struct and typedefs */
struct _modulestate;
typedef struct _modulestate _modulestate;
-struct _endianobj;
-typedef struct _endianobj CpEndianObject;
-struct _archobj;
-typedef struct _archobj CpArchObject;
struct _atomobj;
typedef struct _atomobj CpAtomObject;
struct _catomobj;
typedef struct _catomobj CpCAtomObject;
+struct _archobj;
+typedef struct _archobj CpArchObject;
+struct _endianobj;
+typedef struct _endianobj CpEndianObject;
struct _contextobj;
typedef struct _contextobj CpContextObject;
struct _unaryexpr;
@@ -44,20 +44,14 @@ struct _binaryexpr;
typedef struct _binaryexpr CpBinaryExprObject;
struct _contextpath;
typedef struct _contextpath CpContextPathObject;
-struct _fieldobj;
-typedef struct _fieldobj CpFieldObject;
-struct _fieldatomobj;
-typedef struct _fieldatomobj CpFieldAtomObject;
-struct _fieldcatomobj;
-typedef struct _fieldcatomobj CpFieldCAtomObject;
struct _option;
typedef struct _option CpOptionObject;
struct _stateobj;
typedef struct _stateobj CpStateObject;
struct _layerobj;
typedef struct _layerobj CpLayerObject;
-struct CpStructFieldInfo;
-typedef struct CpStructFieldInfo CpStructFieldInfoObject;
+struct _fieldinfoobj;
+typedef struct _fieldinfoobj CpStructFieldInfoObject;
struct _structobj;
typedef struct _structobj CpStructObject;
struct _floatatomobj;
@@ -72,6 +66,40 @@ struct _paddingatomobj;
typedef struct _paddingatomobj CpPaddingAtomObject;
struct _stringatomobj;
typedef struct _stringatomobj CpStringAtomObject;
+struct _constatomobj;
+typedef struct _constatomobj CpConstAtomObject;
+struct _builtinatomobj;
+typedef struct _builtinatomobj CpBuiltinAtomObject;
+struct _repeatedatomobj;
+typedef struct _repeatedatomobj CpRepeatedAtomObject;
+struct _seqlayerobj;
+typedef struct _seqlayerobj CpSeqLayerObject;
+struct _objlayerobj;
+typedef struct _objlayerobj CpObjLayerObject;
+struct _conditionatomobj;
+typedef struct _conditionatomobj CpConditionAtomObject;
+struct _switchatomobj;
+typedef struct _switchatomobj CpSwitchAtomObject;
+struct _offsetatomobj;
+typedef struct _offsetatomobj CpOffsetAtomObject;
+struct _primitiveatomobj;
+typedef struct _primitiveatomobj CpPrimitiveAtomObject;
+struct _lengthinfoobj;
+typedef struct _lengthinfoobj CpLengthInfoObject;
+struct _bytesatomobj;
+typedef struct _bytesatomobj CpBytesAtomObject;
+struct _pstringatomobj;
+typedef struct _pstringatomobj CpPStringAtomObject;
+struct _enumatomobj;
+typedef struct _enumatomobj CpEnumAtomObject;
+struct _varintatomobj;
+typedef struct _varintatomobj CpVarIntAtomObject;
+struct _computedatomobj;
+typedef struct _computedatomobj CpComputedAtomObject;
+struct _lazyatomobj;
+typedef struct _lazyatomobj CpLazyAtomObject;
+struct _cstringatomobj;
+typedef struct _cstringatomobj CpCStringAtomObject;
#ifdef _CPMODULE
@@ -81,63 +109,99 @@ are then used and implemented in the internal API.
*/
extern PyModuleDef CpModule;
+extern PyTypeObject CpAtom_Type;
+#define CpAtom_NAME "atom"
extern PyTypeObject CpCAtom_Type;
+#define CpCAtom_NAME "catom"
extern PyTypeObject CpArch_Type;
+#define CpArch_NAME "Arch"
extern PyTypeObject CpEndian_Type;
+#define CpEndian_NAME "Endian"
extern PyTypeObject CpContext_Type;
+#define CpContext_NAME "Context"
extern PyTypeObject CpUnaryExpr_Type;
+#define CpUnaryExpr_NAME "unaryexpr"
extern PyTypeObject CpBinaryExpr_Type;
+#define CpBinaryExpr_NAME "binaryexpr"
extern PyTypeObject CpContextPath_Type;
-extern PyTypeObject CpField_Type;
-extern PyTypeObject CpFieldAtom_Type;
-extern PyTypeObject CpFieldCAtom_Type;
+#define CpContextPath_NAME "ContextPath"
extern PyTypeObject CpInvalidDefault_Type;
extern PyTypeObject CpDefaultOption_Type;
extern PyObject _CpInvalidDefault_Object;
extern PyObject _CpDefaultOption_Object;
-extern PyTypeObject CpAtom_Type;
extern PyTypeObject CpOption_Type;
+#define CpOption_NAME "Option"
extern PyTypeObject CpState_Type;
+#define CpState_NAME "State"
extern PyTypeObject CpLayer_Type;
+#define CpLayer_NAME "layer"
extern PyTypeObject CpStructFieldInfo_Type;
+#define CpStructFieldInfo_NAME "fieldinfo"
extern PyTypeObject CpStruct_Type;
+#define CpStruct_NAME "Struct"
extern PyTypeObject CpFloatAtom_Type;
+#define CpFloatAtom_NAME "Float"
extern PyTypeObject CpIntAtom_Type;
+#define CpIntAtom_NAME "Int"
extern PyTypeObject CpBoolAtom_Type;
+#define CpBoolAtom_NAME "Bool"
extern PyTypeObject CpCharAtom_Type;
+#define CpCharAtom_NAME "Char"
extern PyTypeObject CpPaddingAtom_Type;
+#define CpPaddingAtom_NAME "Padding"
extern PyTypeObject CpStringAtom_Type;
+#define CpStringAtom_NAME "string"
+extern PyTypeObject CpConstAtom_Type;
+#define CpConstAtom_NAME "const"
+extern PyTypeObject CpBuiltinAtom_Type;
+#define CpBuiltinAtom_NAME "builtinatom"
+extern PyTypeObject CpRepeatedAtom_Type;
+#define CpRepeatedAtom_NAME "repeated"
+extern PyTypeObject CpSeqLayer_Type;
+#define CpSeqLayer_NAME "seqlayer"
+extern PyTypeObject CpObjLayer_Type;
+#define CpObjLayer_NAME "objlayer"
+extern PyTypeObject CpConditionAtom_Type;
+#define CpConditionAtom_NAME "condition"
+extern PyTypeObject CpSwitchAtom_Type;
+#define CpSwitchAtom_NAME "switch"
+extern PyTypeObject CpOffsetAtom_Type;
+#define CpOffsetAtom_NAME "atoffset"
+extern PyTypeObject CpPrimitiveAtom_Type;
+#define CpPrimitiveAtom_NAME "patom"
+extern PyTypeObject CpLengthInfo_Type;
+#define CpLengthInfo_NAME "lengthinfo"
+extern PyTypeObject CpBytesAtom_Type;
+#define CpBytesAtom_NAME "octetstring"
+extern PyTypeObject CpPStringAtom_Type;
+#define CpPStringAtom_NAME "pstring"
+extern PyTypeObject CpEnumAtom_Type;
+#define CpEnumAtom_NAME "enumeration"
+extern PyTypeObject CpVarIntAtom_Type;
+#define CpVarIntAtom_NAME "VarInt"
+extern PyTypeObject CpComputedAtom_Type;
+#define CpComputedAtom_NAME "computed"
+extern PyTypeObject CpLazyAtom_Type;
+#define CpLazyAtom_NAME "lazy"
+extern PyTypeObject CpCStringAtom_Type;
+#define CpCStringAtom_NAME "cstring"
int CpEndian_IsLittleEndian(CpEndianObject* endian, _modulestate* mod);
CpContextObject* CpContext_New(void);
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);
-CpFieldObject* CpField_New(PyObject* atom);
-int CpField_HasCondition(CpFieldObject* self);
-int CpField_IsEnabled(CpFieldObject* self, PyObject* context);
-Py_ssize_t CpField_GetOffset(CpFieldObject* self, PyObject* context);
-PyObject* CpField_EvalSwitch(CpFieldObject* self, PyObject* op, PyObject* context);
-PyObject* CpField_GetLength(CpFieldObject* self, PyObject* context);
+PyObject* CpTypeMap_Lookup(PyObject* annotation, _modulestate* mod);
+int CpTypeMap_Register(PyObject* annotation, PyObject* handler, _modulestate* mod);
PyObject* CpTypeOf(PyObject* op);
-PyObject* CpTypeOf_Field(CpFieldObject* op);
PyObject* CpTypeOf_Common(PyObject* op);
int CpPack(PyObject* op, PyObject* atom, PyObject* io, PyObject* globals);
-int CpPack_Field(PyObject* op, CpFieldObject* field, CpLayerObject* layer);
-int CpPack_Common(PyObject* op, PyObject* atom, CpLayerObject* layer);
-int CpPack_Struct(PyObject* op, CpStructObject* struct_, CpLayerObject* layer);
-int _Cp_Pack(PyObject* op, PyObject* atom, CpLayerObject* layer);
int _CpPack_EvalLength(CpLayerObject* layer,PyObject* length,Py_ssize_t size,bool* greedy,Py_ssize_t* dstLength);
PyObject* CpSizeOf(PyObject* op, PyObject* globals);
-PyObject* CpSizeOf_Field(CpFieldObject* field, CpLayerObject* layer);
PyObject* CpSizeOf_Struct(CpStructObject* struct_, CpLayerObject* layer);
PyObject* CpSizeOf_Common(PyObject* op, CpLayerObject* layer);
PyObject* _Cp_SizeOf(PyObject* op, CpLayerObject* layer);
PyObject* CpUnpack(PyObject* atom, PyObject* io, PyObject* globals);
-PyObject* CpUnpack_Field(CpFieldObject* field, CpLayerObject* layer);
-PyObject* CpUnpack_Common(PyObject* op, CpLayerObject* layer);
-PyObject* CpUnpack_Struct(CpStructObject* struct_, CpLayerObject* layer);
-PyObject* _Cp_Unpack(PyObject* atom, CpLayerObject* layer);
int _CpUnpack_EvalLength(CpLayerObject* layer,PyObject* length,bool* seq_greedy,Py_ssize_t* seq_length);
PyObject* CpUnpack_CAtom(CpCAtomObject* catom, CpLayerObject* layer);
int CpPack_CAtom(PyObject* op, CpCAtomObject* catom, CpLayerObject* layer);
@@ -145,23 +209,75 @@ PyObject* CpSizeOf_CAtom(CpCAtomObject* catom, CpLayerObject* layer);
PyObject * CpTypeOf_CAtom(CpCAtomObject* op);
CpStateObject* CpState_New(PyObject* io);
PyObject* CpState_Tell(CpStateObject* self);
-PyObject* CpState_Seek(CpStateObject* self, PyObject* offset, int whence);
-PyObject* CpState_Read(CpStateObject* self, Py_ssize_t size);
+PyObject* CpState_Seek(CpStateObject* self, PyObject* offset, PyObject* whence);
+PyObject* CpState_Read(CpStateObject* self, PyObject* sizeobj);
+PyObject* CpState_ReadSsize_t(CpStateObject* self, Py_ssize_t size);
PyObject* CpState_ReadFully(CpStateObject* self);
PyObject* CpState_Write(CpStateObject* self, PyObject* value);
int CpState_SetGlobals(CpStateObject* self, PyObject* globals);
CpLayerObject* CpLayer_New(CpStateObject* state, CpLayerObject* parent);
int CpLayer_Invalidate(CpLayerObject* self);
-int CpLayer_SetSequence(CpLayerObject* self,PyObject* sequence,Py_ssize_t length,int8_t greedy);
-CpStructFieldInfoObject* CpStructFieldInfo_New(CpFieldObject* field);
+CpStructFieldInfoObject* CpStructFieldInfo_New(PyObject* name, PyObject* field);
int CpStruct_AddFieldInfo(CpStructObject* o, CpStructFieldInfoObject* info);
-int CpStruct_AddField(CpStructObject* o, CpFieldObject* field, int exclude);
CpStructObject* CpStruct_New(PyObject* model);
PyObject* CpStruct_GetAnnotations(CpStructObject* o, int eval);
int CpStruct_ReplaceType(CpStructObject* o, PyObject* name, PyObject* type);
int CpStruct_HasOption(CpStructObject* o, PyObject* option);
+int CpStruct_Pack(CpStructObject* self, PyObject* obj, CpLayerObject* layer);
+PyObject* CpStruct_Unpack(CpStructObject* self, CpLayerObject* layer);
+PyObject* CpStruct_SizeOf(CpStructObject* self, CpLayerObject* layer);
int CpStructModel_Check(PyObject* model, _modulestate* state);
PyObject* CpStructModel_GetStruct(PyObject* model, _modulestate* state);
+CpSeqLayerObject* CpSeqLayer_New(CpStateObject* state, CpLayerObject* parent);
+int CpSeqLayer_SetSequence(CpSeqLayerObject* self,PyObject* sequence,Py_ssize_t length,int8_t greedy);
+CpObjLayerObject* CpObjLayer_New(CpStateObject* state, CpLayerObject* parent);
+int CpIntAtom_Pack(CpIntAtomObject* self, PyObject* op, CpLayerObject* layer);
+PyObject* CpIntAtom_Unpack(CpIntAtomObject* self, CpLayerObject* layer);
+int CpFloatAtom_Pack(CpFloatAtomObject* self, PyObject* value, CpLayerObject* layer);
+PyObject* CpFloatAtom_Unpack(CpFloatAtomObject* self, CpLayerObject* layer);
+int CpBoolAtom_Pack(CpBoolAtomObject* self, PyObject* value, CpLayerObject* layer);
+PyObject* CpBoolAtom_Unpack(CpBoolAtomObject* self, CpLayerObject* layer);
+int CpCharAtom_Pack(CpCharAtomObject* self, PyObject* value, CpLayerObject* layer);
+PyObject* CpCharAtom_Unpack(CpCharAtomObject* self, CpLayerObject* layer);
+int CpPaddingAtom_Pack(CpPaddingAtomObject* self,PyObject* value,CpLayerObject* layer);
+int CpPaddingAtom_PackMany(CpPaddingAtomObject* self,PyObject* value,CpLayerObject* layer,CpLengthInfoObject* lengthinfo);
+PyObject* CpPaddingAtom_Unpack(CpPaddingAtomObject* self, CpLayerObject* layer);
+PyObject* CpPaddingAtom_UnpackMany(CpPaddingAtomObject* self,CpLayerObject* layer,CpLengthInfoObject* lengthinfo);
+int CpStringAtom_Pack(CpStringAtomObject* self,PyObject* value,CpLayerObject* layer);
+PyObject* CpStringAtom_Unpack(CpStringAtomObject* self, CpLayerObject* layer);
+int CpConstAtom_Pack(CpConstAtomObject* self, PyObject* value, CpLayerObject* layer);
+PyObject* CpConstAtom_Unpack(CpConstAtomObject* self, CpLayerObject* layer);
+int CpRepeatedAtom_Pack(CpRepeatedAtomObject* self,PyObject* op,CpLayerObject* layer);
+PyObject* CpRepeatedAtom_Unpack(CpRepeatedAtomObject* self, CpLayerObject* layer);
+PyObject* CpRepeatedAtom_GetLength(CpRepeatedAtomObject* self, PyObject* context);
+int CpConditionAtom_Pack(CpConditionAtomObject* self, PyObject* op, PyObject* layer);
+PyObject* CpConditionAtom_Unpack(CpConditionAtomObject* self, CpLayerObject* layer);
+int CpConditionAtom_IsEnabled(CpConditionAtomObject* self, PyObject* context);
+PyObject* CpSwitchAtom_GetNext(CpSwitchAtomObject* self, PyObject* op, PyObject* context);
+int CpSwitchAtom_Pack(CpSwitchAtomObject* self, PyObject* obj, CpLayerObject* layer);
+PyObject* CpSwitchAtom_Unpack(CpSwitchAtomObject* self, CpLayerObject* layer);
+int CpOffsetAtom_Pack(CpOffsetAtomObject* self, PyObject* obj, CpLayerObject* layer);
+PyObject* CpOffsetAtom_Unpack(CpOffsetAtomObject* self, CpLayerObject* layer);
+PyObject* CpOffsetAtom_GetOffset(CpOffsetAtomObject* self, PyObject* layer);
+PyObject* CpBytesAtom_GetLength(CpBytesAtomObject* self, CpLayerObject* layer);
+int CpBytesAtom_Pack(CpBytesAtomObject* self, PyObject* value, CpLayerObject* layer);
+PyObject* CpBytesAtom_Unpack(CpBytesAtomObject* self, CpLayerObject* layer);
+int CpPStringAtom_Pack(CpPStringAtomObject* self,PyObject* value,CpLayerObject* layer);
+PyObject* CpPStringAtom_Unpack(CpPStringAtomObject* self, CpLayerObject* layer);
+int CpEnumAtom_Pack(CpEnumAtomObject* self, PyObject* value, CpLayerObject* layer);
+PyObject* CpEnumAtom_Unpack(CpEnumAtomObject* self, CpLayerObject* layer);
+int CpVarIntAtom_Pack(CpVarIntAtomObject* self,PyObject* value,CpLayerObject* layer);
+PyObject* CpVarIntAtom_Unpack(CpVarIntAtomObject* self, CpLayerObject* layer);
+PyObject* CpVarIntAtom_BSwap(PyObject* number, bool little_endian);
+unsigned long long CpVarIntAtom_BSwapUnsignedLongLong(unsigned long long number,bool little_endian);
+long long CpVarIntAtom_BSwapLongLong(long long number, bool little_endian);
+Py_ssize_t CpVarIntAtom_BSwapSsize_t(Py_ssize_t number, bool little_endian);
+int CpComputedAtom_Pack(CpComputedAtomObject* self,PyObject* obj,CpLayerObject* layer);
+PyObject* CpComputedAtom_Unpack(CpComputedAtomObject* self, CpLayerObject* layer);
+int CpLazyAtom_Pack(CpLazyAtomObject* self, PyObject* obj, CpLayerObject* layer);
+PyObject* CpLazyAtom_Unpack(CpLazyAtomObject* self, CpLayerObject* layer);
+int CpCStringAtom_Pack(CpCStringAtomObject* self,PyObject* value,CpLayerObject* layer);
+PyObject* CpCStringAtom_Unpack(CpCStringAtomObject* self, CpLayerObject* layer);
#else
@@ -172,63 +288,63 @@ internal API functions and types. Their indices are static and defined in
caterpillar_api.py
*/
#define CpModule (*(PyModuleDef *)Cp_API[0])
-#define CpCAtom_Type (*(PyTypeObject *)Cp_API[1])
-#define CpArch_Type (*(PyTypeObject *)Cp_API[2])
-#define CpEndian_Type (*(PyTypeObject *)Cp_API[3])
-#define CpContext_Type (*(PyTypeObject *)Cp_API[4])
-#define CpUnaryExpr_Type (*(PyTypeObject *)Cp_API[5])
-#define CpBinaryExpr_Type (*(PyTypeObject *)Cp_API[6])
-#define CpContextPath_Type (*(PyTypeObject *)Cp_API[7])
-#define CpField_Type (*(PyTypeObject *)Cp_API[8])
-#define CpFieldAtom_Type (*(PyTypeObject *)Cp_API[9])
-#define CpFieldCAtom_Type (*(PyTypeObject *)Cp_API[10])
-#define CpInvalidDefault_Type (*(PyTypeObject *)Cp_API[11])
-#define CpDefaultOption_Type (*(PyTypeObject *)Cp_API[12])
-#define _CpInvalidDefault_Object (*(PyObject *)Cp_API[13])
-#define _CpDefaultOption_Object (*(PyObject *)Cp_API[14])
-#define CpAtom_Type (*(PyTypeObject *)Cp_API[15])
-#define CpOption_Type (*(PyTypeObject *)Cp_API[16])
-#define CpState_Type (*(PyTypeObject *)Cp_API[17])
-#define CpLayer_Type (*(PyTypeObject *)Cp_API[18])
-#define CpStructFieldInfo_Type (*(PyTypeObject *)Cp_API[19])
-#define CpStruct_Type (*(PyTypeObject *)Cp_API[20])
-#define CpFloatAtom_Type (*(PyTypeObject *)Cp_API[21])
-#define CpIntAtom_Type (*(PyTypeObject *)Cp_API[22])
-#define CpBoolAtom_Type (*(PyTypeObject *)Cp_API[23])
-#define CpCharAtom_Type (*(PyTypeObject *)Cp_API[24])
-#define CpPaddingAtom_Type (*(PyTypeObject *)Cp_API[25])
-#define CpStringAtom_Type (*(PyTypeObject *)Cp_API[26])
+#define CpAtom_Type (*(atom *)Cp_API[1])
+#define CpCAtom_Type (*(catom *)Cp_API[2])
+#define CpArch_Type (*(Arch *)Cp_API[3])
+#define CpEndian_Type (*(Endian *)Cp_API[4])
+#define CpContext_Type (*(Context *)Cp_API[5])
+#define CpUnaryExpr_Type (*(unaryexpr *)Cp_API[6])
+#define CpBinaryExpr_Type (*(binaryexpr *)Cp_API[7])
+#define CpContextPath_Type (*(ContextPath *)Cp_API[8])
+#define CpInvalidDefault_Type (*(PyTypeObject *)Cp_API[12])
+#define CpDefaultOption_Type (*(PyTypeObject *)Cp_API[13])
+#define _CpInvalidDefault_Object (*(PyObject *)Cp_API[14])
+#define _CpDefaultOption_Object (*(PyObject *)Cp_API[15])
+#define CpOption_Type (*(Option *)Cp_API[16])
+#define CpState_Type (*(State *)Cp_API[17])
+#define CpLayer_Type (*(layer *)Cp_API[18])
+#define CpStructFieldInfo_Type (*(fieldinfo *)Cp_API[19])
+#define CpStruct_Type (*(Struct *)Cp_API[20])
+#define CpFloatAtom_Type (*(Float *)Cp_API[21])
+#define CpIntAtom_Type (*(Int *)Cp_API[22])
+#define CpBoolAtom_Type (*(Bool *)Cp_API[23])
+#define CpCharAtom_Type (*(Char *)Cp_API[24])
+#define CpPaddingAtom_Type (*(Padding *)Cp_API[25])
+#define CpStringAtom_Type (*(string *)Cp_API[26])
+#define CpConstAtom_Type (*(const *)Cp_API[27])
+#define CpBuiltinAtom_Type (*(builtinatom *)Cp_API[28])
+#define CpRepeatedAtom_Type (*(repeated *)Cp_API[29])
+#define CpSeqLayer_Type (*(seqlayer *)Cp_API[30])
+#define CpObjLayer_Type (*(objlayer *)Cp_API[31])
+#define CpConditionAtom_Type (*(condition *)Cp_API[32])
+#define CpSwitchAtom_Type (*(switch *)Cp_API[33])
+#define CpOffsetAtom_Type (*(atoffset *)Cp_API[34])
+#define CpPrimitiveAtom_Type (*(patom *)Cp_API[35])
+#define CpLengthInfo_Type (*(lengthinfo *)Cp_API[36])
+#define CpBytesAtom_Type (*(octetstring *)Cp_API[37])
+#define CpPStringAtom_Type (*(pstring *)Cp_API[38])
+#define CpEnumAtom_Type (*(enumeration *)Cp_API[39])
+#define CpVarIntAtom_Type (*(VarInt *)Cp_API[40])
+#define CpComputedAtom_Type (*(computed *)Cp_API[41])
+#define CpLazyAtom_Type (*(lazy *)Cp_API[42])
+#define CpCStringAtom_Type (*(cstring *)Cp_API[43])
#define CpEndian_IsLittleEndian (*((int (*)(CpEndianObject* endian, _modulestate* mod)))Cp_API[50])
#define CpContext_New (*((CpContextObject* (*)(void)))Cp_API[53])
#define CpUnaryExpr_New (*((CpUnaryExprObject* (*)(int op, PyObject* value)))Cp_API[54])
#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 CpField_New (*((CpFieldObject* (*)(PyObject* atom)))Cp_API[58])
-#define CpField_HasCondition (*((int (*)(CpFieldObject* self)))Cp_API[59])
-#define CpField_IsEnabled (*((int (*)(CpFieldObject* self, PyObject* context)))Cp_API[60])
-#define CpField_GetOffset (*((Py_ssize_t (*)(CpFieldObject* self, PyObject* context)))Cp_API[61])
-#define CpField_EvalSwitch (*((PyObject* (*)(CpFieldObject* self, PyObject* op, PyObject* context)))Cp_API[62])
-#define CpField_GetLength (*((PyObject* (*)(CpFieldObject* self, PyObject* context)))Cp_API[63])
+#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_Field (*((PyObject* (*)(CpFieldObject* op)))Cp_API[65])
#define CpTypeOf_Common (*((PyObject* (*)(PyObject* op)))Cp_API[66])
#define CpPack (*((int (*)(PyObject* op, PyObject* atom, PyObject* io, PyObject* globals)))Cp_API[67])
-#define CpPack_Field (*((int (*)(PyObject* op, CpFieldObject* field, CpLayerObject* layer)))Cp_API[68])
-#define CpPack_Common (*((int (*)(PyObject* op, PyObject* atom, CpLayerObject* layer)))Cp_API[69])
-#define CpPack_Struct (*((int (*)(PyObject* op, CpStructObject* struct_, CpLayerObject* layer)))Cp_API[70])
-#define _Cp_Pack (*((int (*)(PyObject* op, PyObject* atom, CpLayerObject* layer)))Cp_API[71])
#define _CpPack_EvalLength (*((int (*)(CpLayerObject* layer,PyObject* length,Py_ssize_t size,bool* greedy,Py_ssize_t* dstLength)))Cp_API[72])
#define CpSizeOf (*((PyObject* (*)(PyObject* op, PyObject* globals)))Cp_API[73])
-#define CpSizeOf_Field (*((PyObject* (*)(CpFieldObject* field, CpLayerObject* layer)))Cp_API[74])
#define CpSizeOf_Struct (*((PyObject* (*)(CpStructObject* struct_, CpLayerObject* layer)))Cp_API[75])
#define CpSizeOf_Common (*((PyObject* (*)(PyObject* op, CpLayerObject* layer)))Cp_API[76])
#define _Cp_SizeOf (*((PyObject* (*)(PyObject* op, CpLayerObject* layer)))Cp_API[77])
#define CpUnpack (*((PyObject* (*)(PyObject* atom, PyObject* io, PyObject* globals)))Cp_API[78])
-#define CpUnpack_Field (*((PyObject* (*)(CpFieldObject* field, CpLayerObject* layer)))Cp_API[79])
-#define CpUnpack_Common (*((PyObject* (*)(PyObject* op, CpLayerObject* layer)))Cp_API[80])
-#define CpUnpack_Struct (*((PyObject* (*)(CpStructObject* struct_, CpLayerObject* layer)))Cp_API[81])
-#define _Cp_Unpack (*((PyObject* (*)(PyObject* atom, CpLayerObject* layer)))Cp_API[82])
#define _CpUnpack_EvalLength (*((int (*)(CpLayerObject* layer,PyObject* length,bool* seq_greedy,Py_ssize_t* seq_length)))Cp_API[83])
#define CpUnpack_CAtom (*((PyObject* (*)(CpCAtomObject* catom, CpLayerObject* layer)))Cp_API[84])
#define CpPack_CAtom (*((int (*)(PyObject* op, CpCAtomObject* catom, CpLayerObject* layer)))Cp_API[85])
@@ -236,23 +352,75 @@ caterpillar_api.py
#define CpTypeOf_CAtom (*((PyObject * (*)(CpCAtomObject* op)))Cp_API[87])
#define CpState_New (*((CpStateObject* (*)(PyObject* io)))Cp_API[88])
#define CpState_Tell (*((PyObject* (*)(CpStateObject* self)))Cp_API[89])
-#define CpState_Seek (*((PyObject* (*)(CpStateObject* self, PyObject* offset, int whence)))Cp_API[90])
-#define CpState_Read (*((PyObject* (*)(CpStateObject* self, Py_ssize_t size)))Cp_API[91])
-#define CpState_ReadFully (*((PyObject* (*)(CpStateObject* self)))Cp_API[92])
-#define CpState_Write (*((PyObject* (*)(CpStateObject* self, PyObject* value)))Cp_API[93])
-#define CpState_SetGlobals (*((int (*)(CpStateObject* self, PyObject* globals)))Cp_API[94])
-#define CpLayer_New (*((CpLayerObject* (*)(CpStateObject* state, CpLayerObject* parent)))Cp_API[95])
-#define CpLayer_Invalidate (*((int (*)(CpLayerObject* self)))Cp_API[96])
-#define CpLayer_SetSequence (*((int (*)(CpLayerObject* self,PyObject* sequence,Py_ssize_t length,int8_t greedy)))Cp_API[97])
-#define CpStructFieldInfo_New (*((CpStructFieldInfoObject* (*)(CpFieldObject* field)))Cp_API[98])
-#define CpStruct_AddFieldInfo (*((int (*)(CpStructObject* o, CpStructFieldInfoObject* info)))Cp_API[99])
-#define CpStruct_AddField (*((int (*)(CpStructObject* o, CpFieldObject* field, int exclude)))Cp_API[100])
-#define CpStruct_New (*((CpStructObject* (*)(PyObject* model)))Cp_API[101])
-#define CpStruct_GetAnnotations (*((PyObject* (*)(CpStructObject* o, int eval)))Cp_API[102])
-#define CpStruct_ReplaceType (*((int (*)(CpStructObject* o, PyObject* name, PyObject* type)))Cp_API[103])
-#define CpStruct_HasOption (*((int (*)(CpStructObject* o, PyObject* option)))Cp_API[104])
-#define CpStructModel_Check (*((int (*)(PyObject* model, _modulestate* state)))Cp_API[105])
-#define CpStructModel_GetStruct (*((PyObject* (*)(PyObject* model, _modulestate* state)))Cp_API[106])
+#define CpState_Seek (*((PyObject* (*)(CpStateObject* self, PyObject* offset, PyObject* whence)))Cp_API[90])
+#define CpState_Read (*((PyObject* (*)(CpStateObject* self, PyObject* sizeobj)))Cp_API[91])
+#define CpState_ReadSsize_t (*((PyObject* (*)(CpStateObject* self, Py_ssize_t size)))Cp_API[92])
+#define CpState_ReadFully (*((PyObject* (*)(CpStateObject* self)))Cp_API[93])
+#define CpState_Write (*((PyObject* (*)(CpStateObject* self, PyObject* value)))Cp_API[94])
+#define CpState_SetGlobals (*((int (*)(CpStateObject* self, PyObject* globals)))Cp_API[95])
+#define CpLayer_New (*((CpLayerObject* (*)(CpStateObject* state, CpLayerObject* parent)))Cp_API[96])
+#define CpLayer_Invalidate (*((int (*)(CpLayerObject* self)))Cp_API[98])
+#define CpStructFieldInfo_New (*((CpStructFieldInfoObject* (*)(PyObject* name, PyObject* field)))Cp_API[99])
+#define CpStruct_AddFieldInfo (*((int (*)(CpStructObject* o, CpStructFieldInfoObject* info)))Cp_API[100])
+#define CpStruct_New (*((CpStructObject* (*)(PyObject* model)))Cp_API[102])
+#define CpStruct_GetAnnotations (*((PyObject* (*)(CpStructObject* o, int eval)))Cp_API[103])
+#define CpStruct_ReplaceType (*((int (*)(CpStructObject* o, PyObject* name, PyObject* type)))Cp_API[104])
+#define CpStruct_HasOption (*((int (*)(CpStructObject* o, PyObject* option)))Cp_API[105])
+#define CpStruct_Pack (*((int (*)(CpStructObject* self, PyObject* obj, CpLayerObject* layer)))Cp_API[106])
+#define CpStruct_Unpack (*((PyObject* (*)(CpStructObject* self, CpLayerObject* layer)))Cp_API[107])
+#define CpStruct_SizeOf (*((PyObject* (*)(CpStructObject* self, CpLayerObject* layer)))Cp_API[108])
+#define CpStructModel_Check (*((int (*)(PyObject* model, _modulestate* state)))Cp_API[109])
+#define CpStructModel_GetStruct (*((PyObject* (*)(PyObject* model, _modulestate* state)))Cp_API[110])
+#define CpSeqLayer_New (*((CpSeqLayerObject* (*)(CpStateObject* state, CpLayerObject* parent)))Cp_API[111])
+#define CpSeqLayer_SetSequence (*((int (*)(CpSeqLayerObject* self,PyObject* sequence,Py_ssize_t length,int8_t greedy)))Cp_API[112])
+#define CpObjLayer_New (*((CpObjLayerObject* (*)(CpStateObject* state, CpLayerObject* parent)))Cp_API[113])
+#define CpIntAtom_Pack (*((int (*)(CpIntAtomObject* self, PyObject* op, CpLayerObject* layer)))Cp_API[120])
+#define CpIntAtom_Unpack (*((PyObject* (*)(CpIntAtomObject* self, CpLayerObject* layer)))Cp_API[121])
+#define CpFloatAtom_Pack (*((int (*)(CpFloatAtomObject* self, PyObject* value, CpLayerObject* layer)))Cp_API[122])
+#define CpFloatAtom_Unpack (*((PyObject* (*)(CpFloatAtomObject* self, CpLayerObject* layer)))Cp_API[123])
+#define CpBoolAtom_Pack (*((int (*)(CpBoolAtomObject* self, PyObject* value, CpLayerObject* layer)))Cp_API[124])
+#define CpBoolAtom_Unpack (*((PyObject* (*)(CpBoolAtomObject* self, CpLayerObject* layer)))Cp_API[125])
+#define CpCharAtom_Pack (*((int (*)(CpCharAtomObject* self, PyObject* value, CpLayerObject* layer)))Cp_API[126])
+#define CpCharAtom_Unpack (*((PyObject* (*)(CpCharAtomObject* self, CpLayerObject* layer)))Cp_API[127])
+#define CpPaddingAtom_Pack (*((int (*)(CpPaddingAtomObject* self,PyObject* value,CpLayerObject* layer)))Cp_API[128])
+#define CpPaddingAtom_PackMany (*((int (*)(CpPaddingAtomObject* self,PyObject* value,CpLayerObject* layer,CpLengthInfoObject* lengthinfo)))Cp_API[129])
+#define CpPaddingAtom_Unpack (*((PyObject* (*)(CpPaddingAtomObject* self, CpLayerObject* layer)))Cp_API[130])
+#define CpPaddingAtom_UnpackMany (*((PyObject* (*)(CpPaddingAtomObject* self,CpLayerObject* layer,CpLengthInfoObject* lengthinfo)))Cp_API[131])
+#define CpStringAtom_Pack (*((int (*)(CpStringAtomObject* self,PyObject* value,CpLayerObject* layer)))Cp_API[132])
+#define CpStringAtom_Unpack (*((PyObject* (*)(CpStringAtomObject* self, CpLayerObject* layer)))Cp_API[133])
+#define CpConstAtom_Pack (*((int (*)(CpConstAtomObject* self, PyObject* value, CpLayerObject* layer)))Cp_API[134])
+#define CpConstAtom_Unpack (*((PyObject* (*)(CpConstAtomObject* self, CpLayerObject* layer)))Cp_API[135])
+#define CpRepeatedAtom_Pack (*((int (*)(CpRepeatedAtomObject* self,PyObject* op,CpLayerObject* layer)))Cp_API[136])
+#define CpRepeatedAtom_Unpack (*((PyObject* (*)(CpRepeatedAtomObject* self, CpLayerObject* layer)))Cp_API[137])
+#define CpRepeatedAtom_GetLength (*((PyObject* (*)(CpRepeatedAtomObject* self, PyObject* context)))Cp_API[138])
+#define CpConditionAtom_Pack (*((int (*)(CpConditionAtomObject* self, PyObject* op, PyObject* layer)))Cp_API[139])
+#define CpConditionAtom_Unpack (*((PyObject* (*)(CpConditionAtomObject* self, CpLayerObject* layer)))Cp_API[140])
+#define CpConditionAtom_IsEnabled (*((int (*)(CpConditionAtomObject* self, PyObject* context)))Cp_API[141])
+#define CpSwitchAtom_GetNext (*((PyObject* (*)(CpSwitchAtomObject* self, PyObject* op, PyObject* context)))Cp_API[142])
+#define CpSwitchAtom_Pack (*((int (*)(CpSwitchAtomObject* self, PyObject* obj, CpLayerObject* layer)))Cp_API[143])
+#define CpSwitchAtom_Unpack (*((PyObject* (*)(CpSwitchAtomObject* self, CpLayerObject* layer)))Cp_API[144])
+#define CpOffsetAtom_Pack (*((int (*)(CpOffsetAtomObject* self, PyObject* obj, CpLayerObject* layer)))Cp_API[145])
+#define CpOffsetAtom_Unpack (*((PyObject* (*)(CpOffsetAtomObject* self, CpLayerObject* layer)))Cp_API[146])
+#define CpOffsetAtom_GetOffset (*((PyObject* (*)(CpOffsetAtomObject* self, PyObject* layer)))Cp_API[147])
+#define CpBytesAtom_GetLength (*((PyObject* (*)(CpBytesAtomObject* self, CpLayerObject* layer)))Cp_API[148])
+#define CpBytesAtom_Pack (*((int (*)(CpBytesAtomObject* self, PyObject* value, CpLayerObject* layer)))Cp_API[149])
+#define CpBytesAtom_Unpack (*((PyObject* (*)(CpBytesAtomObject* self, CpLayerObject* layer)))Cp_API[150])
+#define CpPStringAtom_Pack (*((int (*)(CpPStringAtomObject* self,PyObject* value,CpLayerObject* layer)))Cp_API[152])
+#define CpPStringAtom_Unpack (*((PyObject* (*)(CpPStringAtomObject* self, CpLayerObject* layer)))Cp_API[153])
+#define CpEnumAtom_Pack (*((int (*)(CpEnumAtomObject* self, PyObject* value, CpLayerObject* layer)))Cp_API[154])
+#define CpEnumAtom_Unpack (*((PyObject* (*)(CpEnumAtomObject* self, CpLayerObject* layer)))Cp_API[155])
+#define CpVarIntAtom_Pack (*((int (*)(CpVarIntAtomObject* self,PyObject* value,CpLayerObject* layer)))Cp_API[156])
+#define CpVarIntAtom_Unpack (*((PyObject* (*)(CpVarIntAtomObject* self, CpLayerObject* layer)))Cp_API[157])
+#define CpVarIntAtom_BSwap (*((PyObject* (*)(PyObject* number, bool little_endian)))Cp_API[158])
+#define CpVarIntAtom_BSwapUnsignedLongLong (*((unsigned long long (*)(unsigned long long number,bool little_endian)))Cp_API[159])
+#define CpVarIntAtom_BSwapLongLong (*((long long (*)(long long number, bool little_endian)))Cp_API[160])
+#define CpVarIntAtom_BSwapSsize_t (*((Py_ssize_t (*)(Py_ssize_t number, bool little_endian)))Cp_API[161])
+#define CpComputedAtom_Pack (*((int (*)(CpComputedAtomObject* self,PyObject* obj,CpLayerObject* layer)))Cp_API[162])
+#define CpComputedAtom_Unpack (*((PyObject* (*)(CpComputedAtomObject* self, CpLayerObject* layer)))Cp_API[163])
+#define CpLazyAtom_Pack (*((int (*)(CpLazyAtomObject* self, PyObject* obj, CpLayerObject* layer)))Cp_API[164])
+#define CpLazyAtom_Unpack (*((PyObject* (*)(CpLazyAtomObject* self, CpLayerObject* layer)))Cp_API[165])
+#define CpCStringAtom_Pack (*((int (*)(CpCStringAtomObject* self,PyObject* value,CpLayerObject* layer)))Cp_API[166])
+#define CpCStringAtom_Unpack (*((PyObject* (*)(CpCStringAtomObject* self, CpLayerObject* layer)))Cp_API[167])
/**
* @brief Public C API for extension modules as reference table
diff --git a/src/caterpillar/include/caterpillar/context.h b/src/caterpillar/include/caterpillar/context.h
index 905ece0..e43019b 100644
--- a/src/caterpillar/include/caterpillar/context.h
+++ b/src/caterpillar/include/caterpillar/context.h
@@ -59,21 +59,6 @@ struct _contextobj
*/
#define CpContext_Check(op) PyObject_TypeCheck((op), &CpContext_Type)
-/**
- * @brief Get an attribute from a context by using __context_getattr__.
- *
- * @return the attribute, or NULL on failure
- */
-PyAPI_FUNC(PyObject*)
- CpContext_GetAttr(PyObject* ctx, PyObject* key, _modulestate* m);
-
-/**
- * @brief Get an attribute from a context by using __context_getattr__.
- *
- * @return the attribute, or NULL on failure
- */
-PyAPI_FUNC(PyObject*) CpContext_GetAttrString(PyObject* ctx, const char* key);
-
// -----------------------------------------------------------------------------
// unary expression
enum
diff --git a/src/caterpillar/include/caterpillar/default.h b/src/caterpillar/include/caterpillar/default.h
new file mode 100644
index 0000000..eb47cd0
--- /dev/null
+++ b/src/caterpillar/include/caterpillar/default.h
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) MatrixEditor 2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef CP_DEFAULT_H
+#define CP_DEFAULT_H
+
+#include "caterpillarapi.h"
+
+/* immortal objects */
+
+#define CpInvalidDefault (&_CpInvalidDefault_Object)
+#define Cp_IsInvalidDefault(o) ((o) == CpInvalidDefault)
+
+#define CpDefaultOption (&_CpDefaultOption_Object)
+#define Cp_IsDefaultOption(o) ((o) == CpDefaultOption)
+
+#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/field.h b/src/caterpillar/include/caterpillar/field.h
deleted file mode 100644
index 3ac4fd3..0000000
--- a/src/caterpillar/include/caterpillar/field.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * Copyright (C) MatrixEditor 2024
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-#ifndef CP_FIELD_H
-#define CP_FIELD_H
-
-#include "atomobj.h"
-
-/* Caterpillar Field definition */
-
-/**
- * @brief C implementation of the Python equivalent.
- *
- * Represents a field in a data structure.
- */
-struct _fieldobj
-{
- PyObject_HEAD
-
- /// The name of this field
- PyObject* m_name;
-
- /// Stores a reference to the actual parsing struct that will be used
- /// to parse or build our data. This attribute is never null.
- PyObject* m_atom;
-
- /// An automatically inferred or explicitly specified byte order. Note that
- /// this attribute may have no impact on the underlying struct. The default
- /// byte order is `NATIVE_ENDIAN`.
- PyObject* m_endian;
-
- /// Using the `@` operator an offset can be assigned to a field. If set, the
- /// stream will be reset and set to the original position.
- ///
- /// The minus one indicates that no offset has been associated with this
- /// field.
- ///
- /// @see CpField_GetOffset
- PyObject* m_offset;
-
- /// Additional options that can be enabled using the logical OR operator
- /// `|`.
- ///
- /// Note that there are default options that will be set automatically:
- /// * `field:keep_position`:
- /// Persists the streams position after parsing data using the
- /// underlying struct. In relation to `offset`, this option will reset
- /// the stream to its original position if deactivated.
- /// * `field:dynamic`:
- /// Specifies that this field does not store a constant size.
- /// * `field:sequential`:
- /// An automatic flag that indicates this field stores a sequential
- /// struct.
- PyObject* m_options;
-
- /// A constant or dynamic value to represent the amount of structs. Zero
- /// indicates there are no sequence types associated with this field.
- PyObject* m_length;
-
- /// An extra attribute that stores additional options that can be translates
- /// as a switch statement.
- PyObject* m_switch;
-
- /// Given optional execution this attribute should be used to return a boolean
- /// value that decides whether the value of this field should be set. Using
- /// `//` the condition can be set during class declaration.
- PyObject* m_condition;
-
- /// The field's architecture (inferred or explicitly specified).
- PyObject* m_arch;
-
- /// The configured default value.
- PyObject* m_default;
-
- // internal state variables
- int8_t s_size;
- int8_t s_type;
- int8_t s_sequential;
- int8_t s_keep_pos;
-};
-
-/// Field type
-// PyAPI_DATA(PyTypeObject) CpField_Type;
-
-/**
- * @brief Check whether the given object is afield
- *
- * @param v the object to check
- * @return true if the object is a field
- * @return false if the object is not a field
- */
-#define CpField_CheckExact(v) Py_IS_TYPE((v), &CpField_Type)
-
-/**
- * @brief Check whether the given object is a field
- *
- * @param v the object to check
- * @return true if the object is a field
- * @return false if the object is not a field
- */
-#define CpField_Check(v) PyObject_TypeCheck((v), &CpField_Type)
-
-// -----------------------------------------------------------------------------
-// field atom
-
-/**
- * @brief C implementation of the Python equivalent (FieldMixin).
- *
- * A simple mixin to support operators used to create `Field` instances.
- */
-struct _fieldatomobj
-{
- CpAtom_HEAD
-};
-
-/// Field atom type
-// PyAPI_DATA(PyTypeObject) CpFieldAtom_Type;
-
-/**
- * @brief Check whether the given object is a field atom
- *
- * @param v the object to check
- * @return true if the object is a field atom
- * @return false if the object is not a field atom
- */
-#define CpFieldAtom_CheckExact(v) Py_IS_TYPE((v), &CpFieldAtom_Type)
-
-/**
- * @brief Check whether the given object is a field atom
- *
- * @param v the object to check
- * @return true if the object is a field atom
- * @return false if the object is not a field atom
- */
-#define CpFieldAtom_Check(v) PyObject_TypeCheck((v), &CpFieldAtom_Type)
-
-#define CpFieldAtom_HEAD CpFieldAtomObject ob_base;
-
-// -----------------------------------------------------------------------------
-// field C atom
-struct _fieldcatomobj
-{
- CpCAtom_HEAD
-};
-
-/**
- * @brief Check whether the given object is a field C atom
- *
- * @param v the object to check
- * @return true if the object is a field C atom
- * @return false if the object is not a field C atom
- */
-#define CpFieldCAtom_CheckExact(v) Py_IS_TYPE((v), &CpFieldCAtom_Type)
-
-/**
- * @brief Check whether the given object is a field C atom
- *
- * @param v the object to check
- * @return true if the object is a field C atom
- * @return false if the object is not a field C atom
- */
-#define CpFieldCAtom_Check(v) PyObject_TypeCheck((v), &CpFieldCAtom_Type)
-
-#define CpFieldCAtom_HEAD CpFieldCAtomObject ob_base;
-#define CpFieldCAtom_CATOM(x) (x)->ob_base.ob_base
-
-#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/macros.h b/src/caterpillar/include/caterpillar/macros.h
index 11cb4ca..c3fb6bd 100644
--- a/src/caterpillar/include/caterpillar/macros.h
+++ b/src/caterpillar/include/caterpillar/macros.h
@@ -23,6 +23,8 @@
#include
+#define _Cp_NameStr(x) ("caterpillar._C." x)
+
/**
* @brief Appends the name of the C module to the given name.
*/
diff --git a/src/caterpillar/include/caterpillar/module.h b/src/caterpillar/include/caterpillar/module.h
index 2d83c00..70bb7b9 100644
--- a/src/caterpillar/include/caterpillar/module.h
+++ b/src/caterpillar/include/caterpillar/module.h
@@ -52,8 +52,9 @@ struct _modulestate
// typing constants
PyObject* Any_Type;
PyObject* List_Type;
+ PyObject* Optional_Type;
PyObject* Union_Type;
- PyObject *BytesIO_Type;
+ PyObject* BytesIO_Type;
// string constants
// strings
@@ -70,6 +71,7 @@ struct _modulestate
PyObject* str___unpack_many__;
PyObject* str___size__;
PyObject* str___type__;
+ PyObject* str___bits__;
PyObject* str___annotations__;
PyObject* str___mro__;
PyObject* str___struct__;
@@ -78,6 +80,8 @@ struct _modulestate
PyObject* str___weakref__;
PyObject* str___dict__;
PyObject* str___qualname__;
+ PyObject* str__member_map_;
+ PyObject* str__value2member_map_;
PyObject* str_start;
PyObject* str_ctx__root;
@@ -85,16 +89,20 @@ struct _modulestate
PyObject* str_bytesio_getvalue;
PyObject* str_builder_process;
PyObject* str_pattern_match;
+ PyObject* str_cstring_default_pad;
+ PyObject* str_utf8;
// compiled regex for unnamed fields
PyObject* cp_regex__unnamed;
PyObject* inspect_getannotations;
// cached objects
- PyObject *cp_bytes__true;
- PyObject *cp_bytes__false;
-};
+ PyObject* cp_bytes__true;
+ PyObject* cp_bytes__false;
+ // type handler map
+ PyObject* cp_typehandler_map;
+};
/**
* @brief Get the module state object
@@ -121,18 +129,6 @@ get_global_module_state(void)
return get_module_state(PyState_FindModule(&CpModule));
}
-/* immortal objects */
-// PyAPI_DATA(PyTypeObject) CpInvalidDefault_Type;
-// PyAPI_DATA(PyTypeObject) CpDefaultOption_Type;
-
-// PyAPI_DATA(PyObject) _CpInvalidDefault_Object;
-#define CpInvalidDefault (&_CpInvalidDefault_Object)
-#define Cp_IsInvalidDefault(o) ((o) == CpInvalidDefault)
-
-// PyAPI_DATA(PyObject) _CpDefaultOption_Object;
-#define CpDefaultOption (&_CpDefaultOption_Object)
-#define Cp_IsDefaultOption(o) ((o) == CpDefaultOption)
-
/* utility macros */
#define CpModule_SetupType(op) \
if (PyType_Ready(op) < 0) \
diff --git a/src/caterpillar/include/caterpillar/parsing.h b/src/caterpillar/include/caterpillar/parsing.h
new file mode 100644
index 0000000..3a05b0c
--- /dev/null
+++ b/src/caterpillar/include/caterpillar/parsing.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) MatrixEditor 2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef CP_PARSING_H
+#define CP_PARSING_H
+
+#include "caterpillar/atomobj.h"
+#include "caterpillar/module.h"
+#include "caterpillar/state.h"
+
+static inline int
+_Cp_Pack(PyObject* op, PyObject* atom, CpLayerObject* layer)
+{
+ if (CpCAtom_Check(atom)) {
+ return CpPack_CAtom(op, (CpCAtomObject*)atom, layer);
+ }
+
+ _modulestate* mod = layer->m_state->mod;
+ PyObject* result = CpAtom_Pack(atom, mod->str___pack__, op, (PyObject*)layer);
+ if (result == NULL) {
+ return -1;
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+static inline PyObject*
+_Cp_Unpack(PyObject* op, CpLayerObject* layer)
+{
+ if (CpCAtom_Check(op)) {
+ return CpUnpack_CAtom((CpCAtomObject*)op, layer);
+ }
+ return PyObject_CallMethodOneArg(
+ op, layer->m_state->mod->str___unpack__, (PyObject*)layer);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/state.h b/src/caterpillar/include/caterpillar/state.h
index 5133148..68411fe 100644
--- a/src/caterpillar/include/caterpillar/state.h
+++ b/src/caterpillar/include/caterpillar/state.h
@@ -74,37 +74,26 @@ struct _stateobj
//-----------------------------------------------------------------------------
// layer
+enum {
+ CpLayerClass_Default = 0,
+ CpLayerClass_Sequence = 1,
+ CpLayerClass_Object = 2,
+};
/**
- * @brief TODO
+ * @brief Top level layer class with a reference to its parent and the global
+ * parsing state.
*/
struct _layerobj
{
PyObject_HEAD
- /// The parent layer
- struct _layerobj* m_parent;
+ /// The parent layer
+ struct _layerobj* m_parent;
/// the global parsing state
CpStateObject* m_state;
- // --- Context sensitive variables ---
-
- /// In case a struct is linked to a field, the `Field` instance will always
- /// set this layer to be accessible from within the underlying struct.
- PyObject* m_field;
-
- /// When packing or unpacking objects, the current object attributes are
- /// stored within an object context. This is a special context that allows
- /// access to previously parsed fields or attributes of the input object. To
- /// minimize the number of calls using this attribute, a shortcut named `this`
- /// was defined, which automatically inserts a path to the object context.
- PyObject* m_obj;
-
- /// In case a switch-case statement is activated in a field, the layer will
- /// store the parsed value in this variable temporarily.
- PyObject* m_value;
-
/// Although it is optional to provide the current parsing or building path,
/// it is recommended. All nesting structures implement a behavior that
/// automatically adds a sub-path while packing or unpacking. Special names
@@ -112,24 +101,46 @@ struct _layerobj
/// elements
PyObject* m_path;
- /// Same as `m_obj` but for the sequential elements.
- PyObject* m_sequence;
+ // REVISIT: do we need them?
+ /// Lazily loaded context sensitive variables (this field remains NULL until
+ /// the first item gets added to it)
+ // PyObject *m_locals;
- /// The length of the current collection.
- Py_ssize_t m_length;
+ /// The type of the layer
+ int8_t m_class;
- /// When packing or unpacking collections of elements, the current working
- /// index is given under this layer variable. It is set only in this specific
- /// situation.
- Py_ssize_t m_index;
+ // --- Context sensitive variables ---
- // --- Internal state variables ---
- int8_t s_greedy;
- int8_t s_sequential;
-};
+ // /// In case a struct is linked to a field, the `Field` instance will always
+ // /// set this layer to be accessible from within the underlying struct.
+ // PyObject* m_field;
+
+ // /// When packing or unpacking objects, the current object attributes are
+ // /// stored within an object context. This is a special context that allows
+ // /// access to previously parsed fields or attributes of the input object. To
+ // /// minimize the number of calls using this attribute, a shortcut named `this`
+ // /// was defined, which automatically inserts a path to the object context.
+ // PyObject* m_obj;
+
+ // /// In case a switch-case statement is activated in a field, the layer will
+ // /// store the parsed value in this variable temporarily.
+ // PyObject* m_value;
+
+ // /// Same as `m_obj` but for the sequential elements.
+ // PyObject* m_sequence;
-/// Layer type
-// PyAPI_DATA(PyTypeObject) CpLayer_Type;
+ // /// The length of the current collection.
+ // Py_ssize_t m_length;
+
+ // /// When packing or unpacking collections of elements, the current working
+ // /// index is given under this layer variable. It is set only in this specific
+ // /// situation.
+ // Py_ssize_t m_index;
+
+ // // --- Internal state variables ---
+ // int8_t s_greedy;
+ // int8_t s_sequential;
+};
/**
* @brief Check whether the given object is a layer
@@ -148,7 +159,7 @@ struct _layerobj
* @return false if the object is not a layer
*/
#define CpLayer_Check(v) PyObject_TypeCheck((v), &CpLayer_Type)
-
+#define CpLayer_HEAD CpLayerObject ob_base;
#define CpLayer_AppendPath(layer, newpath) \
Py_XSETREF( \
@@ -159,4 +170,58 @@ struct _layerobj
: (layer)->m_path), \
_PyUnicode_AsString((newpath))));
+//-----------------------------------------------------------------------------
+// seq layer
+
+struct _seqlayerobj
+{
+ CpLayer_HEAD
+
+ /// Same as `m_obj` but for the sequential elements.
+ PyObject* m_sequence;
+
+ /// The length of the current collection.
+ Py_ssize_t m_length;
+
+ /// When packing or unpacking collections of elements, the current working
+ /// index is given under this layer variable. It is set only in this specific
+ /// situation.
+ Py_ssize_t m_index;
+
+ int8_t s_greedy;
+};
+
+#define CpSeqLayer_CheckExact(v) Py_IS_TYPE((v), &CpSeqLayer_Type)
+#define CpSeqLayer_Check(v) PyObject_TypeCheck((v), &CpSeqLayer_Type)
+
+//-----------------------------------------------------------------------------
+// obj layer
+
+struct _objlayerobj
+{
+ CpLayer_HEAD
+
+ /// When packing or unpacking objects, the current object attributes are
+ /// stored within an object context. This is a special context that allows
+ /// access to previously parsed fields or attributes of the input object. To
+ /// minimize the number of calls using this attribute, a shortcut named `this`
+ /// was defined, which automatically inserts a path to the object context.
+ PyObject* m_obj;
+};
+
+#define CpObjLayer_CheckExact(v) Py_IS_TYPE((v), &CpObjLayer_Type)
+#define CpObjLayer_Check(v) PyObject_TypeCheck((v), &CpObjLayer_Type)
+
+//-----------------------------------------------------------------------------
+// field layer
+
+struct _fieldlayerobj
+{
+ CpLayer_HEAD
+
+ /// In case a struct is linked to a field, the `Field` instance will always
+ /// set this layer to be accessible from within the underlying struct.
+ PyObject* m_field;
+};
+
#endif
\ No newline at end of file
diff --git a/src/caterpillar/include/caterpillar/struct.h b/src/caterpillar/include/caterpillar/struct.h
index 4d3f0b5..3fe351b 100644
--- a/src/caterpillar/include/caterpillar/struct.h
+++ b/src/caterpillar/include/caterpillar/struct.h
@@ -18,19 +18,25 @@
#define CP_STRUCT_H
#include "caterpillar/caterpillarapi.h"
-#include "caterpillar/field.h"
+#include "caterpillar/atoms/builtins.h"
/* Caterpillar Struct C implementation */
/**
* @brief Internal class used to store information about a field
*/
-struct CpStructFieldInfo
+struct _fieldinfoobj
{
PyObject_HEAD
- /// the referenced field object
- CpFieldObject* m_field;
+ /// the referenced field object
+ PyObject *m_field;
+
+ /// the name of the field
+ PyObject *m_name;
+
+ /// The configured default value.
+ PyObject* m_default;
// Excluded: True if the field is included in the struct
int8_t s_excluded;
@@ -70,7 +76,8 @@ struct CpStructFieldInfo
*/
struct _structobj
{
- CpFieldAtom_HEAD PyTypeObject* m_model; // underlying class
+ CpBuiltinAtom_HEAD
+ PyTypeObject* m_model; // underlying class
PyObject* m_members; // Dict[str, FieldInfo]
PyObject* m_options; // set[CpOption]
@@ -89,9 +96,6 @@ struct _structobj
_modulestate* s_mod;
};
-/// Struct object type
-// PyAPI_DATA(PyTypeObject) CpStruct_Type;
-
/**
* @brief Checks if the given object is a struct object
*
diff --git a/src/caterpillar/_Py.py b/src/caterpillar/py.py
similarity index 100%
rename from src/caterpillar/_Py.py
rename to src/caterpillar/py.py
diff --git a/src/caterpillarapi.c b/src/caterpillarapi.c
deleted file mode 100644
index 5af36dd..0000000
--- a/src/caterpillarapi.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/* These pointers will be stored in the C-object for use in other
- extension modules
-*/
-
-void *Cp_API[] = {
- (void *) &CpModule,
- (void *) &CpCAtom_Type,
- (void *) &CpArch_Type,
- (void *) &CpEndian_Type,
- (void *) &CpContext_Type,
- (void *) &CpUnaryExpr_Type,
- (void *) &CpBinaryExpr_Type,
- (void *) &CpContextPath_Type,
- (void *) &CpField_Type,
- (void *) &CpFieldAtom_Type,
- (void *) &CpFieldCAtom_Type,
- (void *) &CpInvalidDefault_Type,
- (void *) &CpDefaultOption_Type,
- (void *) &_CpInvalidDefault_Object,
- (void *) &_CpDefaultOption_Object,
- (void *) &CpAtom_Type,
- (void *) &CpOption_Type,
- (void *) &CpState_Type,
- (void *) &CpLayer_Type,
- (void *) &CpStructFieldInfo_Type,
- (void *) &CpStruct_Type,
- (void *) &CpFloatAtom_Type,
- (void *) &CpIntAtom_Type,
- (void *) &CpBoolAtom_Type,
- (void *) &CpCharAtom_Type,
- (void *) &CpPaddingAtom_Type,
- (void *) &CpStringAtom_Type,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- (void *) &CpEndian_IsLittleEndian,
- NULL,
- NULL,
- (void *) &CpContext_New,
- (void *) &CpUnaryExpr_New,
- (void *) &CpBinaryExpr_New,
- (void *) &CpContextPath_New,
- (void *) &CpContextPath_FromString,
- (void *) &CpField_New,
- (void *) &CpField_HasCondition,
- (void *) &CpField_IsEnabled,
- (void *) &CpField_GetOffset,
- (void *) &CpField_EvalSwitch,
- (void *) &CpField_GetLength,
- (void *) &CpTypeOf,
- (void *) &CpTypeOf_Field,
- (void *) &CpTypeOf_Common,
- (void *) &CpPack,
- (void *) &CpPack_Field,
- (void *) &CpPack_Common,
- (void *) &CpPack_Struct,
- (void *) &_Cp_Pack,
- (void *) &_CpPack_EvalLength,
- (void *) &CpSizeOf,
- (void *) &CpSizeOf_Field,
- (void *) &CpSizeOf_Struct,
- (void *) &CpSizeOf_Common,
- (void *) &_Cp_SizeOf,
- (void *) &CpUnpack,
- (void *) &CpUnpack_Field,
- (void *) &CpUnpack_Common,
- (void *) &CpUnpack_Struct,
- (void *) &_Cp_Unpack,
- (void *) &_CpUnpack_EvalLength,
- (void *) &CpUnpack_CAtom,
- (void *) &CpPack_CAtom,
- (void *) &CpSizeOf_CAtom,
- (void *) &CpTypeOf_CAtom,
- (void *) &CpState_New,
- (void *) &CpState_Tell,
- (void *) &CpState_Seek,
- (void *) &CpState_Read,
- (void *) &CpState_ReadFully,
- (void *) &CpState_Write,
- (void *) &CpState_SetGlobals,
- (void *) &CpLayer_New,
- (void *) &CpLayer_Invalidate,
- (void *) &CpLayer_SetSequence,
- (void *) &CpStructFieldInfo_New,
- (void *) &CpStruct_AddFieldInfo,
- (void *) &CpStruct_AddField,
- (void *) &CpStruct_New,
- (void *) &CpStruct_GetAnnotations,
- (void *) &CpStruct_ReplaceType,
- (void *) &CpStruct_HasOption,
- (void *) &CpStructModel_Check,
- (void *) &CpStructModel_GetStruct
-};
-
diff --git a/src/arch.c b/src/ccaterpillar/arch.c
similarity index 90%
rename from src/arch.c
rename to src/ccaterpillar/arch.c
index f832e47..2ca84b8 100644
--- a/src/arch.c
+++ b/src/ccaterpillar/arch.c
@@ -1,8 +1,7 @@
/* CpArch and CpEndian */
#include "caterpillar/caterpillar.h"
-#include "caterpillar/arch.h"
-#include "caterpillar/field.h"
-#include "structmember.h"
+
+#include
/* CpArch */
static PyObject*
@@ -54,8 +53,7 @@ cp_arch_init(CpArchObject* self, PyObject* args, PyObject* kw)
static PyObject*
cp_arch_repr(CpArchObject* self)
{
- return PyUnicode_FromFormat(
- "CpArch(name=%R, ptr_size=%i)", self->name, self->pointer_size);
+ return PyUnicode_FromFormat("", self->pointer_size, self->name);
}
static PyObject*
@@ -101,7 +99,7 @@ PyTypeObject CpArch_Type = {
.tp_dealloc = (destructor)cp_arch_dealloc,
.tp_repr = (reprfunc)cp_arch_repr,
.tp_hash = (hashfunc)cp_arch_hash,
- .tp_richcompare =(richcmpfunc)cp_arch_richcmp,
+ .tp_richcompare = (richcmpfunc)cp_arch_richcmp,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = cp_arch_doc,
.tp_members = CpArch_Members,
@@ -174,8 +172,16 @@ cp_endian_init(CpEndianObject* self, PyObject* args, PyObject* kw)
static PyObject*
cp_endian_repr(CpEndianObject* self)
{
- return PyUnicode_FromFormat(
- "CpEndian(name=%R, ch='%c')", self->name, self->id);
+ switch (self->id) {
+ case '=':
+ return PyUnicode_FromFormat("");
+ case '<':
+ return PyUnicode_FromFormat("");
+ case '>':
+ return PyUnicode_FromFormat("");
+ default:
+ return PyUnicode_FromFormat("", self->id, self->name);
+ }
}
static PyObject*
@@ -197,12 +203,12 @@ cp_endian_hash(CpEndianObject* self)
static PyObject*
cp_endian_as_number_add(CpEndianObject* self, PyObject* atom)
{
- CpFieldObject* field = (CpFieldObject*)CpField_New(atom);
- if (!field) {
+ if (!atom) {
+ PyErr_SetString(PyExc_ValueError, "atom must be non-null");
return NULL;
}
- _Cp_SetObj(field->m_endian, self);
- return (PyObject*)field;
+
+ return CpEndian_SetEndian(atom, self);
}
/* Doc strings */
diff --git a/src/atomimpl/boolatomobj.c b/src/ccaterpillar/atomimpl/bool.c
similarity index 72%
rename from src/atomimpl/boolatomobj.c
rename to src/ccaterpillar/atomimpl/bool.c
index bc5a058..9428473 100644
--- a/src/atomimpl/boolatomobj.c
+++ b/src/ccaterpillar/atomimpl/bool.c
@@ -1,7 +1,6 @@
/* boolatom C implementation */
-#include "caterpillar/atoms/primitive.h"
-#include "caterpillar/module.h"
-#include "caterpillar/state.h"
+#include "caterpillar/caterpillar.h"
+
#include
static PyObject*
@@ -21,13 +20,13 @@ cp_boolatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
CpBoolAtomObject* self = (CpBoolAtomObject*)type->tp_alloc(type, 0);
if (self != NULL) {
- CpFieldCAtom_CATOM(self).ob_pack = (packfunc)CpBoolAtom_Pack;
- CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)CpBoolAtom_Unpack;
- CpFieldCAtom_CATOM(self).ob_pack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_unpack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_size = (sizefunc)cp_boolatom__size__;
- CpFieldCAtom_CATOM(self).ob_type = (typefunc)cp_boolatom__type__;
- CpFieldCAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpBoolAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpBoolAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_boolatom__size__;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_boolatom__type__;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
}
return (PyObject*)self;
}
@@ -45,7 +44,7 @@ cp_boolatom_init(CpBoolAtomObject* self, PyObject* args, PyObject* kwds)
}
/* Public API */
-
+/*CpAPI*/
int
CpBoolAtom_Pack(CpBoolAtomObject* self, PyObject* value, CpLayerObject* layer)
{
@@ -63,11 +62,12 @@ CpBoolAtom_Pack(CpBoolAtomObject* self, PyObject* value, CpLayerObject* layer)
return 0;
}
+/*CpAPI*/
PyObject*
CpBoolAtom_Unpack(CpBoolAtomObject* self, CpLayerObject* layer)
{
PyObject* res;
- PyObject* value = CpState_Read(layer->m_state, 1);
+ PyObject* value = CpState_ReadSsize_t(layer->m_state, 1);
if (!value) {
return NULL;
}
@@ -84,7 +84,7 @@ CpBoolAtom_Unpack(CpBoolAtomObject* self, CpLayerObject* layer)
/* type setup */
PyTypeObject CpBoolAtom_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(boolatom),
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpBoolAtom_NAME),
.tp_basicsize = sizeof(CpBoolAtomObject),
.tp_dealloc = (destructor)cp_boolatom_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
diff --git a/src/ccaterpillar/atomimpl/builtins/atoffset.c b/src/ccaterpillar/atomimpl/builtins/atoffset.c
new file mode 100644
index 0000000..7b11102
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/builtins/atoffset.c
@@ -0,0 +1,193 @@
+/* offset atom implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_offsetatom_type(CpOffsetAtomObject* self)
+{
+ return CpTypeOf(self->m_atom);
+}
+
+static PyObject*
+cp_offsetatom_size(CpOffsetAtomObject* self, CpLayerObject* layer)
+{
+ return _Cp_SizeOf(self->m_atom, layer);
+}
+
+static PyObject*
+cp_offsetatom_repr(CpOffsetAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "] %R>", Py_TYPE(self->m_offset)->tp_name, self->m_atom);
+}
+
+static PyObject*
+cp_offsetatom_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpOffsetAtomObject* self = (CpOffsetAtomObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpOffsetAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpOffsetAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_offsetatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_offsetatom_type;
+ self->m_offset = 0;
+ self->m_atom = NULL;
+ return (PyObject*)self;
+}
+
+static void
+cp_offsetatom_dealloc(CpOffsetAtomObject* self)
+{
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = NULL;
+ CpBuiltinAtom_CATOM(self).ob_type = NULL;
+ Py_CLEAR(self->m_atom);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_offsetatom_init(CpOffsetAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "offset", "atom", "whence", NULL };
+ PyObject *offset = NULL, *atom = NULL;
+ int whence = 0;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "OO|i", kwlist, &atom, &offset, &whence))
+ return -1;
+
+ _Cp_SetObj(self->m_atom, atom);
+ _Cp_SetObj(self->m_offset, offset);
+ self->m_whence = PyLong_FromLong(whence);
+ if (!self->m_whence) {
+ return -1;
+ }
+ self->s_is_number = PyNumber_Check(self->m_offset);
+ return 0;
+}
+
+_CpEndian_ImplSetByteorder(CpOffsetAtomObject, offsetatom, self->m_atom);
+
+static PyObject*
+cp_offsetatom_get_offset(CpOffsetAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "layer", NULL };
+ PyObject* layer = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &layer))
+ return NULL;
+ return CpOffsetAtom_GetOffset(self, layer);
+}
+
+/* Public API */
+
+/*CpAPI*/
+PyObject*
+CpOffsetAtom_GetOffset(CpOffsetAtomObject* self, PyObject* layer)
+{
+ if (self->s_is_number) {
+ return Py_NewRef(self->m_offset);
+ }
+
+ // asset offset is callable
+ return PyObject_CallOneArg(self->m_offset, layer);
+}
+
+/*CpAPI*/
+int
+CpOffsetAtom_Pack(CpOffsetAtomObject* self, PyObject* obj, CpLayerObject* layer)
+{
+ // TODO: implement offset table
+ PyObject* fallback = CpState_Tell(layer->m_state);
+ if (!fallback) {
+ return -1;
+ }
+
+ PyObject* res = CpState_Seek(layer->m_state, self->m_offset, self->m_whence);
+ if (!res) {
+ return -1;
+ }
+ Py_DECREF(res);
+
+ if (_Cp_Pack(obj, self->m_atom, layer) < 0) {
+ return -1;
+ }
+
+ res = CpState_Seek(layer->m_state, fallback, PyLong_FromLong(0));
+ if (!res) {
+ return -1;
+ }
+ Py_DECREF(res);
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpOffsetAtom_Unpack(CpOffsetAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* fallback = CpState_Tell(layer->m_state);
+ if (!fallback) {
+ return NULL;
+ }
+
+ PyObject* res = CpState_Seek(layer->m_state, self->m_offset, self->m_whence);
+ if (!res) {
+ return NULL;
+ }
+ Py_DECREF(res);
+
+ PyObject* result = _Cp_Unpack(self->m_atom, layer);
+ if (!result) {
+ return NULL;
+ }
+
+ res = CpState_Seek(layer->m_state, fallback, PyLong_FromLong(0));
+ if (!res) {
+ return NULL;
+ }
+ Py_DECREF(res);
+ return result;
+}
+
+/* docs */
+
+/* members */
+
+static PyMemberDef CpOffsetAtom_Members[] = {
+ { "offset", T_OBJECT_EX, offsetof(CpOffsetAtomObject, m_offset), READONLY },
+ { "whence", T_OBJECT_EX, offsetof(CpOffsetAtomObject, m_whence), READONLY },
+ { "atom", T_OBJECT_EX, offsetof(CpOffsetAtomObject, m_atom), READONLY },
+ { NULL } /* Sentinel */
+};
+
+static PyMethodDef CpOffsetAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(offsetatom, NULL),
+ { "get_offset",
+ (PyCFunction)cp_offsetatom_get_offset,
+ METH_VARARGS | METH_KEYWORDS,
+ NULL },
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject CpOffsetAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpOffsetAtom_NAME),
+ .tp_basicsize = sizeof(CpOffsetAtomObject),
+ .tp_members = CpOffsetAtom_Members,
+ .tp_methods = CpOffsetAtom_Methods,
+ .tp_init = (initproc)cp_offsetatom_init,
+ .tp_dealloc = (destructor)cp_offsetatom_dealloc,
+ .tp_repr = (reprfunc)cp_offsetatom_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_new = (newfunc)cp_offsetatom_new,
+};
diff --git a/src/ccaterpillar/atomimpl/builtins/builtin.c b/src/ccaterpillar/atomimpl/builtins/builtin.c
new file mode 100644
index 0000000..3799b8c
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/builtins/builtin.c
@@ -0,0 +1,89 @@
+/* builtinatom C implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_builtinatom_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpBuiltinAtomObject* self;
+ self = (CpBuiltinAtomObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+ self->ob_base.ob_bits = NULL;
+ self->ob_base.ob_pack = NULL;
+ self->ob_base.ob_pack_many = NULL;
+ self->ob_base.ob_unpack = NULL;
+ self->ob_base.ob_unpack_many = NULL;
+ self->ob_base.ob_size = NULL;
+ self->ob_base.ob_type = NULL;
+ return (PyObject*)self;
+}
+
+static void
+cp_builtinatom_dealloc(CpBuiltinAtomObject* self)
+{
+ self->ob_base.ob_bits = NULL;
+ self->ob_base.ob_pack = NULL;
+ self->ob_base.ob_pack_many = NULL;
+ self->ob_base.ob_unpack = NULL;
+ self->ob_base.ob_unpack_many = NULL;
+ self->ob_base.ob_size = NULL;
+ self->ob_base.ob_type = NULL;
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_builtinatom_init(CpBuiltinAtomObject* self, PyObject* args, PyObject* kw)
+{
+ _Cp_InitNoArgs(CpBuiltinAtomObject, args, kw);
+}
+
+// TODO member methods
+static PyObject*
+cp_builtinatom_as_mapping_getitem(CpBuiltinAtomObject* self, PyObject* length)
+{
+ return (PyObject*)CpRepeatedAtom_New((PyObject*)self, length);
+}
+
+static PyObject*
+cp_builtinatom_as_number_rshift(PyObject* self, PyObject* other)
+{
+ return (PyObject*)CpSwitchAtom_New(self, other);
+}
+
+static PyObject*
+cp_builtinatom_as_number_floordiv(PyObject* self, PyObject* other)
+{
+ return (PyObject*)CpConditionAtom_New(self, other);
+}
+
+static PyObject*
+cp_builtinatom_as_number_matmul(PyObject* self, PyObject* other)
+{
+ return (PyObject*)CpOffsetAtom_New(self, other);
+}
+
+static PyMappingMethods CpBuiltinAtom_MappingMethods = {
+ .mp_subscript = (binaryfunc)cp_builtinatom_as_mapping_getitem,
+};
+
+static PyNumberMethods CpBuiltinAtom_NumberMethods = {
+ .nb_rshift = (binaryfunc)cp_builtinatom_as_number_rshift,
+ .nb_floor_divide = (binaryfunc)cp_builtinatom_as_number_floordiv,
+ .nb_matrix_multiply = (binaryfunc)cp_builtinatom_as_number_matmul,
+};
+
+PyTypeObject CpBuiltinAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpBuiltinAtom_NAME),
+ .tp_basicsize = sizeof(CpBuiltinAtomObject),
+ .tp_dealloc = (destructor)cp_builtinatom_dealloc,
+ .tp_init = (initproc)cp_builtinatom_init,
+ .tp_new = (newfunc)cp_builtinatom_new,
+ .tp_as_mapping = &CpBuiltinAtom_MappingMethods,
+ .tp_as_number = &CpBuiltinAtom_NumberMethods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+};
diff --git a/src/ccaterpillar/atomimpl/builtins/condition.c b/src/ccaterpillar/atomimpl/builtins/condition.c
new file mode 100644
index 0000000..a50f533
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/builtins/condition.c
@@ -0,0 +1,170 @@
+/* condition atom implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+
+static PyObject*
+cp_conditionatom_type(CpConditionAtomObject* self)
+{
+ PyObject* atom_type = CpTypeOf(self->m_atom);
+ if (!atom_type)
+ return NULL;
+
+ _modulestate* state = get_global_module_state();
+ Py_XSETREF(atom_type, PyObject_GetItem(state->Optional_Type, atom_type));
+ return atom_type;
+}
+
+static PyObject*
+cp_conditionatom_size(CpConditionAtomObject* self, CpLayerObject* layer)
+{
+ return _Cp_SizeOf(self->m_atom, layer);
+}
+
+static PyObject*
+cp_conditionatom_repr(CpConditionAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "] %R>", Py_TYPE(self->m_condition)->tp_name, self->m_atom);
+}
+
+static PyObject*
+cp_conditionatom_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpConditionAtomObject* self = (CpConditionAtomObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpConditionAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpConditionAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_conditionatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_conditionatom_type;
+ return (PyObject*)self;
+}
+
+static void
+cp_conditionatom_dealloc(CpConditionAtomObject* self)
+{
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = NULL;
+ CpBuiltinAtom_CATOM(self).ob_type = NULL;
+ Py_CLEAR(self->m_atom);
+ Py_CLEAR(self->m_condition);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_conditionatom_init(CpConditionAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "condition", "atom", NULL };
+ PyObject *condition = NULL, *atom = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &condition, &atom))
+ return -1;
+
+ _Cp_SetObj(self->m_atom, atom);
+ _Cp_SetObj(self->m_condition, condition);
+ return 0;
+}
+
+_CpEndian_ImplSetByteorder(CpConditionAtomObject, conditionatom, self->m_atom);
+
+static PyObject*
+cp_conditionatom_is_enabled(CpConditionAtomObject* self, PyObject* args)
+{
+ PyObject* layer = NULL;
+ if (!PyArg_ParseTuple(args, "O", &layer)) {
+ return NULL;
+ }
+
+ if (CpConditionAtom_IsEnabled(self, layer)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpConditionAtom_IsEnabled(CpConditionAtomObject* self, PyObject* context)
+{
+ if (self->m_condition == NULL) {
+ return true;
+ }
+
+ if (PyCallable_Check(self->m_condition)) {
+ PyObject* result = PyObject_CallOneArg(self->m_condition, context);
+ if (result == NULL) {
+ return -1;
+ }
+ int truth = PyObject_IsTrue(result);
+ Py_DECREF(result);
+ return truth;
+ }
+
+ return PyObject_IsTrue(self->m_condition);
+}
+
+/*CpAPI*/
+int
+CpConditionAtom_Pack(CpConditionAtomObject* self, PyObject* op, PyObject* layer)
+{
+ if (!CpConditionAtom_IsEnabled(self, layer)) {
+ return 0;
+ }
+
+ return _Cp_Pack(op, self->m_atom, (CpLayerObject*)layer);
+}
+
+/*CpAPI*/
+PyObject*
+CpConditionAtom_Unpack(CpConditionAtomObject* self, CpLayerObject* layer)
+{
+ if (!CpConditionAtom_IsEnabled(self, (PyObject*)layer)) {
+ return NULL;
+ }
+ return _Cp_Unpack(self->m_atom, layer);
+}
+
+/* docs */
+
+/* members */
+static PyMemberDef CpConditionAtom_Members[] = {
+ { "condition",
+ T_OBJECT,
+ offsetof(CpConditionAtomObject, m_condition),
+ READONLY },
+ { "atom", T_OBJECT, offsetof(CpConditionAtomObject, m_atom), READONLY },
+ { NULL }
+};
+
+/* methods */
+static PyMethodDef CpConditionAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(conditionatom, NULL),
+ { "is_enabled", (PyCFunction)cp_conditionatom_is_enabled, METH_VARARGS },
+ { NULL }
+};
+
+PyTypeObject CpConditionAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpConditionAtom_NAME),
+ .tp_basicsize = sizeof(CpConditionAtomObject),
+ .tp_dealloc = (destructor)cp_conditionatom_dealloc,
+ .tp_init = (initproc)cp_conditionatom_init,
+ .tp_members = CpConditionAtom_Members,
+ .tp_methods = CpConditionAtom_Methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_new = (newfunc)cp_conditionatom_new,
+ .tp_repr = (reprfunc)cp_conditionatom_repr,
+};
diff --git a/src/ccaterpillar/atomimpl/builtins/primitive.c b/src/ccaterpillar/atomimpl/builtins/primitive.c
new file mode 100644
index 0000000..85a8da7
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/builtins/primitive.c
@@ -0,0 +1,83 @@
+/* primitive atom C implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_primitiveatom_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpPrimitiveAtomObject* self;
+ self = (CpPrimitiveAtomObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+ return (PyObject*)self;
+}
+
+static void
+cp_primitiveatom_dealloc(CpPrimitiveAtomObject* self)
+{
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_primitiveatom_init(CpPrimitiveAtomObject* self, PyObject* args, PyObject* kw)
+{
+ _Cp_InitNoArgs(CpPrimitiveAtomObject, args, kw);
+}
+
+
+static PyObject *
+cp_primitiveatom_repr(CpPrimitiveAtomObject *self)
+{
+ return PyUnicode_FromString("");
+}
+
+// TODO member methods
+static PyObject*
+cp_primitiveatom_as_mapping_getitem(CpPrimitiveAtomObject* self, PyObject* length)
+{
+ return (PyObject*)CpRepeatedAtom_New((PyObject*)self, length);
+}
+
+static PyObject*
+cp_primitiveatom_as_number_rshift(PyObject* self, PyObject* other)
+{
+ return (PyObject*)CpSwitchAtom_New(self, other);
+}
+
+static PyObject*
+cp_primitiveatom_as_number_floordiv(PyObject* self, PyObject* other)
+{
+ return (PyObject*)CpConditionAtom_New(self, other);
+}
+
+static PyObject*
+cp_primitiveatom_as_number_matmul(PyObject* self, PyObject* other)
+{
+ return (PyObject*)CpOffsetAtom_New(self, other);
+}
+
+static PyMappingMethods CpPrimitiveAtom_MappingMethods = {
+ .mp_subscript = (binaryfunc)cp_primitiveatom_as_mapping_getitem,
+};
+
+static PyNumberMethods CpPrimitiveAtom_NumberMethods = {
+ .nb_rshift = (binaryfunc)cp_primitiveatom_as_number_rshift,
+ .nb_floor_divide = (binaryfunc)cp_primitiveatom_as_number_floordiv,
+ .nb_matrix_multiply = (binaryfunc)cp_primitiveatom_as_number_matmul,
+};
+
+PyTypeObject CpPrimitiveAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpPrimitiveAtom_NAME),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_basicsize = sizeof(CpPrimitiveAtomObject),
+ .tp_dealloc = (destructor)cp_primitiveatom_dealloc,
+ .tp_init = (initproc)cp_primitiveatom_init,
+ .tp_repr = (reprfunc)cp_primitiveatom_repr,
+ .tp_new = (newfunc)cp_primitiveatom_new,
+ .tp_doc = NULL,
+ .tp_as_mapping = &CpPrimitiveAtom_MappingMethods,
+ .tp_as_number = &CpPrimitiveAtom_NumberMethods,
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/builtins/repeated.c b/src/ccaterpillar/atomimpl/builtins/repeated.c
new file mode 100644
index 0000000..bf09cdc
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/builtins/repeated.c
@@ -0,0 +1,350 @@
+/* repeated atom C implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+
+static PyObject*
+cp_repeatedatomobj_type(CpRepeatedAtomObject* self)
+{
+ PyObject* atom_type = CpTypeOf(self->m_atom);
+ if (!atom_type)
+ return NULL;
+
+ _modulestate* state = get_global_module_state();
+ Py_XSETREF(atom_type, PyObject_GetItem(state->List_Type, atom_type));
+ return atom_type;
+}
+
+static PyObject*
+cp_repeatedatomobj_size(CpRepeatedAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* length = CpRepeatedAtom_GetLength(self, (PyObject*)layer);
+ if (!length) {
+ return NULL;
+ }
+ PyObject* atom_size = _Cp_SizeOf(self->m_atom, layer);
+ if (!atom_size) {
+ return NULL;
+ }
+
+ PyObject* result = PyNumber_Multiply(atom_size, length);
+ Py_DECREF(length);
+ return result;
+}
+
+static PyObject*
+cp_repeatedatomobj__repr__(CpRepeatedAtomObject* self)
+{
+ return PyUnicode_FromFormat("", self->m_atom);
+}
+
+static PyObject*
+cp_repeatedatomobj_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpRepeatedAtomObject* self;
+ self = (CpRepeatedAtomObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpRepeatedAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpRepeatedAtom_Unpack;
+ 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;
+}
+
+static void
+cp_repeatedatomobj_dealloc(CpRepeatedAtomObject* self)
+{
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = NULL;
+ CpBuiltinAtom_CATOM(self).ob_type = NULL;
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_repeatedatomobj_init(CpRepeatedAtomObject* self,
+ PyObject* args,
+ PyObject* kw)
+{
+ static char* kwlist[] = { "atom", "length", NULL };
+ PyObject *atom = NULL, *length = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &atom, &length)) {
+ return -1;
+ }
+ _Cp_SetObj(self->m_atom, atom);
+ _Cp_SetObj(self->m_length, length);
+ return 0;
+}
+
+_CpEndian_ImplSetByteorder(CpRepeatedAtomObject, repeatedatom, self->m_atom);
+
+/* Public API */
+
+/*CpAPI*/
+PyObject*
+CpRepeatedAtom_GetLength(CpRepeatedAtomObject* self, PyObject* context)
+{
+ if (PyCallable_Check(self->m_length)) {
+ PyObject* result = PyObject_CallOneArg(self->m_length, context);
+ if (!result)
+ return NULL;
+ return result;
+ }
+ return Py_NewRef(self->m_length);
+}
+
+/*CpAPI*/
+int
+CpRepeatedAtom_Pack(CpRepeatedAtomObject* self,
+ PyObject* op,
+ CpLayerObject* layer)
+{
+ CpStateObject* state = layer->m_state;
+ CpLengthInfoObject* lengthinfo =
+ (CpLengthInfoObject*)CpObject_CreateNoArgs(&CpLengthInfo_Type);
+
+ bool pack_many_attr =
+ PyObject_HasAttr(self->m_atom, state->mod->str___pack_many__);
+ bool is_seq = PySequence_Check(op);
+
+ Py_ssize_t obj_length = 0;
+ if (is_seq) {
+ obj_length = PySequence_Length(op);
+ if (obj_length < 0) {
+ goto failure;
+ }
+ }
+
+ PyObject* length = CpRepeatedAtom_GetLength(self, (PyObject*)layer);
+ if (!length) {
+ goto failure;
+ }
+
+ if (_CpPack_EvalLength(layer,
+ length,
+ obj_length,
+ (bool*)&lengthinfo->m_greedy,
+ &lengthinfo->m_length) < 0) {
+ Py_XDECREF(length);
+ goto failure;
+ }
+ Py_XDECREF(length);
+
+ if (pack_many_attr) {
+ // class explicitly defines __pack_many__ -> use it
+ PyObject* res = PyObject_CallMethodObjArgs(self->m_atom,
+ state->mod->str___pack_many__,
+ op,
+ (PyObject*)layer,
+ lengthinfo,
+ NULL);
+ PyObject* exc = NULL;
+ if ((exc = PyErr_GetRaisedException(),
+ exc && PyErr_GivenExceptionMatches(exc, PyExc_NotImplementedError))) {
+ // Make sure this method continues to pack the given object
+ Py_XDECREF(exc);
+ PyErr_Clear();
+ Py_XDECREF(res);
+ } else {
+ int success = res ? 0 : -1;
+ Py_XDECREF(res);
+ if (success < 0) {
+ PyErr_SetRaisedException(exc);
+ } else {
+ Py_XDECREF(exc);
+ }
+ return success;
+ }
+ }
+
+ if (!is_seq) {
+ PyErr_Format(PyExc_TypeError, "input object (%R) is not a sequence", op);
+ return -1;
+ }
+
+ if (lengthinfo->m_greedy) {
+ lengthinfo->m_length = obj_length;
+ } else {
+ if (lengthinfo->m_length != obj_length) {
+ PyErr_Format(PyExc_ValueError,
+ "given length %d does not match sequence size %d",
+ lengthinfo->m_length,
+ obj_length);
+ goto failure;
+ }
+ }
+
+ CpSeqLayerObject* seq_layer = CpSeqLayer_New(state, layer);
+ if (!seq_layer) {
+ return -1;
+ }
+
+ CpSeqLayer_SetSequence(
+ seq_layer, op, lengthinfo->m_length, lengthinfo->m_greedy);
+
+ PyObject* obj = NULL;
+ int success = 0;
+ for (seq_layer->m_index = 0; seq_layer->m_index < seq_layer->m_length;
+ seq_layer->m_index++) {
+ obj = PySequence_GetItem(op, seq_layer->m_index);
+ if (!obj) {
+ goto failure;
+ }
+ seq_layer->ob_base.m_path = PyUnicode_FromFormat(
+ "%s.%d", _PyUnicode_AsString(layer->m_path), seq_layer->m_index);
+ if (!seq_layer->ob_base.m_path) {
+ Py_XDECREF(obj);
+ goto failure;
+ }
+ success = _Cp_Pack(obj, self->m_atom, (CpLayerObject*)seq_layer);
+ Py_XSETREF(obj, NULL);
+ if (success < 0) {
+ goto failure;
+ }
+ }
+ CpLayer_Invalidate((CpLayerObject*)seq_layer);
+ return 0;
+failure:
+ Py_XDECREF(lengthinfo);
+ CpLayer_Invalidate((CpLayerObject*)seq_layer);
+ return -1;
+}
+
+/*CpAPI*/
+PyObject*
+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__);
+
+ PyObject* length = CpRepeatedAtom_GetLength(self, (PyObject*)layer);
+ if (!length) {
+ goto fail;
+ }
+ if (_CpUnpack_EvalLength(
+ layer, length, (bool*)&lengthinfo->m_greedy, &lengthinfo->m_length) <
+ 0) {
+ goto fail;
+ }
+
+ if (unpack_many_attr) {
+ PyObject* res = PyObject_CallMethodObjArgs(self->m_atom,
+ mod->str___unpack_many__,
+ (PyObject*)layer,
+ lengthinfo,
+ NULL);
+ PyObject* exc = NULL;
+ if ((exc = PyErr_GetRaisedException(),
+ exc && PyErr_GivenExceptionMatches(exc, PyExc_NotImplementedError))) {
+ // Make sure this method continues to unpack
+ Py_XDECREF(exc);
+ PyErr_Clear();
+ Py_XDECREF(res);
+ } else {
+ PyErr_SetRaisedException(exc);
+ return res;
+ }
+ }
+
+ CpSeqLayerObject* seq_layer = CpSeqLayer_New(layer->m_state, layer);
+ if (!layer) {
+ goto fail;
+ }
+
+ // REVISIT: add sequence factory here
+ PyObject* seq = PyList_New(0);
+ PyObject* obj = NULL;
+ if (!seq) {
+ goto fail;
+ }
+
+ CpSeqLayer_SetSequence(
+ seq_layer, seq, lengthinfo->m_length, lengthinfo->m_greedy);
+ while (seq_layer->s_greedy || (seq_layer->m_index < seq_layer->m_length)) {
+ seq_layer->ob_base.m_path = PyUnicode_FromFormat(
+ "%s.%d", _PyUnicode_AsString(layer->m_path), seq_layer->m_index);
+ if (!seq_layer->ob_base.m_path) {
+ goto fail;
+ }
+
+ Py_XSETREF(obj, _Cp_Unpack(self->m_atom, (CpLayerObject*)seq_layer));
+ if (!obj) {
+
+ if (seq_layer->s_greedy) {
+ PyErr_Clear();
+ break;
+ }
+ goto fail;
+ }
+
+ if (PyList_Append(seq_layer->m_sequence, Py_NewRef(obj)) < 0) {
+ goto fail;
+ }
+ seq_layer->m_index++;
+ }
+
+success:
+ Py_XDECREF(obj);
+ Py_XDECREF(length);
+ Py_XDECREF(lengthinfo);
+ CpLayer_Invalidate((CpLayerObject*)seq_layer);
+ seq_layer = NULL;
+ return Py_NewRef(seq);
+
+fail:
+ Py_XDECREF(obj);
+ Py_XDECREF(length);
+ Py_XDECREF(lengthinfo);
+ if (seq_layer) {
+ CpLayer_Invalidate((CpLayerObject*)seq_layer);
+ }
+ return NULL;
+}
+
+/* type setup */
+static PyMemberDef CpRepeatedAtom_Members[] = {
+ { "length", T_OBJECT, offsetof(CpRepeatedAtomObject, m_length), READONLY },
+ { "atom", T_OBJECT, offsetof(CpRepeatedAtomObject, m_atom), READONLY },
+ { NULL }
+};
+
+static PyMethodDef CpRepeatedAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(repeatedatom, NULL),
+ {
+ NULL,
+ }
+};
+
+PyTypeObject CpRepeatedAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpRepeatedAtom_NAME),
+ .tp_basicsize = sizeof(CpRepeatedAtomObject),
+ .tp_dealloc = (destructor)cp_repeatedatomobj_dealloc,
+ .tp_repr = (reprfunc)cp_repeatedatomobj__repr__,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_new = (newfunc)cp_repeatedatomobj_new,
+ .tp_init = (initproc)cp_repeatedatomobj_init,
+ .tp_members = CpRepeatedAtom_Members,
+ .tp_methods = CpRepeatedAtom_Methods,
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/builtins/switch.c b/src/ccaterpillar/atomimpl/builtins/switch.c
new file mode 100644
index 0000000..5f341a5
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/builtins/switch.c
@@ -0,0 +1,265 @@
+/* switch atom implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_switchatom_type(CpSwitchAtomObject* self)
+{
+ _modulestate* mod = get_global_module_state();
+ if (!self->s_callable) {
+ return Py_NewRef(mod->Any_Type);
+ }
+
+ PyObject* types = PyList_New(0);
+ if (!types) {
+ return NULL;
+ }
+
+ PyObject* atomType = CpTypeOf(self->m_atom);
+ if (!atomType) {
+ Py_DECREF(types);
+ return NULL;
+ }
+
+ if (PyList_Append(types, atomType) < 0) {
+ Py_DECREF(types);
+ return NULL;
+ }
+
+ PyObject* values = PyDict_Values(self->m_cases);
+ if (!values) {
+ Py_XDECREF(types);
+ Py_XDECREF(atomType);
+ return NULL;
+ }
+
+ Py_ssize_t length = PyList_GET_SIZE(values);
+ PyObject* switch_type = NULL;
+ for (Py_ssize_t i = 0; i < length; i++) {
+ PyObject* value = PyList_GetItem(values, i);
+ if (!value) {
+ Py_XDECREF(values);
+ Py_XDECREF(types);
+ Py_XDECREF(switch_type);
+ return NULL;
+ }
+
+ switch_type = CpTypeOf(value);
+ if (!switch_type) {
+ Py_XDECREF(values);
+ Py_XDECREF(types);
+ return NULL;
+ }
+
+ if (!PySequence_Contains(types, switch_type)) {
+ PyList_Append(types, switch_type);
+ }
+ Py_XSETREF(switch_type, NULL);
+ }
+
+ Py_XDECREF(values);
+ PyObject* tuple = PyList_AsTuple(types);
+ Py_XDECREF(types);
+ if (!tuple) {
+ Py_XDECREF(atomType);
+ Py_XDECREF(switch_type);
+ return NULL;
+ }
+
+ PyObject* type = PyObject_GetItem(mod->Union_Type, tuple);
+ Py_XDECREF(tuple);
+ Py_XDECREF(atomType);
+ Py_XDECREF(switch_type);
+ return type;
+}
+
+static PyObject*
+cp_switchatom_size(CpSwitchAtomObject* self, CpLayerObject* layer)
+{
+ PyErr_SetString(PyExc_TypeError, "Switch atoms does not have a static size!");
+ return NULL;
+}
+
+static PyObject*
+cp_switchatom_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpSwitchAtomObject* self = (CpSwitchAtomObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpSwitchAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpSwitchAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_switchatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_switchatom_type;
+ self->s_callable = false;
+ return (PyObject*)self;
+}
+
+static void
+cp_switchatom_dealloc(CpSwitchAtomObject* self)
+{
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = NULL;
+ CpBuiltinAtom_CATOM(self).ob_type = NULL;
+ Py_CLEAR(self->m_cases);
+ Py_CLEAR(self->m_atom);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_switchatom_init(CpSwitchAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "atom", "cases", NULL };
+ PyObject* cases = NULL;
+ PyObject* atom = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &atom, &cases)) {
+ return -1;
+ }
+ _Cp_SetObj(self->m_cases, cases);
+ _Cp_SetObj(self->m_atom, atom);
+ self->s_callable = PyCallable_Check(self->m_atom);
+ return 0;
+}
+
+_CpEndian_ImplSetByteorder(CpSwitchAtomObject, switchatom, self->m_atom);
+
+static PyObject*
+cp_switchatom_get_next(CpSwitchAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "obj", "context", NULL };
+ PyObject* op = NULL;
+ PyObject* context = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &op, &context)) {
+ return NULL;
+ }
+
+ return CpSwitchAtom_GetNext(self, op, context);
+}
+
+static PyObject*
+cp_switchatom_repr(CpSwitchAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "] %R>", Py_TYPE(self->m_cases)->tp_name, self->m_atom);
+}
+
+/* Public API */
+
+/*CpAPI*/
+PyObject*
+CpSwitchAtom_GetNext(CpSwitchAtomObject* self, PyObject* op, PyObject* context)
+{
+ PyObject* result = NULL;
+ if (PyCallable_Check(self->m_cases)) {
+ result = PyObject_CallOneArg(self->m_cases, context);
+ if (!result)
+ return NULL;
+ } else {
+ result = PyObject_GetItem(self->m_cases, op);
+ if (!result) {
+ result = PyObject_GetItem(self->m_cases, CpInvalidDefault);
+ if (!result)
+ return NULL;
+ }
+ }
+ // TODO: check for nested struct
+ return result;
+}
+
+/*CpAPI*/
+PyObject*
+CpSwitchAtom_Unpack(CpSwitchAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* value = _Cp_Unpack(self->m_atom, layer);
+ if (!value) {
+ return NULL;
+ }
+
+ PyObject* next_atom = CpSwitchAtom_GetNext(self, value, (PyObject*)layer);
+ if (!next_atom) {
+ Py_DECREF(value);
+ return NULL;
+ }
+
+ if (Py_IsNone(next_atom)) {
+ // TODO: document this behaviour
+ return value;
+ }
+
+ PyObject* result = _Cp_Unpack(next_atom, layer);
+ Py_DECREF(value);
+ Py_DECREF(next_atom);
+ return result;
+}
+
+/*CpAPI*/
+int
+CpSwitchAtom_Pack(CpSwitchAtomObject* self, PyObject* obj, CpLayerObject* layer)
+{
+ if (!self->s_callable) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ ("Switch atom currently supports only callable atoms when used as a "
+ "condition to select the final atom to be used to pack the given "
+ "value."));
+ return -1;
+ }
+
+ PyObject* value = PyObject_CallOneArg(self->m_atom, (PyObject*)layer);
+ if (!value) {
+ return -1;
+ }
+
+ PyObject* next_atom = CpSwitchAtom_GetNext(self, value, (PyObject*)layer);
+ if (!next_atom) {
+ Py_DECREF(value);
+ return -1;
+ }
+
+ int result = _Cp_Pack(obj, next_atom, layer);
+ Py_DECREF(next_atom);
+ Py_DECREF(value);
+ return result;
+}
+
+/* docs */
+
+/* members */
+static PyMemberDef CpSwitchAtom_Members[] = {
+ { "cases", T_OBJECT, offsetof(CpSwitchAtomObject, m_cases), READONLY },
+ { "atom", T_OBJECT, offsetof(CpSwitchAtomObject, m_atom), READONLY },
+ { NULL }
+};
+
+/* methods */
+static PyMethodDef CpSwitchAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(switchatom, NULL),
+ { "get_next",
+ (PyCFunction)cp_switchatom_get_next,
+ METH_VARARGS | METH_KEYWORDS,
+ NULL },
+ { NULL }
+};
+
+PyTypeObject CpSwitchAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpConditionAtom_NAME),
+ .tp_basicsize = sizeof(CpConditionAtomObject),
+ .tp_dealloc = (destructor)cp_switchatom_dealloc,
+ .tp_init = (initproc)cp_switchatom_init,
+ .tp_members = CpSwitchAtom_Members,
+ .tp_methods = CpSwitchAtom_Methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_new = (newfunc)cp_switchatom_new,
+ .tp_repr = (reprfunc)cp_switchatom_repr,
+};
diff --git a/src/ccaterpillar/atomimpl/bytes.c b/src/ccaterpillar/atomimpl/bytes.c
new file mode 100644
index 0000000..6a42a38
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/bytes.c
@@ -0,0 +1,127 @@
+/* constatom C implementation */
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/*impl*/
+
+static PyObject*
+cp_bytesatom_type(CpBytesAtomObject* self)
+{
+ return Py_NewRef(&PyBytes_Type);
+}
+
+static PyObject*
+cp_bytesatom_size(CpBytesAtomObject* self, CpLayerObject* layer)
+{
+ return CpBytesAtom_GetLength(self, layer);
+}
+
+static PyObject*
+cp_bytesatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpBytesAtomObject* self = (CpBytesAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpBytesAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpBytesAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_bytesatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_bytesatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ self->m_length = NULL;
+ self->s_callable = 0;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_bytesatom_dealloc(CpBytesAtomObject* self)
+{
+ Py_XDECREF(self->m_length);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_bytesatom_init(CpBytesAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "length", NULL };
+ PyObject* length = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &length)) {
+ return -1;
+ }
+ _Cp_SetObj(self->m_length, length);
+ self->s_callable = PyCallable_Check(self->m_length);
+ return 0;
+}
+
+static PyObject*
+cp_bytesatom_repr(CpBytesAtomObject* self)
+{
+ return PyUnicode_FromFormat("", self->m_length);
+}
+
+/* Public API */
+
+/*CpAPI*/
+PyObject*
+CpBytesAtom_GetLength(CpBytesAtomObject* self, CpLayerObject* layer)
+{
+ if (self->s_callable) {
+ return PyObject_CallOneArg(self->m_length, (PyObject*)layer);
+ }
+ return Py_NewRef(self->m_length);
+}
+
+/*CpAPI*/
+int
+CpBytesAtom_Pack(CpBytesAtomObject* self, PyObject* value, CpLayerObject* layer)
+{
+ PyObject* length = CpBytesAtom_GetLength(self, layer);
+ if (length == NULL) {
+ return -1;
+ }
+
+ if (PyLong_AS_LONG(length) != PyBytes_GET_SIZE(value)) {
+ PyErr_SetString(PyExc_ValueError, "Invalid length");
+ return -1;
+ }
+ Py_DECREF(length);
+
+ PyObject* res = CpState_Write(layer->m_state, value);
+ if (!res) {
+ return -1;
+ }
+ Py_DECREF(res);
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpBytesAtom_Unpack(CpBytesAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* length = CpBytesAtom_GetLength(self, layer);
+ if (length == NULL) {
+ return NULL;
+ }
+ return CpState_Read(layer->m_state, length);
+}
+
+/* docs */
+
+/* type setup */
+static PyMemberDef CpBytesAtom_Members[] = {
+ { "length", T_OBJECT, offsetof(CpBytesAtomObject, m_length), READONLY },
+ { NULL }
+};
+
+PyTypeObject CpBytesAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpBytesAtom_NAME),
+ .tp_basicsize = sizeof(CpBytesAtomObject),
+ .tp_members = CpBytesAtom_Members,
+ .tp_new = (newfunc)cp_bytesatom_new,
+ .tp_init = (initproc)cp_bytesatom_init,
+ .tp_dealloc = (destructor)cp_bytesatom_dealloc,
+ .tp_repr = (reprfunc)cp_bytesatom_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
\ No newline at end of file
diff --git a/src/atomimpl/charatomobj.c b/src/ccaterpillar/atomimpl/char.c
similarity index 72%
rename from src/atomimpl/charatomobj.c
rename to src/ccaterpillar/atomimpl/char.c
index 7a05d88..8087d00 100644
--- a/src/atomimpl/charatomobj.c
+++ b/src/ccaterpillar/atomimpl/char.c
@@ -1,6 +1,6 @@
/* charatom C implementation */
-#include "caterpillar/atoms/primitive.h"
-#include "caterpillar/state.h"
+#include "caterpillar/caterpillar.h"
+
#include
static PyObject*
@@ -20,13 +20,13 @@ cp_charatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
CpCharAtomObject* self = (CpCharAtomObject*)type->tp_alloc(type, 0);
if (self != NULL) {
- CpFieldCAtom_CATOM(self).ob_pack = (packfunc)CpCharAtom_Pack;
- CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)CpCharAtom_Unpack;
- CpFieldCAtom_CATOM(self).ob_pack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_unpack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_size = (sizefunc)cp_charatom__size__;
- CpFieldCAtom_CATOM(self).ob_type = (typefunc)cp_charatom__type__;
- CpFieldCAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpCharAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpCharAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_charatom__size__;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_charatom__type__;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
}
return (PyObject*)self;
}
@@ -44,7 +44,7 @@ cp_charatom_init(CpCharAtomObject* self, PyObject* args, PyObject* kwds)
}
/* Public API */
-
+/*CpAPI*/
int
CpCharAtom_Pack(CpCharAtomObject* self, PyObject* value, CpLayerObject* layer)
{
@@ -66,10 +66,11 @@ CpCharAtom_Pack(CpCharAtomObject* self, PyObject* value, CpLayerObject* layer)
return 0;
}
+/*CpAPI*/
PyObject*
CpCharAtom_Unpack(CpCharAtomObject* self, CpLayerObject* layer)
{
- PyObject* res = CpState_Read(layer->m_state, 1);
+ PyObject* res = CpState_ReadSsize_t(layer->m_state, 1);
if (!res) {
return NULL;
}
@@ -78,7 +79,7 @@ CpCharAtom_Unpack(CpCharAtomObject* self, CpLayerObject* layer)
/* type setup */
PyTypeObject CpCharAtom_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(charatom),
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpCharAtom_NAME),
.tp_basicsize = sizeof(CpCharAtomObject),
.tp_dealloc = (destructor)cp_charatom_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
diff --git a/src/ccaterpillar/atomimpl/computed.c b/src/ccaterpillar/atomimpl/computed.c
new file mode 100644
index 0000000..ae141f7
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/computed.c
@@ -0,0 +1,109 @@
+/* computed atom C implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_computedatom_type(CpComputedAtomObject* self)
+{
+ if (self->s_callable) {
+ return Py_XNewRef(get_global_module_state()->Any_Type);
+ }
+ return Py_NewRef(Py_TYPE(self->m_value));
+}
+
+static PyObject*
+cp_computedatom_size(CpComputedAtomObject* self, PyObject* ctx)
+{
+ // TODO: explain why computed atom objects are of size 0
+ return PyLong_FromSize_t(0);
+}
+
+static PyObject*
+cp_computedatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpComputedAtomObject* self = (CpComputedAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpComputedAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpComputedAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_computedatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_computedatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+
+ self->m_value = NULL;
+ self->s_callable = false;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_computedatom_dealloc(CpComputedAtomObject* self)
+{
+ Py_XDECREF(self->m_value);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_computedatom_init(CpComputedAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "value", NULL };
+ PyObject* value = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &value)) {
+ return -1;
+ }
+
+ _Cp_SetObj(self->m_value, value);
+ self->s_callable = PyCallable_Check(self->m_value);
+ return 0;
+}
+
+static PyObject*
+cp_computedatom_repr(CpComputedAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "",
+ self->s_callable ? "" : Py_TYPE(self->m_value)->tp_name);
+}
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpComputedAtom_Pack(CpComputedAtomObject* self,
+ PyObject* obj,
+ CpLayerObject* layer)
+{
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpComputedAtom_Unpack(CpComputedAtomObject* self, CpLayerObject* layer)
+{
+ return CpComputedAtom_Value(self, layer);
+}
+
+/* docs */
+
+/* type */
+
+static PyMemberDef cp_computedatom_members[] = {
+ { "value", T_OBJECT, offsetof(CpComputedAtomObject, m_value), READONLY, "value" },
+ { NULL }
+};
+
+PyTypeObject CpComputedAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpComputedAtom_NAME),
+ .tp_basicsize = sizeof(CpComputedAtomObject),
+ .tp_dealloc = (destructor)cp_computedatom_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_init = (initproc)cp_computedatom_init,
+ .tp_new = (newfunc)cp_computedatom_new,
+ .tp_members = cp_computedatom_members,
+ .tp_repr = (reprfunc)cp_computedatom_repr,
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/const.c b/src/ccaterpillar/atomimpl/const.c
new file mode 100644
index 0000000..27c476b
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/const.c
@@ -0,0 +1,120 @@
+/* constatom C implementation */
+#include "caterpillar/caterpillar.h"
+
+#include
+
+static PyObject*
+cp_constatom__type__(CpConstAtomObject* self)
+{
+ return CpTypeOf(self->m_atom);
+}
+
+static PyObject*
+cp_constatom__size__(CpConstAtomObject* self, CpLayerObject* layer)
+{
+ return _Cp_SizeOf(self->m_atom, layer);
+}
+
+static PyObject*
+cp_constatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpConstAtomObject* self = (CpConstAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpConstAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpConstAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_constatom__size__;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_constatom__type__;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+
+ self->m_atom = NULL;
+ self->m_value = NULL;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_constatom_dealloc(CpConstAtomObject* self)
+{
+ Py_XDECREF(self->m_atom);
+ Py_XDECREF(self->m_value);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_constatom_init(CpConstAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "atom", "value", NULL };
+ PyObject* atom = NULL;
+ PyObject* value = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &atom, &value)) {
+ return -1;
+ }
+ _Cp_SetObj(self->m_atom, atom);
+ _Cp_SetObj(self->m_value, value);
+ return 0;
+}
+
+static PyObject*
+cp_constatom_repr(CpConstAtomObject* self)
+{
+ return PyUnicode_FromFormat("", self->m_value, self->m_atom);
+}
+
+_CpEndian_ImplSetByteorder(CpConstAtomObject, constatom, self->m_atom);
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpConstAtom_Pack(CpConstAtomObject* self, PyObject* value, CpLayerObject* layer)
+{
+ return _Cp_Pack(self->m_value, self->m_atom, layer);
+}
+
+/*CpAPI*/
+PyObject*
+CpConstAtom_Unpack(CpConstAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* res = _Cp_Unpack(self->m_atom, layer);
+ if (!res) {
+ return NULL;
+ }
+
+ if (PyObject_RichCompareBool(res, self->m_value, Py_NE)) {
+ PyErr_Format(PyExc_ValueError,
+ "Value is not equal to constant (parsed=%R, constant=%R)",
+ res,
+ self->m_value);
+ Py_DECREF(res);
+ return NULL;
+ }
+ // REVISIT: Should we free the 'res' object here?
+ return res;
+}
+
+/* type setup */
+
+static PyMemberDef cp_constatom_members[] = {
+ { "atom", T_OBJECT_EX, offsetof(CpConstAtomObject, m_atom), READONLY },
+ { "value", T_OBJECT_EX, offsetof(CpConstAtomObject, m_value), READONLY },
+ { NULL },
+};
+
+static PyMethodDef cp_constatom_methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(constatom, NULL),
+ { NULL }
+};
+
+PyTypeObject CpConstAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpConstAtom_NAME),
+ .tp_basicsize = sizeof(CpConstAtomObject),
+ .tp_dealloc = (destructor)cp_constatom_dealloc,
+ .tp_init = (initproc)cp_constatom_init,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = (newfunc)cp_constatom_new,
+ .tp_repr = (reprfunc)cp_constatom_repr,
+ .tp_members = cp_constatom_members,
+ .tp_methods = cp_constatom_methods
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/cstring.c b/src/ccaterpillar/atomimpl/cstring.c
new file mode 100644
index 0000000..25e3795
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/cstring.c
@@ -0,0 +1,317 @@
+/* c-string atom implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+#include
+
+/* impl */
+static PyObject*
+cp_cstringatom_type(CpCStringAtomObject* self)
+{
+ return Py_NewRef(&PyUnicode_Type);
+}
+
+static PyObject*
+cp_cstringatom_size(CpCStringAtomObject* self, CpLayerObject* layer)
+{
+ if (self->s_number) {
+ return Py_NewRef(self->m_length);
+ }
+ PyErr_SetString(PyExc_ValueError,
+ "dynamic-sized c-string atoms do not have a static size");
+ return NULL;
+}
+
+static PyObject*
+cp_cstringatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpCStringAtomObject* self = (CpCStringAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpCStringAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpCStringAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_cstringatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_cstringatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+
+ self->m_length = NULL;
+ self->m_encoding = NULL;
+ self->m_errors = NULL;
+ self->m_terminator = NULL;
+ self->_m_terminator_bytes = NULL;
+ self->s_keep_terminator = false;
+ self->s_number = false;
+ self->s_callable = false;
+ self->s_greedy = false;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_cstringatom_dealloc(CpCStringAtomObject* self)
+{
+ Py_XDECREF(self->m_length);
+ Py_XDECREF(self->m_encoding);
+ Py_XDECREF(self->m_errors);
+ Py_XDECREF(self->m_terminator);
+ Py_XDECREF(self->_m_terminator_bytes);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_cstringatom_init(CpCStringAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "length", "encoding", "errors",
+ "sep", "keep", NULL };
+ PyObject* length = NULL;
+ PyObject* encoding = NULL;
+ PyObject* errors = NULL;
+ PyObject* terminator = NULL;
+ int keep_terminator = false;
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwds,
+ "|OOOOp",
+ kwlist,
+ &length,
+ &encoding,
+ &errors,
+ &terminator,
+ &keep_terminator)) {
+ return -1;
+ }
+
+ _modulestate* mod = get_global_module_state();
+
+ _Cp_SetObj(self->m_length, length);
+ if (!self->m_length) {
+ // TODO: document this
+ _Cp_SetObj(self->m_length, Py_Ellipsis);
+ }
+
+ _Cp_SetObj(self->m_encoding, encoding);
+ if (!self->m_encoding) {
+ Py_XSETREF(self->m_encoding, mod->str_utf8);
+ }
+
+ _Cp_SetObj(self->m_errors, errors);
+ if (!self->m_errors) {
+ Py_XSETREF(self->m_errors, mod->str_strict);
+ }
+
+ _Cp_SetObj(self->m_terminator, terminator);
+ if (!self->m_terminator) {
+ _Cp_SetObj(self->_m_terminator_bytes, mod->cp_bytes__false);
+ _Cp_SetObj(self->m_terminator, mod->str_cstring_default_pad);
+ } else {
+ if (!PyUnicode_Check(self->m_terminator)) {
+ PyErr_SetString(PyExc_TypeError, "terminator must be a string");
+ return -1;
+ }
+ self->_m_terminator_bytes =
+ PyUnicode_AsEncodedString(self->m_terminator,
+ PyUnicode_AsUTF8AndSize(self->m_encoding, NULL),
+ PyUnicode_AsUTF8AndSize(self->m_errors, NULL));
+ if (!self->_m_terminator_bytes) {
+ return -1;
+ }
+ }
+
+ self->s_keep_terminator = keep_terminator;
+ self->s_number = PyLong_Check(self->m_length);
+ self->s_callable = PyCallable_Check(self->m_length);
+ self->s_greedy = Py_IS_TYPE(self->m_length, &PyEllipsis_Type);
+ return 0;
+}
+
+static PyObject*
+cp_cstringatom_repr(CpCStringAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "", self->m_encoding, self->m_errors);
+}
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpCStringAtom_Pack(CpCStringAtomObject* self,
+ PyObject* value,
+ CpLayerObject* layer)
+{
+ if (!PyUnicode_Check(value)) {
+ PyErr_Format(
+ PyExc_TypeError, "C strings must be packed from unicode - got %R", value);
+ return -1;
+ }
+ PyObject* bytes = PyUnicode_AsEncodedString(
+ value, PyUnicode_DATA(self->m_encoding), PyUnicode_DATA(self->m_errors));
+ if (!bytes) {
+ return -1;
+ }
+
+ if (!self->s_callable && !self->s_number && !self->s_greedy) {
+ // is atom, must pack length as prefix
+ PyObject* length = PyLong_FromSsize_t(PyUnicode_GET_LENGTH(value) + 1);
+ if (!length) {
+ return -1;
+ }
+ if (_Cp_Pack(length, self->m_length, layer)) {
+ Py_DECREF(length);
+ return -1;
+ }
+ Py_DECREF(length);
+ }
+
+ PyObject* res = CpState_Write(layer->m_state, bytes);
+ Py_DECREF(bytes);
+ if (!res) {
+ return -1;
+ }
+ Py_XDECREF(res);
+
+ if (self->s_greedy || (!self->s_callable && !self->s_number)) {
+ res = CpState_Write(layer->m_state, self->_m_terminator_bytes);
+ if (!res) {
+ return -1;
+ }
+ Py_XDECREF(res);
+ } else {
+ // NOTE: we know unpack won't be called
+ PyObject* length = CpCStringAtom_Length(self, layer);
+ if (length == NULL) {
+ return -1;
+ }
+ PyObject* objLength = PyLong_FromSsize_t(PyUnicode_GET_LENGTH(value));
+ if (!objLength) {
+ Py_DECREF(length);
+ return -1;
+ }
+
+ PyObject* realLength = PyNumber_Subtract(length, objLength);
+ if (!realLength) {
+ Py_DECREF(length);
+ return -1;
+ }
+
+ PyObject* trailer =
+ PyNumber_Multiply(self->_m_terminator_bytes, realLength);
+ Py_DECREF(objLength);
+ Py_DECREF(realLength);
+ Py_DECREF(length);
+ if (!trailer) {
+ return -1;
+ }
+
+ res = CpState_Write(layer->m_state, trailer);
+ Py_DECREF(trailer);
+ if (!res) {
+ return -1;
+ }
+ Py_XDECREF(res);
+ }
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpCStringAtom_Unpack(CpCStringAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* length = CpCStringAtom_Length(self, layer);
+ if (length == NULL) {
+ return NULL;
+ }
+
+ PyObject* result = NULL;
+ if (Py_IsNone(length)) {
+ // greedy parsing
+ result = CpObject_CreateNoArgs(&PyBytes_Type);
+ if (!result) {
+ return NULL;
+ }
+ // REVISIT: is this efficient?
+ while (true) {
+ PyObject* bytes = CpState_ReadSsize_t(layer->m_state, 1);
+ if (!bytes) {
+ return NULL;
+ }
+ if (PyBytes_AS_STRING(bytes)[0] ==
+ PyBytes_AS_STRING(self->_m_terminator_bytes)[0]) {
+ break;
+ }
+ PyBytes_Concat(&result, bytes);
+ Py_DECREF(bytes);
+ if (!result) {
+ return NULL;
+ }
+ }
+ } else {
+ result = CpState_Read(layer->m_state, length);
+ Py_DECREF(length);
+ if (!result) {
+ return NULL;
+ }
+ }
+ if (!result) {
+ return NULL;
+ }
+ PyObject* string = PyUnicode_Decode(PyBytes_AS_STRING(result),
+ PyBytes_GET_SIZE(result),
+ PyUnicode_DATA(self->m_encoding),
+ PyUnicode_DATA(self->m_errors));
+ Py_XDECREF(result);
+ if (!string) {
+ return NULL;
+ }
+
+ if (!self->s_keep_terminator) {
+ Py_XSETREF(string, _PyUnicode_XStrip(string, 1, self->m_terminator));
+ }
+ return string;
+}
+
+/* docs */
+
+/* type */
+static PyMemberDef CpCStringAtom_Members[] = {
+ { "encoding",
+ T_OBJECT_EX,
+ offsetof(CpCStringAtomObject, m_encoding),
+ 0,
+ "the encoding of the string" },
+ { "errors",
+ T_OBJECT_EX,
+ offsetof(CpCStringAtomObject, m_errors),
+ 0,
+ "the error handling for the string" },
+ { "terminator",
+ T_OBJECT_EX,
+ offsetof(CpCStringAtomObject, m_terminator),
+ READONLY,
+ "the terminator for the string" },
+ { "keep_terminator",
+ T_BOOL,
+ offsetof(CpCStringAtomObject, s_keep_terminator),
+ 0,
+ "whether to keep the terminator" },
+ { "length",
+ T_OBJECT_EX,
+ offsetof(CpCStringAtomObject, m_length),
+ 0,
+ "the length of the string" },
+ { NULL }
+};
+
+PyTypeObject CpCStringAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpCStringAtom_NAME),
+ .tp_basicsize = sizeof(CpCStringAtomObject),
+ .tp_itemsize = 0,
+ .tp_dealloc = (destructor)cp_cstringatom_dealloc,
+ .tp_repr = (reprfunc)cp_cstringatom_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = (newfunc)cp_cstringatom_new,
+ .tp_members = CpCStringAtom_Members,
+ .tp_init = (initproc)cp_cstringatom_init,
+ .tp_doc = NULL,
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/enum.c b/src/ccaterpillar/atomimpl/enum.c
new file mode 100644
index 0000000..266778e
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/enum.c
@@ -0,0 +1,187 @@
+/* enum atom C implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_enumatom_type(CpEnumAtomObject* self)
+{
+ if (self->m_enum_type) {
+ return Py_NewRef(self->m_enum_type);
+ }
+ return CpTypeOf(self->m_atom);
+}
+
+static PyObject*
+cp_enumatom_size(CpEnumAtomObject* self, CpLayerObject* layer)
+{
+ return _Cp_SizeOf(self->m_atom, layer);
+}
+
+static PyObject*
+cp_enumatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpEnumAtomObject* atom = (CpEnumAtomObject*)type->tp_alloc(type, 0);
+ if (atom != NULL) {
+ CpBuiltinAtom_CATOM(atom).ob_pack = (packfunc)CpEnumAtom_Pack;
+ CpBuiltinAtom_CATOM(atom).ob_unpack = (unpackfunc)CpEnumAtom_Unpack;
+ CpBuiltinAtom_CATOM(atom).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(atom).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(atom).ob_size = (sizefunc)cp_enumatom_size;
+ CpBuiltinAtom_CATOM(atom).ob_type = (typefunc)cp_enumatom_type;
+ CpBuiltinAtom_CATOM(atom).ob_bits = NULL;
+
+ atom->m_atom = NULL;
+ atom->m_enum_type = NULL;
+ atom->m_members = NULL;
+ atom->m_value2member_map = NULL;
+ atom->m_default = Py_NewRef(CpInvalidDefault);
+ }
+ return (PyObject*)atom;
+}
+
+static void
+cp_enumatom_dealloc(CpEnumAtomObject* self)
+{
+ Py_XDECREF(self->m_atom);
+ Py_XDECREF(self->m_members);
+ Py_XDECREF(self->m_enum_type);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_enumatom_init(CpEnumAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "atom", "enum_type", "default", NULL };
+ PyObject *atom = NULL, *enum_type = NULL, *default_value = NULL;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "OO|O", kwlist, &atom, &enum_type, &default_value)) {
+ return -1;
+ }
+ _Cp_SetObj(self->m_atom, atom);
+ _Cp_SetObj(self->m_enum_type, enum_type);
+ _Cp_SetObj(self->m_default, default_value);
+
+ _modulestate* mod = get_global_module_state();
+ PyObject* members =
+ PyObject_GetAttr(self->m_enum_type, mod->str__member_map_);
+ if (!members) {
+ return -1;
+ }
+
+ _Cp_SetObj(self->m_members, members);
+ PyObject* value2member_map =
+ PyObject_GetAttr(self->m_enum_type, mod->str__value2member_map_);
+ if (!value2member_map) {
+ return -1;
+ }
+ _Cp_SetObj(self->m_value2member_map, value2member_map);
+
+ return 0;
+}
+
+_CpEndian_ImplSetByteorder(CpEnumAtomObject, enumatom, self->m_atom);
+
+static PyObject*
+cp_enumatom_repr(CpEnumAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "] %R>", ((PyTypeObject *)self->m_enum_type)->tp_name, self->m_atom);
+}
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpEnumAtom_Pack(CpEnumAtomObject* self, PyObject* value, CpLayerObject* layer)
+{
+ // TODO: check enum type - add strict flag
+ // if (!PyObject_TypeCheck(value, (PyTypeObject*)self->m_enum_type)) {
+ // PyErr_Format(PyExc_TypeError,
+ // "Expected a %s, got %s",
+ // Py_TYPE(self->m_enum_type)->tp_name,
+ // Py_TYPE(value)->tp_name);
+ // return -1;
+ // }
+ int res = 0;
+ if (PyObject_TypeCheck(value, (PyTypeObject*)self->m_enum_type)) {
+ PyObject* real_value = PyObject_GetAttrString(value, "value");
+ if (!real_value) {
+ return -1;
+ }
+ res = _Cp_Pack(real_value, self->m_atom, layer);
+ Py_DECREF(real_value);
+ } else {
+ res = _Cp_Pack(value, self->m_atom, layer);
+ }
+
+ return res;
+}
+
+/*CpAPI*/
+PyObject*
+CpEnumAtom_Unpack(CpEnumAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* value = _Cp_Unpack(self->m_atom, layer);
+ if (!value) {
+ return NULL;
+ }
+
+ PyObject* by_name = PyDict_GetItem(self->m_members, value);
+ if (by_name) {
+ Py_DECREF(value);
+ return Py_NewRef(by_name);
+ }
+ // not found
+ PyErr_Clear();
+
+ PyObject* by_value = PyDict_GetItem(self->m_value2member_map, value);
+ if (by_value) {
+ Py_DECREF(value);
+ return Py_NewRef(by_value);
+ }
+
+ // not found, fallback to default value
+ PyErr_Clear();
+
+ if (Cp_IsInvalidDefault(self->m_default)) {
+ // TODO: add strict parsing flag here
+ return value;
+ }
+ Py_DECREF(value);
+ return Py_NewRef(self->m_default);
+}
+
+/* docs */
+
+/* type setup */
+
+static PyMemberDef CpEnumAtom_Members[] = {
+ { "atom", T_OBJECT, offsetof(CpEnumAtomObject, m_atom), READONLY },
+ { "enum_type", T_OBJECT, offsetof(CpEnumAtomObject, m_enum_type), READONLY },
+ { "members", T_OBJECT, offsetof(CpEnumAtomObject, m_members), READONLY },
+ { "default", T_OBJECT, offsetof(CpEnumAtomObject, m_default), 0 },
+ { NULL }
+};
+
+static PyMethodDef CpEnumAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(enumatom, NULL),
+ {
+ NULL,
+ }
+};
+
+PyTypeObject CpEnumAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpEnumAtom_NAME),
+ .tp_basicsize = sizeof(CpEnumAtomObject),
+ .tp_dealloc = (destructor)cp_enumatom_dealloc,
+ .tp_init = (initproc)cp_enumatom_init,
+ .tp_repr = (reprfunc)cp_enumatom_repr,
+ .tp_members = CpEnumAtom_Members,
+ .tp_methods = CpEnumAtom_Methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_new = (newfunc)cp_enumatom_new,
+};
diff --git a/src/atomimpl/floatatomobj.c b/src/ccaterpillar/atomimpl/float.c
similarity index 71%
rename from src/atomimpl/floatatomobj.c
rename to src/ccaterpillar/atomimpl/float.c
index bdf277d..575052c 100644
--- a/src/atomimpl/floatatomobj.c
+++ b/src/ccaterpillar/atomimpl/float.c
@@ -1,9 +1,5 @@
/* floatatom C implementation */
#include "caterpillar/caterpillar.h"
-#include "caterpillar/arch.h"
-#include "caterpillar/atoms/float.h"
-#include "caterpillar/state.h"
-#include "caterpillar/module.h"
#include
static PyObject*
@@ -27,13 +23,13 @@ cp_floatatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
self->_m_bits = 0;
self->_m_little_endian = true;
- CpFieldCAtom_CATOM(self).ob_pack = (packfunc)CpFloatAtom_Pack;
- CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)CpFloatAtom_Unpack;
- CpFieldCAtom_CATOM(self).ob_pack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_unpack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_size = (sizefunc)cp_floatatom__size__;
- CpFieldCAtom_CATOM(self).ob_type = (typefunc)cp_floatatom__type__;
- CpFieldCAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpFloatAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpFloatAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_floatatom__size__;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_floatatom__type__;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
}
return (PyObject*)self;
}
@@ -73,8 +69,28 @@ cp_floatatom_init(CpFloatAtomObject* self, PyObject* args, PyObject* kwds)
return 0;
}
-/* Public API */
+static PyObject*
+cp_floatatom_set_byteorder(CpFloatAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "byteorder", NULL };
+ PyObject* byteorder = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &byteorder)) {
+ return NULL;
+ }
+ if (!CpEndian_Check(byteorder)) {
+ PyErr_SetString(PyExc_TypeError, "byteorder must be an Endian object");
+ return NULL;
+ }
+ return CpObject_Create(&CpIntAtom_Type,
+ "Ii",
+ self->_m_bits,
+ CpEndian_IsLittleEndian((CpEndianObject*)byteorder,
+ get_global_module_state()));
+}
+
+/* Public API */
+/*CpAPI*/
int
CpFloatAtom_Pack(CpFloatAtomObject* self, PyObject* value, CpLayerObject* layer)
{
@@ -89,15 +105,6 @@ CpFloatAtom_Pack(CpFloatAtomObject* self, PyObject* value, CpLayerObject* layer)
}
int little_endian = self->_m_little_endian;
- if (layer->m_field) {
- _modulestate* mod = layer->m_state->mod;
- PyObject* endian = ((CpFieldObject*)layer->m_field)->m_endian;
-
- if (CpEndian_Check(endian))
- /* If the field has an endian specified, use that. */
- little_endian = CpEndian_IsLittleEndian((CpEndianObject*)endian, mod);
- }
-
int res = 0;
switch (self->_m_byte_count) {
case 2: {
@@ -135,25 +142,16 @@ CpFloatAtom_Pack(CpFloatAtomObject* self, PyObject* value, CpLayerObject* layer)
return 0;
}
+/*CpAPI*/
PyObject*
CpFloatAtom_Unpack(CpFloatAtomObject* self, CpLayerObject* layer)
{
- PyObject* bytes = CpState_Read(layer->m_state, self->_m_byte_count);
+ PyObject* bytes = CpState_ReadSsize_t(layer->m_state, self->_m_byte_count);
if (!bytes) {
return NULL;
}
int little_endian = self->_m_little_endian;
- if (layer->m_field) {
- _modulestate* mod = layer->m_state->mod;
- PyObject* endian = ((CpFieldObject*)layer->m_field)->m_endian;
-
- if (CpEndian_Check(endian)) {
- /* If the field has an endian specified, use that. */
- little_endian = CpEndian_IsLittleEndian((CpEndianObject*)endian, mod);
- }
- }
-
double res;
switch (self->_m_byte_count) {
case 2: {
@@ -188,19 +186,23 @@ static PyMemberDef CpFloatAtom_Members[] = {
READONLY,
NULL },
{ "bits", T_PYSSIZET, offsetof(CpFloatAtomObject, _m_bits), READONLY, NULL },
- { "little_endian",
- T_BOOL,
- offsetof(CpFloatAtomObject, _m_little_endian)},
+ { "little_endian", T_BOOL, offsetof(CpFloatAtomObject, _m_little_endian) },
+ { NULL } /* Sentinel */
+};
+
+static PyMethodDef CpFloatAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(floatatom, NULL),
{ NULL } /* Sentinel */
};
PyTypeObject CpFloatAtom_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(floatatom),
+ 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,
.tp_init = (initproc)cp_floatatom_init,
.tp_new = (newfunc)cp_floatatom_new,
};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/int.c b/src/ccaterpillar/atomimpl/int.c
new file mode 100644
index 0000000..7714cd2
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/int.c
@@ -0,0 +1,240 @@
+/* intatom C implementation */
+#include
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+static PyObject*
+cp_intatom__type__(CpIntAtomObject* self)
+{
+ return Py_XNewRef(&PyLong_Type);
+}
+
+static PyObject*
+cp_intatom__size__(CpIntAtomObject* self, PyObject* ctx)
+{
+ return Py_XNewRef(self->m_byte_count);
+}
+
+static PyObject*
+cp_intatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpIntAtomObject* self = (CpIntAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->m_byte_count = NULL;
+ self->_m_bits = 0;
+ self->_m_signed = true;
+ self->_m_little_endian = true;
+
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpIntAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpIntAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_intatom__size__;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_intatom__type__;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_intatom_dealloc(CpIntAtomObject* self)
+{
+ Py_XDECREF(self->m_byte_count);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_intatom_init(CpIntAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "nbits", "signed", "little_endian", NULL };
+ int _signed = true, little_endian = true;
+ unsigned int bits = 0;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "I|pp", kwlist, &bits, &_signed, &little_endian)) {
+ return -1;
+ }
+ if (bits == 0) {
+ PyErr_SetString(PyExc_ValueError, "bytes cannot be zero");
+ return -1;
+ }
+ if (bits % 8) {
+ PyErr_SetString(PyExc_ValueError, "nbits must be a multiple of 8");
+ return -1;
+ }
+
+ self->_m_bits = bits;
+ self->_m_byte_count = bits / 8;
+ self->_m_signed = _signed;
+ self->_m_little_endian = little_endian;
+ self->m_byte_count = PyLong_FromUnsignedLong(self->_m_byte_count);
+ if (!self->m_byte_count) {
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject*
+cp_intatom_repr(CpIntAtomObject* self)
+{
+ char sign = 'i';
+ if (!self->_m_signed) {
+ sign = 'u';
+ }
+ char endian = 'l';
+ if (!self->_m_little_endian) {
+ endian = 'b';
+ }
+ return PyUnicode_FromFormat("<%ce %cint%d>", endian, sign, self->_m_bits);
+}
+
+static PyObject*
+cp_intatom_set_byteorder(CpIntAtomObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "byteorder", NULL };
+ PyObject* byteorder = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &byteorder)) {
+ return NULL;
+ }
+ if (!CpEndian_Check(byteorder)) {
+ PyErr_SetString(PyExc_TypeError, "byteorder must be an Endian object");
+ return NULL;
+ }
+
+ return CpObject_Create(&CpIntAtom_Type,
+ "Iii",
+ (unsigned int)self->_m_bits,
+ self->_m_signed,
+ CpEndian_IsLittleEndian((CpEndianObject*)byteorder,
+ get_global_module_state()));
+}
+
+/* Public API */
+/*CpAPI*/
+int
+CpIntAtom_Pack(CpIntAtomObject* self, PyObject* op, CpLayerObject* layer)
+{
+ PyObject* bytes = CpObject_CreateOneArg(&PyBytes_Type, self->m_byte_count);
+ if (!bytes) {
+ return -1;
+ }
+
+ if (!PyLong_Check(op)) {
+ PyErr_Format(PyExc_TypeError, "op must be an int, got %R", op);
+ Py_DECREF(bytes);
+ }
+
+ int little_endian = self->_m_little_endian;
+ int res = _PyLong_AsByteArray((PyLongObject*)op,
+ (unsigned char*)PyBytes_AS_STRING(bytes),
+ self->_m_byte_count,
+ little_endian,
+ self->_m_signed);
+ if (res == -1) {
+ return -1;
+ }
+
+ PyObject* result = CpState_Write(layer->m_state, bytes);
+ if (!result) {
+ return -1;
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpIntAtom_Unpack(CpIntAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* bytes = CpState_ReadSsize_t(layer->m_state, self->_m_byte_count);
+ if (!bytes) {
+ return NULL;
+ }
+
+ int little_endian = self->_m_little_endian;
+ PyObject* obj =
+ _PyLong_FromByteArray((unsigned char*)PyBytes_AS_STRING(bytes),
+ self->_m_byte_count,
+ little_endian,
+ self->_m_signed);
+ Py_DECREF(bytes);
+ return obj;
+}
+
+/* docs */
+// clang-format off
+PyDoc_STRVAR(cp_intatom_member__nbytes__doc,
+"Stores the amount of bytes in the atom (not the number of bits).");
+
+PyDoc_STRVAR(cp_intatom_member__nbits__doc,
+"Stores the amount of bits in the atom.");
+
+PyDoc_STRVAR(cp_intatom_member__signed__doc,
+"Stores whether the atom is signed or not.");
+
+PyDoc_STRVAR(cp_intatom_member__little_endian__doc,
+"Stores whether the atom is little endian or not.");
+
+PyDoc_STRVAR(cp_intatom__doc,
+"Int(bits, signed=True, little_endian=True)\n"
+"--\n"
+"\n"
+"Implements an integer atom with a variable amount of bits.\n"
+"\n"
+"bits\n"
+" The amount of bits in the atom.\n"
+"signed\n"
+" Whether the atom is signed or not.\n"
+"little_endian\n"
+" Whether the atom is little endian or not.\n"
+"\n"
+"Unlike its Python counterpart (caterpillar.py.FormatField), this C-based "
+"class focuses solely on integer operations. It utilizes `int.from_bytes` and "
+"`int.to_bytes` internally, optimizing runtime performance. However, it is "
+"recommended to use the global instances of this class instead of creating "
+"new objects directly.");
+// clang-format on
+
+/* type */
+static PyMemberDef CpIntAtom_Members[] = {
+ { "nbytes",
+ T_PYSSIZET,
+ offsetof(CpIntAtomObject, _m_byte_count),
+ READONLY,
+ cp_intatom_member__nbytes__doc },
+ { "nbits",
+ T_PYSSIZET,
+ offsetof(CpIntAtomObject, _m_bits),
+ READONLY,
+ cp_intatom_member__nbits__doc },
+ { "signed",
+ T_BOOL,
+ offsetof(CpIntAtomObject, _m_signed),
+ READONLY,
+ cp_intatom_member__signed__doc },
+ { "little_endian",
+ T_BOOL,
+ offsetof(CpIntAtomObject, _m_little_endian),
+ READONLY,
+ cp_intatom_member__little_endian__doc },
+ { NULL } /* Sentinel */
+};
+
+static PyMethodDef CpIntAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(intatom, NULL),
+ { NULL } /* Sentinel */
+};
+
+/* --- */ PyTypeObject CpIntAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpIntAtom_NAME),
+ .tp_basicsize = sizeof(CpIntAtomObject),
+ .tp_dealloc = (destructor)cp_intatom_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = cp_intatom__doc,
+ .tp_members = CpIntAtom_Members,
+ .tp_new = (newfunc)cp_intatom_new,
+ .tp_init = (initproc)cp_intatom_init,
+ .tp_repr = (reprfunc)cp_intatom_repr,
+ .tp_methods = CpIntAtom_Methods
+};
diff --git a/src/ccaterpillar/atomimpl/lazy.c b/src/ccaterpillar/atomimpl/lazy.c
new file mode 100644
index 0000000..564f27f
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/lazy.c
@@ -0,0 +1,163 @@
+/* lazy C atom implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* impl */
+static PyObject*
+cp_lazyatom_type(CpLazyAtomObject* self)
+{
+ PyObject* atom = CpLazyAtom_Atom(self);
+ if (!atom)
+ return NULL;
+
+ PyObject* type = CpTypeOf(atom);
+ Py_DECREF(atom);
+ return type;
+}
+
+static PyObject*
+cp_lazyatom_size(CpLazyAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* atom = CpLazyAtom_Atom(self);
+ if (!atom)
+ return NULL;
+
+ PyObject* size = _Cp_SizeOf(atom, layer);
+ Py_DECREF(atom);
+ return size;
+}
+
+static PyObject*
+cp_lazyatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpLazyAtomObject* self = (CpLazyAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpLazyAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpLazyAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_lazyatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_lazyatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+
+ self->m_atom = NULL;
+ self->m_fn = NULL;
+ self->s_always_lazy = false;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_lazyatom_dealloc(CpLazyAtomObject* self)
+{
+ Py_XDECREF(self->m_atom);
+ Py_XDECREF(self->m_fn);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_lazyatom_init(CpLazyAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "fn", "always_lazy", NULL };
+ PyObject* fn = NULL;
+ int always_lazy = 0;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "O|p", kwlist, &fn, &always_lazy)) {
+ return -1;
+ }
+
+ _Cp_SetObj(self->m_fn, fn);
+ self->s_always_lazy = always_lazy;
+ self->m_atom = NULL;
+ return 0;
+}
+
+static PyObject*
+cp_lazyatom_repr(CpLazyAtomObject* self)
+{
+ return PyUnicode_FromFormat("<%slazy %s>",
+ self->s_always_lazy ? "always_" : "",
+ self->m_atom ? Py_TYPE(self->m_atom)->tp_name
+ : "");
+}
+
+static PyObject*
+cp_lazyatom_set_byteorder(CpLazyAtomObject* self, PyObject* args, PyObject* kw)
+{
+ _CpEndian_KwArgsGetByteorder(NULL);
+
+ PyObject* atom = CpLazyAtom_Atom(self);
+ if (!atom) {
+ return NULL;
+ }
+ PyObject* ret = CpEndian_SetEndian(atom, (CpEndianObject*)byteorder);
+ if (!ret) {
+ Py_DECREF(atom);
+ return NULL;
+ }
+ _Cp_SetObj(self->m_atom, ret);
+ Py_DECREF(atom);
+ Py_DECREF(ret);
+ return Py_NewRef((PyObject*)self);
+}
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpLazyAtom_Pack(CpLazyAtomObject* self, PyObject* obj, CpLayerObject* layer)
+{
+ PyObject* atom = CpLazyAtom_Atom(self);
+ if (!atom) {
+ return -1;
+ }
+ int res = _Cp_Pack(obj, atom, layer);
+ Py_DECREF(atom);
+ return res;
+}
+
+/*CpAPI*/
+PyObject*
+CpLazyAtom_Unpack(CpLazyAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* atom = CpLazyAtom_Atom(self);
+ if (!atom) {
+ return NULL;
+ }
+ PyObject* res = _Cp_Unpack(atom, layer);
+ Py_DECREF(atom);
+ return res;
+}
+
+/* docs */
+
+/* type */
+
+static PyMemberDef CpLazyAtom_Members[] = {
+ { "fn", T_OBJECT_EX, offsetof(CpLazyAtomObject, m_fn), READONLY },
+ { "always_lazy",
+ T_BOOL,
+ offsetof(CpLazyAtomObject, s_always_lazy),
+ READONLY },
+ { NULL } /* Sentinel */
+};
+
+static PyMethodDef CpLazyAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(lazyatom, NULL),
+ { NULL } /* Sentinel */
+};
+
+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 | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_init = (initproc)cp_lazyatom_init,
+ .tp_new = (newfunc)cp_lazyatom_new,
+ .tp_repr = (reprfunc)cp_lazyatom_repr,
+ .tp_members = CpLazyAtom_Members,
+ .tp_methods = CpLazyAtom_Methods,
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/pad.c b/src/ccaterpillar/atomimpl/pad.c
new file mode 100644
index 0000000..6793354
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/pad.c
@@ -0,0 +1,190 @@
+/* padding atom implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+static PyObject*
+cp_paddingatom_type(PyObject* self)
+{
+ return Py_XNewRef(&_PyNone_Type);
+}
+
+static PyObject*
+cp_paddingatom_size(PyObject* self, PyObject* ctx)
+{
+ /* NOTE:
+ We are using the size of one byte here to allow padding atoms to be
+ statically sized. It helps size calculation as well as defines the
+ default size for padding atoms.
+ */
+ return PyLong_FromSize_t(1);
+}
+
+static PyObject*
+cp_paddingatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpPaddingAtomObject* self = (CpPaddingAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpPaddingAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpPaddingAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many =
+ (packmanyfunc)CpPaddingAtom_PackMany;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many =
+ (unpackmanyfunc)CpPaddingAtom_UnpackMany;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_paddingatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_paddingatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_paddingatom_dealloc(CpPaddingAtomObject* self)
+{
+ self->_m_padding = 0;
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_paddingatom_init(CpPaddingAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "pad", NULL };
+ char value = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|b", kwlist, &value))
+ return -1;
+
+ self->_m_padding = value;
+ return 0;
+}
+
+/* Public API */
+/*CpAPI*/
+int
+CpPaddingAtom_Pack(CpPaddingAtomObject* self,
+ PyObject* value,
+ CpLayerObject* layer)
+{
+ /* value will be ignored here */
+ PyObject* res;
+ if (!self->_m_padding) {
+ res = CpState_Write(layer->m_state, layer->m_state->mod->cp_bytes__false);
+ } else {
+ PyObject* bytes = PyBytes_FromStringAndSize((char*)&self->_m_padding, 1);
+ if (!bytes) {
+ return -1;
+ }
+ res = CpState_Write(layer->m_state, bytes);
+ Py_DECREF(bytes);
+ }
+
+ if (!res) {
+ return -1;
+ }
+ Py_XDECREF(res);
+ return 0;
+}
+
+/*CpAPI*/
+int
+CpPaddingAtom_PackMany(CpPaddingAtomObject* self,
+ PyObject* value,
+ CpLayerObject* layer,
+ CpLengthInfoObject* lengthinfo)
+{
+ PyObject* objSize = PyLong_FromSsize_t(lengthinfo->m_length);
+ if (!objSize) {
+ return -1;
+ }
+
+ PyObject* bytes = CpObject_CreateOneArg(&PyBytes_Type, objSize);
+ if (!bytes) {
+ Py_DECREF(objSize);
+ return -1;
+ }
+ /* unsafe { */
+ memset(PyBytes_AS_STRING(bytes), self->_m_padding, lengthinfo->m_length);
+ /* } */
+ PyObject* res = CpState_Write(layer->m_state, bytes);
+ Py_DECREF(bytes);
+ Py_DECREF(objSize);
+ if (!res) {
+ return -1;
+ }
+ Py_XDECREF(res);
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpPaddingAtom_Unpack(CpPaddingAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* res = CpState_ReadSsize_t(layer->m_state, 1);
+ if (!res) {
+ return NULL;
+ }
+ Py_XDECREF(res);
+ Py_RETURN_NONE;
+}
+
+/*CpAPI*/
+PyObject*
+CpPaddingAtom_UnpackMany(CpPaddingAtomObject* self,
+ CpLayerObject* layer,
+ CpLengthInfoObject* lengthinfo)
+{
+ PyObject* res = NULL;
+
+ if (lengthinfo->m_greedy) {
+ res = CpState_ReadFully(layer->m_state);
+ } else {
+ res = CpState_ReadSsize_t(layer->m_state, lengthinfo->m_length);
+ }
+
+ if (!res) {
+ return NULL;
+ }
+
+ Py_ssize_t length = PyBytes_GET_SIZE(res);
+ Py_ssize_t offset = 0;
+ const char* ptr = PyBytes_AS_STRING(res);
+ while (offset < length && *(ptr + offset) == self->_m_padding) {
+ offset++;
+ }
+ if (offset != length) {
+ PyErr_Format(PyExc_ValueError,
+ ("The parsed padding contains invalid padding characters "
+ "(possible padding overflow?). "
+ "Expected %ld bytes of 0x%02x but parsed only %ld bytes."),
+ length,
+ self->_m_padding,
+ offset);
+ Py_XDECREF(res);
+ return NULL;
+ }
+
+ Py_XDECREF(res);
+ Py_RETURN_NONE;
+}
+
+PyObject*
+cp_paddingatom_repr(CpPaddingAtomObject* self)
+{
+ if (self->_m_padding == 0) {
+ return PyUnicode_FromFormat("");
+ }
+ return PyUnicode_FromFormat("", self->_m_padding);
+}
+
+/* type setup */
+PyTypeObject CpPaddingAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpPaddingAtom_NAME),
+ .tp_basicsize = sizeof(CpPaddingAtomObject),
+ .tp_dealloc = (destructor)cp_paddingatom_dealloc,
+ .tp_repr = (reprfunc)cp_paddingatom_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_init = (initproc)cp_paddingatom_init,
+ .tp_new = (newfunc)cp_paddingatom_new,
+};
\ No newline at end of file
diff --git a/src/ccaterpillar/atomimpl/pstring.c b/src/ccaterpillar/atomimpl/pstring.c
new file mode 100644
index 0000000..7fe1cd1
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/pstring.c
@@ -0,0 +1,168 @@
+/* pascal string implementation */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/*impl*/
+static PyObject*
+cp_pstringatom_type(PyObject* self)
+{
+ return Py_XNewRef(&PyUnicode_Type);
+}
+
+static PyObject*
+cp_pstringatom_size(CpPStringAtomObject* self, CpLayerObject* layer)
+{
+ PyErr_SetString(PyExc_TypeError, "Pascal strings do not have a static size!");
+ return NULL;
+}
+
+static PyObject*
+cp_pstringatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpPStringAtomObject* self = (CpPStringAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpPStringAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpPStringAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_pstringatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_pstringatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+
+ self->m_encoding = NULL;
+ self->m_errors = NULL;
+ self->m_atom = NULL;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_pstringatom_dealloc(CpPStringAtomObject* self)
+{
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_pstringatom_init(CpPStringAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "atom", "encoding", "errors", NULL };
+ PyObject *encoding = NULL, *errors = NULL, *atom = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "O|OO", kwlist, &atom, &encoding, &errors)) {
+ return -1;
+ }
+ _modulestate* mod = get_global_module_state();
+
+ _Cp_SetObj(self->m_atom, atom);
+ _Cp_SetObj(self->m_encoding, encoding);
+ if (!self->m_encoding) {
+ _Cp_SetObj(self->m_encoding, mod->str_utf8);
+ }
+ _Cp_SetObj(self->m_errors, errors);
+ if (!self->m_errors) {
+ _Cp_SetObj(self->m_errors, mod->str_strict);
+ }
+ return 0;
+}
+
+static PyObject*
+cp_pstringatom_repr(CpPStringAtomObject* self)
+{
+ return PyUnicode_FromFormat(
+ "", self->m_encoding, self->m_errors, self->m_atom);
+}
+
+_CpEndian_ImplSetByteorder(CpPStringAtomObject, pstringatom, self->m_atom);
+
+/* Public API */
+
+/*CpAPI*/
+int
+CpPStringAtom_Pack(CpPStringAtomObject* self,
+ PyObject* value,
+ CpLayerObject* layer)
+{
+ if (!PyUnicode_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "Pascal strings must be packed from unicode - got %R",
+ value);
+ return -1;
+ }
+
+ PyObject* length = PyLong_FromSsize_t(PyUnicode_GET_LENGTH(value));
+ if (!length) {
+ return -1;
+ }
+
+ if (_Cp_Pack(length, self->m_atom, layer)) {
+ Py_DECREF(length);
+ return -1;
+ }
+ Py_DECREF(length);
+
+ PyObject* bytes = PyUnicode_AsEncodedString(
+ value, PyUnicode_DATA(self->m_encoding), PyUnicode_DATA(self->m_errors));
+ if (!bytes) {
+ return -1;
+ }
+ PyObject* res = CpState_Write(layer->m_state, bytes);
+ Py_DECREF(bytes);
+ if (!res) {
+ return -1;
+ }
+ Py_XDECREF(res);
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpPStringAtom_Unpack(CpPStringAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* length = _Cp_Unpack(self->m_atom, layer);
+ if (length == NULL) {
+ return NULL;
+ }
+
+ PyObject* bytes = CpState_Read(layer->m_state, length);
+ Py_DECREF(length);
+ if (!bytes) {
+ return NULL;
+ }
+ PyObject* string = PyUnicode_Decode(PyBytes_AS_STRING(bytes),
+ PyBytes_GET_SIZE(bytes),
+ PyUnicode_DATA(self->m_encoding),
+ PyUnicode_DATA(self->m_errors));
+ Py_DECREF(bytes);
+ return string;
+}
+
+/* docs */
+
+/* type */
+static PyMemberDef CpPStringAtom_Members[] = {
+ { "atom", T_OBJECT, offsetof(CpPStringAtomObject, m_atom), 0, NULL },
+ { "encoding", T_STRING, offsetof(CpPStringAtomObject, m_encoding), 0, NULL },
+ { "errors", T_STRING, offsetof(CpPStringAtomObject, m_errors), 0, NULL },
+ { NULL } /* Sentinel */
+};
+
+static PyMethodDef CpPStringAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(pstringatom, NULL),
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject CpPStringAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpPStringAtom_NAME),
+ .tp_basicsize = sizeof(CpPStringAtomObject),
+ .tp_dealloc = (destructor)cp_pstringatom_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_members = CpPStringAtom_Members,
+ .tp_init = (initproc)cp_pstringatom_init,
+ .tp_new = (newfunc)cp_pstringatom_new,
+ .tp_repr = (reprfunc)cp_pstringatom_repr,
+ .tp_methods = CpPStringAtom_Methods,
+};
\ No newline at end of file
diff --git a/src/atomimpl/stringatomobj.c b/src/ccaterpillar/atomimpl/string.c
similarity index 79%
rename from src/atomimpl/stringatomobj.c
rename to src/ccaterpillar/atomimpl/string.c
index b480d03..56589c5 100644
--- a/src/atomimpl/stringatomobj.c
+++ b/src/ccaterpillar/atomimpl/string.c
@@ -1,9 +1,6 @@
/* string atom implementation */
#include "caterpillar/caterpillar.h"
-#include "caterpillar/atoms/string.h"
-#include "caterpillar/parsing.h"
-#include "caterpillar/module.h"
#include
@@ -34,13 +31,13 @@ cp_stringatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
CpStringAtomObject* self = (CpStringAtomObject*)type->tp_alloc(type, 0);
if (self != NULL) {
- CpFieldCAtom_CATOM(self).ob_pack = (packfunc)CpStringAtom_Pack;
- CpFieldCAtom_CATOM(self).ob_unpack = (unpackfunc)CpStringAtom_Unpack;
- CpFieldCAtom_CATOM(self).ob_pack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_unpack_many = NULL;
- CpFieldCAtom_CATOM(self).ob_size = (sizefunc)cp_stringatom__size__;
- CpFieldCAtom_CATOM(self).ob_type = (typefunc)cp_stringatom__type__;
- CpFieldCAtom_CATOM(self).ob_bits = NULL;
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpStringAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpStringAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_stringatom__size__;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_stringatom__type__;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
self->m_encoding = NULL;
self->m_length = NULL;
@@ -64,15 +61,19 @@ cp_stringatom_init(CpStringAtomObject* self, PyObject* args, PyObject* kwds)
static char* kwlist[] = { "length", "encoding", "errors", NULL };
PyObject *length = NULL, *encoding = NULL, *errors = NULL;
if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "OU|U", kwlist, &length, &encoding, &errors)) {
+ args, kwds, "O|OO", kwlist, &length, &encoding, &errors)) {
return -1;
}
+ _modulestate* mod = get_global_module_state();
+
_Cp_SetObj(self->m_length, length);
_Cp_SetObj(self->m_encoding, encoding);
+ if (!self->m_encoding) {
+ _Cp_SetObj(self->m_encoding, mod->str_utf8);
+ }
_Cp_SetObj(self->m_errors, errors);
if (!self->m_errors) {
- _modulestate* mod = get_global_module_state();
_Cp_SetObj(self->m_errors, mod->str_strict);
}
return 0;
@@ -80,6 +81,7 @@ cp_stringatom_init(CpStringAtomObject* self, PyObject* args, PyObject* kwds)
/* Public API */
+/*CpAPI*/
int
CpStringAtom_Pack(CpStringAtomObject* self,
PyObject* value,
@@ -99,6 +101,7 @@ CpStringAtom_Pack(CpStringAtomObject* self,
return 0;
}
+/*CpAPI*/
PyObject*
CpStringAtom_Unpack(CpStringAtomObject* self, CpLayerObject* layer)
{
@@ -111,7 +114,7 @@ CpStringAtom_Unpack(CpStringAtomObject* self, CpLayerObject* layer)
if (greedy) {
res = CpState_ReadFully(layer->m_state);
} else {
- res = CpState_Read(layer->m_state, length);
+ res = CpState_ReadSsize_t(layer->m_state, length);
}
if (!res) {
return NULL;
@@ -134,10 +137,10 @@ static PyMemberDef CpStringAtom_Members[] = {
};
PyTypeObject CpStringAtom_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(string),
+ 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
new file mode 100644
index 0000000..6f47fd1
--- /dev/null
+++ b/src/ccaterpillar/atomimpl/varint.c
@@ -0,0 +1,342 @@
+/* varint implementation for Little Endian and Big Endian */
+
+#include "caterpillar/caterpillar.h"
+
+#include
+
+// Inspired by https://github.com/google/cityhash/blob/master/src/city.cc
+#ifdef _MSC_VER
+#include
+#define CP_BSWAP_U32(x) _byteswap_ulong(x)
+#define CP_BSWAP_U64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+#include
+#define CP_BSWAP_U32(x) OSSwapInt32(x)
+#define CP_BSWAP_U64(x) OSSwapInt64(x)
+
+#elif defined(sun) || defined(__sun)
+#include
+#define CP_BSWAP_U32(x) BSWAP_32(x)
+#define CP_BSWAP_U64(x) BSWAP_64(x)
+
+#elif defined(__FreeBSD__)
+#include
+#define CP_BSWAP_U32(x) bswap32(x)
+#define CP_BSWAP_U64(x) bswap64(x)
+
+#elif defined(__OpenBSD__)
+#include
+#define CP_BSWAP_U32(x) swap32(x)
+#define CP_BSWAP_U64(x) swap64(x)
+
+#else
+#include
+#define CP_BSWAP_U32(x) bswap_32(x)
+#define CP_BSWAP_U64(x) bswap_64(x)
+#endif
+
+/* impl */
+static PyObject*
+cp_varintatom_type(PyObject* self)
+{
+ return Py_XNewRef(&PyLong_Type);
+}
+
+static PyObject*
+cp_varintatom_size(CpVarIntAtomObject* self, PyObject* ctx)
+{
+ PyErr_SetString(PyExc_TypeError, "VarInts do not have a static size!");
+ return NULL;
+}
+
+static PyObject*
+cp_varintatom_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ CpVarIntAtomObject* self = (CpVarIntAtomObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->_m_little_endian = true;
+ self->_m_lsb = false;
+
+ CpBuiltinAtom_CATOM(self).ob_pack = (packfunc)CpVarIntAtom_Pack;
+ CpBuiltinAtom_CATOM(self).ob_unpack = (unpackfunc)CpVarIntAtom_Unpack;
+ CpBuiltinAtom_CATOM(self).ob_pack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_unpack_many = NULL;
+ CpBuiltinAtom_CATOM(self).ob_size = (sizefunc)cp_varintatom_size;
+ CpBuiltinAtom_CATOM(self).ob_type = (typefunc)cp_varintatom_type;
+ CpBuiltinAtom_CATOM(self).ob_bits = NULL;
+ }
+ return (PyObject*)self;
+}
+
+static void
+cp_varintatom_dealloc(CpVarIntAtomObject* self)
+{
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_varintatom_init(CpVarIntAtomObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = { "little_endian", "lsb", NULL };
+ int little_endian = true;
+ int lsb = false;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "|pp", kwlist, &little_endian, &lsb)) {
+ return -1;
+ }
+ self->_m_little_endian = little_endian;
+ self->_m_lsb = lsb;
+ return 0;
+}
+
+static PyObject*
+cp_varintatom_repr(CpVarIntAtomObject* self)
+{
+ char endian = 'l';
+ if (!self->_m_little_endian) {
+ endian = 'b';
+ }
+ if (self->_m_lsb) {
+ return PyUnicode_FromFormat("<%ce varint [lsb]>", endian);
+ }
+ return PyUnicode_FromFormat("<%ce varint>", endian);
+}
+
+static PyObject*
+cp_varintatom_set_byteorder(CpVarIntAtomObject* self,
+ PyObject* args,
+ PyObject* kw)
+{
+ _CpEndian_KwArgsGetByteorder(NULL);
+
+ _modulestate* mod = get_global_module_state();
+ int little_endian = CpEndian_IsLittleEndian((CpEndianObject*)byteorder, mod);
+ PyObject* new_varint =
+ (PyObject*)CpVarIntAtom_New(little_endian, self->_m_lsb);
+
+ return new_varint;
+}
+
+/* Public API */
+
+#define _CP_VARINT_BSWAP_IMPL(name, type) \
+ PyObject* result_int = PyLong_From##name(number); \
+ if (!result_int) { \
+ return 0; \
+ } \
+ PyObject* result = CpVarIntAtom_BSwap(result_int, little_endian); \
+ Py_DECREF(result_int); \
+ if (!result) { \
+ return 0; \
+ } \
+ type res = PyLong_As##name(result); \
+ Py_DECREF(result); \
+ return res;
+
+/*CpAPI*/
+unsigned long long
+CpVarIntAtom_BSwapUnsignedLongLong(unsigned long long number,
+ bool little_endian)
+{
+ _CP_VARINT_BSWAP_IMPL(UnsignedLongLong, unsigned long long);
+}
+
+/*CpAPI*/
+long long
+CpVarIntAtom_BSwapLongLong(long long number, bool little_endian)
+{
+ _CP_VARINT_BSWAP_IMPL(LongLong, long long);
+}
+
+/*CpAPI*/
+Py_ssize_t
+CpVarIntAtom_BSwapSsize_t(Py_ssize_t number, bool little_endian)
+{
+ _CP_VARINT_BSWAP_IMPL(Ssize_t, Py_ssize_t);
+}
+
+#undef _CP_VARINT_BSWAP_IMPL
+
+/*CpAPI*/
+PyObject*
+CpVarIntAtom_BSwap(PyObject* number, bool little_endian)
+{
+ size_t nbytes = _PyLong_NumBits(number) / 8;
+ PyObject* bytesSize = PyLong_FromSize_t(nbytes);
+ if (!bytesSize) {
+ return NULL;
+ }
+
+ PyObject* bytes = CpObject_CreateOneArg(&PyBytes_Type, bytesSize);
+ if (!bytes) {
+ Py_DECREF(bytesSize);
+ return NULL;
+ }
+
+ int res = _PyLong_AsByteArray((PyLongObject*)number,
+ (unsigned char*)PyBytes_AS_STRING(bytes),
+ nbytes,
+ little_endian,
+ false);
+ if (res == -1) {
+ Py_DECREF(bytesSize);
+ Py_DECREF(bytes);
+ return NULL;
+ }
+
+ PyObject* result = _PyLong_FromByteArray(
+ (unsigned char*)PyBytes_AS_STRING(bytes), nbytes, !little_endian, false);
+
+ Py_DECREF(bytesSize);
+ Py_DECREF(bytes);
+ return result;
+}
+
+PyObject*
+_CpVarIntAtom_Length(unsigned long long number)
+{
+ if (number == 0) {
+ return PyLong_FromLong(1);
+ }
+ int length = 1;
+ while ((number >> 7) != 0) {
+ number >>= 7;
+ length++;
+ }
+ return PyLong_FromLong(length);
+}
+
+/*CpAPI*/
+int
+CpVarIntAtom_Pack(CpVarIntAtomObject* self,
+ PyObject* value,
+ CpLayerObject* layer)
+{
+ unsigned long long raw_value = PyLong_AsUnsignedLongLongMask(value);
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ int little_endian = self->_m_little_endian;
+ int lsb = self->_m_lsb;
+
+ PyObject* length = _CpVarIntAtom_Length(raw_value);
+ if (!length) {
+ return -1;
+ }
+
+ PyObject* bytes = CpObject_CreateOneArg(&PyBytes_Type, length);
+ Py_DECREF(length);
+ if (!bytes) {
+ return -1;
+ }
+
+ int raw_length = PyBytes_GET_SIZE(bytes);
+ for (int i = 0; i < raw_length; i++) {
+ unsigned char byte = 0;
+ if (little_endian) {
+ byte = (raw_value >> (i * 7)) & 0x7f;
+ } else {
+ byte = (raw_value >> ((raw_length - 1 - i) * 7)) & 0x7f;
+ }
+ if (!lsb && i != raw_length - 1) {
+ byte |= 0x80;
+ } else if (lsb && (i == raw_length - 1)) {
+ byte |= 0x80;
+ }
+
+ PyBytes_AS_STRING(bytes)[i] = byte;
+ }
+
+ PyObject* result = CpState_Write(layer->m_state, bytes);
+ Py_DECREF(bytes);
+ if (!result) {
+ return -1;
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+/*CpAPI*/
+PyObject*
+CpVarIntAtom_Unpack(CpVarIntAtomObject* self, CpLayerObject* layer)
+{
+ PyObject* tmp_val = NULL;
+ unsigned long long result = 0, tmp = 0;
+ int shift = 0;
+ int little_endian = self->_m_little_endian;
+ int lsb = self->_m_lsb;
+ int nbytes = 0;
+
+ while (true) {
+ tmp_val = CpState_ReadSsize_t(layer->m_state, 1);
+ if (!tmp_val) {
+ return NULL;
+ }
+ // only one byte
+ tmp = *(PyBytes_AS_STRING(tmp_val));
+ Py_DECREF(tmp_val);
+
+ result |= (tmp & 0x7f) << shift;
+ shift += 7;
+ nbytes++;
+ if ((lsb && (tmp & 0x80) == 1) || (!lsb && (tmp & 0x80) == 0)) {
+ break;
+ }
+ }
+
+ if (!little_endian) {
+ if (nbytes == 1) {
+ // no swap needed
+ } else if (nbytes == 2) {
+ // manually swap
+ // TODO: explain why we use 7 bits here
+ result = ((result >> 7) & 0xff) | ((result << 7) & 0xff00);
+ } else if (nbytes <= 4) {
+ // swap using compiler directive
+ result = CP_BSWAP_U32(result);
+ } else if (nbytes <= 8) {
+ // compiler directive
+ result = CP_BSWAP_U64(result);
+ } else {
+ // use Python methods
+ result = CpVarIntAtom_BSwapUnsignedLongLong(result, true);
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ }
+
+ return PyLong_FromUnsignedLongLong(result);
+}
+
+/* docs */
+
+/* type setup */
+static PyMemberDef CpVarIntAtom_Members[] = {
+ { "little_endian",
+ T_BOOL,
+ offsetof(CpVarIntAtomObject, _m_little_endian),
+ READONLY },
+ { "lsb", T_BOOL, offsetof(CpVarIntAtomObject, _m_lsb), READONLY },
+ { NULL }
+};
+
+static PyMethodDef CpVarIntAtom_Methods[] = {
+ _CpEndian_ImplSetByteorder_MethDef(varintatom, NULL),
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject CpVarIntAtom_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpVarIntAtom_NAME),
+ .tp_basicsize = sizeof(CpVarIntAtomObject),
+ .tp_dealloc = (destructor)cp_varintatom_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_members = CpVarIntAtom_Members,
+ .tp_methods = CpVarIntAtom_Methods,
+ .tp_init = (initproc)cp_varintatom_init,
+ .tp_new = (newfunc)cp_varintatom_new,
+ .tp_repr = (reprfunc)cp_varintatom_repr,
+};
diff --git a/src/atomobj.c b/src/ccaterpillar/atomobj.c
similarity index 76%
rename from src/atomobj.c
rename to src/ccaterpillar/atomobj.c
index a75686f..3948553 100644
--- a/src/atomobj.c
+++ b/src/ccaterpillar/atomobj.c
@@ -3,6 +3,7 @@
#include "caterpillar/atomobj.h"
#include "caterpillar/macros.h"
+#include
#include
static PyObject*
@@ -111,11 +112,11 @@ static PyMethodDef CpAtom_Methods[] = {
PyTypeObject CpAtom_Type = {
PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(atom),
.tp_basicsize = sizeof(CpAtomObject),
- .tp_dealloc =(destructor)cp_atom_dealloc,
+ .tp_dealloc = (destructor)cp_atom_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = cp_atom_doc,
.tp_methods = CpAtom_Methods,
- .tp_init =(initproc)cp_atom_init,
+ .tp_init = (initproc)cp_atom_init,
.tp_new = (newfunc)cp_atom_new,
};
@@ -193,13 +194,16 @@ cp_catom_pack_many(CpCAtomObject* self, PyObject* args, PyObject* kw)
return NULL;
}
- static char* kwlist[] = { "ops", "context", NULL };
- PyObject *ops = NULL, *context = NULL;
- if (PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &ops, &context) < 0) {
+ static char* kwlist[] = { "ops", "context", "lengthinfo", NULL };
+ PyObject *ops = NULL, *context = NULL, *lengthinfo = NULL;
+ if (PyArg_ParseTupleAndKeywords(
+ args, kw, "OOO", kwlist, &ops, &context, &lengthinfo) < 0) {
return NULL;
}
- return self->ob_pack_many((PyObject*)self, ops, context) ? NULL : Py_NewRef(Py_None);
+ return self->ob_pack_many((PyObject*)self, ops, context, lengthinfo)
+ ? NULL
+ : Py_NewRef(Py_None);
}
static PyObject*
@@ -232,12 +236,12 @@ cp_catom_unpack_many(CpCAtomObject* self, PyObject* args, PyObject* kw)
return NULL;
}
- static char* kwlist[] = { "context", NULL };
- PyObject* context = NULL;
- if (PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &context) < 0) {
+ static char* kwlist[] = { "context", "lengthinfo", NULL };
+ PyObject *context = NULL, *lengthinfo = NULL;
+ if (PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &context, &lengthinfo) < 0) {
return NULL;
}
- return self->ob_unpack_many((PyObject*)self, context);
+ return self->ob_unpack_many((PyObject*)self, context, lengthinfo);
}
static PyObject*
@@ -264,6 +268,12 @@ cp_catom_size(CpCAtomObject* self, PyObject* args, PyObject* kw)
if (PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &context) < 0) {
return NULL;
}
+
+ if (context == Py_None || context == NULL) {
+ PyErr_SetString(PyExc_ValueError, "context must be set!");
+ return NULL;
+ }
+
return self->ob_size((PyObject*)self, context);
}
@@ -310,4 +320,61 @@ PyTypeObject CpCAtom_Type = {
.tp_methods = CpCAtom_Methods,
.tp_init = (initproc)cp_catom_init,
.tp_new = (newfunc)cp_catom_new,
+};
+
+// ------------------------------------------------------------------------------
+// length info
+
+static PyObject*
+cp_lengthinfo_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ return type->tp_alloc(type, 0);
+}
+
+static void
+cp_lengthinfo_dealloc(CpLengthInfoObject* self)
+{
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_lengthinfo_init(CpLengthInfoObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "length", "greedy", NULL };
+ Py_ssize_t length = 0;
+ int greedy = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|np", kwlist, &length, &greedy)) {
+ return -1;
+ }
+
+ self->m_length = length;
+ self->m_greedy = greedy;
+ return 0;
+}
+
+static PyObject*
+cp_lengthinfo_repr(CpLengthInfoObject* self)
+{
+ if (self->m_greedy) {
+ return PyUnicode_FromString("");
+ }
+ 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),
+ .tp_dealloc = (destructor)cp_lengthinfo_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = (newfunc)cp_lengthinfo_new,
+ .tp_init = (initproc)cp_lengthinfo_init,
+ .tp_repr = (reprfunc)cp_lengthinfo_repr,
+ .tp_doc = NULL,
};
\ No newline at end of file
diff --git a/src/ccaterpillar/caterpillarapi.c b/src/ccaterpillar/caterpillarapi.c
new file mode 100644
index 0000000..e7b5ef5
--- /dev/null
+++ b/src/ccaterpillar/caterpillarapi.c
@@ -0,0 +1,175 @@
+/* These pointers will be stored in the C-object for use in other
+ extension modules
+*/
+
+void *Cp_API[] = {
+ (void *) &CpModule,
+ (void *) &CpAtom_Type,
+ (void *) &CpCAtom_Type,
+ (void *) &CpArch_Type,
+ (void *) &CpEndian_Type,
+ (void *) &CpContext_Type,
+ (void *) &CpUnaryExpr_Type,
+ (void *) &CpBinaryExpr_Type,
+ (void *) &CpContextPath_Type,
+ NULL,
+ NULL,
+ NULL,
+ (void *) &CpInvalidDefault_Type,
+ (void *) &CpDefaultOption_Type,
+ (void *) &_CpInvalidDefault_Object,
+ (void *) &_CpDefaultOption_Object,
+ (void *) &CpOption_Type,
+ (void *) &CpState_Type,
+ (void *) &CpLayer_Type,
+ (void *) &CpStructFieldInfo_Type,
+ (void *) &CpStruct_Type,
+ (void *) &CpFloatAtom_Type,
+ (void *) &CpIntAtom_Type,
+ (void *) &CpBoolAtom_Type,
+ (void *) &CpCharAtom_Type,
+ (void *) &CpPaddingAtom_Type,
+ (void *) &CpStringAtom_Type,
+ (void *) &CpConstAtom_Type,
+ (void *) &CpBuiltinAtom_Type,
+ (void *) &CpRepeatedAtom_Type,
+ (void *) &CpSeqLayer_Type,
+ (void *) &CpObjLayer_Type,
+ (void *) &CpConditionAtom_Type,
+ (void *) &CpSwitchAtom_Type,
+ (void *) &CpOffsetAtom_Type,
+ (void *) &CpPrimitiveAtom_Type,
+ (void *) &CpLengthInfo_Type,
+ (void *) &CpBytesAtom_Type,
+ (void *) &CpPStringAtom_Type,
+ (void *) &CpEnumAtom_Type,
+ (void *) &CpVarIntAtom_Type,
+ (void *) &CpComputedAtom_Type,
+ (void *) &CpLazyAtom_Type,
+ (void *) &CpCStringAtom_Type,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (void *) &CpEndian_IsLittleEndian,
+ NULL,
+ NULL,
+ (void *) &CpContext_New,
+ (void *) &CpUnaryExpr_New,
+ (void *) &CpBinaryExpr_New,
+ (void *) &CpContextPath_New,
+ (void *) &CpContextPath_FromString,
+ (void *) &CpTypeMap_Lookup,
+ (void *) &CpTypeMap_Register,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (void *) &CpTypeOf,
+ NULL,
+ (void *) &CpTypeOf_Common,
+ (void *) &CpPack,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (void *) &_CpPack_EvalLength,
+ (void *) &CpSizeOf,
+ NULL,
+ (void *) &CpSizeOf_Struct,
+ (void *) &CpSizeOf_Common,
+ (void *) &_Cp_SizeOf,
+ (void *) &CpUnpack,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (void *) &_CpUnpack_EvalLength,
+ (void *) &CpUnpack_CAtom,
+ (void *) &CpPack_CAtom,
+ (void *) &CpSizeOf_CAtom,
+ (void *) &CpTypeOf_CAtom,
+ (void *) &CpState_New,
+ (void *) &CpState_Tell,
+ (void *) &CpState_Seek,
+ (void *) &CpState_Read,
+ (void *) &CpState_ReadSsize_t,
+ (void *) &CpState_ReadFully,
+ (void *) &CpState_Write,
+ (void *) &CpState_SetGlobals,
+ (void *) &CpLayer_New,
+ NULL,
+ (void *) &CpLayer_Invalidate,
+ (void *) &CpStructFieldInfo_New,
+ (void *) &CpStruct_AddFieldInfo,
+ NULL,
+ (void *) &CpStruct_New,
+ (void *) &CpStruct_GetAnnotations,
+ (void *) &CpStruct_ReplaceType,
+ (void *) &CpStruct_HasOption,
+ (void *) &CpStruct_Pack,
+ (void *) &CpStruct_Unpack,
+ (void *) &CpStruct_SizeOf,
+ (void *) &CpStructModel_Check,
+ (void *) &CpStructModel_GetStruct,
+ (void *) &CpSeqLayer_New,
+ (void *) &CpSeqLayer_SetSequence,
+ (void *) &CpObjLayer_New,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (void *) &CpIntAtom_Pack,
+ (void *) &CpIntAtom_Unpack,
+ (void *) &CpFloatAtom_Pack,
+ (void *) &CpFloatAtom_Unpack,
+ (void *) &CpBoolAtom_Pack,
+ (void *) &CpBoolAtom_Unpack,
+ (void *) &CpCharAtom_Pack,
+ (void *) &CpCharAtom_Unpack,
+ (void *) &CpPaddingAtom_Pack,
+ (void *) &CpPaddingAtom_PackMany,
+ (void *) &CpPaddingAtom_Unpack,
+ (void *) &CpPaddingAtom_UnpackMany,
+ (void *) &CpStringAtom_Pack,
+ (void *) &CpStringAtom_Unpack,
+ (void *) &CpConstAtom_Pack,
+ (void *) &CpConstAtom_Unpack,
+ (void *) &CpRepeatedAtom_Pack,
+ (void *) &CpRepeatedAtom_Unpack,
+ (void *) &CpRepeatedAtom_GetLength,
+ (void *) &CpConditionAtom_Pack,
+ (void *) &CpConditionAtom_Unpack,
+ (void *) &CpConditionAtom_IsEnabled,
+ (void *) &CpSwitchAtom_GetNext,
+ (void *) &CpSwitchAtom_Pack,
+ (void *) &CpSwitchAtom_Unpack,
+ (void *) &CpOffsetAtom_Pack,
+ (void *) &CpOffsetAtom_Unpack,
+ (void *) &CpOffsetAtom_GetOffset,
+ (void *) &CpBytesAtom_GetLength,
+ (void *) &CpBytesAtom_Pack,
+ (void *) &CpBytesAtom_Unpack,
+ NULL,
+ (void *) &CpPStringAtom_Pack,
+ (void *) &CpPStringAtom_Unpack,
+ (void *) &CpEnumAtom_Pack,
+ (void *) &CpEnumAtom_Unpack,
+ (void *) &CpVarIntAtom_Pack,
+ (void *) &CpVarIntAtom_Unpack,
+ (void *) &CpVarIntAtom_BSwap,
+ (void *) &CpVarIntAtom_BSwapUnsignedLongLong,
+ (void *) &CpVarIntAtom_BSwapLongLong,
+ (void *) &CpVarIntAtom_BSwapSsize_t,
+ (void *) &CpComputedAtom_Pack,
+ (void *) &CpComputedAtom_Unpack,
+ (void *) &CpLazyAtom_Pack,
+ (void *) &CpLazyAtom_Unpack,
+ (void *) &CpCStringAtom_Pack,
+ (void *) &CpCStringAtom_Unpack
+};
+
diff --git a/src/caterpillarapi.c.in b/src/ccaterpillar/caterpillarapi.c.in
similarity index 100%
rename from src/caterpillarapi.c.in
rename to src/ccaterpillar/caterpillarapi.c.in
diff --git a/src/context.c b/src/ccaterpillar/context.c
similarity index 76%
rename from src/context.c
rename to src/ccaterpillar/context.c
index 680b350..f23dd8d 100644
--- a/src/context.c
+++ b/src/ccaterpillar/context.c
@@ -99,15 +99,6 @@ cp_context__getattro__(CpContextObject* self, PyObject* name)
}
/* public API */
-/*CpAPI*/
-PyObject*
-CpContext_GetAttr(PyObject* ctx, PyObject* key, _modulestate* m)
-{
- // TODO
- // return PyObject_CallMethodOneArg(ctx, m->str, PyObject *arg)
- PyErr_SetString(PyExc_NotImplementedError, "GetAttr not implemented yet");
- return NULL;
-}
/*CpAPI*/
CpContextObject*
@@ -271,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)
@@ -294,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),
@@ -306,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,
};
//------------------------------------------------------------------------------
@@ -551,6 +607,49 @@ 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 _CpUnaryExpr_BinaryNumberMethod(name, op) \
+ static PyObject* cp_binaryexpr_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*/
CpBinaryExprObject*
CpBinaryExpr_New(int op, PyObject* left, PyObject* right)
@@ -582,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),
@@ -593,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,
};
//------------------------------------------------------------------------------
@@ -639,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
@@ -768,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\
@@ -816,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/default.c b/src/ccaterpillar/default.c
new file mode 100644
index 0000000..cc02764
--- /dev/null
+++ b/src/ccaterpillar/default.c
@@ -0,0 +1,63 @@
+/* immortal objects */
+
+#include "caterpillar/caterpillar.h"
+
+static PyObject*
+cp_invaliddefault_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ if (PyTuple_GET_SIZE(args) || PyDict_GET_SIZE(kw)) {
+ PyErr_SetString(PyExc_TypeError,
+ "InvalidDefaultType does not accept arguments");
+ return NULL;
+ }
+
+ Py_INCREF(CpInvalidDefault);
+ return CpInvalidDefault;
+}
+
+static PyObject*
+cp_invaliddefault_repr(PyObject* self)
+{
+ return PyUnicode_FromString("");
+}
+
+PyTypeObject CpInvalidDefault_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(InvalidDefaultType),
+ .tp_repr = (reprfunc)cp_invaliddefault_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = (newfunc)cp_invaliddefault_new,
+};
+
+PyObject _CpInvalidDefault_Object = { _PyObject_EXTRA_INIT{
+ _Py_IMMORTAL_REFCNT /* ob_refcnt */ },
+ &CpInvalidDefault_Type /* ob_type */ };
+
+static PyObject*
+cp_defaultoption_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ if (PyTuple_GET_SIZE(args) || PyDict_GET_SIZE(kw)) {
+ PyErr_SetString(PyExc_TypeError,
+ "InvalidDefaultType does not accept arguments");
+ return NULL;
+ }
+
+ Py_INCREF(CpDefaultOption);
+ return CpDefaultOption;
+}
+
+static PyObject*
+cp_defaultoption_repr(PyObject* self)
+{
+ return PyUnicode_FromString("");
+}
+
+PyTypeObject CpDefaultOption_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(DefaultOptionType),
+ .tp_repr = (reprfunc)cp_defaultoption_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = (newfunc)cp_defaultoption_new,
+};
+
+PyObject _CpDefaultOption_Object = { _PyObject_EXTRA_INIT{
+ _Py_IMMORTAL_REFCNT /* ob_refcnt */ },
+ &CpDefaultOption_Type /* ob_type */ };
diff --git a/src/ccaterpillar/layer.c b/src/ccaterpillar/layer.c
new file mode 100644
index 0000000..248e0d0
--- /dev/null
+++ b/src/ccaterpillar/layer.c
@@ -0,0 +1,380 @@
+/* layer implementation */
+#include "caterpillar/caterpillar.h"
+
+#include
+
+/* top level layer impl */
+static PyObject*
+cp_layer_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpLayerObject* self;
+ self = (CpLayerObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ self->m_path = NULL;
+ self->m_parent = NULL;
+ self->m_state = NULL;
+ self->m_class = CpLayerClass_Default;
+ return (PyObject*)self;
+}
+
+static void
+cp_layer_dealloc(CpLayerObject* self)
+{
+ Py_XDECREF(self->m_path);
+ Py_XDECREF(self->m_state);
+ Py_XDECREF(self->m_parent);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_layer_init(CpLayerObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "state", "path", "parent", NULL };
+ PyObject *state = NULL, *path = NULL, *parent = NULL;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O|OO", kwlist, &state, &path, &parent))
+ return -1;
+
+ if (!PyObject_IsInstance(state, (PyObject*)(&CpState_Type))) {
+ PyErr_SetString(PyExc_TypeError, "state must be an instance of CpState");
+ return -1;
+ }
+
+ Py_XSETREF(self->m_state, (CpStateObject*)Py_NewRef(state));
+ _Cp_SetObj(self->m_path, path);
+ Py_XSETREF(self->m_parent, (CpLayerObject*)Py_XNewRef(parent));
+ return 0;
+}
+
+/*CpAPI*/
+CpLayerObject*
+CpLayer_New(CpStateObject* state, CpLayerObject* parent)
+{
+ CpLayerObject* self =
+ (CpLayerObject*)CpObject_Create(&CpLayer_Type, "O", state);
+ if (!self) {
+ return NULL;
+ }
+ if (parent) {
+ Py_XSETREF(self->m_parent, (CpLayerObject*)Py_XNewRef(parent));
+ Py_XSETREF(self->m_path, Py_XNewRef(parent->m_path));
+ }
+ return self;
+}
+
+/*CpAPI*/
+int
+CpLayer_Invalidate(CpLayerObject* self)
+{
+ if (!self) {
+ PyErr_SetString(PyExc_ValueError, "layer must be non-null");
+ return -1;
+ }
+
+ Py_XSETREF(self->m_parent, NULL);
+ Py_XSETREF(self->m_state, NULL);
+ Py_XSETREF(self->m_path, NULL);
+ Py_CLEAR(self);
+ return 0;
+}
+
+/* docs */
+
+/* type */
+static PyMemberDef CpLayer_Members[] = {
+ { "state", T_OBJECT, offsetof(CpLayerObject, m_state), READONLY, "state" },
+ { "path", T_OBJECT, offsetof(CpLayerObject, m_path), 0, "path" },
+ { "parent", T_OBJECT, offsetof(CpLayerObject, m_parent), 0, "parent" },
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject CpLayer_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(layer),
+ .tp_basicsize = sizeof(CpLayerObject),
+ .tp_dealloc = (destructor)cp_layer_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_members = CpLayer_Members,
+ .tp_init = (initproc)cp_layer_init,
+ .tp_new = (newfunc)cp_layer_new,
+};
+
+//-------------------------------------------------------------------------------
+// seq layer
+
+static PyObject*
+cp_seqlayer_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpSeqLayerObject* self = (CpSeqLayerObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+ self->ob_base.m_path = NULL;
+ self->m_sequence = NULL;
+ self->m_length = -1;
+ self->m_index = -1;
+ self->s_greedy = false;
+ self->ob_base.m_parent = NULL;
+ self->ob_base.m_state = NULL;
+ self->ob_base.m_class = CpLayerClass_Sequence;
+ return (PyObject*)self;
+}
+
+static void
+cp_seqlayer_dealloc(CpSeqLayerObject* self)
+{
+ Py_XDECREF(self->ob_base.m_path);
+ Py_XDECREF(self->m_sequence);
+ Py_XDECREF(self->ob_base.m_parent);
+ Py_XDECREF(self->ob_base.m_state);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_seqlayer_init(CpSeqLayerObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "state", "path", "parent", NULL };
+ PyObject *state = NULL, *sequence = NULL, *path = NULL, *parent = NULL;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O|OOO", kwlist, &state, &sequence, &path, &parent))
+ return -1;
+
+ if (!PyObject_IsInstance(state, (PyObject*)(&CpState_Type))) {
+ PyErr_SetString(PyExc_TypeError, "state must be an instance of CpState");
+ return -1;
+ }
+
+ Py_XSETREF(self->ob_base.m_state, (CpStateObject*)Py_NewRef(state));
+ _Cp_SetObj(self->ob_base.m_path, path);
+ Py_XSETREF(self->ob_base.m_parent, (CpLayerObject*)Py_XNewRef(parent));
+ return 0;
+}
+
+/*CpAPI*/
+int
+CpSeqLayer_SetSequence(CpSeqLayerObject* self,
+ PyObject* sequence,
+ Py_ssize_t length,
+ int8_t greedy)
+{
+ if (!sequence || !self) {
+ PyErr_SetString(PyExc_ValueError, "sequence and layer must be non-null");
+ return -1;
+ }
+
+ Py_XSETREF(self->m_sequence, Py_NewRef(sequence));
+ self->m_length = length;
+ self->s_greedy = greedy;
+ self->m_index = 0;
+ return 0;
+}
+
+/*CpAPI*/
+CpSeqLayerObject*
+CpSeqLayer_New(CpStateObject* state, CpLayerObject* parent)
+{
+ CpSeqLayerObject* self =
+ (CpSeqLayerObject*)CpObject_Create(&CpSeqLayer_Type, "O", state);
+ if (!self) {
+ return NULL;
+ }
+ if (parent) {
+ Py_XSETREF(self->ob_base.m_parent, (CpLayerObject*)Py_XNewRef(parent));
+ Py_XSETREF(self->ob_base.m_path, Py_XNewRef(parent->m_path));
+ }
+ return self;
+}
+
+/* docs */
+
+/* type */
+static PyMemberDef CpSeqLayer_Members[] = {
+ {
+ "sequence",
+ T_OBJECT,
+ offsetof(CpSeqLayerObject, m_sequence),
+ READONLY,
+ },
+ {
+ "length",
+ T_PYSSIZET,
+ offsetof(CpSeqLayerObject, m_length),
+ 0,
+ },
+ {
+ "index",
+ T_PYSSIZET,
+ offsetof(CpSeqLayerObject, m_index),
+ 0,
+ },
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject CpSeqLayer_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpSeqLayer_NAME),
+ .tp_basicsize = sizeof(CpSeqLayerObject),
+ .tp_dealloc = (destructor)cp_seqlayer_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = NULL,
+ .tp_members = CpSeqLayer_Members,
+ .tp_init = (initproc)cp_seqlayer_init,
+ .tp_new = (newfunc)cp_seqlayer_new,
+};
+
+//-------------------------------------------------------------------------------
+// obj layer
+
+static PyObject*
+cp_objlayer_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+ CpObjLayerObject* self = (CpObjLayerObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+ self->ob_base.m_path = NULL;
+ self->m_obj = NULL;
+ self->ob_base.m_parent = NULL;
+ self->ob_base.m_state = NULL;
+ self->ob_base.m_class = CpLayerClass_Object;
+ return (PyObject*)self;
+}
+
+static void
+cp_objlayer_dealloc(CpObjLayerObject* self)
+{
+ Py_XDECREF(self->ob_base.m_path);
+ Py_XDECREF(self->m_obj);
+ Py_XDECREF(self->ob_base.m_parent);
+ Py_XDECREF(self->ob_base.m_state);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static int
+cp_objlayer_init(CpObjLayerObject* self, PyObject* args, PyObject* kw)
+{
+ static char* kwlist[] = { "state", "path", "parent", "obj", NULL };
+ PyObject *state = NULL, *path = NULL, *parent = NULL, *obj = NULL;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O|OOO", kwlist, &state, &path, &parent, &obj))
+ return -1;
+
+ if (!PyObject_IsInstance(state, (PyObject*)(&CpState_Type))) {
+ PyErr_SetString(PyExc_TypeError, "state must be an instance of CpState");
+ return -1;
+ }
+
+ Py_XSETREF(self->ob_base.m_state, (CpStateObject*)Py_NewRef(state));
+ Py_XSETREF(self->m_obj, obj ? Py_NewRef(obj) : (PyObject*)CpContext_New());
+ Py_XSETREF(self->ob_base.m_path, Py_XNewRef(path));
+ Py_XSETREF(self->ob_base.m_parent, (CpLayerObject*)Py_XNewRef(parent));
+ return 0;
+}
+
+static PyObject*
+cp_objlayer_context_getattr(CpLayerObject* self, PyObject* args)
+{
+ _modulestate* state = get_global_module_state();
+ PyObject *tmp = NULL, *lineValue = NULL, *lastKey = NULL, *key = NULL,
+ *obj = NULL;
+ if (!PyArg_ParseTuple(args, "O", &lineValue)) {
+ return NULL;
+ }
+
+ PyObject* values = PyUnicode_Split(lineValue, state->str_path_delim, -1);
+ if (!values) {
+ return NULL;
+ }
+
+ size_t length = PyList_Size(values);
+ if (length == 0) {
+ Py_XDECREF(values);
+ PyErr_SetString(PyExc_ValueError, "Empty path");
+ return NULL;
+ }
+
+ key = PyList_GetItem(values, 0);
+ if (!key) {
+ Py_XDECREF(values);
+ return NULL;
+ }
+
+ tmp = PyObject_GenericGetAttr((PyObject*)self, key);
+ if (!tmp) {
+ PyErr_Clear();
+ PyErr_Format(PyExc_ValueError, "CpObjLayer has no attribute '%s'", key);
+ Py_XDECREF(values);
+ return NULL;
+ }
+
+ obj = tmp;
+ Py_XSETREF(lastKey, Py_XNewRef(key));
+ for (size_t i = 1; i < length; i++) {
+ key = PyList_GetItem(values, i);
+ if (!key) {
+ Py_XDECREF(values);
+ return NULL;
+ }
+
+ tmp = PyObject_GetAttr(obj, key);
+ if (!tmp) {
+ PyErr_Clear();
+ PyErr_Format(
+ PyExc_ValueError, "'%s' has no attribute '%s'", lastKey, key);
+ Py_XDECREF(values);
+ return NULL;
+ }
+
+ Py_XSETREF(obj, tmp);
+ Py_XSETREF(lastKey, Py_XNewRef(key));
+ }
+ Py_XDECREF(values);
+ return obj;
+}
+
+/*CpAPI*/
+CpObjLayerObject*
+CpObjLayer_New(CpStateObject* state, CpLayerObject* parent)
+{
+ CpObjLayerObject* self = (CpObjLayerObject*)CpObject_CreateOneArg(
+ &CpObjLayer_Type, (PyObject*)state);
+ if (!self) {
+ return NULL;
+ }
+ if (parent) {
+ Py_XSETREF(self->ob_base.m_parent, (CpLayerObject*)Py_XNewRef(parent));
+ Py_XSETREF(self->ob_base.m_path, Py_XNewRef(parent->m_path));
+ }
+ return self;
+}
+
+/* docs */
+
+/* type */
+static PyMemberDef CpObjLayer_Members[] = {
+ {
+ "obj",
+ T_OBJECT,
+ offsetof(CpObjLayerObject, m_obj),
+ 0,
+ },
+ { NULL } /* Sentinel */
+};
+
+static PyMethodDef CpObjLayer_Methods[] = {
+ { "__context_getattr__",
+ (PyCFunction)cp_objlayer_context_getattr,
+ METH_VARARGS },
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject CpObjLayer_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) _Cp_NameStr(CpObjLayer_NAME),
+ .tp_basicsize = sizeof(CpObjLayerObject),
+ .tp_dealloc = (destructor)cp_objlayer_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = NULL,
+ .tp_methods = CpObjLayer_Methods,
+ .tp_members = CpObjLayer_Members,
+ .tp_init = (initproc)cp_objlayer_init,
+ .tp_new = (newfunc)cp_objlayer_new,
+};
\ No newline at end of file
diff --git a/src/module.c b/src/ccaterpillar/module.c
similarity index 73%
rename from src/module.c
rename to src/ccaterpillar/module.c
index 5502121..69847d8 100644
--- a/src/module.c
+++ b/src/ccaterpillar/module.c
@@ -3,8 +3,7 @@
#define _CPMODULE
#endif
-#include "caterpillar/module.h" /* invalid default object and types through caterpillar.h*/
-#include "caterpillar/parsing.h" /* Pack and unpack functions */
+#include "caterpillar/caterpillar.h"
#include "caterpillarapi.c"
@@ -34,67 +33,6 @@
return NULL; \
}
-/* immortal objects */
-static PyObject*
-cp_invaliddefault_new(PyTypeObject* type, PyObject* args, PyObject* kw)
-{
- if (PyTuple_GET_SIZE(args) || PyDict_GET_SIZE(kw)) {
- PyErr_SetString(PyExc_TypeError,
- "InvalidDefaultType does not accept arguments");
- return NULL;
- }
-
- Py_INCREF(CpInvalidDefault);
- return CpInvalidDefault;
-}
-
-static PyObject*
-cp_invaliddefault_repr(PyObject* self)
-{
- return PyUnicode_FromString("");
-}
-
-PyTypeObject CpInvalidDefault_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(InvalidDefaultType),
- .tp_repr = (reprfunc)cp_invaliddefault_repr,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_new = (newfunc)cp_invaliddefault_new,
-};
-
-PyObject _CpInvalidDefault_Object = { _PyObject_EXTRA_INIT{
- _Py_IMMORTAL_REFCNT /* ob_refcnt */ },
- &CpInvalidDefault_Type /* ob_type */ };
-
-static PyObject*
-cp_defaultoption_new(PyTypeObject* type, PyObject* args, PyObject* kw)
-{
- if (PyTuple_GET_SIZE(args) || PyDict_GET_SIZE(kw)) {
- PyErr_SetString(PyExc_TypeError,
- "InvalidDefaultType does not accept arguments");
- return NULL;
- }
-
- Py_INCREF(CpDefaultOption);
- return CpDefaultOption;
-}
-
-static PyObject*
-cp_defaultoption_repr(PyObject* self)
-{
- return PyUnicode_FromString("");
-}
-
-PyTypeObject CpDefaultOption_Type = {
- PyVarObject_HEAD_INIT(NULL, 0) _Cp_Name(DefaultOptionType),
- .tp_repr = (reprfunc)cp_defaultoption_repr,
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_new = (newfunc)cp_defaultoption_new,
-};
-
-PyObject _CpDefaultOption_Object = { _PyObject_EXTRA_INIT{
- _Py_IMMORTAL_REFCNT /* ob_refcnt */ },
- &CpDefaultOption_Type /* ob_type */ };
-
// ------------------------------------------------------------------------------
// module
static PyObject*
@@ -137,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;
@@ -174,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) {
@@ -194,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);
}
@@ -236,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) {
@@ -275,8 +255,9 @@ cp_module_clear(PyObject* m)
Py_CLEAR(state->Any_Type);
Py_CLEAR(state->List_Type);
Py_CLEAR(state->Union_Type);
+ Py_CLEAR(state->Optional_Type);
- // sttings
+ // strings
Py_CLEAR(state->str_path_delim);
Py_CLEAR(state->str___pack__);
Py_CLEAR(state->str___unpack__);
@@ -304,11 +285,17 @@ cp_module_clear(PyObject* m)
Py_CLEAR(state->str___weakref__);
Py_CLEAR(state->str___qualname__);
Py_CLEAR(state->str_strict);
+ Py_CLEAR(state->str__value2member_map_);
+ Py_CLEAR(state->str__member_map_);
+ Py_CLEAR(state->str_utf8);
+ Py_CLEAR(state->str_cstring_default_pad);
+ Py_CLEAR(state->str___bits__);
Py_CLEAR(state->cp_regex__unnamed);
Py_CLEAR(state->cp_bytes__false);
Py_CLEAR(state->cp_bytes__true);
+ Py_CLEAR(state->cp_typehandler_map);
}
return 0;
}
@@ -374,36 +361,61 @@ PyInit__C(void)
CpModule_SetupType(&CpBinaryExpr_Type);
CpModule_SetupType(&CpUnaryExpr_Type);
CpModule_SetupType(&CpContextPath_Type);
- CpModule_SetupType(&CpField_Type);
-
- CpFieldAtom_Type.tp_base = &CpAtom_Type;
- CpFieldCAtom_Type.tp_base = &CpCAtom_Type;
- CpModule_SetupType(&CpFieldAtom_Type);
- CpModule_SetupType(&CpFieldCAtom_Type);
CpModule_SetupType(&CpLayer_Type);
+
+ CpSeqLayer_Type.tp_base = &CpLayer_Type;
+ CpObjLayer_Type.tp_base = &CpLayer_Type;
+ CpModule_SetupType(&CpSeqLayer_Type);
+ CpModule_SetupType(&CpObjLayer_Type);
CpModule_SetupType(&CpState_Type);
CpModule_SetupType(&CpStructFieldInfo_Type);
- CpStruct_Type.tp_base = &CpFieldAtom_Type;
+ CpModule_SetupType(&CpLengthInfo_Type);
+
+ // builtins setup
+ CpPrimitiveAtom_Type.tp_base = &CpAtom_Type;
+ CpBuiltinAtom_Type.tp_base = &CpCAtom_Type;
+ CpRepeatedAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpConditionAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpSwitchAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpOffsetAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpStruct_Type.tp_base = &CpBuiltinAtom_Type;
+ CpBoolAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpFloatAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpIntAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpCharAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpPaddingAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpStringAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpConstAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpBytesAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpPStringAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpEnumAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpVarIntAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpComputedAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpLazyAtom_Type.tp_base = &CpBuiltinAtom_Type;
+ CpCStringAtom_Type.tp_base = &CpBuiltinAtom_Type;
+
+ CpModule_SetupType(&CpBuiltinAtom_Type);
+ CpModule_SetupType(&CpPrimitiveAtom_Type);
+ CpModule_SetupType(&CpRepeatedAtom_Type);
+ CpModule_SetupType(&CpConditionAtom_Type);
+ CpModule_SetupType(&CpSwitchAtom_Type);
+ CpModule_SetupType(&CpOffsetAtom_Type);
CpModule_SetupType(&CpStruct_Type);
-
- CpIntAtom_Type.tp_base = &CpFieldCAtom_Type;
CpModule_SetupType(&CpIntAtom_Type);
-
- CpFloatAtom_Type.tp_base = &CpFieldCAtom_Type;
CpModule_SetupType(&CpFloatAtom_Type);
-
- CpBoolAtom_Type.tp_base = &CpFieldCAtom_Type;
CpModule_SetupType(&CpBoolAtom_Type);
-
- CpCharAtom_Type.tp_base = &CpFieldCAtom_Type;
CpModule_SetupType(&CpCharAtom_Type);
-
- CpPaddingAtom_Type.tp_base = &CpFieldCAtom_Type;
CpModule_SetupType(&CpPaddingAtom_Type);
-
- CpStringAtom_Type.tp_base = &CpFieldCAtom_Type;
CpModule_SetupType(&CpStringAtom_Type);
+ CpModule_SetupType(&CpConstAtom_Type);
+ CpModule_SetupType(&CpBytesAtom_Type);
+ CpModule_SetupType(&CpPStringAtom_Type);
+ CpModule_SetupType(&CpEnumAtom_Type);
+ CpModule_SetupType(&CpVarIntAtom_Type);
+ CpModule_SetupType(&CpComputedAtom_Type);
+ CpModule_SetupType(&CpLazyAtom_Type);
+ CpModule_SetupType(&CpCStringAtom_Type);
// module setup
m = PyModule_Create(&CpModule);
@@ -411,33 +423,48 @@ PyInit__C(void)
return NULL;
}
- CpModule_AddObject("atom", &CpAtom_Type);
- CpModule_AddObject("catom", &CpCAtom_Type);
- CpModule_AddObject("Option", &CpOption_Type);
- CpModule_AddObject("Arch", &CpArch_Type);
- CpModule_AddObject("Endian", &CpEndian_Type);
- CpModule_AddObject("Context", &CpContext_Type);
- CpModule_AddObject("BinaryExpr", &CpBinaryExpr_Type);
- CpModule_AddObject("UnaryExpr", &CpUnaryExpr_Type);
- CpModule_AddObject("ContextPath", &CpContextPath_Type);
+ CpModule_AddObject(CpAtom_NAME, &CpAtom_Type);
+ CpModule_AddObject(CpCAtom_NAME, &CpCAtom_Type);
+ CpModule_AddObject(CpOption_NAME, &CpOption_Type);
+ CpModule_AddObject(CpArch_NAME, &CpArch_Type);
+ CpModule_AddObject(CpEndian_NAME, &CpEndian_Type);
+ CpModule_AddObject(CpContext_NAME, &CpContext_Type);
+ CpModule_AddObject(CpBinaryExpr_NAME, &CpBinaryExpr_Type);
+ CpModule_AddObject(CpUnaryExpr_NAME, &CpUnaryExpr_Type);
+ CpModule_AddObject(CpContextPath_NAME, &CpContextPath_Type);
CpModule_AddObject("InvalidDefaultType", &CpInvalidDefault_Type);
CpModule_AddObject("InvalidDefault", CpInvalidDefault);
CpModule_AddObject("DefaultOptionType", &CpDefaultOption_Type);
CpModule_AddObject("DefaultOption", CpDefaultOption);
- CpModule_AddObject("Field", &CpField_Type);
- CpModule_AddObject("fieldatom", &CpFieldAtom_Type);
- CpModule_AddObject("fieldcatom", &CpFieldCAtom_Type);
- CpModule_AddObject("layer", &CpLayer_Type);
- CpModule_AddObject("State", &CpState_Type);
- CpModule_AddObject("fieldinfo", &CpStructFieldInfo_Type);
- CpModule_AddObject("Struct", &CpStruct_Type);
-
- CpModule_AddObject("intatom", &CpIntAtom_Type);
- CpModule_AddObject("floatatom", &CpFloatAtom_Type);
- CpModule_AddObject("boolatom", &CpBoolAtom_Type);
- CpModule_AddObject("charatom", &CpCharAtom_Type);
- CpModule_AddObject("paddingatom", &CpPaddingAtom_Type);
- CpModule_AddObject("string", &CpStringAtom_Type);
+ CpModule_AddObject(CpLayer_NAME, &CpLayer_Type);
+ CpModule_AddObject(CpSeqLayer_NAME, &CpSeqLayer_Type);
+ CpModule_AddObject(CpObjLayer_NAME, &CpObjLayer_Type);
+ CpModule_AddObject(CpState_NAME, &CpState_Type);
+ CpModule_AddObject(CpStructFieldInfo_NAME, &CpStructFieldInfo_Type);
+ CpModule_AddObject(CpStruct_NAME, &CpStruct_Type);
+ CpModule_AddObject(CpLengthInfo_NAME, &CpLengthInfo_Type);
+
+ CpModule_AddObject(CpPrimitiveAtom_NAME, &CpPrimitiveAtom_Type);
+ CpModule_AddObject(CpBuiltinAtom_NAME, &CpBuiltinAtom_Type);
+ CpModule_AddObject(CpRepeatedAtom_NAME, &CpRepeatedAtom_Type);
+ CpModule_AddObject(CpConditionAtom_NAME, &CpConditionAtom_Type);
+ CpModule_AddObject(CpSwitchAtom_NAME, &CpSwitchAtom_Type);
+ CpModule_AddObject(CpOffsetAtom_NAME, &CpOffsetAtom_Type);
+
+ CpModule_AddObject(CpIntAtom_NAME, &CpIntAtom_Type);
+ CpModule_AddObject(CpFloatAtom_NAME, &CpFloatAtom_Type);
+ CpModule_AddObject(CpBoolAtom_NAME, &CpBoolAtom_Type);
+ CpModule_AddObject(CpCharAtom_NAME, &CpCharAtom_Type);
+ CpModule_AddObject(CpPaddingAtom_NAME, &CpPaddingAtom_Type);
+ CpModule_AddObject(CpStringAtom_NAME, &CpStringAtom_Type);
+ CpModule_AddObject(CpConstAtom_NAME, &CpConstAtom_Type);
+ CpModule_AddObject(CpBytesAtom_NAME, &CpBytesAtom_Type);
+ CpModule_AddObject(CpPStringAtom_NAME, &CpPStringAtom_Type);
+ CpModule_AddObject(CpEnumAtom_NAME, &CpEnumAtom_Type);
+ CpModule_AddObject(CpVarIntAtom_NAME, &CpVarIntAtom_Type);
+ CpModule_AddObject(CpComputedAtom_NAME, &CpComputedAtom_Type);
+ CpModule_AddObject(CpLazyAtom_NAME, &CpLazyAtom_Type);
+ CpModule_AddObject(CpCStringAtom_NAME, &CpCStringAtom_Type);
/* setup custom intatoms */
#define CpModule_DefAtom(name, ...) \
@@ -463,6 +490,8 @@ PyInit__C(void)
CpModule_DefIntAtom("u32", 32, false);
CpModule_DefIntAtom("i64", 64, true);
CpModule_DefIntAtom("u64", 64, false);
+ CpModule_DefIntAtom("i128", 128, true);
+ CpModule_DefIntAtom("u128", 128, false);
#define CpModule_DefFloatAtom(name, bits) \
CpModule_DefAtom(name, CpObject_Create(&CpFloatAtom_Type, "I", bits));
@@ -477,6 +506,8 @@ PyInit__C(void)
CpModule_AddObject("boolean", CpObject_CreateNoArgs(&CpBoolAtom_Type));
CpModule_AddObject("char", CpObject_CreateNoArgs(&CpCharAtom_Type));
CpModule_AddObject("padding", CpObject_CreateNoArgs(&CpPaddingAtom_Type));
+ CpModule_AddObject("varint", CpVarIntAtom_New(true, false));
+ CpModule_AddObject("lsbvarint", CpVarIntAtom_New(true, true));
/* setup state */
_modulestate* state = get_module_state(m);
@@ -518,7 +549,10 @@ PyInit__C(void)
CpModuleState_AddObject(
cp_option__global_struct_options, "STRUCT_OPTIONS", PySet_New(NULL));
CpModuleState_AddObject(
- cp_option__global_struct_options, "FIELD_OPTIONS", PySet_New(NULL));
+ 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,
@@ -567,6 +601,10 @@ PyInit__C(void)
CACHED_STRING(str___qualname__, "__qualname__");
CACHED_STRING(str_path_delim, ".");
CACHED_STRING(str_strict, "strict");
+ CACHED_STRING(str__member_map_, "_member_map_");
+ CACHED_STRING(str__value2member_map_, "_value2member_map_");
+ CACHED_STRING(str_utf8, "utf-8");
+ CACHED_STRING(str___bits__, "__bits__");
#undef CACHED_STRING
@@ -580,6 +618,12 @@ PyInit__C(void)
#undef CACHED_BYTES
+ if ((state->str_cstring_default_pad = PyUnicode_Decode(
+ PyBytes_AS_STRING(state->cp_bytes__false), 1, "utf-8", "replace")) ==
+ NULL) {
+ return NULL;
+ }
+
// setup typing constants
PyObject* typing = PyImport_ImportModule("typing");
if (!typing) {
@@ -590,6 +634,7 @@ PyInit__C(void)
CpModuleState_Set(Any_Type, PyObject_GetAttrString(typing, "Any"));
CpModuleState_Set(List_Type, PyObject_GetAttrString(typing, "List"));
CpModuleState_Set(Union_Type, PyObject_GetAttrString(typing, "Union"));
+ CpModuleState_Set(Optional_Type, PyObject_GetAttrString(typing, "Optional"));
Py_XDECREF(typing);
// regex setup
diff --git a/src/option.c b/src/ccaterpillar/option.c
similarity index 97%
rename from src/option.c
rename to src/ccaterpillar/option.c
index f39665c..1c8e1c6 100644
--- a/src/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);
+ "