-
Notifications
You must be signed in to change notification settings - Fork 62
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
feature: Support for compilation via tinygo #295
Comments
Supporting tinygo seems like a worthwhile feature. I would need to look into it to understand how much work this would take. |
@fxamacker Awesome thanks. Really looking forward to using your cbor module on something like a NRF52 series microcontroller. |
Seems that only following in Lines 1256 to 1262 in 9e37184
so for instance following compiles with tinygo if v.IsNil() {
v.Set(reflect.MakeMap(tInfo.nonPtrType))
} |
Hey @mkungla thanks for looking into this! I'll take a closer look this weekend. |
TL;DR master branch requires extensive changes to support tinygo but features/stream-mode branch (superset of master branch) would require fewer changes to allow tinygo to use @dereulenspiegel @mkungla I've been looking into tinygo and it's a really cool project! I experimented with this version of tinygo:
tinygo's support for reflect is still very much a work in progress and it uses stubs that allow compiling which would panic at runtime. For example, tinygo has many stubs like these: In tinygo's value.go, there are 30 matches for
Based on these findings, adding support for tinygo 0.23 would require more extensive changes than anticipated. I'm going to keep this issue open because tinygo is making progress incrementally adding more support for reflect. In the meantime, I will:
Thanks again for suggesting tinygo! |
Replaced reflect.MakeMapWithSize() with reflect.MakeMap() to allow compiling with tinygo 0.23 (latest version). Only StreamEncoder and StreamDecoder can be used with tinygo. Other features may encounter stubs in tinygo that panics due to unimplmented Go features. For more details, see #295 (comment)
@fxamacker in brief look of your code I see that If so, it might be possible to get rid of the `reflect' package as a dependency altogether and problem solved 😄. For example, you can create an "internal" package to get types and values that contains something as follows. The following is just a bit of experimental code I have, which of course is not a out of box solution. package internal
import "unsafe"
// interface for the header of builtin value
type typeiface struct {
kind *kindinfo
ptr unsafe.Pointer
}
// minimal builtin kind info struct needed to get that info
type typeinfo struct {
size uintptr
ptrdata uintptr // number of bytes in the kinde that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag uint8 // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
}
type Kind uint
const (
KindInvalid Kind = iota
KindBool
KindInt
KindInt8
KindInt16
KindInt32
KindInt64
KindUint
KindUint8
KindUint16
KindUint32
KindUint64
KindUintptr
KindFloat32
KindFloat64
KindComplex64
KindComplex128
KindArray
KindChan
KindFunc
KindInterface
KindMap
KindPointer
KindSlice
KindString
KindStruct
KindUnsafePointer
)
var kindNames = []string{
KindInvalid: "invalid",
KindBool: "bool",
KindInt: "int",
KindInt8: "int8",
KindInt16: "int16",
KindInt32: "int32",
KindInt64: "int64",
KindUint: "uint",
KindUint8: "uint8",
KindUint16: "uint16",
KindUint32: "uint32",
KindUint64: "uint64",
KindUintptr: "uintptr",
KindFloat32: "float32",
KindFloat64: "float64",
KindComplex64: "complex64",
KindComplex128: "complex128",
KindArray: "array",
KindChan: "chan",
KindFunc: "func",
KindInterface: "interface",
KindMap: "map",
KindPointer: "ptr",
KindSlice: "slice",
KindString: "string",
KindStruct: "struct",
KindUnsafePointer: "unsafe.Pointer",
}
func (t Kind) String() (str string) {
if uint(t) < uint(len(kindNames)) {
str = kindNames[uint(t)]
}
return
}
// Kill "reflect"
func GetUnderlyingValueAndKind(in any, withvalue bool) (value any, kind Kind) {
e := (*typeiface)(unsafe.Pointer(&in))
// check whether it is really a pointer or not.
t := e.kind
if in == nil || t == nil {
return nil, KindInvalid
}
// there are 27 kinds.
// check whether t is stored indirectly in an interface value.
f := uintptr(Kind(t.kind & ((1 << 5) - 1)))
if t.kind&(1<<5) == 0 {
f |= uintptr(1 << 7)
kind = Kind(f & (1<<5 - 1))
} else {
kind = Kind(t.kind & ((1 << 5) - 1))
}
// return early if you only need to know underlying kind
if !withvalue {
return nil, kind
}
switch kind {
case KindBool:
value = *(*bool)(e.ptr)
case KindInt:
value = *(*int)(e.ptr)
case KindInt8:
value = *(*int8)(e.ptr)
case KindInt16:
value = *(*int16)(e.ptr)
case KindInt32:
value = *(*int32)(e.ptr)
case KindInt64:
value = *(*int64)(e.ptr)
case KindUint:
value = *(*uint)(e.ptr)
case KindUint8:
value = *(*uint8)(e.ptr)
case KindUint16:
value = *(*uint16)(e.ptr)
case KindUint32:
value = *(*uint32)(e.ptr)
case KindUint64:
value = *(*uint64)(e.ptr)
case KindUintptr, KindPointer, KindUnsafePointer:
value = *(*uintptr)(e.ptr)
case KindFloat32:
value = *(*float32)(e.ptr)
case KindFloat64:
value = *(*float64)(e.ptr)
case KindComplex64:
value = *(*complex64)(e.ptr)
case KindComplex128:
value = *(*complex128)(e.ptr)
case KindString:
value = *(*string)(e.ptr)
// ... other supported kinds
}
return value, kind
} |
@fxamacker has that issue become obsolete |
hi @mkungla As of today, both dev and release branches in TinyGo don't yet have all the Ideally, I would love to support TinyGo without adding special workaround for it or providing new API, but that may be necessary (if TinyGo decides not to implement some missing features) so I would like to keep this issue open. |
Quick update: TinyGo released 0.28 today (June 11, 2023) and it includes 24 improvements related to I need to work this weekend but will take a look after wrapping up a couple urgent projects. At a glance, this looks promising. When time allows, it would be useful to see what |
TinyGo 0.28.1 allowed fxamacker/cbor to pass more tests, so that's the good news. 😅
The above two items appear to be the main blockers and other remaining issues appear to be limited to tests. |
I created branch https://github.com/fxamacker/cbor/tree/fxamacker/cbor-tinygo based on fxamacker/cbor v2.5.0-beta4. It passes tests when compiled with TinyGo 0.28.1 patched with PR tinygo-org/tinygo#3795. A tradeoff is the removal of one feature: codec cannot decode CBOR tag to Go interface when compiled with TinyGo. Instead, it will return I'll update this issue as we make more progress. |
dgryski's tinygo-org/tinygo#3795 got merged 🎉 |
@fxamacker Sorry, I didn't see this issue and opened #499 to add build tag for tinygo to move it into main branch. |
Is there an issue in TinyGo to track |
Latest tinygo 0.33 only produces the following errors:
I wonder if that's all that's left for it to work? @fxamacker |
Having the same issue as above, Any solutions? github.com/fxamacker/cbor/v2../../../../../go/pkg/mod/github.com/fxamacker/cbor/[email protected]/encode_map.go:33:10: iterk.SetIterKey undefined (type *reflect.Value has no field or method SetIterKey) |
I will try to look into this either tomorrow (Saturday) or next day. |
I created a new feature branch feature/cbor-tinygo-beta based on fxamacker/cbor v2.7.0 and it can be compiled using tinygo v0.33 (also compiles with golang/go). It passes tests (with both go1.22 and tinygo v0.33) and is considered beta/experimental for tinygo. Changes in this branch only affect tinygo. Summary of changes:
See tinygo-org/tinygo#4277 and tinygo-org/tinygo#4458. I will update README and close this issue. Please let me know if it works for you. NOTE: I will also be deleting the old non-feature branch fxamacker/cbor-tinygo. Please use feature/cbor-tinygo-beta. |
Closed by new feature branch feature/cbor-tinygo-beta: It obsoletes old branch for tinygo. Please comment or reopen if needed. 🙏 |
I tried to build a project using this library via tinygo (because this project would also make sense in microcontrollers), but unfortunately tinygo doesn't support
reflect
(or at least certain parts of it. The error reported is:It would be awesome if we could get this library to compile via tinygo to support microcontrollers and smaller WASM modules. Unfortunately I am not deep enough into the architecture of
cbor
to understand how hard the dependency to these reflect methods is and how to resolve this.Is this possible at all?
The text was updated successfully, but these errors were encountered: