github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/serializer/serializer.go (about) 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 4 package serializer 5 6 import ( 7 "reflect" 8 "strings" 9 10 "fmt" 11 "io" 12 ) 13 14 // Write writes Go-syntax representation of v into w. 15 // This is similar to fmt.Fprintf(w, "%#v", v), but properly handles pointers, 16 // does not write package names before types, omits struct fields with default values, 17 // omits type names where possible, etc. On the other hand, it currently does not 18 // support all types (e.g. channels and maps). 19 func Write(ww io.Writer, i interface{}) { 20 w := writer{ww} 21 v := reflect.ValueOf(i) 22 if v.Kind() == reflect.Slice && (v.IsNil() || v.Len() == 0) { 23 w.typ(v.Type()) 24 w.string("(nil)") 25 return 26 } 27 w.do(v, false) 28 } 29 30 func WriteString(i interface{}) string { 31 var sb strings.Builder 32 Write(&sb, i) 33 return sb.String() 34 } 35 36 type writer struct { 37 w io.Writer 38 } 39 40 func (w *writer) do(v reflect.Value, sliceElem bool) { 41 switch v.Kind() { 42 case reflect.Ptr: 43 w.doPtr(v, sliceElem) 44 case reflect.Interface: 45 w.doInterface(v) 46 case reflect.Slice: 47 w.doSlice(v) 48 case reflect.Struct: 49 w.doStruct(v, sliceElem) 50 case reflect.Bool: 51 if v.Bool() { 52 w.string("true") 53 } else { 54 w.string("false") 55 } 56 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 57 fmt.Fprintf(w.w, "%v", v.Int()) 58 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 59 fmt.Fprintf(w.w, "%v", v.Uint()) 60 case reflect.String: 61 fmt.Fprintf(w.w, "%q", v.String()) 62 case reflect.Func: 63 if !v.IsNil() { 64 panic("no way to serialize funcs") 65 } 66 fmt.Fprintf(w.w, "nil") 67 default: 68 panic(fmt.Sprintf("unsupported type: %#v", v.Type().String())) 69 } 70 } 71 72 func (w *writer) doPtr(v reflect.Value, sliceElem bool) { 73 if v.IsNil() { 74 w.string("nil") 75 return 76 } 77 if !sliceElem { 78 w.byte('&') 79 } 80 if v.Elem().Kind() != reflect.Struct { 81 panic(fmt.Sprintf("only pointers to structs are supported, got %v", 82 v.Type().Name())) 83 } 84 w.do(v.Elem(), sliceElem) 85 } 86 87 func (w *writer) doInterface(v reflect.Value) { 88 if v.IsNil() { 89 w.string("nil") 90 return 91 } 92 elem := v.Elem() 93 // Handling of user types that has underlying primitive types. Consider: 94 // type T int 95 // var obj interface{} = T(42) 96 // T has kind reflect.Int. But if we serialize obj as just "42", it will be turned into plain int. 97 // Detect this case and serialize obj as "T(42)". 98 if (elem.Kind() == reflect.Bool || elem.Kind() == reflect.String || 99 elem.Type().ConvertibleTo(reflect.TypeOf(0))) && 100 strings.Contains(elem.Type().String(), ".") { 101 w.string(elem.Type().Name()) 102 w.byte('(') 103 w.do(elem, false) 104 w.byte(')') 105 return 106 } 107 w.do(elem, false) 108 } 109 110 func (w *writer) doSlice(v reflect.Value) { 111 if v.IsNil() || v.Len() == 0 { 112 w.string("nil") 113 return 114 } 115 w.typ(v.Type()) 116 sub := v.Type().Elem().Kind() 117 if sub == reflect.Ptr || sub == reflect.Interface || sub == reflect.Struct { 118 // Elem per-line. 119 w.string("{\n") 120 for i := 0; i < v.Len(); i++ { 121 w.do(v.Index(i), true) 122 w.string(",\n") 123 } 124 w.byte('}') 125 return 126 } 127 // All on one line. 128 w.byte('{') 129 for i := 0; i < v.Len(); i++ { 130 if i > 0 { 131 w.byte(',') 132 } 133 w.do(v.Index(i), true) 134 } 135 w.byte('}') 136 } 137 138 func (w *writer) doStruct(v reflect.Value, sliceElem bool) { 139 if !sliceElem { 140 w.string(v.Type().Name()) 141 } 142 w.byte('{') 143 fieldNames := false 144 for i := 0; i < v.NumField(); i++ { 145 f := v.Field(i) 146 if isDefaultValue(f) || !f.CanSet() { 147 fieldNames = true 148 break 149 } 150 } 151 needComma := false 152 for i := 0; i < v.NumField(); i++ { 153 f := v.Field(i) 154 if fieldNames && (isDefaultValue(f) || !f.CanSet()) { 155 continue 156 } 157 if needComma { 158 w.byte(',') 159 } 160 if fieldNames { 161 w.string(v.Type().Field(i).Name) 162 w.byte(':') 163 } 164 w.do(f, false) 165 needComma = true 166 } 167 w.byte('}') 168 } 169 170 func (w *writer) typ(t reflect.Type) { 171 switch t.Kind() { 172 case reflect.Ptr: 173 w.byte('*') 174 w.typ(t.Elem()) 175 case reflect.Slice: 176 w.string("[]") 177 w.typ(t.Elem()) 178 default: 179 w.string(t.Name()) 180 } 181 } 182 183 func (w *writer) string(v string) { 184 io.WriteString(w.w, v) 185 } 186 187 func (w *writer) byte(v byte) { 188 if bw, ok := w.w.(io.ByteWriter); ok { 189 bw.WriteByte(v) 190 } else { 191 w.w.Write([]byte{v}) 192 } 193 } 194 195 func isDefaultValue(v reflect.Value) bool { 196 switch v.Kind() { 197 case reflect.Ptr: 198 return v.IsNil() 199 case reflect.Interface: 200 return v.IsNil() 201 case reflect.Slice: 202 return v.IsNil() || v.Len() == 0 203 case reflect.Struct: 204 for i := 0; i < v.NumField(); i++ { 205 if !isDefaultValue(v.Field(i)) { 206 return false 207 } 208 } 209 return true 210 case reflect.Bool: 211 return !v.Bool() 212 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 213 return v.Int() == 0 214 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 215 return v.Uint() == 0 216 case reflect.String: 217 return v.String() == "" 218 case reflect.Func: 219 return false 220 default: 221 return false 222 } 223 }