Home | History | Annotate | Download | only in gcprog
      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 }
    299