How we can know what kind of struct is on byte array - go

I'm looking for some solution to know whats the struct type of the hash. It is possible to do that without try an error method (casting to a specific type and see the cast is successfully)?
Please check the code:
import (
"bytes"
"encoding/binary"
"fmt"
"reflect"
)
type T struct {
A int64
B float64
}
type D struct {
A int64
B float64
C string
}
func main() {
// Create a struct and write it.
t := T{A: 0xEEFFEEFF, B: 3.14}
buf := &bytes.Buffer{}
err := binary.Write(buf, binary.BigEndian, t)
if err != nil {
panic(err)
}
fmt.Println(buf.Bytes())
out := getType(buf)
fmt.Println(out)
}
func getType(v interface{})(r string){
fmt.Println(reflect.TypeOf(v))
switch t := v.(type) {
case T:
return "Is type T"
case D:
return "Is type D"
default:
_ = t
return "unknown"
}
}

Since the encoding/binary package does not write out type information, it is not possible to tell what type was written / serialized.
And you're in a worse position that you might originally think: even trying to decode into a value of different type might succeed without errors, so there isn't even a reliable way to tell the type.
For example if you serialize a value of this type:
type T struct {
A int64
B float64
}
You can read it into a value of this type:
type T2 struct {
B float64
A int64
}
It will give no errors because the size of both structs is the same, but obviously you will get different numbers in the fields.
You are in a little better position if you use encoding/gob, as the gob package does transmit type information, and encoding a value of type T and then decoding it into a value of type T2 would work: order of fields does not matter, and extra or missing fields also do not cause trouble.
See this example:
// Create a struct and write it.
t := T{A: 0xEEFFEEFF, B: 3.14}
fmt.Println("Encoding:", t)
buf := &bytes.Buffer{}
fmt.Println(binary.Write(buf, binary.BigEndian, t))
fmt.Println(buf.Bytes())
fmt.Println(gob.NewEncoder(buf).Encode(t))
t2 := T2{}
fmt.Println(binary.Read(buf, binary.BigEndian, &t2))
fmt.Println(t2)
t2 = T2{}
fmt.Println(gob.NewDecoder(buf).Decode(&t2))
fmt.Println(t2)
Output (try it on the Go Playground):
Encoding: {4009750271 3.14}
<nil>
[0 0 0 0 238 255 238 255 64 9 30 184 81 235 133 31]
<nil>
<nil>
{1.9810798573e-314 4614253070214989087}
<nil>
{3.14 4009750271}
If you want to be able to detect the type before reading it, you have to take care of it yourself: you have to transmit type information (e.g. name of the type). Or even better, use a serialization method that already does this, for example Google's protocol buffers, and here is the Go implementation for it: github.com/golang/protobuf.

Related

What exactly is this "cast" doing in Golang?

Can someone point me in the right direction of this Go's syntax :
(*int)(nil)
If I have a a value of a given type and I want to convert it to, lets say, float64 I can do this :
var num int = 65
fnum := float64(num)
If I have an interface and I want to "cast it" to some type I can do this :
func main() {
concretevalue := dosomething("hello!")
fmt.Printf("%T : %v", concretevalue, concretevalue)
}
func dosomething( v interface{} ) string {
return v.(string)
}
Where does the (*int)(nil) fit? How can I get information about this specific syntax?
It is a type-conversion, same as float64(num), however, because the converted type is a pointer, you need extra parentheses, because otherwise *int(nil) would mean converting nil to int, and then dereferencing it.

How to handle dynamic interface types in Golang?

