1 | // Copyright 2016 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // Binary package export. |
6 | // This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; |
7 | // see that file for specification of the format. |
8 | |
9 | package gcimporter |
10 | |
11 | import ( |
12 | "bytes" |
13 | "encoding/binary" |
14 | "fmt" |
15 | "go/constant" |
16 | "go/token" |
17 | "go/types" |
18 | "math" |
19 | "math/big" |
20 | "sort" |
21 | "strings" |
22 | ) |
23 | |
24 | // If debugFormat is set, each integer and string value is preceded by a marker |
25 | // and position information in the encoding. This mechanism permits an importer |
26 | // to recognize immediately when it is out of sync. The importer recognizes this |
27 | // mode automatically (i.e., it can import export data produced with debugging |
28 | // support even if debugFormat is not set at the time of import). This mode will |
29 | // lead to massively larger export data (by a factor of 2 to 3) and should only |
30 | // be enabled during development and debugging. |
31 | // |
32 | // NOTE: This flag is the first flag to enable if importing dies because of |
33 | // (suspected) format errors, and whenever a change is made to the format. |
34 | const debugFormat = false // default: false |
35 | |
36 | // Current export format version. Increase with each format change. |
37 | // |
38 | // Note: The latest binary (non-indexed) export format is at version 6. |
39 | // This exporter is still at level 4, but it doesn't matter since |
40 | // the binary importer can handle older versions just fine. |
41 | // |
42 | // 6: package height (CL 105038) -- NOT IMPLEMENTED HERE |
43 | // 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE |
44 | // 4: type name objects support type aliases, uses aliasTag |
45 | // 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) |
46 | // 2: removed unused bool in ODCL export (compiler only) |
47 | // 1: header format change (more regular), export package for _ struct fields |
48 | // 0: Go1.7 encoding |
49 | const exportVersion = 4 |
50 | |
51 | // trackAllTypes enables cycle tracking for all types, not just named |
52 | // types. The existing compiler invariants assume that unnamed types |
53 | // that are not completely set up are not used, or else there are spurious |
54 | // errors. |
55 | // If disabled, only named types are tracked, possibly leading to slightly |
56 | // less efficient encoding in rare cases. It also prevents the export of |
57 | // some corner-case type declarations (but those are not handled correctly |
58 | // with with the textual export format either). |
59 | // TODO(gri) enable and remove once issues caused by it are fixed |
60 | const trackAllTypes = false |
61 | |
62 | type exporter struct { |
63 | fset *token.FileSet |
64 | out bytes.Buffer |
65 | |
66 | // object -> index maps, indexed in order of serialization |
67 | strIndex map[string]int |
68 | pkgIndex map[*types.Package]int |
69 | typIndex map[types.Type]int |
70 | |
71 | // position encoding |
72 | posInfoFormat bool |
73 | prevFile string |
74 | prevLine int |
75 | |
76 | // debugging support |
77 | written int // bytes written |
78 | indent int // for trace |
79 | } |
80 | |
81 | // internalError represents an error generated inside this package. |
82 | type internalError string |
83 | |
84 | func (e internalError) Error() string { return "gcimporter: " + string(e) } |
85 | |
86 | func internalErrorf(format string, args ...interface{}) error { |
87 | return internalError(fmt.Sprintf(format, args...)) |
88 | } |
89 | |
90 | // BExportData returns binary export data for pkg. |
91 | // If no file set is provided, position info will be missing. |
92 | func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { |
93 | if !debug { |
94 | defer func() { |
95 | if e := recover(); e != nil { |
96 | if ierr, ok := e.(internalError); ok { |
97 | err = ierr |
98 | return |
99 | } |
100 | // Not an internal error; panic again. |
101 | panic(e) |
102 | } |
103 | }() |
104 | } |
105 | |
106 | p := exporter{ |
107 | fset: fset, |
108 | strIndex: map[string]int{"": 0}, // empty string is mapped to 0 |
109 | pkgIndex: make(map[*types.Package]int), |
110 | typIndex: make(map[types.Type]int), |
111 | posInfoFormat: true, // TODO(gri) might become a flag, eventually |
112 | } |
113 | |
114 | // write version info |
115 | // The version string must start with "version %d" where %d is the version |
116 | // number. Additional debugging information may follow after a blank; that |
117 | // text is ignored by the importer. |
118 | p.rawStringln(fmt.Sprintf("version %d", exportVersion)) |
119 | var debug string |
120 | if debugFormat { |
121 | debug = "debug" |
122 | } |
123 | p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly |
124 | p.bool(trackAllTypes) |
125 | p.bool(p.posInfoFormat) |
126 | |
127 | // --- generic export data --- |
128 | |
129 | // populate type map with predeclared "known" types |
130 | for index, typ := range predeclared() { |
131 | p.typIndex[typ] = index |
132 | } |
133 | if len(p.typIndex) != len(predeclared()) { |
134 | return nil, internalError("duplicate entries in type map?") |
135 | } |
136 | |
137 | // write package data |
138 | p.pkg(pkg, true) |
139 | if trace { |
140 | p.tracef("\n") |
141 | } |
142 | |
143 | // write objects |
144 | objcount := 0 |
145 | scope := pkg.Scope() |
146 | for _, name := range scope.Names() { |
147 | if !token.IsExported(name) { |
148 | continue |
149 | } |
150 | if trace { |
151 | p.tracef("\n") |
152 | } |
153 | p.obj(scope.Lookup(name)) |
154 | objcount++ |
155 | } |
156 | |
157 | // indicate end of list |
158 | if trace { |
159 | p.tracef("\n") |
160 | } |
161 | p.tag(endTag) |
162 | |
163 | // for self-verification only (redundant) |
164 | p.int(objcount) |
165 | |
166 | if trace { |
167 | p.tracef("\n") |
168 | } |
169 | |
170 | // --- end of export data --- |
171 | |
172 | return p.out.Bytes(), nil |
173 | } |
174 | |
175 | func (p *exporter) pkg(pkg *types.Package, emptypath bool) { |
176 | if pkg == nil { |
177 | panic(internalError("unexpected nil pkg")) |
178 | } |
179 | |
180 | // if we saw the package before, write its index (>= 0) |
181 | if i, ok := p.pkgIndex[pkg]; ok { |
182 | p.index('P', i) |
183 | return |
184 | } |
185 | |
186 | // otherwise, remember the package, write the package tag (< 0) and package data |
187 | if trace { |
188 | p.tracef("P%d = { ", len(p.pkgIndex)) |
189 | defer p.tracef("} ") |
190 | } |
191 | p.pkgIndex[pkg] = len(p.pkgIndex) |
192 | |
193 | p.tag(packageTag) |
194 | p.string(pkg.Name()) |
195 | if emptypath { |
196 | p.string("") |
197 | } else { |
198 | p.string(pkg.Path()) |
199 | } |
200 | } |
201 | |
202 | func (p *exporter) obj(obj types.Object) { |
203 | switch obj := obj.(type) { |
204 | case *types.Const: |
205 | p.tag(constTag) |
206 | p.pos(obj) |
207 | p.qualifiedName(obj) |
208 | p.typ(obj.Type()) |
209 | p.value(obj.Val()) |
210 | |
211 | case *types.TypeName: |
212 | if obj.IsAlias() { |
213 | p.tag(aliasTag) |
214 | p.pos(obj) |
215 | p.qualifiedName(obj) |
216 | } else { |
217 | p.tag(typeTag) |
218 | } |
219 | p.typ(obj.Type()) |
220 | |
221 | case *types.Var: |
222 | p.tag(varTag) |
223 | p.pos(obj) |
224 | p.qualifiedName(obj) |
225 | p.typ(obj.Type()) |
226 | |
227 | case *types.Func: |
228 | p.tag(funcTag) |
229 | p.pos(obj) |
230 | p.qualifiedName(obj) |
231 | sig := obj.Type().(*types.Signature) |
232 | p.paramList(sig.Params(), sig.Variadic()) |
233 | p.paramList(sig.Results(), false) |
234 | |
235 | default: |
236 | panic(internalErrorf("unexpected object %v (%T)", obj, obj)) |
237 | } |
238 | } |
239 | |
240 | func (p *exporter) pos(obj types.Object) { |
241 | if !p.posInfoFormat { |
242 | return |
243 | } |
244 | |
245 | file, line := p.fileLine(obj) |
246 | if file == p.prevFile { |
247 | // common case: write line delta |
248 | // delta == 0 means different file or no line change |
249 | delta := line - p.prevLine |
250 | p.int(delta) |
251 | if delta == 0 { |
252 | p.int(-1) // -1 means no file change |
253 | } |
254 | } else { |
255 | // different file |
256 | p.int(0) |
257 | // Encode filename as length of common prefix with previous |
258 | // filename, followed by (possibly empty) suffix. Filenames |
259 | // frequently share path prefixes, so this can save a lot |
260 | // of space and make export data size less dependent on file |
261 | // path length. The suffix is unlikely to be empty because |
262 | // file names tend to end in ".go". |
263 | n := commonPrefixLen(p.prevFile, file) |
264 | p.int(n) // n >= 0 |
265 | p.string(file[n:]) // write suffix only |
266 | p.prevFile = file |
267 | p.int(line) |
268 | } |
269 | p.prevLine = line |
270 | } |
271 | |
272 | func (p *exporter) fileLine(obj types.Object) (file string, line int) { |
273 | if p.fset != nil { |
274 | pos := p.fset.Position(obj.Pos()) |
275 | file = pos.Filename |
276 | line = pos.Line |
277 | } |
278 | return |
279 | } |
280 | |
281 | func commonPrefixLen(a, b string) int { |
282 | if len(a) > len(b) { |
283 | a, b = b, a |
284 | } |
285 | // len(a) <= len(b) |
286 | i := 0 |
287 | for i < len(a) && a[i] == b[i] { |
288 | i++ |
289 | } |
290 | return i |
291 | } |
292 | |
293 | func (p *exporter) qualifiedName(obj types.Object) { |
294 | p.string(obj.Name()) |
295 | p.pkg(obj.Pkg(), false) |
296 | } |
297 | |
298 | func (p *exporter) typ(t types.Type) { |
299 | if t == nil { |
300 | panic(internalError("nil type")) |
301 | } |
302 | |
303 | // Possible optimization: Anonymous pointer types *T where |
304 | // T is a named type are common. We could canonicalize all |
305 | // such types *T to a single type PT = *T. This would lead |
306 | // to at most one *T entry in typIndex, and all future *T's |
307 | // would be encoded as the respective index directly. Would |
308 | // save 1 byte (pointerTag) per *T and reduce the typIndex |
309 | // size (at the cost of a canonicalization map). We can do |
310 | // this later, without encoding format change. |
311 | |
312 | // if we saw the type before, write its index (>= 0) |
313 | if i, ok := p.typIndex[t]; ok { |
314 | p.index('T', i) |
315 | return |
316 | } |
317 | |
318 | // otherwise, remember the type, write the type tag (< 0) and type data |
319 | if trackAllTypes { |
320 | if trace { |
321 | p.tracef("T%d = {>\n", len(p.typIndex)) |
322 | defer p.tracef("<\n} ") |
323 | } |
324 | p.typIndex[t] = len(p.typIndex) |
325 | } |
326 | |
327 | switch t := t.(type) { |
328 | case *types.Named: |
329 | if !trackAllTypes { |
330 | // if we don't track all types, track named types now |
331 | p.typIndex[t] = len(p.typIndex) |
332 | } |
333 | |
334 | p.tag(namedTag) |
335 | p.pos(t.Obj()) |
336 | p.qualifiedName(t.Obj()) |
337 | p.typ(t.Underlying()) |
338 | if !types.IsInterface(t) { |
339 | p.assocMethods(t) |
340 | } |
341 | |
342 | case *types.Array: |
343 | p.tag(arrayTag) |
344 | p.int64(t.Len()) |
345 | p.typ(t.Elem()) |
346 | |
347 | case *types.Slice: |
348 | p.tag(sliceTag) |
349 | p.typ(t.Elem()) |
350 | |
351 | case *dddSlice: |
352 | p.tag(dddTag) |
353 | p.typ(t.elem) |
354 | |
355 | case *types.Struct: |
356 | p.tag(structTag) |
357 | p.fieldList(t) |
358 | |
359 | case *types.Pointer: |
360 | p.tag(pointerTag) |
361 | p.typ(t.Elem()) |
362 | |
363 | case *types.Signature: |
364 | p.tag(signatureTag) |
365 | p.paramList(t.Params(), t.Variadic()) |
366 | p.paramList(t.Results(), false) |
367 | |
368 | case *types.Interface: |
369 | p.tag(interfaceTag) |
370 | p.iface(t) |
371 | |
372 | case *types.Map: |
373 | p.tag(mapTag) |
374 | p.typ(t.Key()) |
375 | p.typ(t.Elem()) |
376 | |
377 | case *types.Chan: |
378 | p.tag(chanTag) |
379 | p.int(int(3 - t.Dir())) // hack |
380 | p.typ(t.Elem()) |
381 | |
382 | default: |
383 | panic(internalErrorf("unexpected type %T: %s", t, t)) |
384 | } |
385 | } |
386 | |
387 | func (p *exporter) assocMethods(named *types.Named) { |
388 | // Sort methods (for determinism). |
389 | var methods []*types.Func |
390 | for i := 0; i < named.NumMethods(); i++ { |
391 | methods = append(methods, named.Method(i)) |
392 | } |
393 | sort.Sort(methodsByName(methods)) |
394 | |
395 | p.int(len(methods)) |
396 | |
397 | if trace && methods != nil { |
398 | p.tracef("associated methods {>\n") |
399 | } |
400 | |
401 | for i, m := range methods { |
402 | if trace && i > 0 { |
403 | p.tracef("\n") |
404 | } |
405 | |
406 | p.pos(m) |
407 | name := m.Name() |
408 | p.string(name) |
409 | if !exported(name) { |
410 | p.pkg(m.Pkg(), false) |
411 | } |
412 | |
413 | sig := m.Type().(*types.Signature) |
414 | p.paramList(types.NewTuple(sig.Recv()), false) |
415 | p.paramList(sig.Params(), sig.Variadic()) |
416 | p.paramList(sig.Results(), false) |
417 | p.int(0) // dummy value for go:nointerface pragma - ignored by importer |
418 | } |
419 | |
420 | if trace && methods != nil { |
421 | p.tracef("<\n} ") |
422 | } |
423 | } |
424 | |
425 | type methodsByName []*types.Func |
426 | |
427 | func (x methodsByName) Len() int { return len(x) } |
428 | func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } |
429 | func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } |
430 | |
431 | func (p *exporter) fieldList(t *types.Struct) { |
432 | if trace && t.NumFields() > 0 { |
433 | p.tracef("fields {>\n") |
434 | defer p.tracef("<\n} ") |
435 | } |
436 | |
437 | p.int(t.NumFields()) |
438 | for i := 0; i < t.NumFields(); i++ { |
439 | if trace && i > 0 { |
440 | p.tracef("\n") |
441 | } |
442 | p.field(t.Field(i)) |
443 | p.string(t.Tag(i)) |
444 | } |
445 | } |
446 | |
447 | func (p *exporter) field(f *types.Var) { |
448 | if !f.IsField() { |
449 | panic(internalError("field expected")) |
450 | } |
451 | |
452 | p.pos(f) |
453 | p.fieldName(f) |
454 | p.typ(f.Type()) |
455 | } |
456 | |
457 | func (p *exporter) iface(t *types.Interface) { |
458 | // TODO(gri): enable importer to load embedded interfaces, |
459 | // then emit Embeddeds and ExplicitMethods separately here. |
460 | p.int(0) |
461 | |
462 | n := t.NumMethods() |
463 | if trace && n > 0 { |
464 | p.tracef("methods {>\n") |
465 | defer p.tracef("<\n} ") |
466 | } |
467 | p.int(n) |
468 | for i := 0; i < n; i++ { |
469 | if trace && i > 0 { |
470 | p.tracef("\n") |
471 | } |
472 | p.method(t.Method(i)) |
473 | } |
474 | } |
475 | |
476 | func (p *exporter) method(m *types.Func) { |
477 | sig := m.Type().(*types.Signature) |
478 | if sig.Recv() == nil { |
479 | panic(internalError("method expected")) |
480 | } |
481 | |
482 | p.pos(m) |
483 | p.string(m.Name()) |
484 | if m.Name() != "_" && !token.IsExported(m.Name()) { |
485 | p.pkg(m.Pkg(), false) |
486 | } |
487 | |
488 | // interface method; no need to encode receiver. |
489 | p.paramList(sig.Params(), sig.Variadic()) |
490 | p.paramList(sig.Results(), false) |
491 | } |
492 | |
493 | func (p *exporter) fieldName(f *types.Var) { |
494 | name := f.Name() |
495 | |
496 | if f.Anonymous() { |
497 | // anonymous field - we distinguish between 3 cases: |
498 | // 1) field name matches base type name and is exported |
499 | // 2) field name matches base type name and is not exported |
500 | // 3) field name doesn't match base type name (alias name) |
501 | bname := basetypeName(f.Type()) |
502 | if name == bname { |
503 | if token.IsExported(name) { |
504 | name = "" // 1) we don't need to know the field name or package |
505 | } else { |
506 | name = "?" // 2) use unexported name "?" to force package export |
507 | } |
508 | } else { |
509 | // 3) indicate alias and export name as is |
510 | // (this requires an extra "@" but this is a rare case) |
511 | p.string("@") |
512 | } |
513 | } |
514 | |
515 | p.string(name) |
516 | if name != "" && !token.IsExported(name) { |
517 | p.pkg(f.Pkg(), false) |
518 | } |
519 | } |
520 | |
521 | func basetypeName(typ types.Type) string { |
522 | switch typ := deref(typ).(type) { |
523 | case *types.Basic: |
524 | return typ.Name() |
525 | case *types.Named: |
526 | return typ.Obj().Name() |
527 | default: |
528 | return "" // unnamed type |
529 | } |
530 | } |
531 | |
532 | func (p *exporter) paramList(params *types.Tuple, variadic bool) { |
533 | // use negative length to indicate unnamed parameters |
534 | // (look at the first parameter only since either all |
535 | // names are present or all are absent) |
536 | n := params.Len() |
537 | if n > 0 && params.At(0).Name() == "" { |
538 | n = -n |
539 | } |
540 | p.int(n) |
541 | for i := 0; i < params.Len(); i++ { |
542 | q := params.At(i) |
543 | t := q.Type() |
544 | if variadic && i == params.Len()-1 { |
545 | t = &dddSlice{t.(*types.Slice).Elem()} |
546 | } |
547 | p.typ(t) |
548 | if n > 0 { |
549 | name := q.Name() |
550 | p.string(name) |
551 | if name != "_" { |
552 | p.pkg(q.Pkg(), false) |
553 | } |
554 | } |
555 | p.string("") // no compiler-specific info |
556 | } |
557 | } |
558 | |
559 | func (p *exporter) value(x constant.Value) { |
560 | if trace { |
561 | p.tracef("= ") |
562 | } |
563 | |
564 | switch x.Kind() { |
565 | case constant.Bool: |
566 | tag := falseTag |
567 | if constant.BoolVal(x) { |
568 | tag = trueTag |
569 | } |
570 | p.tag(tag) |
571 | |
572 | case constant.Int: |
573 | if v, exact := constant.Int64Val(x); exact { |
574 | // common case: x fits into an int64 - use compact encoding |
575 | p.tag(int64Tag) |
576 | p.int64(v) |
577 | return |
578 | } |
579 | // uncommon case: large x - use float encoding |
580 | // (powers of 2 will be encoded efficiently with exponent) |
581 | p.tag(floatTag) |
582 | p.float(constant.ToFloat(x)) |
583 | |
584 | case constant.Float: |
585 | p.tag(floatTag) |
586 | p.float(x) |
587 | |
588 | case constant.Complex: |
589 | p.tag(complexTag) |
590 | p.float(constant.Real(x)) |
591 | p.float(constant.Imag(x)) |
592 | |
593 | case constant.String: |
594 | p.tag(stringTag) |
595 | p.string(constant.StringVal(x)) |
596 | |
597 | case constant.Unknown: |
598 | // package contains type errors |
599 | p.tag(unknownTag) |
600 | |
601 | default: |
602 | panic(internalErrorf("unexpected value %v (%T)", x, x)) |
603 | } |
604 | } |
605 | |
606 | func (p *exporter) float(x constant.Value) { |
607 | if x.Kind() != constant.Float { |
608 | panic(internalErrorf("unexpected constant %v, want float", x)) |
609 | } |
610 | // extract sign (there is no -0) |
611 | sign := constant.Sign(x) |
612 | if sign == 0 { |
613 | // x == 0 |
614 | p.int(0) |
615 | return |
616 | } |
617 | // x != 0 |
618 | |
619 | var f big.Float |
620 | if v, exact := constant.Float64Val(x); exact { |
621 | // float64 |
622 | f.SetFloat64(v) |
623 | } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { |
624 | // TODO(gri): add big.Rat accessor to constant.Value. |
625 | r := valueToRat(num) |
626 | f.SetRat(r.Quo(r, valueToRat(denom))) |
627 | } else { |
628 | // Value too large to represent as a fraction => inaccessible. |
629 | // TODO(gri): add big.Float accessor to constant.Value. |
630 | f.SetFloat64(math.MaxFloat64) // FIXME |
631 | } |
632 | |
633 | // extract exponent such that 0.5 <= m < 1.0 |
634 | var m big.Float |
635 | exp := f.MantExp(&m) |
636 | |
637 | // extract mantissa as *big.Int |
638 | // - set exponent large enough so mant satisfies mant.IsInt() |
639 | // - get *big.Int from mant |
640 | m.SetMantExp(&m, int(m.MinPrec())) |
641 | mant, acc := m.Int(nil) |
642 | if acc != big.Exact { |
643 | panic(internalError("internal error")) |
644 | } |
645 | |
646 | p.int(sign) |
647 | p.int(exp) |
648 | p.string(string(mant.Bytes())) |
649 | } |
650 | |
651 | func valueToRat(x constant.Value) *big.Rat { |
652 | // Convert little-endian to big-endian. |
653 | // I can't believe this is necessary. |
654 | bytes := constant.Bytes(x) |
655 | for i := 0; i < len(bytes)/2; i++ { |
656 | bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] |
657 | } |
658 | return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) |
659 | } |
660 | |
661 | func (p *exporter) bool(b bool) bool { |
662 | if trace { |
663 | p.tracef("[") |
664 | defer p.tracef("= %v] ", b) |
665 | } |
666 | |
667 | x := 0 |
668 | if b { |
669 | x = 1 |
670 | } |
671 | p.int(x) |
672 | return b |
673 | } |
674 | |
675 | // ---------------------------------------------------------------------------- |
676 | // Low-level encoders |
677 | |
678 | func (p *exporter) index(marker byte, index int) { |
679 | if index < 0 { |
680 | panic(internalError("invalid index < 0")) |
681 | } |
682 | if debugFormat { |
683 | p.marker('t') |
684 | } |
685 | if trace { |
686 | p.tracef("%c%d ", marker, index) |
687 | } |
688 | p.rawInt64(int64(index)) |
689 | } |
690 | |
691 | func (p *exporter) tag(tag int) { |
692 | if tag >= 0 { |
693 | panic(internalError("invalid tag >= 0")) |
694 | } |
695 | if debugFormat { |
696 | p.marker('t') |
697 | } |
698 | if trace { |
699 | p.tracef("%s ", tagString[-tag]) |
700 | } |
701 | p.rawInt64(int64(tag)) |
702 | } |
703 | |
704 | func (p *exporter) int(x int) { |
705 | p.int64(int64(x)) |
706 | } |
707 | |
708 | func (p *exporter) int64(x int64) { |
709 | if debugFormat { |
710 | p.marker('i') |
711 | } |
712 | if trace { |
713 | p.tracef("%d ", x) |
714 | } |
715 | p.rawInt64(x) |
716 | } |
717 | |
718 | func (p *exporter) string(s string) { |
719 | if debugFormat { |
720 | p.marker('s') |
721 | } |
722 | if trace { |
723 | p.tracef("%q ", s) |
724 | } |
725 | // if we saw the string before, write its index (>= 0) |
726 | // (the empty string is mapped to 0) |
727 | if i, ok := p.strIndex[s]; ok { |
728 | p.rawInt64(int64(i)) |
729 | return |
730 | } |
731 | // otherwise, remember string and write its negative length and bytes |
732 | p.strIndex[s] = len(p.strIndex) |
733 | p.rawInt64(-int64(len(s))) |
734 | for i := 0; i < len(s); i++ { |
735 | p.rawByte(s[i]) |
736 | } |
737 | } |
738 | |
739 | // marker emits a marker byte and position information which makes |
740 | // it easy for a reader to detect if it is "out of sync". Used for |
741 | // debugFormat format only. |
742 | func (p *exporter) marker(m byte) { |
743 | p.rawByte(m) |
744 | // Enable this for help tracking down the location |
745 | // of an incorrect marker when running in debugFormat. |
746 | if false && trace { |
747 | p.tracef("#%d ", p.written) |
748 | } |
749 | p.rawInt64(int64(p.written)) |
750 | } |
751 | |
752 | // rawInt64 should only be used by low-level encoders. |
753 | func (p *exporter) rawInt64(x int64) { |
754 | var tmp [binary.MaxVarintLen64]byte |
755 | n := binary.PutVarint(tmp[:], x) |
756 | for i := 0; i < n; i++ { |
757 | p.rawByte(tmp[i]) |
758 | } |
759 | } |
760 | |
761 | // rawStringln should only be used to emit the initial version string. |
762 | func (p *exporter) rawStringln(s string) { |
763 | for i := 0; i < len(s); i++ { |
764 | p.rawByte(s[i]) |
765 | } |
766 | p.rawByte('\n') |
767 | } |
768 | |
769 | // rawByte is the bottleneck interface to write to p.out. |
770 | // rawByte escapes b as follows (any encoding does that |
771 | // hides '$'): |
772 | // |
773 | // '$' => '|' 'S' |
774 | // '|' => '|' '|' |
775 | // |
776 | // Necessary so other tools can find the end of the |
777 | // export data by searching for "$$". |
778 | // rawByte should only be used by low-level encoders. |
779 | func (p *exporter) rawByte(b byte) { |
780 | switch b { |
781 | case '$': |
782 | // write '$' as '|' 'S' |
783 | b = 'S' |
784 | fallthrough |
785 | case '|': |
786 | // write '|' as '|' '|' |
787 | p.out.WriteByte('|') |
788 | p.written++ |
789 | } |
790 | p.out.WriteByte(b) |
791 | p.written++ |
792 | } |
793 | |
794 | // tracef is like fmt.Printf but it rewrites the format string |
795 | // to take care of indentation. |
796 | func (p *exporter) tracef(format string, args ...interface{}) { |
797 | if strings.ContainsAny(format, "<>\n") { |
798 | var buf bytes.Buffer |
799 | for i := 0; i < len(format); i++ { |
800 | // no need to deal with runes |
801 | ch := format[i] |
802 | switch ch { |
803 | case '>': |
804 | p.indent++ |
805 | continue |
806 | case '<': |
807 | p.indent-- |
808 | continue |
809 | } |
810 | buf.WriteByte(ch) |
811 | if ch == '\n' { |
812 | for j := p.indent; j > 0; j-- { |
813 | buf.WriteString(". ") |
814 | } |
815 | } |
816 | } |
817 | format = buf.String() |
818 | } |
819 | fmt.Printf(format, args...) |
820 | } |
821 | |
822 | // Debugging support. |
823 | // (tagString is only used when tracing is enabled) |
824 | var tagString = [...]string{ |
825 | // Packages |
826 | -packageTag: "package", |
827 | |
828 | // Types |
829 | -namedTag: "named type", |
830 | -arrayTag: "array", |
831 | -sliceTag: "slice", |
832 | -dddTag: "ddd", |
833 | -structTag: "struct", |
834 | -pointerTag: "pointer", |
835 | -signatureTag: "signature", |
836 | -interfaceTag: "interface", |
837 | -mapTag: "map", |
838 | -chanTag: "chan", |
839 | |
840 | // Values |
841 | -falseTag: "false", |
842 | -trueTag: "true", |
843 | -int64Tag: "int64", |
844 | -floatTag: "float", |
845 | -fractionTag: "fraction", |
846 | -complexTag: "complex", |
847 | -stringTag: "string", |
848 | -unknownTag: "unknown", |
849 | |
850 | // Type aliases |
851 | -aliasTag: "alias", |
852 | } |
853 |
Members