github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/internal/gcprog/gcprog.go (about)

     1  // Copyright 2015 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 gcprog implements an encoder for packed GC pointer bitmaps,
     6  // known as GC programs.
     7  //
     8  // Program Format
     9  //
    10  // The GC program encodes a sequence of 0 and 1 bits indicating scalar or pointer words in an object.
    11  // The encoding is a simple Lempel-Ziv program, with codes to emit literal bits and to repeat the
    12  // last n bits c times.
    13  //
    14  // The possible codes are:
    15  //
    16  //	00000000: stop
    17  //	0nnnnnnn: emit n bits copied from the next (n+7)/8 bytes, least significant bit first
    18  //	10000000 n c: repeat the previous n bits c times; n, c are varints
    19  //	1nnnnnnn c: repeat the previous n bits c times; c is a varint
    20  //
    21  // The numbers n and c, when they follow a code, are encoded as varints
    22  // using the same encoding as encoding/binary's Uvarint.
    23  //
    24  package gcprog
    25  
    26  import (
    27  	"fmt"
    28  	"io"
    29  )
    30  
    31  const progMaxLiteral = 127 // maximum n for literal n bit code
    32  
    33  // A Writer is an encoder for GC programs.
    34  //
    35  // The typical use of a Writer is to call Init, maybe call Debug,
    36  // make a sequence of Ptr, Advance, Repeat, and Append calls
    37  // to describe the data type, and then finally call End.
    38  type Writer struct {
    39  	writeByte func(byte)
    40  	symoff    int
    41  	index     int64
    42  	b         [progMaxLiteral]byte
    43  	nb        int
    44  	debug     io.Writer
    45  	debugBuf  []byte
    46  }
    47  
    48  // Init initializes w to write a new GC program
    49  // by calling writeByte for each byte in the program.
    50  func (w *Writer) Init(writeByte func(byte)) {
    51  	w.writeByte = writeByte
    52  }
    53  
    54  // Debug causes the writer to print a debugging trace to out
    55  // during future calls to methods like Ptr, Advance, and End.
    56  // It also enables debugging checks during the encoding.
    57  func (w *Writer) Debug(out io.Writer) {
    58  	w.debug = out
    59  }
    60  
    61  // BitIndex returns the number of bits written to the bit stream so far.
    62  func (w *Writer) BitIndex() int64 {
    63  	return w.index
    64  }
    65  
    66  // byte writes the byte x to the output.
    67  func (w *Writer) byte(x byte) {
    68  	if w.debug != nil {
    69  		w.debugBuf = append(w.debugBuf, x)
    70  	}
    71  	w.writeByte(x)
    72  }
    73  
    74  // End marks the end of the program, writing any remaining bytes.
    75  func (w *Writer) End() {
    76  	w.flushlit()
    77  	w.byte(0)
    78  	if w.debug != nil {
    79  		index := progbits(w.debugBuf)
    80  		if index != w.index {
    81  			println("gcprog: End wrote program for", index, "bits, but current index is", w.index)
    82  			panic("gcprog: out of sync")
    83  		}
    84  	}
    85  }
    86  
    87  // Ptr emits a 1 into the bit stream at the given bit index.
    88  // that is, it records that the index'th word in the object memory is a pointer.
    89  // Any bits between the current index and the new index
    90  // are set to zero, meaning the corresponding words are scalars.
    91  func (w *Writer) Ptr(index int64) {
    92  	if index < w.index {
    93  		println("gcprog: Ptr at index", index, "but current index is", w.index)
    94  		panic("gcprog: invalid Ptr index")
    95  	}
    96  	w.ZeroUntil(index)
    97  	if w.debug != nil {
    98  		fmt.Fprintf(w.debug, "gcprog: ptr at %d\n", index)
    99  	}
   100  	w.lit(1)
   101  }
   102  
   103  // ShouldRepeat reports whether it would be worthwhile to
   104  // use a Repeat to describe c elements of n bits each,
   105  // compared to just emitting c copies of the n-bit description.
   106  func (w *Writer) ShouldRepeat(n, c int64) bool {
   107  	// Should we lay out the bits directly instead of
   108  	// encoding them as a repetition? Certainly if count==1,
   109  	// since there's nothing to repeat, but also if the total
   110  	// size of the plain pointer bits for the type will fit in
   111  	// 4 or fewer bytes, since using a repetition will require
   112  	// flushing the current bits plus at least one byte for
   113  	// the repeat size and one for the repeat count.
   114  	return c > 1 && c*n > 4*8
   115  }
   116  
   117  // Repeat emits an instruction to repeat the description
   118  // of the last n words c times (including the initial description, c+1 times in total).
   119  func (w *Writer) Repeat(n, c int64) {
   120  	if n == 0 || c == 0 {
   121  		return
   122  	}
   123  	w.flushlit()
   124  	if w.debug != nil {
   125  		fmt.Fprintf(w.debug, "gcprog: repeat %d × %d\n", n, c)
   126  	}
   127  	if n < 128 {
   128  		w.byte(0x80 | byte(n))
   129  	} else {
   130  		w.byte(0x80)
   131  		w.varint(n)
   132  	}
   133  	w.varint(c)
   134  	w.index += n * c
   135  }
   136  
   137  // ZeroUntil adds zeros to the bit stream until reaching the given index;
   138  // that is, it records that the words from the most recent pointer until
   139  // the index'th word are scalars.
   140  // ZeroUntil is usually called in preparation for a call to Repeat, Append, or End.
   141  func (w *Writer) ZeroUntil(index int64) {
   142  	if index < w.index {
   143  		println("gcprog: Advance", index, "but index is", w.index)
   144  		panic("gcprog: invalid Advance index")
   145  	}
   146  	skip := (index - w.index)
   147  	if skip == 0 {
   148  		return
   149  	}
   150  	if skip < 4*8 {
   151  		if w.debug != nil {
   152  			fmt.Fprintf(w.debug, "gcprog: advance to %d by literals\n", index)
   153  		}
   154  		for i := int64(0); i < skip; i++ {
   155  			w.lit(0)
   156  		}
   157  		return
   158  	}
   159  
   160  	if w.debug != nil {
   161  		fmt.Fprintf(w.debug, "gcprog: advance to %d by repeat\n", index)
   162  	}
   163  	w.lit(0)
   164  	w.flushlit()
   165  	w.Repeat(1, skip-1)
   166  }
   167  
   168  // Append emits the given GC program into the current output.
   169  // The caller asserts that the program emits n bits (describes n words),
   170  // and Append panics if that is not true.
   171  func (w *Writer) Append(prog []byte, n int64) {
   172  	w.flushlit()
   173  	if w.debug != nil {
   174  		fmt.Fprintf(w.debug, "gcprog: append prog for %d ptrs\n", n)
   175  		fmt.Fprintf(w.debug, "\t")
   176  	}
   177  	n1 := progbits(prog)
   178  	if n1 != n {
   179  		panic("gcprog: wrong bit count in append")
   180  	}
   181  	// The last byte of the prog terminates the program.
   182  	// Don't emit that, or else our own program will end.
   183  	for i, x := range prog[:len(prog)-1] {
   184  		if w.debug != nil {
   185  			if i > 0 {
   186  				fmt.Fprintf(w.debug, " ")
   187  			}
   188  			fmt.Fprintf(w.debug, "%02x", x)
   189  		}
   190  		w.byte(x)
   191  	}
   192  	if w.debug != nil {
   193  		fmt.Fprintf(w.debug, "\n")
   194  	}
   195  	w.index += n
   196  }
   197  
   198  // progbits returns the length of the bit stream encoded by the program p.
   199  func progbits(p []byte) int64 {
   200  	var n int64
   201  	for len(p) > 0 {
   202  		x := p[0]
   203  		p = p[1:]
   204  		if x == 0 {
   205  			break
   206  		}
   207  		if x&0x80 == 0 {
   208  			count := x &^ 0x80
   209  			n += int64(count)
   210  			p = p[(count+7)/8:]
   211  			continue
   212  		}
   213  		nbit := int64(x &^ 0x80)
   214  		if nbit == 0 {
   215  			nbit, p = readvarint(p)
   216  		}
   217  		var count int64
   218  		count, p = readvarint(p)
   219  		n += nbit * count
   220  	}
   221  	if len(p) > 0 {
   222  		println("gcprog: found end instruction after", n, "ptrs, with", len(p), "bytes remaining")
   223  		panic("gcprog: extra data at end of program")
   224  	}
   225  	return n
   226  }
   227  
   228  // readvarint reads a varint from p, returning the value and the remainder of p.
   229  func readvarint(p []byte) (int64, []byte) {
   230  	var v int64
   231  	var nb uint
   232  	for {
   233  		c := p[0]
   234  		p = p[1:]
   235  		v |= int64(c&^0x80) << nb
   236  		nb += 7
   237  		if c&0x80 == 0 {
   238  			break
   239  		}
   240  	}
   241  	return v, p
   242  }
   243  
   244  // lit adds a single literal bit to w.
   245  func (w *Writer) lit(x byte) {
   246  	if w.nb == progMaxLiteral {
   247  		w.flushlit()
   248  	}
   249  	w.b[w.nb] = x
   250  	w.nb++
   251  	w.index++
   252  }
   253  
   254  // varint emits the varint encoding of x.
   255  func (w *Writer) varint(x int64) {
   256  	if x < 0 {
   257  		panic("gcprog: negative varint")
   258  	}
   259  	for x >= 0x80 {
   260  		w.byte(byte(0x80 | x))
   261  		x >>= 7
   262  	}
   263  	w.byte(byte(x))
   264  }
   265  
   266  // flushlit flushes any pending literal bits.
   267  func (w *Writer) flushlit() {
   268  	if w.nb == 0 {
   269  		return
   270  	}
   271  	if w.debug != nil {
   272  		fmt.Fprintf(w.debug, "gcprog: flush %d literals\n", w.nb)
   273  		fmt.Fprintf(w.debug, "\t%v\n", w.b[:w.nb])
   274  		fmt.Fprintf(w.debug, "\t%02x", byte(w.nb))
   275  	}
   276  	w.byte(byte(w.nb))
   277  	var bits uint8
   278  	for i := 0; i < w.nb; i++ {
   279  		bits |= w.b[i] << uint(i%8)
   280  		if (i+1)%8 == 0 {
   281  			if w.debug != nil {
   282  				fmt.Fprintf(w.debug, " %02x", bits)
   283  			}
   284  			w.byte(bits)
   285  			bits = 0
   286  		}
   287  	}
   288  	if w.nb%8 != 0 {
   289  		if w.debug != nil {
   290  			fmt.Fprintf(w.debug, " %02x", bits)
   291  		}
   292  		w.byte(bits)
   293  	}
   294  	if w.debug != nil {
   295  		fmt.Fprintf(w.debug, "\n")
   296  	}
   297  	w.nb = 0
   298  }