Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neoppod
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Levin Zimmermann
neoppod
Commits
732db7ce
Commit
732db7ce
authored
Jan 23, 2017
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
X separate-out sizer
parent
2cd55b6f
Changes
2
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
342 additions
and
506 deletions
+342
-506
t/neo/marshal.go
t/neo/marshal.go
+239
-416
t/neo/protogen.go
t/neo/protogen.go
+103
-90
No files found.
t/neo/marshal.go
View file @
732db7ce
This diff is collapsed.
Click to expand it.
t/neo/protogen.go
View file @
732db7ce
...
@@ -120,7 +120,7 @@ import (
...
@@ -120,7 +120,7 @@ import (
fmt
.
Fprintf
(
&
buf
,
"// %d. %s
\n\n
"
,
pktCode
,
typename
)
fmt
.
Fprintf
(
&
buf
,
"// %d. %s
\n\n
"
,
pktCode
,
typename
)
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
encoder
{
SizeOnly
:
true
}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
sizer
{
}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
encoder
{}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
encoder
{}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
decoder
{}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
decoder
{}))
...
@@ -215,7 +215,7 @@ type CodecCodeGen interface {
...
@@ -215,7 +215,7 @@ type CodecCodeGen interface {
// emit code to process basic fixed types (not string)
// emit code to process basic fixed types (not string)
// userType is type actually used in source (for which typ is underlying), or nil
// userType is type actually used in source (for which typ is underlying), or nil
genBasic
(
path
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
,
obj
types
.
Object
)
genBasic
(
path
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
)
genSlice
(
path
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
genSlice
(
path
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
genMap
(
path
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
genMap
(
path
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
...
@@ -227,10 +227,14 @@ type CodecCodeGen interface {
...
@@ -227,10 +227,14 @@ type CodecCodeGen interface {
}
}
// encode/decode codegen
// encode/decode codegen
type
sizer
struct
{
Buffer
// XXX
n
int
}
type
encoder
struct
{
type
encoder
struct
{
Buffer
// XXX
Buffer
// XXX
n
int
n
int
SizeOnly
bool
// generate code only to compute encoded size
}
}
type
decoder
struct
{
type
decoder
struct
{
...
@@ -241,6 +245,10 @@ type decoder struct {
...
@@ -241,6 +245,10 @@ type decoder struct {
var
_
CodecCodeGen
=
(
*
encoder
)(
nil
)
var
_
CodecCodeGen
=
(
*
encoder
)(
nil
)
var
_
CodecCodeGen
=
(
*
decoder
)(
nil
)
var
_
CodecCodeGen
=
(
*
decoder
)(
nil
)
func
(
s
*
sizer
)
generatedCode
()
string
{
return
s
.
String
()
// XXX -> d.buf.String() ?
}
func
(
e
*
encoder
)
generatedCode
()
string
{
func
(
e
*
encoder
)
generatedCode
()
string
{
return
e
.
String
()
// XXX -> d.buf.String() ?
return
e
.
String
()
// XXX -> d.buf.String() ?
}
}
...
@@ -249,13 +257,13 @@ func (d *decoder) generatedCode() string {
...
@@ -249,13 +257,13 @@ func (d *decoder) generatedCode() string {
return
d
.
String
()
// XXX -> d.buf.String() ?
return
d
.
String
()
// XXX -> d.buf.String() ?
}
}
func
(
s
*
sizer
)
genPrologue
(
recvName
,
typeName
string
)
{
s
.
emit
(
"func (%s *%s) NEOEncodedLen() int {"
,
recvName
,
typeName
)
s
.
emit
(
"var size int"
)
}
func
(
e
*
encoder
)
genPrologue
(
recvName
,
typeName
string
)
{
func
(
e
*
encoder
)
genPrologue
(
recvName
,
typeName
string
)
{
if
e
.
SizeOnly
{
e
.
emit
(
"func (%s *%s) NEOEncodedLen() int {"
,
recvName
,
typeName
)
e
.
emit
(
"var size uint32"
)
}
else
{
e
.
emit
(
"func (%s *%s) NEOEncode(data []byte) {"
,
recvName
,
typeName
)
e
.
emit
(
"func (%s *%s) NEOEncode(data []byte) {"
,
recvName
,
typeName
)
}
}
}
func
(
d
*
decoder
)
genPrologue
(
recvName
,
typeName
string
)
{
func
(
d
*
decoder
)
genPrologue
(
recvName
,
typeName
string
)
{
...
@@ -263,10 +271,12 @@ func (d *decoder) genPrologue(recvName, typeName string) {
...
@@ -263,10 +271,12 @@ func (d *decoder) genPrologue(recvName, typeName string) {
d
.
emit
(
"var nread uint32"
)
d
.
emit
(
"var nread uint32"
)
}
}
func
(
e
*
sizer
)
genEpilogue
()
{
e
.
emit
(
"return size + %v"
,
e
.
n
)
e
.
emit
(
"}
\n
"
)
}
func
(
e
*
encoder
)
genEpilogue
()
{
func
(
e
*
encoder
)
genEpilogue
()
{
if
e
.
SizeOnly
{
e
.
emit
(
"return int(size) + %v"
,
e
.
n
)
}
e
.
emit
(
"}
\n
"
)
e
.
emit
(
"}
\n
"
)
}
}
...
@@ -274,11 +284,16 @@ func (d *decoder) genEpilogue() {
...
@@ -274,11 +284,16 @@ func (d *decoder) genEpilogue() {
d
.
emit
(
"return int(nread) + %v, nil"
,
d
.
n
)
d
.
emit
(
"return int(nread) + %v, nil"
,
d
.
n
)
d
.
emit
(
"
\n
overflow:"
)
d
.
emit
(
"
\n
overflow:"
)
d
.
emit
(
"return 0, ErrDecodeOverflow"
)
d
.
emit
(
"return 0, ErrDecodeOverflow"
)
d
.
emit
(
"goto overflow"
)
// TODO remove
d
.
emit
(
"goto overflow"
)
// TODO
check if overflow used at all and
remove
d
.
emit
(
"}
\n
"
)
d
.
emit
(
"}
\n
"
)
}
}
func
(
e
*
encoder
)
genBasic
(
path
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
,
obj
types
.
Object
)
{
func
(
s
*
sizer
)
genBasic
(
path
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
)
{
basic
:=
basicTypes
[
typ
.
Kind
()]
s
.
n
+=
basic
.
wireSize
}
func
(
e
*
encoder
)
genBasic
(
path
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
)
{
basic
:=
basicTypes
[
typ
.
Kind
()]
basic
:=
basicTypes
[
typ
.
Kind
()]
dataptr
:=
fmt
.
Sprintf
(
"data[%v:]"
,
e
.
n
)
dataptr
:=
fmt
.
Sprintf
(
"data[%v:]"
,
e
.
n
)
if
userType
!=
nil
&&
userType
!=
typ
{
if
userType
!=
nil
&&
userType
!=
typ
{
...
@@ -288,14 +303,10 @@ func (e *encoder) genBasic(path string, typ *types.Basic, userType types.Type, o
...
@@ -288,14 +303,10 @@ func (e *encoder) genBasic(path string, typ *types.Basic, userType types.Type, o
path
=
fmt
.
Sprintf
(
"%v(%v)"
,
typeName
(
typ
),
path
)
path
=
fmt
.
Sprintf
(
"%v(%v)"
,
typeName
(
typ
),
path
)
}
}
e
.
n
+=
basic
.
wireSize
e
.
n
+=
basic
.
wireSize
if
!
e
.
SizeOnly
{
// NOTE no space before "=" - to be able to merge with ":"
// prefix and become defining assignment
e
.
emit
(
basic
.
encode
,
dataptr
,
path
)
e
.
emit
(
basic
.
encode
,
dataptr
,
path
)
}
}
}
func
(
d
*
decoder
)
genBasic
(
assignto
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
,
obj
types
.
Object
)
{
func
(
d
*
decoder
)
genBasic
(
assignto
string
,
typ
*
types
.
Basic
,
userType
types
.
Type
)
{
basic
:=
basicTypes
[
typ
.
Kind
()]
basic
:=
basicTypes
[
typ
.
Kind
()]
d
.
emit
(
"if len(data) < %v { goto overflow }"
,
d
.
n
+
basic
.
wireSize
)
d
.
emit
(
"if len(data) < %v { goto overflow }"
,
d
.
n
+
basic
.
wireSize
)
dataptr
:=
fmt
.
Sprintf
(
"data[%v:]"
,
d
.
n
)
dataptr
:=
fmt
.
Sprintf
(
"data[%v:]"
,
d
.
n
)
...
@@ -316,24 +327,26 @@ func (d *decoder) genBasic(assignto string, typ *types.Basic, userType types.Typ
...
@@ -316,24 +327,26 @@ func (d *decoder) genBasic(assignto string, typ *types.Basic, userType types.Typ
// len u32
// len u32
// [len]byte
// [len]byte
// TODO []byte support
// TODO []byte support
func
(
s
*
sizer
)
genStrBytes
(
path
string
)
{
s
.
n
+=
4
s
.
emit
(
"size += %v + len(%s)"
,
s
.
n
,
path
)
s
.
n
=
0
}
func
(
e
*
encoder
)
genStrBytes
(
path
string
)
{
func
(
e
*
encoder
)
genStrBytes
(
path
string
)
{
e
.
emit
(
"{"
)
e
.
emit
(
"{"
)
e
.
emit
(
"l := uint32(len(%s))"
,
path
)
e
.
emit
(
"l := uint32(len(%s))"
,
path
)
e
.
genBasic
(
"l"
,
types
.
Typ
[
types
.
Uint32
],
nil
,
nil
)
e
.
genBasic
(
"l"
,
types
.
Typ
[
types
.
Uint32
],
nil
)
if
!
e
.
SizeOnly
{
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
e
.
emit
(
"copy(data, %v)"
,
path
)
e
.
emit
(
"copy(data, %v)"
,
path
)
e
.
emit
(
"data = data[l:]"
)
e
.
emit
(
"data = data[l:]"
)
}
else
{
e
.
emit
(
"size += %v + l"
,
e
.
n
)
}
e
.
emit
(
"}"
)
e
.
emit
(
"}"
)
e
.
n
=
0
e
.
n
=
0
}
}
func
(
d
*
decoder
)
genStrBytes
(
assignto
string
)
{
func
(
d
*
decoder
)
genStrBytes
(
assignto
string
)
{
d
.
emit
(
"{"
)
d
.
emit
(
"{"
)
d
.
genBasic
(
"l:"
,
types
.
Typ
[
types
.
Uint32
],
nil
,
nil
)
d
.
genBasic
(
"l:"
,
types
.
Typ
[
types
.
Uint32
],
nil
)
d
.
emit
(
"data = data[%v:]"
,
d
.
n
)
d
.
emit
(
"data = data[%v:]"
,
d
.
n
)
d
.
emit
(
"if uint32(len(data)) < l { goto overflow }"
)
d
.
emit
(
"if uint32(len(data)) < l { goto overflow }"
)
d
.
emit
(
"%v= string(data[:l])"
,
assignto
)
d
.
emit
(
"%v= string(data[:l])"
,
assignto
)
...
@@ -347,32 +360,35 @@ func (d *decoder) genStrBytes(assignto string) {
...
@@ -347,32 +360,35 @@ func (d *decoder) genStrBytes(assignto string) {
// len u32
// len u32
// [len]item
// [len]item
// TODO optimize for []byte
// TODO optimize for []byte
func
(
s
*
sizer
)
genSlice
(
path
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
{
// if size(item)==const - size update in one go
elemSize
,
ok
:=
typeSizeFixed
(
typ
.
Elem
())
if
ok
{
s
.
emit
(
"size += 4 + len(%v) * %v"
,
path
,
elemSize
)
return
}
s
.
emit
(
"size += %v + 4"
,
s
.
n
)
s
.
n
=
0
s
.
emit
(
"for i := 0; i < len(%v); i++ {"
,
path
)
s
.
emit
(
"a := &%s[i]"
,
path
)
codegenType
(
"(*a)"
,
typ
.
Elem
(),
obj
,
s
)
s
.
emit
(
"size += %v"
,
s
.
n
)
s
.
emit
(
"}"
)
s
.
n
=
0
}
func
(
e
*
encoder
)
genSlice
(
path
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
{
func
(
e
*
encoder
)
genSlice
(
path
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
{
e
.
emit
(
"{"
)
e
.
emit
(
"{"
)
e
.
emit
(
"l := uint32(len(%s))"
,
path
)
e
.
emit
(
"l := uint32(len(%s))"
,
path
)
e
.
genBasic
(
"l"
,
types
.
Typ
[
types
.
Uint32
],
nil
,
nil
)
e
.
genBasic
(
"l"
,
types
.
Typ
[
types
.
Uint32
],
nil
)
if
!
e
.
SizeOnly
{
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
}
else
{
e
.
emit
(
"size += %v"
,
e
.
n
)
}
e
.
n
=
0
e
.
n
=
0
// TODO if size(item)==const - size update in one go
elemSize
,
ok
:=
typeSizeFixed
(
typ
.
Elem
())
if
e
.
SizeOnly
&&
ok
{
e
.
emit
(
"size += l * %v"
,
elemSize
)
}
else
{
e
.
emit
(
"for i := 0; uint32(i) <l; i++ {"
)
e
.
emit
(
"for i := 0; uint32(i) <l; i++ {"
)
e
.
emit
(
"a := &%s[i]"
,
path
)
e
.
emit
(
"a := &%s[i]"
,
path
)
codegenType
(
"(*a)"
,
typ
.
Elem
(),
obj
,
e
)
codegenType
(
"(*a)"
,
typ
.
Elem
(),
obj
,
e
)
if
!
e
.
SizeOnly
{
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
// FIXME wrt slice of slice ?
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
// FIXME wrt slice of slice ?
}
else
{
e
.
emit
(
"_ = a"
)
// FIXME try to remove
e
.
emit
(
"size += %v"
,
e
.
n
)
}
e
.
emit
(
"}"
)
e
.
emit
(
"}"
)
}
// see vvv
// see vvv
e
.
emit
(
"}"
)
e
.
emit
(
"}"
)
e
.
n
=
0
e
.
n
=
0
...
@@ -380,7 +396,7 @@ func (e *encoder) genSlice(path string, typ *types.Slice, obj types.Object) {
...
@@ -380,7 +396,7 @@ func (e *encoder) genSlice(path string, typ *types.Slice, obj types.Object) {
func
(
d
*
decoder
)
genSlice
(
assignto
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
{
func
(
d
*
decoder
)
genSlice
(
assignto
string
,
typ
*
types
.
Slice
,
obj
types
.
Object
)
{
d
.
emit
(
"{"
)
d
.
emit
(
"{"
)
d
.
genBasic
(
"l:"
,
types
.
Typ
[
types
.
Uint32
],
nil
,
nil
)
d
.
genBasic
(
"l:"
,
types
.
Typ
[
types
.
Uint32
],
nil
)
d
.
emit
(
"data = data[%v:]"
,
d
.
n
)
d
.
emit
(
"data = data[%v:]"
,
d
.
n
)
d
.
emit
(
"nread += %v"
,
d
.
n
)
d
.
emit
(
"nread += %v"
,
d
.
n
)
d
.
n
=
0
d
.
n
=
0
...
@@ -403,20 +419,31 @@ func (d *decoder) genSlice(assignto string, typ *types.Slice, obj types.Object)
...
@@ -403,20 +419,31 @@ func (d *decoder) genSlice(assignto string, typ *types.Slice, obj types.Object)
// generate code to encode/decode map
// generate code to encode/decode map
// len u32
// len u32
// [len](key, value)
// [len](key, value)
func
(
s
*
sizer
)
genMap
(
path
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
{
keySize
,
keyFixed
:=
typeSizeFixed
(
typ
.
Key
())
elemSize
,
elemFixed
:=
typeSizeFixed
(
typ
.
Elem
())
if
keyFixed
&&
elemFixed
{
s
.
emit
(
"size += 4 + len(%v) * %v"
,
path
,
keySize
+
elemSize
)
return
}
s
.
emit
(
"size += %v + 4"
,
s
.
n
)
s
.
n
=
0
s
.
emit
(
"for key := range %s {"
,
path
)
codegenType
(
"key"
,
typ
.
Key
(),
obj
,
s
)
codegenType
(
fmt
.
Sprintf
(
"%s[key]"
,
path
),
typ
.
Elem
(),
obj
,
s
)
s
.
emit
(
"size += %v"
,
s
.
n
)
s
.
emit
(
"}"
)
s
.
n
=
0
}
func
(
e
*
encoder
)
genMap
(
path
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
{
func
(
e
*
encoder
)
genMap
(
path
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
{
e
.
emit
(
"{"
)
e
.
emit
(
"{"
)
e
.
emit
(
"l := uint32(len(%s))"
,
path
)
e
.
emit
(
"l := uint32(len(%s))"
,
path
)
e
.
genBasic
(
"l"
,
types
.
Typ
[
types
.
Uint32
],
nil
,
nil
)
e
.
genBasic
(
"l"
,
types
.
Typ
[
types
.
Uint32
],
nil
)
if
!
e
.
SizeOnly
{
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
}
else
{
e
.
emit
(
"_ = l"
)
// FIXME remove
e
.
emit
(
"size += %v"
,
e
.
n
)
}
e
.
n
=
0
e
.
n
=
0
keySize
,
keyFixed
:=
typeSizeFixed
(
typ
.
Key
())
elemSize
,
elemFixed
:=
typeSizeFixed
(
typ
.
Elem
())
if
!
e
.
SizeOnly
{
// output keys in sorted order on the wire
// output keys in sorted order on the wire
// (easier for debugging & deterministic for testing)
// (easier for debugging & deterministic for testing)
e
.
emit
(
"keyv := make([]%s, 0, l)"
,
typeName
(
typ
.
Key
()))
e
.
emit
(
"keyv := make([]%s, 0, l)"
,
typeName
(
typ
.
Key
()))
...
@@ -425,24 +452,10 @@ func (e *encoder) genMap(path string, typ *types.Map, obj types.Object) {
...
@@ -425,24 +452,10 @@ func (e *encoder) genMap(path string, typ *types.Map, obj types.Object) {
e
.
emit
(
"}"
)
e
.
emit
(
"}"
)
e
.
emit
(
"sort.Slice(keyv, func (i, j int) bool { return keyv[i] < keyv[j] })"
)
e
.
emit
(
"sort.Slice(keyv, func (i, j int) bool { return keyv[i] < keyv[j] })"
)
e
.
emit
(
"for _, key := range keyv {"
)
e
.
emit
(
"for _, key := range keyv {"
)
}
else
{
if
keyFixed
&&
elemFixed
{
e
.
emit
(
"size += l * %v"
,
keySize
+
elemSize
)
}
else
{
e
.
emit
(
"for key := range %s {"
,
path
)
}
}
if
!
(
e
.
SizeOnly
&&
keyFixed
&&
elemFixed
)
{
codegenType
(
"key"
,
typ
.
Key
(),
obj
,
e
)
codegenType
(
"key"
,
typ
.
Key
(),
obj
,
e
)
codegenType
(
fmt
.
Sprintf
(
"%s[key]"
,
path
),
typ
.
Elem
(),
obj
,
e
)
codegenType
(
fmt
.
Sprintf
(
"%s[key]"
,
path
),
typ
.
Elem
(),
obj
,
e
)
if
!
e
.
SizeOnly
{
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
// XXX wrt map of map?
e
.
emit
(
"data = data[%v:]"
,
e
.
n
)
// XXX wrt map of map?
}
else
{
e
.
emit
(
"_ = key"
)
// FIXME remove
e
.
emit
(
"size += %v"
,
e
.
n
)
}
e
.
emit
(
"}"
)
e
.
emit
(
"}"
)
}
// XXX vvv ?
// XXX vvv ?
e
.
emit
(
"}"
)
e
.
emit
(
"}"
)
e
.
n
=
0
e
.
n
=
0
...
@@ -450,7 +463,7 @@ func (e *encoder) genMap(path string, typ *types.Map, obj types.Object) {
...
@@ -450,7 +463,7 @@ func (e *encoder) genMap(path string, typ *types.Map, obj types.Object) {
func
(
d
*
decoder
)
genMap
(
assignto
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
{
func
(
d
*
decoder
)
genMap
(
assignto
string
,
typ
*
types
.
Map
,
obj
types
.
Object
)
{
d
.
emit
(
"{"
)
d
.
emit
(
"{"
)
d
.
genBasic
(
"l:"
,
types
.
Typ
[
types
.
Uint32
],
nil
,
nil
)
d
.
genBasic
(
"l:"
,
types
.
Typ
[
types
.
Uint32
],
nil
)
d
.
emit
(
"data = data[%v:]"
,
d
.
n
)
d
.
emit
(
"data = data[%v:]"
,
d
.
n
)
d
.
emit
(
"nread += %v"
,
d
.
n
)
d
.
emit
(
"nread += %v"
,
d
.
n
)
d
.
n
=
0
d
.
n
=
0
...
@@ -503,7 +516,7 @@ func codegenType(path string, typ types.Type, obj types.Object, codegen CodecCod
...
@@ -503,7 +516,7 @@ func codegenType(path string, typ types.Type, obj types.Object, codegen CodecCod
if
!
ok
{
if
!
ok
{
log
.
Fatalf
(
"%v: %v: basic type %v not supported"
,
pos
(
obj
),
obj
.
Name
(),
u
)
log
.
Fatalf
(
"%v: %v: basic type %v not supported"
,
pos
(
obj
),
obj
.
Name
(),
u
)
}
}
codegen
.
genBasic
(
path
,
u
,
typ
,
obj
)
codegen
.
genBasic
(
path
,
u
,
typ
)
case
*
types
.
Struct
:
case
*
types
.
Struct
:
for
i
:=
0
;
i
<
u
.
NumFields
();
i
++
{
for
i
:=
0
;
i
<
u
.
NumFields
();
i
++
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment