Skip to content

Commit

Permalink
Allow slice casts. Fixes #681 (#682)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamphaus authored and elliotchance committed Apr 24, 2018
1 parent f29045a commit 3bd4fb4
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
24 changes: 24 additions & 0 deletions noarch/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package noarch

import (
"reflect"
"unsafe"
)

// CStringToString returns a string that contains all the bytes in the
Expand Down Expand Up @@ -72,3 +73,26 @@ func GoPointerToCPointer(destination interface{}, value interface{}) {
v := reflect.ValueOf(destination).Elem()
reflect.ValueOf(value).Index(0).Set(v)
}

// UnsafeSliceToSlice takes a slice and transforms it into a slice of a different type.
// For this we need to adjust the length and capacity in accordance with the sizes
// of the underlying types.
func UnsafeSliceToSlice(a interface{}, fromSize int, toSize int) *reflect.SliceHeader {
v := reflect.ValueOf(a)

// v might not be addressable, use this trick to get v2 = v,
// with v2 being addressable
v2 := reflect.New(v.Type()).Elem()
v2.Set(v)

// get a pointer to the SliceHeader
// Calling Pointer() on the slice directly only gets a pointer to the 1st element, not the slice header,
// which is why we first call Addr()
ptr := unsafe.Pointer(v2.Addr().Pointer())

// adjust header to adjust sizes for the new type
header := *(*reflect.SliceHeader)(ptr)
header.Len = (header.Len * fromSize) / toSize
header.Cap = (header.Cap * fromSize) / toSize
return &header
}
11 changes: 10 additions & 1 deletion tests/cast.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,15 @@ void test_preprocessor()
is_eq(chr, CHAR_NBSP);
}

typedef unsigned char pcre_uchar;
void caststr() {
pcre_uchar str[] = "abcd";
is_streq((char *) str, "abcd");
}

int main()
{
plan(30);
plan(31);

START_TEST(cast);
START_TEST(castbool);
Expand Down Expand Up @@ -185,5 +191,8 @@ int main()
diag("Compare preprocessor with type")
test_preprocessor();

diag("Typedef slice convertion")
caststr();

done_testing();
}
33 changes: 31 additions & 2 deletions types/cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func CastExpr(p *program.Program, expr goast.Expr, cFromType, cToType string) (
}
}

// Checking registated typedef types in program
// Checking registered typedef types in program
if v, ok := p.TypedefType[toType]; ok {
if fromType == v {
toType, err := ResolveType(p, toType)
Expand Down Expand Up @@ -437,8 +437,37 @@ func CastExpr(p *program.Program, expr goast.Expr, cFromType, cToType string) (
return expr, nil
}

exportedLeftName := util.GetExportedName(leftName)
exportedRightName := util.GetExportedName(rightName)
functionName := fmt.Sprintf("noarch.%sTo%s",
util.GetExportedName(leftName), util.GetExportedName(rightName))
exportedLeftName, exportedRightName)

if strings.HasSuffix(exportedLeftName, "Slice") && strings.HasSuffix(exportedRightName, "Slice") {
p.AddMessage(fmt.Sprintf("// Warning: using unsafe slice cast to convert from %s to %s", fromType, toType))
fromSize, err := SizeOf(p, GetBaseType(cFromType))
if err != nil {
return nil, err
}
toSize, err := SizeOf(p, GetBaseType(cToType))
if err != nil {
return nil, err
}
return &goast.StarExpr{
X: &goast.CallExpr{
Fun: &goast.StarExpr{
X: &goast.Ident{
Name: toType,
},
},
Lparen: 1,
Args: []goast.Expr{
util.NewCallExpr("unsafe.Pointer",
util.NewCallExpr("noarch.UnsafeSliceToSlice", expr, util.NewIntLit(fromSize), util.NewIntLit(toSize))),
},
Rparen: 2,
},
}, nil
}

// FIXME: This is a hack to get SQLite3 to transpile.
if strings.Contains(functionName, "RowSetEntry") {
Expand Down

0 comments on commit 3bd4fb4

Please sign in to comment.