Source file src/cmd/internal/gcprog/gcprog.go

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

View as plain text