I am trying to handle dynamic interface type where I don't know whether interface is of type float64 or int64. I am using an API and Decode the response on a map where price is sometimes integer and sometimes float. e.g. JSON response is sometimes {price: 35} and sometimes {price: 35}
I have created an example code here
package main
import "fmt"
func main() {
response := make(map[string]interface{})
response["price"] = 2.1
response1 := make(map[string]interface{})
response1["price"] = 2
price_response, _ := response["price"].(float64)
price_response1, _ := response1["price"].(float64)
fmt.Println(price_response, "==> price_response") //output = 2.1
fmt.Println(price_response1,"==> price_response1") // //output = 0
}
The Output is get is
2.1 price_response
0 price_response1
Now here, I have to define type statically while doing type assertions on interface types. How am I supposed to handle this type issue to avoid getting 0 and instead getting the actual value converted into float64?
How am I supposed to handle this type issue to avoid getting 0 and instead getting the actual value converted into float64?
t, ok := i.(T)
This line of code checks whether the interface value i holds the concrete type T. If not, ok will be false and t will be the zero value of type T
price_response1, _ := response1["price"].(float64)
Here response1["price"] doesn't hold a type float64. Hence price_response1 has zero value of type float64, which is 0.
To print the underlying type of interface{} as a string you can use:
getType := fmt.Sprintf("%T", response1["price"])
fmt.Println(getType)
See the below code to get the actual value converted into float64, if the underlying type is int:
package main
import "fmt"
func convertToFloat64(resp interface{}) {
switch v := resp.(type) {
case int:
fmt.Println(float64(v), "==> price_response1")
case float64:
fmt.Println(v, "==> price_response")
default:
fmt.Println("unknown")
}
}
func main() {
response := make(map[string]interface{})
response["price"] = 2.1
convertToFloat64(response["price"])
response1 := make(map[string]interface{})
response1["price"] = 2
convertToFloat64(response1["price"])
}
Output:
2.1 ==> price_response
2 ==> price_response1

casting overlapping structs in golang

I'm new to golang and trying to figure out the correct way of casting a block of bytes to the correct struct. All structs start with two bytes that dictate the layout of the remaining bytes. In C I would point to the beginning of the block of memory and cast it as a simple struct that only contained those two bytes (X below) but here I get an invalid type assertion. I'm probably way off base here any help you be appreciated.
package main
import (
"fmt"
)
type A struct {
tag byte
ver byte
data1 int
data2 int
data3 int
}
type B struct {
tag byte
ver byte
data1 float32
}
type X struct {
tag byte
ver byte
}
func main() {
var a A
a.tag = 1
a.ver = 1
x := a.(X)
fmt.Printf("%d,%d", x.tag, x.ver)
}
Edit
In short I just want to create a custom method on type Foo that is the only reason why I want to perform the cast. If the solutions are very complex I will just create functions instead of methods I guess. I was just curios.
playground link
Here's my solution. It involves a few different tips:
Embed the shared struct in the individual structs.
Use encoding/binary package to load bytes into structs.
Fill header struct with first two bytes, then make a decision on which subtype to make and fill.
Always use fixed length int types for this kind of thing.
Your field names must be UpperCase to be fillable from encoding/binary
This is a pretty brittle way to manage marshalling.unmarshalling of data, but I'm sure you know that.
Here's my solution:
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
)
type A struct {
X
Data1 int32
Data2 int32
Data3 int32
}
type B struct {
X
Data1 int32
}
type X struct {
Tag byte
Ver byte
}
func main() {
var err error
data := []byte{1, 1, 0, 0, 0, 42}
hdr := X{}
err = binary.Read(bytes.NewReader(data[:2]), binary.BigEndian, &hdr)
if err != nil {
log.Fatal(err)
}
fmt.Println(hdr.Tag, hdr.Ver)
if hdr.Tag == 1 {
b := B{}
err = binary.Read(bytes.NewReader(data), binary.BigEndian, &b)
if err != nil {
log.Fatal(err)
}
fmt.Println(b.Data1)
}
}
playground link
Go generally tries to discourage C-like memory fiddling as it leads to memory leaks, incorrect behavior, and security vulnerabilities unless extraordinary caution and testing are applied. That doesn't mean it's impossible though; in fact, the aptly-named unsafe.Pointer is exposed for exactly this purpose. Use it with caution.

Unmarshaling a JSON integer to an empty interface results in wrong type assertion

