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 	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