string.go (6029B)
1 package mp4 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "reflect" 8 "strconv" 9 10 "github.com/abema/go-mp4/util" 11 ) 12 13 type stringifier struct { 14 buf *bytes.Buffer 15 src IImmutableBox 16 indent string 17 ctx Context 18 } 19 20 func Stringify(src IImmutableBox, ctx Context) (string, error) { 21 return StringifyWithIndent(src, "", ctx) 22 } 23 24 func StringifyWithIndent(src IImmutableBox, indent string, ctx Context) (string, error) { 25 boxDef := src.GetType().getBoxDef(ctx) 26 if boxDef == nil { 27 return "", ErrBoxInfoNotFound 28 } 29 30 v := reflect.ValueOf(src).Elem() 31 32 m := &stringifier{ 33 buf: bytes.NewBuffer(nil), 34 src: src, 35 indent: indent, 36 ctx: ctx, 37 } 38 39 err := m.stringifyStruct(v, boxDef.fields, 0, true) 40 if err != nil { 41 return "", err 42 } 43 44 return m.buf.String(), nil 45 } 46 47 func (m *stringifier) stringify(v reflect.Value, fi *fieldInstance, depth int) error { 48 switch v.Type().Kind() { 49 case reflect.Ptr: 50 return m.stringifyPtr(v, fi, depth) 51 case reflect.Struct: 52 return m.stringifyStruct(v, fi.children, depth, fi.is(fieldExtend)) 53 case reflect.Array: 54 return m.stringifyArray(v, fi, depth) 55 case reflect.Slice: 56 return m.stringifySlice(v, fi, depth) 57 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 58 return m.stringifyInt(v, fi, depth) 59 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 60 return m.stringifyUint(v, fi, depth) 61 case reflect.Bool: 62 return m.stringifyBool(v, depth) 63 case reflect.String: 64 return m.stringifyString(v, depth) 65 default: 66 return fmt.Errorf("unsupported type: %s", v.Type().Kind()) 67 } 68 } 69 70 func (m *stringifier) stringifyPtr(v reflect.Value, fi *fieldInstance, depth int) error { 71 return m.stringify(v.Elem(), fi, depth) 72 } 73 74 func (m *stringifier) stringifyStruct(v reflect.Value, fs []*field, depth int, extended bool) error { 75 if !extended { 76 m.buf.WriteString("{") 77 if m.indent != "" { 78 m.buf.WriteString("\n") 79 } 80 depth++ 81 } 82 83 for _, f := range fs { 84 fi := resolveFieldInstance(f, m.src, v, m.ctx) 85 86 if !isTargetField(m.src, fi, m.ctx) { 87 continue 88 } 89 90 if f.cnst != "" || f.is(fieldHidden) { 91 continue 92 } 93 94 if !f.is(fieldExtend) { 95 if m.indent != "" { 96 writeIndent(m.buf, m.indent, depth+1) 97 } else if m.buf.Len() != 0 && m.buf.Bytes()[m.buf.Len()-1] != '{' { 98 m.buf.WriteString(" ") 99 } 100 m.buf.WriteString(f.name) 101 m.buf.WriteString("=") 102 } 103 104 str, ok := fi.cfo.StringifyField(f.name, m.indent, depth+1, m.ctx) 105 if ok { 106 m.buf.WriteString(str) 107 if !f.is(fieldExtend) && m.indent != "" { 108 m.buf.WriteString("\n") 109 } 110 continue 111 } 112 113 if f.name == "Version" { 114 m.buf.WriteString(strconv.Itoa(int(m.src.GetVersion()))) 115 } else if f.name == "Flags" { 116 fmt.Fprintf(m.buf, "0x%06x", m.src.GetFlags()) 117 } else { 118 err := m.stringify(v.FieldByName(f.name), fi, depth) 119 if err != nil { 120 return err 121 } 122 } 123 124 if !f.is(fieldExtend) && m.indent != "" { 125 m.buf.WriteString("\n") 126 } 127 } 128 129 if !extended { 130 if m.indent != "" { 131 writeIndent(m.buf, m.indent, depth) 132 } 133 m.buf.WriteString("}") 134 } 135 136 return nil 137 } 138 139 func (m *stringifier) stringifyArray(v reflect.Value, fi *fieldInstance, depth int) error { 140 begin, sep, end := "[", ", ", "]" 141 if fi.is(fieldString) || fi.is(fieldISO639_2) { 142 begin, sep, end = "\"", "", "\"" 143 } else if fi.is(fieldUUID) { 144 begin, sep, end = "", "", "" 145 } 146 147 m.buf.WriteString(begin) 148 149 m2 := *m 150 if fi.is(fieldString) { 151 m2.buf = bytes.NewBuffer(nil) 152 } 153 size := v.Type().Size() 154 for i := 0; i < int(size)/int(v.Type().Elem().Size()); i++ { 155 if i != 0 { 156 m2.buf.WriteString(sep) 157 } 158 159 if err := m2.stringify(v.Index(i), fi, depth+1); err != nil { 160 return err 161 } 162 163 if fi.is(fieldUUID) && (i == 3 || i == 5 || i == 7 || i == 9) { 164 m.buf.WriteString("-") 165 } 166 } 167 if fi.is(fieldString) { 168 m.buf.WriteString(util.EscapeUnprintables(m2.buf.String())) 169 } 170 171 m.buf.WriteString(end) 172 173 return nil 174 } 175 176 func (m *stringifier) stringifySlice(v reflect.Value, fi *fieldInstance, depth int) error { 177 begin, sep, end := "[", ", ", "]" 178 if fi.is(fieldString) || fi.is(fieldISO639_2) { 179 begin, sep, end = "\"", "", "\"" 180 } 181 182 m.buf.WriteString(begin) 183 184 m2 := *m 185 if fi.is(fieldString) { 186 m2.buf = bytes.NewBuffer(nil) 187 } 188 for i := 0; i < v.Len(); i++ { 189 if fi.length != LengthUnlimited && uint(i) >= fi.length { 190 break 191 } 192 193 if i != 0 { 194 m2.buf.WriteString(sep) 195 } 196 197 if err := m2.stringify(v.Index(i), fi, depth+1); err != nil { 198 return err 199 } 200 } 201 if fi.is(fieldString) { 202 m.buf.WriteString(util.EscapeUnprintables(m2.buf.String())) 203 } 204 205 m.buf.WriteString(end) 206 207 return nil 208 } 209 210 func (m *stringifier) stringifyInt(v reflect.Value, fi *fieldInstance, depth int) error { 211 if fi.is(fieldHex) { 212 val := v.Int() 213 if val >= 0 { 214 m.buf.WriteString("0x") 215 m.buf.WriteString(strconv.FormatInt(val, 16)) 216 } else { 217 m.buf.WriteString("-0x") 218 m.buf.WriteString(strconv.FormatInt(-val, 16)) 219 } 220 } else { 221 m.buf.WriteString(strconv.FormatInt(v.Int(), 10)) 222 } 223 return nil 224 } 225 226 func (m *stringifier) stringifyUint(v reflect.Value, fi *fieldInstance, depth int) error { 227 if fi.is(fieldISO639_2) { 228 m.buf.WriteString(string([]byte{byte(v.Uint() + 0x60)})) 229 } else if fi.is(fieldUUID) { 230 fmt.Fprintf(m.buf, "%02x", v.Uint()) 231 } else if fi.is(fieldString) { 232 m.buf.WriteString(string([]byte{byte(v.Uint())})) 233 } else if fi.is(fieldHex) || (!fi.is(fieldDec) && v.Type().Kind() == reflect.Uint8) || v.Type().Kind() == reflect.Uintptr { 234 m.buf.WriteString("0x") 235 m.buf.WriteString(strconv.FormatUint(v.Uint(), 16)) 236 } else { 237 m.buf.WriteString(strconv.FormatUint(v.Uint(), 10)) 238 } 239 240 return nil 241 } 242 243 func (m *stringifier) stringifyBool(v reflect.Value, depth int) error { 244 m.buf.WriteString(strconv.FormatBool(v.Bool())) 245 246 return nil 247 } 248 249 func (m *stringifier) stringifyString(v reflect.Value, depth int) error { 250 m.buf.WriteString("\"") 251 m.buf.WriteString(util.EscapeUnprintables(v.String())) 252 m.buf.WriteString("\"") 253 254 return nil 255 } 256 257 func writeIndent(w io.Writer, indent string, depth int) { 258 for i := 0; i < depth; i++ { 259 io.WriteString(w, indent) 260 } 261 }