github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/modindex/write.go (about)

     1  // Copyright 2022 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  package modindex
     6  
     7  import (
     8  	"encoding/binary"
     9  	"go/token"
    10  	"sort"
    11  
    12  	"github.com/go-asm/go/cmd/go/base"
    13  )
    14  
    15  const indexVersion = "go index v2" // 11 bytes (plus \n), to align uint32s in index
    16  
    17  // encodeModuleBytes produces the encoded representation of the module index.
    18  // encodeModuleBytes may modify the packages slice.
    19  func encodeModuleBytes(packages []*rawPackage) []byte {
    20  	e := newEncoder()
    21  	e.Bytes([]byte(indexVersion + "\n"))
    22  	stringTableOffsetPos := e.Pos() // fill this at the end
    23  	e.Uint32(0)                     // string table offset
    24  	sort.Slice(packages, func(i, j int) bool {
    25  		return packages[i].dir < packages[j].dir
    26  	})
    27  	e.Int(len(packages))
    28  	packagesPos := e.Pos()
    29  	for _, p := range packages {
    30  		e.String(p.dir)
    31  		e.Int(0)
    32  	}
    33  	for i, p := range packages {
    34  		e.IntAt(e.Pos(), packagesPos+8*i+4)
    35  		encodePackage(e, p)
    36  	}
    37  	e.IntAt(e.Pos(), stringTableOffsetPos)
    38  	e.Bytes(e.stringTable)
    39  	e.Bytes([]byte{0xFF}) // end of string table marker
    40  	return e.b
    41  }
    42  
    43  func encodePackageBytes(p *rawPackage) []byte {
    44  	return encodeModuleBytes([]*rawPackage{p})
    45  }
    46  
    47  func encodePackage(e *encoder, p *rawPackage) {
    48  	e.String(p.error)
    49  	e.String(p.dir)
    50  	e.Int(len(p.sourceFiles))      // number of source files
    51  	sourceFileOffsetPos := e.Pos() // the pos of the start of the source file offsets
    52  	for range p.sourceFiles {
    53  		e.Int(0)
    54  	}
    55  	for i, f := range p.sourceFiles {
    56  		e.IntAt(e.Pos(), sourceFileOffsetPos+4*i)
    57  		encodeFile(e, f)
    58  	}
    59  }
    60  
    61  func encodeFile(e *encoder, f *rawFile) {
    62  	e.String(f.error)
    63  	e.String(f.parseError)
    64  	e.String(f.synopsis)
    65  	e.String(f.name)
    66  	e.String(f.pkgName)
    67  	e.Bool(f.ignoreFile)
    68  	e.Bool(f.binaryOnly)
    69  	e.String(f.cgoDirectives)
    70  	e.String(f.goBuildConstraint)
    71  
    72  	e.Int(len(f.plusBuildConstraints))
    73  	for _, s := range f.plusBuildConstraints {
    74  		e.String(s)
    75  	}
    76  
    77  	e.Int(len(f.imports))
    78  	for _, m := range f.imports {
    79  		e.String(m.path)
    80  		e.Position(m.position)
    81  	}
    82  
    83  	e.Int(len(f.embeds))
    84  	for _, embed := range f.embeds {
    85  		e.String(embed.pattern)
    86  		e.Position(embed.position)
    87  	}
    88  
    89  	e.Int(len(f.directives))
    90  	for _, d := range f.directives {
    91  		e.String(d.Text)
    92  		e.Position(d.Pos)
    93  	}
    94  }
    95  
    96  func newEncoder() *encoder {
    97  	e := &encoder{strings: make(map[string]int)}
    98  
    99  	// place the empty string at position 0 in the string table
   100  	e.stringTable = append(e.stringTable, 0)
   101  	e.strings[""] = 0
   102  
   103  	return e
   104  }
   105  
   106  func (e *encoder) Position(position token.Position) {
   107  	e.String(position.Filename)
   108  	e.Int(position.Offset)
   109  	e.Int(position.Line)
   110  	e.Int(position.Column)
   111  }
   112  
   113  type encoder struct {
   114  	b           []byte
   115  	stringTable []byte
   116  	strings     map[string]int
   117  }
   118  
   119  func (e *encoder) Pos() int {
   120  	return len(e.b)
   121  }
   122  
   123  func (e *encoder) Bytes(b []byte) {
   124  	e.b = append(e.b, b...)
   125  }
   126  
   127  func (e *encoder) String(s string) {
   128  	if n, ok := e.strings[s]; ok {
   129  		e.Int(n)
   130  		return
   131  	}
   132  	pos := len(e.stringTable)
   133  	e.strings[s] = pos
   134  	e.Int(pos)
   135  	e.stringTable = binary.AppendUvarint(e.stringTable, uint64(len(s)))
   136  	e.stringTable = append(e.stringTable, s...)
   137  }
   138  
   139  func (e *encoder) Bool(b bool) {
   140  	if b {
   141  		e.Uint32(1)
   142  	} else {
   143  		e.Uint32(0)
   144  	}
   145  }
   146  
   147  func (e *encoder) Uint32(n uint32) {
   148  	e.b = binary.LittleEndian.AppendUint32(e.b, n)
   149  }
   150  
   151  // Int encodes n. Note that all ints are written to the index as uint32s,
   152  // and to avoid problems on 32-bit systems we require fitting into a 32-bit int.
   153  func (e *encoder) Int(n int) {
   154  	if n < 0 || int(int32(n)) != n {
   155  		base.Fatalf("go: attempting to write an int to the index that overflows int32")
   156  	}
   157  	e.Uint32(uint32(n))
   158  }
   159  
   160  func (e *encoder) IntAt(n int, at int) {
   161  	if n < 0 || int(int32(n)) != n {
   162  		base.Fatalf("go: attempting to write an int to the index that overflows int32")
   163  	}
   164  	binary.LittleEndian.PutUint32(e.b[at:], uint32(n))
   165  }