Skip to content

Commit 772d473

Browse files
authored
Add support for array and pointer typedefs (#70)
1 parent ff65d41 commit 772d473

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

dissect/cstruct/parser.py

+25-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@
22

33
import ast
44
import re
5-
from typing import TYPE_CHECKING, Dict, List
5+
from typing import TYPE_CHECKING, Dict, List, Optional
66

77
from dissect.cstruct.compiler import Compiler
88
from dissect.cstruct.exceptions import ParserError
99
from dissect.cstruct.expression import Expression
10-
from dissect.cstruct.types import Array, Enum, Field, Flag, Pointer, Structure, Union
10+
from dissect.cstruct.types import (
11+
Array,
12+
BaseType,
13+
Enum,
14+
Field,
15+
Flag,
16+
Pointer,
17+
Structure,
18+
Union,
19+
)
1120

1221
if TYPE_CHECKING:
1322
from dissect.cstruct import cstruct
@@ -158,6 +167,9 @@ def _typedef(self, tokens: TokenConsumer) -> None:
158167

159168
names = self._names(tokens)
160169
for name in names:
170+
type_, name, bits = self._parse_field_type(type_, name)
171+
if bits is not None:
172+
raise ParserError(f"line {self._lineno(tokens.previous)}: typedefs cannot have bitfields")
161173
self.cstruct.addtype(name, type_)
162174

163175
def _struct(self, tokens: TokenConsumer, register: bool = False) -> None:
@@ -236,9 +248,15 @@ def _parse_field(self, tokens: TokenConsumer) -> Field:
236248
raise ParserError(f"line {self._lineno(tokens.next)}: expected name")
237249
nametok = tokens.consume()
238250

251+
type_, name, bits = self._parse_field_type(type_, nametok.value)
252+
253+
tokens.eol()
254+
return Field(name.strip(), type_, bits)
255+
256+
def _parse_field_type(self, type_: BaseType, name: str) -> tuple[BaseType, str, Optional[int]]:
239257
pattern = self.TOK.patterns[self.TOK.NAME]
240258
# Dirty trick because the regex expects a ; but we don't want it to be part of the value
241-
d = pattern.match(nametok.value + ";").groupdict()
259+
d = pattern.match(name + ";").groupdict()
242260

243261
name = d["name"]
244262
count_expression = d["count"]
@@ -269,8 +287,7 @@ def _parse_field(self, tokens: TokenConsumer) -> Field:
269287

270288
type_ = Array(self.cstruct, type_, count)
271289

272-
tokens.eol()
273-
return Field(name, type_, int(d["bits"]) if d["bits"] else None)
290+
return type_, name, int(d["bits"]) if d["bits"] else None
274291

275292
def _names(self, tokens: TokenConsumer) -> List[str]:
276293
names = []
@@ -564,6 +581,7 @@ class TokenConsumer:
564581
def __init__(self, tokens: List[Token]):
565582
self.tokens = tokens
566583
self.flags = []
584+
self.previous = None
567585

568586
def __contains__(self, token) -> bool:
569587
return token in self.tokens
@@ -582,7 +600,8 @@ def next(self) -> Token:
582600
return None
583601

584602
def consume(self) -> Token:
585-
return self.tokens.pop(0)
603+
self.previous = self.tokens.pop(0)
604+
return self.previous
586605

587606
def reset_flags(self) -> None:
588607
self.flags = []

tests/test_basic.py

+29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from dissect import cstruct
55

66
from dissect.cstruct.exceptions import ArraySizeError, ParserError, ResolveError
7+
from dissect.cstruct.types import Array, Pointer
78

89
from .utils import verify_compiled
910

@@ -571,3 +572,31 @@ def test_reserved_keyword(compiled: bool):
571572
assert verify_compiled(cs.resolve(name), compiled)
572573

573574
assert cs.resolve(name)(b"\x01").a == 1
575+
576+
577+
def test_typedef_types():
578+
cdef = """
579+
typedef char uuid_t[16];
580+
typedef uint32 *ptr;
581+
582+
struct test {
583+
uuid_t uuid;
584+
ptr ptr;
585+
};
586+
"""
587+
cs = cstruct.cstruct(pointer="uint8")
588+
cs.load(cdef)
589+
590+
assert isinstance(cs.uuid_t, Array)
591+
assert cs.uuid_t(b"\x01" * 16) == b"\x01" * 16
592+
593+
assert isinstance(cs.ptr, Pointer)
594+
assert cs.ptr(b"\x01AAAA") == 1
595+
assert cs.ptr(b"\x01AAAA").dereference() == 0x41414141
596+
597+
obj = cs.test(b"\x01" * 16 + b"\x11AAAA")
598+
assert obj.uuid == b"\x01" * 16
599+
assert obj.ptr.dereference() == 0x41414141
600+
601+
with pytest.raises(ParserError, match="line 1: typedefs cannot have bitfields"):
602+
cs.load("""typedef uint8 with_bits : 4;""")

0 commit comments

Comments
 (0)