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