Repository: fox-it/dissect.cstruct

Issue:
Having an enum or flag with a value `default` breaks
```python
cdef = """
enum test {
    default = 0,
};

struct test2 {
    test a;
};
"""
cs.load(cdef)
```

Discussion:

Solution patch:
diff --git a/dissect/cstruct/types/base.py b/dissect/cstruct/types/base.py
index 53c57b83..4ef40b97 100644
--- a/dissect/cstruct/types/base.py
+++ b/dissect/cstruct/types/base.py
@@ -59,7 +59,7 @@ def __len__(cls) -> int:
 
         return cls.size
 
-    def default(cls) -> BaseType:
+    def __default__(cls) -> BaseType:
         """Return the default value of this type."""
         return cls()
 
@@ -179,7 +179,7 @@ def _write_0(cls, stream: BinaryIO, array: list[BaseType]) -> int:
             stream: The stream to read from.
             array: The array to write.
         """
-        return cls._write_array(stream, array + [cls.default()])
+        return cls._write_array(stream, array + [cls.__default__()])
 
 
 class _overload:
@@ -225,8 +225,10 @@ class ArrayMetaType(MetaType):
     num_entries: int | Expression | None
     null_terminated: bool
 
-    def default(cls) -> BaseType:
-        return type.__call__(cls, [cls.type.default()] * (cls.num_entries if isinstance(cls.num_entries, int) else 0))
+    def __default__(cls) -> BaseType:
+        return type.__call__(
+            cls, [cls.type.__default__()] * (cls.num_entries if isinstance(cls.num_entries, int) else 0)
+        )
 
     def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Array:
         if cls.null_terminated:
diff --git a/dissect/cstruct/types/char.py b/dissect/cstruct/types/char.py
index 268d690c..117deb64 100644
--- a/dissect/cstruct/types/char.py
+++ b/dissect/cstruct/types/char.py
@@ -25,7 +25,7 @@ def _write(cls, stream: BinaryIO, data: bytes) -> int:
         return stream.write(data)
 
     @classmethod
-    def default(cls) -> CharArray:
+    def __default__(cls) -> CharArray:
         return type.__call__(cls, b"\x00" * (0 if cls.dynamic or cls.null_terminated else cls.num_entries))
 
 
@@ -75,5 +75,5 @@ def _write(cls, stream: BinaryIO, data: bytes | int | str) -> int:
         return stream.write(data)
 
     @classmethod
-    def default(cls) -> Char:
+    def __default__(cls) -> Char:
         return type.__call__(cls, b"\x00")
diff --git a/dissect/cstruct/types/enum.py b/dissect/cstruct/types/enum.py
index eea2b7a2..a2825847 100644
--- a/dissect/cstruct/types/enum.py
+++ b/dissect/cstruct/types/enum.py
@@ -27,7 +27,7 @@ def __call__(
     ) -> EnumMetaType:
         if name is None:
             if value is None:
-                value = cls.type.default()
+                value = cls.type.__default__()
 
             if not isinstance(value, int):
                 # value is a parsable value
@@ -82,7 +82,7 @@ def _write_array(cls, stream: BinaryIO, array: list[Enum]) -> int:
 
     def _write_0(cls, stream: BinaryIO, array: list[BaseType]) -> int:
         data = [entry.value if isinstance(entry, Enum) else entry for entry in array]
-        return cls._write_array(stream, data + [cls.type.default()])
+        return cls._write_array(stream, data + [cls.type.__default__()])
 
 
 def _fix_alias_members(cls: type[Enum]) -> None:
diff --git a/dissect/cstruct/types/pointer.py b/dissect/cstruct/types/pointer.py
index e398ff69..bb852863 100644
--- a/dissect/cstruct/types/pointer.py
+++ b/dissect/cstruct/types/pointer.py
@@ -66,8 +66,8 @@ def __or__(self, other: int) -> Pointer:
         return type.__call__(self.__class__, int.__or__(self, other), self._stream, self._context)
 
     @classmethod
-    def default(cls) -> Pointer:
-        return cls.__new__(cls, cls.cs.pointer.default(), None, None)
+    def __default__(cls) -> Pointer:
+        return cls.__new__(cls, cls.cs.pointer.__default__(), None, None)
 
     @classmethod
     def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Pointer:
diff --git a/dissect/cstruct/types/structure.py b/dissect/cstruct/types/structure.py
index 6dbc5cfd..5aae944b 100644
--- a/dissect/cstruct/types/structure.py
+++ b/dissect/cstruct/types/structure.py
@@ -325,7 +325,7 @@ def _write(cls, stream: BinaryIO, data: Structure) -> int:
 
             value = getattr(data, field.name, None)
             if value is None:
-                value = field_type.default()
+                value = field_type.__default__()
 
             if field.bits:
                 if isinstance(field_type, EnumMetaType):
@@ -560,7 +560,7 @@ def _rebuild(self, attr: str) -> None:
             buf.seek(field.offset)
 
         if (value := getattr(self, attr)) is None:
-            value = field.type.default()
+            value = field.type.__default__()
 
         field.type._write(buf, value)
 
@@ -774,7 +774,7 @@ def _generate_structure__init__(fields: list[Field]) -> FunctionType:
     template: FunctionType = _make_structure__init__(len(field_names))
     return type(template)(
         template.__code__.replace(
-            co_consts=(None, *[field.type.default() for field in fields]),
+            co_consts=(None, *[field.type.__default__() for field in fields]),
             co_names=(*field_names,),
             co_varnames=("self", *field_names),
         ),
@@ -794,7 +794,7 @@ def _generate_union__init__(fields: list[Field]) -> FunctionType:
     template: FunctionType = _make_union__init__(len(field_names))
     return type(template)(
         template.__code__.replace(
-            co_consts=(None, *sum([(field.name, field.type.default()) for field in fields], ())),
+            co_consts=(None, *sum([(field.name, field.type.__default__()) for field in fields], ())),
             co_varnames=("self", *field_names),
         ),
         template.__globals__,
diff --git a/dissect/cstruct/types/wchar.py b/dissect/cstruct/types/wchar.py
index ecb98dcc..1a95eb0a 100644
--- a/dissect/cstruct/types/wchar.py
+++ b/dissect/cstruct/types/wchar.py
@@ -20,7 +20,7 @@ def _write(cls, stream: BinaryIO, data: str) -> int:
         return stream.write(data.encode(Wchar.__encoding_map__[cls.cs.endian]))
 
     @classmethod
-    def default(cls) -> WcharArray:
+    def __default__(cls) -> WcharArray:
         return type.__call__(cls, "\x00" * (0 if cls.dynamic or cls.null_terminated else cls.num_entries))
 
 
@@ -75,5 +75,5 @@ def _write(cls, stream: BinaryIO, data: str) -> int:
         return stream.write(data.encode(cls.__encoding_map__[cls.cs.endian]))
 
     @classmethod
-    def default(cls) -> Wchar:
+    def __default__(cls) -> Wchar:
         return type.__call__(cls, "\x00")


Test patch:
diff --git a/tests/test_types_base.py b/tests/test_types_base.py
index 8ef87088..93d42506 100644
--- a/tests/test_types_base.py
+++ b/tests/test_types_base.py
@@ -98,7 +98,7 @@ def _read(cls, stream: BinaryIO, context: dict | None = None) -> CustomType:
 
         class ArrayType(BaseType, metaclass=ArrayMetaType):
             @classmethod
-            def default(cls) -> CustomType:
+            def __default__(cls) -> CustomType:
                 return cls.type()
 
             @classmethod
diff --git a/tests/test_types_char.py b/tests/test_types_char.py
index 71b07759..ea2aec9e 100644
--- a/tests/test_types_char.py
+++ b/tests/test_types_char.py
@@ -46,6 +46,6 @@ def test_char_eof(cs: cstruct) -> None:
 
 
 def test_char_default(cs: cstruct) -> None:
-    assert cs.char.default() == b"\x00"
-    assert cs.char[4].default() == b"\x00\x00\x00\x00"
-    assert cs.char[None].default() == b""
+    assert cs.char.__default__() == b"\x00"
+    assert cs.char[4].__default__() == b"\x00\x00\x00\x00"
+    assert cs.char[None].__default__() == b""
diff --git a/tests/test_types_custom.py b/tests/test_types_custom.py
index 4cf624cb..e6ee5429 100644
--- a/tests/test_types_custom.py
+++ b/tests/test_types_custom.py
@@ -13,8 +13,8 @@ class EtwPointer(BaseType):
     size: int | None
 
     @classmethod
-    def default(cls) -> int:
-        return cls.cs.uint64.default()
+    def __default__(cls) -> int:
+        return cls.cs.uint64.__default__()
 
     @classmethod
     def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> BaseType:
@@ -82,10 +82,10 @@ def test_custom_default(cs: cstruct) -> None:
     cs.add_custom_type("EtwPointer", EtwPointer)
 
     cs.EtwPointer.as_64bit()
-    assert cs.EtwPointer.default() == 0
+    assert cs.EtwPointer.__default__() == 0
 
     cs.EtwPointer.as_32bit()
-    assert cs.EtwPointer.default() == 0
+    assert cs.EtwPointer.__default__() == 0
 
-    assert cs.EtwPointer[1].default() == [0]
-    assert cs.EtwPointer[None].default() == []
+    assert cs.EtwPointer[1].__default__() == [0]
+    assert cs.EtwPointer[None].__default__() == []
diff --git a/tests/test_types_enum.py b/tests/test_types_enum.py
index 9045e2b5..f04a26f4 100644
--- a/tests/test_types_enum.py
+++ b/tests/test_types_enum.py
@@ -418,6 +418,21 @@ def test_enum_default(cs: cstruct) -> None:
     """
     cs.load(cdef)
 
