• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 syzkaller project authors. All rights reserved.
2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3
4package serializer
5
6import (
7	"reflect"
8
9	"fmt"
10	"io"
11)
12
13// Write writes Go-syntax representation of v into w.
14// This is similar to fmt.Fprintf(w, "%#v", v), but properly handles pointers,
15// does not write package names before types, omits struct fields with default values,
16// omits type names where possible, etc. On the other hand, it currently does not
17// support all types (e.g. channels and maps).
18func Write(ww io.Writer, i interface{}) {
19	w := writer{ww}
20	v := reflect.ValueOf(i)
21	if v.Kind() == reflect.Slice && (v.IsNil() || v.Len() == 0) {
22		w.typ(v.Type())
23		w.string("(nil)")
24		return
25	}
26	w.do(v, false)
27}
28
29type writer struct {
30	w io.Writer
31}
32
33func (w *writer) do(v reflect.Value, sliceElem bool) {
34	switch v.Kind() {
35	case reflect.Ptr:
36		w.doPtr(v, sliceElem)
37	case reflect.Interface:
38		if v.IsNil() {
39			w.string("nil")
40		} else {
41			w.do(v.Elem(), false)
42		}
43	case reflect.Slice:
44		w.doSlice(v)
45	case reflect.Struct:
46		w.doStruct(v, sliceElem)
47	case reflect.Bool:
48		if v.Bool() {
49			w.string("true")
50		} else {
51			w.string("false")
52		}
53	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
54		fmt.Fprintf(w.w, "%v", v.Int())
55	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
56		fmt.Fprintf(w.w, "%v", v.Uint())
57	case reflect.String:
58		fmt.Fprintf(w.w, "%q", v.String())
59	case reflect.Func:
60		// Skip, no way to serialize this.
61	default:
62		panic(fmt.Sprintf("unsupported type: %#v", v.Type().String()))
63	}
64}
65
66func (w *writer) doPtr(v reflect.Value, sliceElem bool) {
67	if v.IsNil() {
68		w.string("nil")
69		return
70	}
71	if !sliceElem {
72		w.byte('&')
73	}
74	if v.Elem().Kind() != reflect.Struct {
75		panic(fmt.Sprintf("only pointers to structs are supported, got %v",
76			v.Type().Name()))
77	}
78	w.do(v.Elem(), sliceElem)
79}
80
81func (w *writer) doSlice(v reflect.Value) {
82	if v.IsNil() || v.Len() == 0 {
83		w.string("nil")
84		return
85	}
86	w.typ(v.Type())
87	sub := v.Type().Elem().Kind()
88	if sub == reflect.Ptr || sub == reflect.Interface || sub == reflect.Struct {
89		// Elem per-line.
90		w.string("{\n")
91		for i := 0; i < v.Len(); i++ {
92			w.do(v.Index(i), true)
93			w.string(",\n")
94		}
95		w.byte('}')
96		return
97	}
98	// All on one line.
99	w.byte('{')
100	for i := 0; i < v.Len(); i++ {
101		if i > 0 {
102			w.byte(',')
103		}
104		w.do(v.Index(i), true)
105	}
106	w.byte('}')
107}
108
109func (w *writer) doStruct(v reflect.Value, sliceElem bool) {
110	if !sliceElem {
111		w.string(v.Type().Name())
112	}
113	w.byte('{')
114	needComma := false
115	for i := 0; i < v.NumField(); i++ {
116		f := v.Field(i)
117		if isDefaultValue(f) {
118			continue
119		}
120		if needComma {
121			w.byte(',')
122		}
123		w.string(v.Type().Field(i).Name)
124		w.byte(':')
125		w.do(f, false)
126		needComma = true
127	}
128	w.byte('}')
129}
130
131func (w *writer) typ(t reflect.Type) {
132	switch t.Kind() {
133	case reflect.Ptr:
134		w.byte('*')
135		w.typ(t.Elem())
136	case reflect.Slice:
137		w.string("[]")
138		w.typ(t.Elem())
139	default:
140		w.string(t.Name())
141	}
142}
143
144func (w *writer) string(v string) {
145	io.WriteString(w.w, v)
146}
147
148func (w *writer) byte(v byte) {
149	if bw, ok := w.w.(io.ByteWriter); ok {
150		bw.WriteByte(v)
151	} else {
152		w.w.Write([]byte{v})
153	}
154}
155
156func isDefaultValue(v reflect.Value) bool {
157	switch v.Kind() {
158	case reflect.Ptr:
159		return v.IsNil()
160	case reflect.Interface:
161		return v.IsNil()
162	case reflect.Slice:
163		return v.IsNil() || v.Len() == 0
164	case reflect.Struct:
165		for i := 0; i < v.NumField(); i++ {
166			if !isDefaultValue(v.Field(i)) {
167				return false
168			}
169		}
170		return true
171	case reflect.Bool:
172		return !v.Bool()
173	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
174		return v.Int() == 0
175	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
176		return v.Uint() == 0
177	case reflect.String:
178		return v.String() == ""
179	case reflect.Func:
180		return true
181	default:
182		return false
183	}
184}
185