Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: decode vectors #55

Merged
merged 2 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 85 additions & 9 deletions types/objc/type_encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package objc

import (
"fmt"
"regexp"
"strings"
"unicode"
)
Expand Down Expand Up @@ -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?
Expand All @@ -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)
}
}

Expand All @@ -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 {
Expand All @@ -350,6 +371,27 @@ func decodeUnion(unionType string) string {
return decodeStructOrUnion(unionType, "union")
}

var (
vectorRegExp = regexp.MustCompile(`(?P<size>\d+),(?P<alignment>\d+)(?P<type>.+)`)
)

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)
Expand All @@ -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 == "?" {
Expand Down Expand Up @@ -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++
}
Expand Down Expand Up @@ -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' {
Expand Down Expand Up @@ -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' {
Expand Down
31 changes: 26 additions & 5 deletions types/objc/type_encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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) {
Expand Down