-    assert cs.test.default() == cs.test.A == cs.test(0)
-    assert cs.test[1].default() == [cs.test.A]
-    assert cs.test[None].default() == []
+    assert cs.test.__default__() == cs.test.A == cs.test(0)
+    assert cs.test[1].__default__() == [cs.test.A]
+    assert cs.test[None].__default__() == []
+
+
+def test_enum_default_default(cs: cstruct) -> None:
+    cdef = """
+    enum test {
+        default = 0,
+    };
+
+    struct test2 {
+        test a;
+    };
+    """
+    cs.load(cdef)
+
+    assert cs.test.__default__() == cs.test.default == cs.test(0)
diff --git a/tests/test_types_flag.py b/tests/test_types_flag.py
index 935aac97..6e8127ce 100644
--- a/tests/test_types_flag.py
+++ b/tests/test_types_flag.py
@@ -266,6 +266,6 @@ def test_flag_default(cs: cstruct) -> None:
     """
     cs.load(cdef)
 
-    assert cs.test.default() == cs.test(0)
-    assert cs.test[1].default() == [cs.test(0)]
-    assert cs.test[None].default() == []
+    assert cs.test.__default__() == cs.test(0)
+    assert cs.test[1].__default__() == [cs.test(0)]
+    assert cs.test[None].__default__() == []
diff --git a/tests/test_types_int.py b/tests/test_types_int.py
index 329c9d71..524fa806 100644
--- a/tests/test_types_int.py
+++ b/tests/test_types_int.py
@@ -403,10 +403,10 @@ def test_int_struct_unsigned_be(cs: cstruct, compiled: bool) -> None:
 
 
 def test_int_default(cs: cstruct) -> None:
-    assert cs.int24.default() == 0
-    assert cs.uint24.default() == 0
-    assert cs.int128.default() == 0
-    assert cs.uint128.default() == 0
+    assert cs.int24.__default__() == 0
+    assert cs.uint24.__default__() == 0
+    assert cs.int128.__default__() == 0
+    assert cs.uint128.__default__() == 0
 
-    assert cs.int24[1].default() == [0]
-    assert cs.int24[None].default() == []
+    assert cs.int24[1].__default__() == [0]
+    assert cs.int24[None].__default__() == []
diff --git a/tests/test_types_leb128.py b/tests/test_types_leb128.py
index 30e6dae9..1c425be1 100644
--- a/tests/test_types_leb128.py
+++ b/tests/test_types_leb128.py
@@ -192,8 +192,8 @@ def test_leb128_unsigned_write_amount_written(cs: cstruct) -> None:
 
 
 def test_leb128_default(cs: cstruct) -> None:
-    assert cs.uleb128.default() == 0
-    assert cs.ileb128.default() == 0
+    assert cs.uleb128.__default__() == 0
+    assert cs.ileb128.__default__() == 0
 
-    assert cs.uleb128[1].default() == [0]
-    assert cs.uleb128[None].default() == []
+    assert cs.uleb128[1].__default__() == [0]
+    assert cs.uleb128[None].__default__() == []
diff --git a/tests/test_types_packed.py b/tests/test_types_packed.py
index 5e4d2f21..2126d5e5 100644
--- a/tests/test_types_packed.py
+++ b/tests/test_types_packed.py
@@ -172,17 +172,17 @@ def test_packed_float_struct_be(cs: cstruct, compiled: bool) -> None:
 
 
 def test_packed_default(cs: cstruct) -> None:
-    assert cs.int8.default() == 0
-    assert cs.uint8.default() == 0
-    assert cs.int16.default() == 0
-    assert cs.uint16.default() == 0
-    assert cs.int32.default() == 0
-    assert cs.uint32.default() == 0
-    assert cs.int64.default() == 0
-    assert cs.uint64.default() == 0
-    assert cs.float16.default() == 0.0
-    assert cs.float.default() == 0.0
-    assert cs.double.default() == 0.0
-
-    assert cs.int8[2].default() == [0, 0]
-    assert cs.int8[None].default() == []
+    assert cs.int8.__default__() == 0
+    assert cs.uint8.__default__() == 0
+    assert cs.int16.__default__() == 0
+    assert cs.uint16.__default__() == 0
+    assert cs.int32.__default__() == 0
+    assert cs.uint32.__default__() == 0
+    assert cs.int64.__default__() == 0
+    assert cs.uint64.__default__() == 0
+    assert cs.float16.__default__() == 0.0
+    assert cs.float.__default__() == 0.0
+    assert cs.double.__default__() == 0.0
+
+    assert cs.int8[2].__default__() == [0, 0]
+    assert cs.int8[None].__default__() == []
diff --git a/tests/test_types_pointer.py b/tests/test_types_pointer.py
index 65be5dd6..269cda40 100644
--- a/tests/test_types_pointer.py
+++ b/tests/test_types_pointer.py
@@ -242,10 +242,10 @@ def test_pointer_default(cs: cstruct) -> None:
     cs.pointer = cs.uint8
 
     ptr = cs._make_pointer(cs.uint8)
-    assert isinstance(ptr.default(), Pointer)
-    assert ptr.default() == 0
-    assert ptr[1].default() == [0]
-    assert ptr[None].default() == []
+    assert isinstance(ptr.__default__(), Pointer)
+    assert ptr.__default__() == 0
+    assert ptr[1].__default__() == [0]
+    assert ptr[None].__default__() == []
 
     with pytest.raises(NullPointerDereference):
-        ptr.default().dereference()
+        ptr.__default__().dereference()
diff --git a/tests/test_types_structure.py b/tests/test_types_structure.py
index d2d65d06..8d251eba 100644
--- a/tests/test_types_structure.py
+++ b/tests/test_types_structure.py
@@ -599,9 +599,9 @@ def test_structure_default(cs: cstruct, compiled: bool) -> None:
 
     assert verify_compiled(cs.test, compiled)
 
-    assert cs.test() == cs.test.default()
+    assert cs.test() == cs.test.__default__()
 
-    obj = cs.test.default()
+    obj = cs.test.__default__()
     assert obj.t_int == 0
     assert obj.t_int_array == [0, 0]
     assert obj.t_bytesint == 0
@@ -625,11 +625,11 @@ def test_structure_default(cs: cstruct, compiled: bool) -> None:
     for name in obj.fields.keys():
         assert isinstance(getattr(obj, name), BaseType)
 
-    assert cs.test_nested() == cs.test_nested.default()
+    assert cs.test_nested() == cs.test_nested.__default__()
 
-    obj = cs.test_nested.default()
-    assert obj.t_struct == cs.test.default()
-    assert obj.t_struct_array == [cs.test.default(), cs.test.default()]
+    obj = cs.test_nested.__default__()
+    assert obj.t_struct == cs.test.__default__()
+    assert obj.t_struct_array == [cs.test.__default__(), cs.test.__default__()]
 
     assert obj.dumps() == b"\x00" * 171
 
@@ -678,7 +678,7 @@ def test_structure_default_dynamic(cs: cstruct, compiled: bool) -> None:
 
     assert verify_compiled(cs.test, compiled)
 
-    assert cs.test() == cs.test.default()
+    assert cs.test() == cs.test.__default__()
 
     obj = cs.test()
     assert obj.t_int_array_n == obj.t_int_array_d == []
@@ -694,9 +694,9 @@ def test_structure_default_dynamic(cs: cstruct, compiled: bool) -> None:
     for name in obj.fields.keys():
         assert isinstance(getattr(obj, name), BaseType)
 
-    assert cs.test_nested() == cs.test_nested.default()
+    assert cs.test_nested() == cs.test_nested.__default__()
 
-    obj = cs.test_nested.default()
+    obj = cs.test_nested.__default__()
     assert obj.t_struct_n == obj.t_struct_array_d == []
 
     assert obj.dumps() == b"\x00" * 21
diff --git a/tests/test_types_union.py b/tests/test_types_union.py
index 0caf59a9..c36eb52c 100644
--- a/tests/test_types_union.py
+++ b/tests/test_types_union.py
@@ -414,7 +414,7 @@ def test_union_default(cs: cstruct) -> None:
     """
     cs.load(cdef)
 
-    assert cs.test() == cs.test.default()
+    assert cs.test() == cs.test.__default__()
 
     obj = cs.test()
     assert obj.a == 0
@@ -425,11 +425,11 @@ def test_union_default(cs: cstruct) -> None:
     for name in obj.fields.keys():
         assert isinstance(getattr(obj, name), BaseType)
 
-    assert cs.test_nested() == cs.test_nested.default()
+    assert cs.test_nested() == cs.test_nested.__default__()
 
-    obj = cs.test_nested.default()
-    assert obj.t_union == cs.test.default()
-    assert obj.t_union_array == [cs.test.default(), cs.test.default()]
+    obj = cs.test_nested.__default__()
+    assert obj.t_union == cs.test.__default__()
+    assert obj.t_union_array == [cs.test.__default__(), cs.test.__default__()]
 
     assert obj.dumps() == b"\x00" * 24
 
diff --git a/tests/test_types_void.py b/tests/test_types_void.py
index 6e825857..2b554569 100644
--- a/tests/test_types_void.py
+++ b/tests/test_types_void.py
@@ -33,12 +33,12 @@ def test_void_array_write(cs: cstruct) -> None:
 
 
 def test_void_default(cs: cstruct) -> None:
-    assert cs.void() == cs.void.default()
+    assert cs.void() == cs.void.__default__()
     assert not cs.void()
-    assert not cs.void.default()
+    assert not cs.void.__default__()
 
-    assert cs.void[1].default() == [cs.void()]
-    assert cs.void[None].default() == []
+    assert cs.void[1].__default__() == [cs.void()]
+    assert cs.void[None].__default__() == []
 
 
 def test_void_struct(cs: cstruct, compiled: bool) -> None:
diff --git a/tests/test_types_wchar.py b/tests/test_types_wchar.py
index 65a63d1e..4c8b9e09 100644
--- a/tests/test_types_wchar.py
+++ b/tests/test_types_wchar.py
@@ -78,6 +78,6 @@ def test_wchar_eof(cs: cstruct) -> None:
 
 
 def test_wchar_default(cs: cstruct) -> None:
-    assert cs.wchar.default() == "\x00"
-    assert cs.wchar[4].default() == "\x00\x00\x00\x00"
-    assert cs.wchar[None].default() == ""
+    assert cs.wchar.__default__() == "\x00"
+    assert cs.wchar[4].__default__() == "\x00\x00\x00\x00"
+    assert cs.wchar[None].__default__() == ""