I have this code. I expect the interface to be type asserted to int. However, the type of the interface is float64 instead. Can anyone explain why this is happening? And what are the best ways to circumvent it.
package main
import (
"fmt"
"encoding/json"
)
type obj struct {
X interface{}
}
func main() {
var x int
x = 5
o := &obj {
X: x,
}
b, _ := json.Marshal(o)
var newObj obj
json.Unmarshal(b, &newObj)
if _, ok := newObj.X.(int); ok {
fmt.Println("X is an int")
} else if _, ok := newObj.X.(float64); ok {
fmt.Println("X is a float64")
} else {
fmt.Println("This does not make any sense")
}
}
This code prints "X is a float64". You can run the code there https://play.golang.org/p/9L9unW8l3n
Numbers are encoded as "Json numbers" Unmarshal decodes Json numbers as floats. From the docs:
Marshal
Floating point, integer, and Number values encode as JSON numbers.
Unmarshal
To unmarshal JSON into an interface value, Unmarshal stores one of
these in the interface value:
bool, for JSON booleans float64, for JSON numbers string, for JSON
strings []interface{}, for JSON arrays map[string]interface{}, for
JSON objects nil for JSON null

cannot convert data (type interface {}) to type string: need type assertion

I am pretty new to go and I was playing with this notify package.
At first I had code that looked like this:
func doit(w http.ResponseWriter, r *http.Request) {
notify.Post("my_event", "Hello World!")
fmt.Fprint(w, "+OK")
}
I wanted to append newline to Hello World! but not in the function doit above, because that would be pretty trivial, but in the handler afterwards like this below:
func handler(w http.ResponseWriter, r *http.Request) {
myEventChan := make(chan interface{})
notify.Start("my_event", myEventChan)
data := <-myEventChan
fmt.Fprint(w, data + "\n")
}
After go run:
$ go run lp.go
# command-line-arguments
./lp.go:15: invalid operation: data + "\n" (mismatched types interface {} and string)
After a little bit of Googling I found this question on SO.
Then I updated my code to:
func handler(w http.ResponseWriter, r *http.Request) {
myEventChan := make(chan interface{})
notify.Start("my_event", myEventChan)
data := <-myEventChan
s:= data.(string) + "\n"
fmt.Fprint(w, s)
}
Is this what I was supposed to do? My compiler errors are gone so I guess that's pretty good? Is this efficient? Should you do it differently?
According to the Go specification:
For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T.
A "type assertion" allows you to declare an interface value contains a certain concrete type or that its concrete type satisfies another interface.
In your example, you were asserting data (type interface{}) has the concrete type string. If you are wrong, the program will panic at runtime. You do not need to worry about efficiency, checking just requires comparing two pointer values.
If you were unsure if it was a string or not, you could test using the two return syntax.
str, ok := data.(string)
If data is not a string, ok will be false. It is then common to wrap such a statement into an if statement like so:
if str, ok := data.(string); ok {
/* act on str */
} else {
/* not string */
}
Type Assertion
This is known as type assertion in golang, and it is a common practice.
Here is the explanation from a tour of go:
A type assertion provides access to an interface value's underlying concrete value.
t := i.(T)
This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t.
If i does not hold a T, the statement will trigger a panic.
To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.
t, ok := i.(T)
If i holds a T, then t will be the underlying value and ok will be true.
If not, ok will be false and t will be the zero value of type T, and no panic occurs.
NOTE: value i should be interface type.
Pitfalls
Even if i is an interface type, []i is not interface type. As a result, in order to convert []i to its value type, we have to do it individually:
// var items []i
for _, item := range items {
value, ok := item.(T)
dosomethingWith(value)
}
Performance
As for performance, it can be slower than direct access to the actual value as show in this stackoverflow answer.
//an easy way:
str := fmt.Sprint(data)
As asked for by #ρяσѕρєя an explanation can be found at https://golang.org/pkg/fmt/#Sprint. Related explanations can be found at https://stackoverflow.com/a/44027953/12817546 and at https://stackoverflow.com/a/42302709/12817546. Here is #Yuanbo's answer in full.
package main
import "fmt"
func main() {
var data interface{} = 2
str := fmt.Sprint(data)
fmt.Println(str)
}
In addition to other answers, I think it's good to have a look at "type switch":
package main
import "fmt"
func printType(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("type of %v is %v\n", i, v)
// type of 21 is int
case string:
fmt.Printf("type of %v is %v\n", i, v)
// type of hello is string
default:
fmt.Printf("type of %v is %v\n", i, v)
// type of true is bool
}
}
func main() {
printType(21)
printType("hello")
printType(true)
}
I hope it helps.
More information: https://go.dev/tour/methods/16

Resources