From ca7676f1ecf5a8fee4d8d4369ed5a78bc5a82b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20T=C3=B4rres?= Date: Fri, 17 May 2024 01:12:01 -0700 Subject: [PATCH] feat: decode vectors (#55) --- types/objc/type_encoding.go | 94 +++++++++++++++++++++++++++++--- types/objc/type_encoding_test.go | 31 +++++++++-- 2 files changed, 111 insertions(+), 14 deletions(-) diff --git a/types/objc/type_encoding.go b/types/objc/type_encoding.go index c70ad61..0de6f3d 100644 --- a/types/objc/type_encoding.go +++ b/types/objc/type_encoding.go @@ -2,6 +2,7 @@ package objc import ( "fmt" + "regexp" "strings" "unicode" ) @@ -291,7 +292,14 @@ func decodeType(encType string) string { if typ, ok := typeEncoding[encType]; ok { return typ } - return decodeType(encType[1:]) + " *" // pointer + + decType := decodeType(encType[1:]) + + if encType[1] == '!' { + return strings.Replace(decType, "x", "*x", 1) // vector pointer + } + + return decType + " *" // pointer } if spec, ok := typeSpecifiers[string(encType[0])]; ok { // TODO: can there be more than 2 specifiers? @@ -312,18 +320,25 @@ func decodeType(encType string) string { } if len(encType) > 2 { - if strings.HasPrefix(encType, "[") { // ARRAY + switch encType[0] { + case '!': // VECTOR + inner := encType[strings.IndexByte(encType, '[')+1 : strings.LastIndexByte(encType, ']')] + s += decodeVector(inner) + + case '(': // UNION + inner := encType[strings.IndexByte(encType, '(')+1 : strings.LastIndexByte(encType, ')')] + s += decodeUnion(inner) + + case '[': // ARRAY inner := encType[strings.IndexByte(encType, '[')+1 : strings.LastIndexByte(encType, ']')] s += decodeArray(inner) - } else if strings.HasPrefix(encType, "{") { // STRUCT + + case '{': // STRUCT if !(strings.Contains(encType, "{") && strings.Contains(encType, "}")) { return "?" } inner := encType[strings.IndexByte(encType, '{')+1 : strings.LastIndexByte(encType, '}')] s += decodeStructure(inner) - } else if strings.HasPrefix(encType, "(") { // UNION - inner := encType[strings.IndexByte(encType, '(')+1 : strings.LastIndexByte(encType, ')')] - s += decodeUnion(inner) } } @@ -339,7 +354,13 @@ func decodeArray(arrayType string) string { if len(arrayType) == 1 { return fmt.Sprintf("x[%s]", arrayType) } - return fmt.Sprintf("%s x[%s]", decodeType(arrayType[numIdx+1:]), arrayType[:numIdx+1]) + + decType := decodeType(arrayType[numIdx+1:]) + if !strings.HasSuffix(decType, "*") { + decType += " " + } + + return fmt.Sprintf("%sx[%s]", decType, arrayType[:numIdx+1]) } func decodeStructure(structure string) string { @@ -350,6 +371,27 @@ func decodeUnion(unionType string) string { return decodeStructOrUnion(unionType, "union") } +var ( + vectorRegExp = regexp.MustCompile(`(?P\d+),(?P\d+)(?P.+)`) +) + +func decodeVector(vectorType string) string { + matches := vectorRegExp.FindStringSubmatch(vectorType) + if len(matches) != 4 { + return "" + } + + vSize := matches[1] + vAlignment := matches[2] + + eType := decodeType(matches[3]) + if !strings.HasSuffix(eType, "*") { + eType += " " + } + + return fmt.Sprintf("%sx __attribute__((aligned(%s), vector_size(%s)))", eType, vAlignment, vSize) +} + func decodeBitfield(bitfield string) string { span := encodingGetSizeOfArguments(bitfield) return fmt.Sprintf("unsigned int x:%d", span) @@ -366,6 +408,10 @@ func getFieldName(field string) (string, string) { return "", field } +var ( + vectorIdentifierRegExp = regexp.MustCompile(`(.+[ *]x)( __attribute__.+)`) +) + func decodeStructOrUnion(typ, kind string) string { name, rest, _ := strings.Cut(typ, "=") if name == "?" { @@ -397,10 +443,28 @@ func decodeStructOrUnion(typ, kind string) string { fields = append(fields, fmt.Sprintf("unsigned int x%d:%d;", idx, span)) } else if strings.HasPrefix(field, "[") { array := decodeType(field) - array = strings.TrimSpace(strings.Replace(array, "x", fmt.Sprintf("x%d", idx), 1)) + array = strings.TrimSpace(strings.Replace(array, "x", fmt.Sprintf("x%d", idx), 1)) + ";" fields = append(fields, array) } else { - fields = append(fields, fmt.Sprintf("%s x%d;", decodeType(field), idx)) + decType := decodeType(field) + if !strings.HasSuffix(decType, "))") { + if !strings.HasSuffix(decType, "*") { + decType += " " + } + + fields = append(fields, fmt.Sprintf("%sx%d;", decType, idx)) + } else { + matches := vectorIdentifierRegExp.FindStringSubmatchIndex(decType) + if len(matches) != 6 { + fields = append(fields, fmt.Sprintf("%sx%d;", decType, idx)) + } else { + middle := matches[4] + prefix := decType[:middle] + suffix := decType[middle:] + + fields = append(fields, fmt.Sprintf("%s%d%s;", prefix, idx, suffix)) + } + } } idx++ } @@ -441,6 +505,12 @@ func skipFirstType(typStr string) string { i++ } return string(typ[i+1:]) + case '!': /* vectors */ + i += 2 + for typ[i] == ',' || typ[i] >= '0' && typ[i] <= '9' { + i++ + } + return string(typ[i+subtypeUntil(string(typ[i:]), ']')+1:]) case '[': /* arrays */ i++ for typ[i] >= '0' && typ[i] <= '9' { @@ -522,6 +592,12 @@ func CutType(typStr string) (string, string, bool) { i++ } return string(typ[:i]), string(typ[i:]), true + case '!': /* vectors */ + i += 2 + for typ[i] == ',' || typ[i] >= '0' && typ[i] <= '9' { + i++ + } + return string(typ[:i+subtypeUntil(string(typ[i:]), ']')+1]), string(typ[i+subtypeUntil(string(typ[i:]), ']')+1:]), true case '[': /* arrays */ i++ for typ[i] >= '0' && typ[i] <= '9' { diff --git a/types/objc/type_encoding_test.go b/types/objc/type_encoding_test.go index f8009db..6d5cd6c 100644 --- a/types/objc/type_encoding_test.go +++ b/types/objc/type_encoding_test.go @@ -14,16 +14,16 @@ func Test_decodeType(t *testing.T) { { name: "Test all", args: args{ - encType: "^{OutterStruct=(InnerUnion=q{InnerStruct=ii})b1b2b10b1q}", + encType: "^{OutterStruct=(InnerUnion=q{InnerStruct=ii})b1b2b10b1q[2^v]^![4,8c]}", }, - want: "struct OutterStruct { union InnerUnion { long long x0; struct InnerStruct { int x0; int x1; } x1; } x0; unsigned int x1:1; unsigned int x2:2; unsigned int x3:10; unsigned int x4:1; long long x5; } *", + want: "struct OutterStruct { union InnerUnion { long long x0; struct InnerStruct { int x0; int x1; } x1; } x0; unsigned int x1:1; unsigned int x2:2; unsigned int x3:10; unsigned int x4:1; long long x5; void *x6[2]; signed char *x7 __attribute__((aligned(8), vector_size(4))); } *", }, { name: "Test array", args: args{ encType: "[2^v]", }, - want: "void * x[2]", + want: "void *x[2]", }, { name: "Test bitfield", @@ -33,11 +33,18 @@ func Test_decodeType(t *testing.T) { want: "unsigned int x:13", }, { - name: "Test struct", + name: "Test struct 0", args: args{ encType: "{test=@*i}", }, - want: "struct test { id x0; char * x1; int x2; }", + want: "struct test { id x0; char *x1; int x2; }", + }, + { + name: "Test struct 1", + args: args{ + encType: "{?=i[3f]b3b2c}", + }, + want: "struct { int x0; float x1[3]; unsigned int x2:3; unsigned int x3:2; signed char x4; }", }, { name: "Test union", @@ -53,6 +60,20 @@ func Test_decodeType(t *testing.T) { }, want: "id /* block */", }, + { + name: "Test vector 0", + args: args{ + encType: "![16,8i]", + }, + want: "int x __attribute__((aligned(8), vector_size(16)))", + }, + { + name: "Test vector 1", + args: args{ + encType: "^![16,8c]", + }, + want: "signed char *x __attribute__((aligned(8), vector_size(16)))", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {