Skip to content

Commit af1faf2

Browse files
committed
concepts: add page with some porting hints
1 parent 24c6586 commit af1faf2

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
title: "Porting code to TinyGo"
3+
type: "docs"
4+
description: >
5+
How to port existing code to TinyGo.
6+
---
7+
8+
TinyGo tries to stay compatible with the `gc` compiler toolchain. It should over time become more and more compatible as new features are implemented. However, if you're porting existing code over then you may encounter a number of issues.
9+
10+
## Serialization and deserialization
11+
12+
TinyGo does have limited support for the `reflect` package, but it is not complete. This is especially noticeable with serialization/deserialization packages such as `encoding/json`.
13+
14+
### How to fix
15+
16+
Your best bet is is to use a different package than the standard library package. Packages optimized for speed will often work because they may avoid using reflection.
17+
18+
## `reflect.SliceHeader` and `reflect.StringHeader`
19+
20+
These two structs are sometimes used for unsafely casting between strings and slices. In Go, the structs look like this:
21+
22+
```go
23+
type SliceHeader struct {
24+
Data uintptr
25+
Len int
26+
Cap int
27+
}
28+
```
29+
30+
In TinyGo, they look like this:
31+
32+
```go
33+
type SliceHeader struct {
34+
Data uintptr
35+
Len uintptr
36+
Cap uintptr
37+
}
38+
```
39+
40+
The difference is that `Len` and `Cap` are of type `uintptr` instead of `int`. This is because TinyGo supports AVR, which is a mixed 8/16-bit architecture with 16-bit pointers. The Go language requires `int` to be either 32-bit or 64-bit. To solve this mismatch, TinyGo has chosen to use `uintptr` for the `Len` and `Cap` fields.
41+
42+
Also, note the comment, which explicitly states that using this struct is not portable (emphasis mine):
43+
44+
> SliceHeader is the runtime representation of a slice. **It cannot be used safely or portably** and its representation may change in a later release. Moreover, the Data field is not sufficient to guarantee the data it references will not be garbage collected, so programs must keep a separate, correctly typed pointer to the underlying data.
45+
46+
### How to fix
47+
48+
Cast the types as appropriate. For example, code like this:
49+
50+
```go
51+
func split(s []byte) (*byte, int, int) {
52+
header := (*reflect.SliceHeader)(unsafe.Pointer(&s))
53+
return (*byte)(unsafe.Pointer(header.Data)), header.Len, header.Cap
54+
}
55+
```
56+
57+
Can easily be supported in both Go versions, with some type casts:
58+
59+
```go
60+
func split(s []byte) (*byte, int, int) {
61+
header := (*reflect.SliceHeader)(unsafe.Pointer(&s))
62+
return (*byte)(unsafe.Pointer(header.Data)), int(header.Len), int(header.Cap)
63+
}
64+
```
65+
66+
The difference is that `header.Len` and `header.Cap` are now cast to an int before returning.

0 commit comments

Comments
 (0)