funcr: Use WriteByte instead of WriteRune
We know all these inputs are single-byte, so the overhead of WriteRune
is wasted.
Before:
```
BenchmarkFuncrLogInfoOneArg-6 2006905 597.2 ns/op
BenchmarkFuncrJSONLogInfoOneArg-6 1728062 694.4 ns/op
BenchmarkFuncrLogInfoSeveralArgs-6 973250 1239 ns/op
BenchmarkFuncrJSONLogInfoSeveralArgs-6 874827 1347 ns/op
BenchmarkFuncrLogV0Info-6 977566 1231 ns/op
BenchmarkFuncrJSONLogV0Info-6 891244 1345 ns/op
BenchmarkFuncrLogV9Info-6 14962072 80.68 ns/op
BenchmarkFuncrJSONLogV9Info-6 14989345 80.21 ns/op
BenchmarkFuncrLogError-6 815452 1469 ns/op
BenchmarkFuncrJSONLogError-6 754250 1587 ns/op
```
After:
```
BenchmarkFuncrLogInfoOneArg-6 2062580 582.6 ns/op
BenchmarkFuncrJSONLogInfoOneArg-6 1790674 669.6 ns/op
BenchmarkFuncrLogInfoSeveralArgs-6 1000000 1124 ns/op
BenchmarkFuncrJSONLogInfoSeveralArgs-6 949893 1255 ns/op
BenchmarkFuncrLogV0Info-6 1000000 1153 ns/op
BenchmarkFuncrJSONLogV0Info-6 953002 1259 ns/op
BenchmarkFuncrLogV9Info-6 14849647 81.30 ns/op
BenchmarkFuncrJSONLogV9Info-6 14510906 81.32 ns/op
BenchmarkFuncrLogError-6 857920 1389 ns/op
BenchmarkFuncrJSONLogError-6 803823 1499 ns/op
```
Tim Hockin
2 years ago
196 | 196 | // Empirically bytes.Buffer is faster than strings.Builder for this. |
197 | 197 | buf := bytes.NewBuffer(make([]byte, 0, 1024)) |
198 | 198 | if f.outputFormat == outputJSON { |
199 | buf.WriteRune('{') | |
199 | buf.WriteByte('{') | |
200 | 200 | } |
201 | 201 | f.flatten(buf, builtins, false) |
202 | 202 | continuing := len(builtins) > 0 |
203 | 203 | if len(f.valuesStr) > 0 { |
204 | 204 | if continuing { |
205 | 205 | if f.outputFormat == outputJSON { |
206 | buf.WriteRune(',') | |
206 | buf.WriteByte(',') | |
207 | 207 | } else { |
208 | buf.WriteRune(' ') | |
208 | buf.WriteByte(' ') | |
209 | 209 | } |
210 | 210 | } |
211 | 211 | continuing = true |
213 | 213 | } |
214 | 214 | f.flatten(buf, args, continuing) |
215 | 215 | if f.outputFormat == outputJSON { |
216 | buf.WriteRune('}') | |
216 | buf.WriteByte('}') | |
217 | 217 | } |
218 | 218 | return buf.String() |
219 | 219 | } |
235 | 235 | |
236 | 236 | if i > 0 || continuing { |
237 | 237 | if f.outputFormat == outputJSON { |
238 | buf.WriteRune(',') | |
238 | buf.WriteByte(',') | |
239 | 239 | } else { |
240 | 240 | // In theory the format could be something we don't understand. In |
241 | 241 | // practice, we control it, so it won't |
242 | buf.WriteRune(' ') | |
243 | } | |
244 | } | |
245 | buf.WriteRune('"') | |
242 | buf.WriteByte(' ') | |
243 | } | |
244 | } | |
245 | buf.WriteByte('"') | |
246 | 246 | buf.WriteString(k) |
247 | buf.WriteRune('"') | |
247 | buf.WriteByte('"') | |
248 | 248 | if f.outputFormat == outputJSON { |
249 | buf.WriteRune(':') | |
249 | buf.WriteByte(':') | |
250 | 250 | } else { |
251 | buf.WriteRune('=') | |
251 | buf.WriteByte('=') | |
252 | 252 | } |
253 | 253 | buf.WriteString(f.pretty(v)) |
254 | 254 | } |
349 | 349 | case reflect.Complex128: |
350 | 350 | return `"` + strconv.FormatComplex(v.Complex(), 'f', -1, 128) + `"` |
351 | 351 | case reflect.Struct: |
352 | buf.WriteRune('{') | |
352 | buf.WriteByte('{') | |
353 | 353 | for i := 0; i < t.NumField(); i++ { |
354 | 354 | fld := t.Field(i) |
355 | 355 | if fld.PkgPath != "" { |
357 | 357 | continue |
358 | 358 | } |
359 | 359 | if i > 0 { |
360 | buf.WriteRune(',') | |
361 | } | |
362 | buf.WriteRune('"') | |
360 | buf.WriteByte(',') | |
361 | } | |
362 | buf.WriteByte('"') | |
363 | 363 | name := fld.Name |
364 | 364 | if tag, found := fld.Tag.Lookup("json"); found { |
365 | 365 | if comma := strings.Index(tag, ","); comma != -1 { |
369 | 369 | } |
370 | 370 | } |
371 | 371 | buf.WriteString(name) |
372 | buf.WriteRune('"') | |
373 | buf.WriteRune(':') | |
372 | buf.WriteByte('"') | |
373 | buf.WriteByte(':') | |
374 | 374 | buf.WriteString(f.pretty(v.Field(i).Interface())) |
375 | 375 | } |
376 | buf.WriteRune('}') | |
376 | buf.WriteByte('}') | |
377 | 377 | return buf.String() |
378 | 378 | case reflect.Slice, reflect.Array: |
379 | buf.WriteRune('[') | |
379 | buf.WriteByte('[') | |
380 | 380 | for i := 0; i < v.Len(); i++ { |
381 | 381 | if i > 0 { |
382 | buf.WriteRune(',') | |
382 | buf.WriteByte(',') | |
383 | 383 | } |
384 | 384 | e := v.Index(i) |
385 | 385 | buf.WriteString(f.pretty(e.Interface())) |
386 | 386 | } |
387 | buf.WriteRune(']') | |
387 | buf.WriteByte(']') | |
388 | 388 | return buf.String() |
389 | 389 | case reflect.Map: |
390 | buf.WriteRune('{') | |
390 | buf.WriteByte('{') | |
391 | 391 | // This does not sort the map keys, for best perf. |
392 | 392 | it := v.MapRange() |
393 | 393 | i := 0 |
394 | 394 | for it.Next() { |
395 | 395 | if i > 0 { |
396 | buf.WriteRune(',') | |
396 | buf.WriteByte(',') | |
397 | 397 | } |
398 | 398 | // JSON only does string keys. |
399 | buf.WriteRune('"') | |
399 | buf.WriteByte('"') | |
400 | 400 | buf.WriteString(f.prettyWithFlags(it.Key().Interface(), flagRawString)) |
401 | buf.WriteRune('"') | |
402 | buf.WriteRune(':') | |
401 | buf.WriteByte('"') | |
402 | buf.WriteByte(':') | |
403 | 403 | buf.WriteString(f.pretty(it.Value().Interface())) |
404 | 404 | i++ |
405 | 405 | } |
406 | buf.WriteRune('}') | |
406 | buf.WriteByte('}') | |
407 | 407 | return buf.String() |
408 | 408 | case reflect.Ptr, reflect.Interface: |
409 | 409 | if v.IsNil() { |