|  | // Copyright 2015 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // Package gcprog implements an encoder for packed GC pointer bitmaps, | 
|  | // known as GC programs. | 
|  | // | 
|  | // Program Format | 
|  | // | 
|  | // The GC program encodes a sequence of 0 and 1 bits indicating scalar or pointer words in an object. | 
|  | // The encoding is a simple Lempel-Ziv program, with codes to emit literal bits and to repeat the | 
|  | // last n bits c times. | 
|  | // | 
|  | // The possible codes are: | 
|  | // | 
|  | //	00000000: stop | 
|  | //	0nnnnnnn: emit n bits copied from the next (n+7)/8 bytes, least significant bit first | 
|  | //	10000000 n c: repeat the previous n bits c times; n, c are varints | 
|  | //	1nnnnnnn c: repeat the previous n bits c times; c is a varint | 
|  | // | 
|  | // The numbers n and c, when they follow a code, are encoded as varints | 
|  | // using the same encoding as encoding/binary's Uvarint. | 
|  | // | 
|  | package gcprog | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "io" | 
|  | ) | 
|  |  | 
|  | const progMaxLiteral = 127 // maximum n for literal n bit code | 
|  |  | 
|  | // A Writer is an encoder for GC programs. | 
|  | // | 
|  | // The typical use of a Writer is to call Init, maybe call Debug, | 
|  | // make a sequence of Ptr, Advance, Repeat, and Append calls | 
|  | // to describe the data type, and then finally call End. | 
|  | type Writer struct { | 
|  | writeByte func(byte) | 
|  | index     int64 | 
|  | b         [progMaxLiteral]byte | 
|  | nb        int | 
|  | debug     io.Writer | 
|  | debugBuf  []byte | 
|  | } | 
|  |  | 
|  | // Init initializes w to write a new GC program | 
|  | // by calling writeByte for each byte in the program. | 
|  | func (w *Writer) Init(writeByte func(byte)) { | 
|  | w.writeByte = writeByte | 
|  | } | 
|  |  | 
|  | // Debug causes the writer to print a debugging trace to out | 
|  | // during future calls to methods like Ptr, Advance, and End. | 
|  | // It also enables debugging checks during the encoding. | 
|  | func (w *Writer) Debug(out io.Writer) { | 
|  | w.debug = out | 
|  | } | 
|  |  | 
|  | // BitIndex returns the number of bits written to the bit stream so far. | 
|  | func (w *Writer) BitIndex() int64 { | 
|  | return w.index | 
|  | } | 
|  |  | 
|  | // byte writes the byte x to the output. | 
|  | func (w *Writer) byte(x byte) { | 
|  | if w.debug != nil { | 
|  | w.debugBuf = append(w.debugBuf, x) | 
|  | } | 
|  | w.writeByte(x) | 
|  | } | 
|  |  | 
|  | // End marks the end of the program, writing any remaining bytes. | 
|  | func (w *Writer) End() { | 
|  | w.flushlit() | 
|  | w.byte(0) | 
|  | if w.debug != nil { | 
|  | index := progbits(w.debugBuf) | 
|  | if index != w.index { | 
|  | println("gcprog: End wrote program for", index, "bits, but current index is", w.index) | 
|  | panic("gcprog: out of sync") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Ptr emits a 1 into the bit stream at the given bit index. | 
|  | // that is, it records that the index'th word in the object memory is a pointer. | 
|  | // Any bits between the current index and the new index | 
|  | // are set to zero, meaning the corresponding words are scalars. | 
|  | func (w *Writer) Ptr(index int64) { | 
|  | if index < w.index { | 
|  | println("gcprog: Ptr at index", index, "but current index is", w.index) | 
|  | panic("gcprog: invalid Ptr index") | 
|  | } | 
|  | w.ZeroUntil(index) | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "gcprog: ptr at %d\n", index) | 
|  | } | 
|  | w.lit(1) | 
|  | } | 
|  |  | 
|  | // ShouldRepeat reports whether it would be worthwhile to | 
|  | // use a Repeat to describe c elements of n bits each, | 
|  | // compared to just emitting c copies of the n-bit description. | 
|  | func (w *Writer) ShouldRepeat(n, c int64) bool { | 
|  | // Should we lay out the bits directly instead of | 
|  | // encoding them as a repetition? Certainly if count==1, | 
|  | // since there's nothing to repeat, but also if the total | 
|  | // size of the plain pointer bits for the type will fit in | 
|  | // 4 or fewer bytes, since using a repetition will require | 
|  | // flushing the current bits plus at least one byte for | 
|  | // the repeat size and one for the repeat count. | 
|  | return c > 1 && c*n > 4*8 | 
|  | } | 
|  |  | 
|  | // Repeat emits an instruction to repeat the description | 
|  | // of the last n words c times (including the initial description, c+1 times in total). | 
|  | func (w *Writer) Repeat(n, c int64) { | 
|  | if n == 0 || c == 0 { | 
|  | return | 
|  | } | 
|  | w.flushlit() | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "gcprog: repeat %d × %d\n", n, c) | 
|  | } | 
|  | if n < 128 { | 
|  | w.byte(0x80 | byte(n)) | 
|  | } else { | 
|  | w.byte(0x80) | 
|  | w.varint(n) | 
|  | } | 
|  | w.varint(c) | 
|  | w.index += n * c | 
|  | } | 
|  |  | 
|  | // ZeroUntil adds zeros to the bit stream until reaching the given index; | 
|  | // that is, it records that the words from the most recent pointer until | 
|  | // the index'th word are scalars. | 
|  | // ZeroUntil is usually called in preparation for a call to Repeat, Append, or End. | 
|  | func (w *Writer) ZeroUntil(index int64) { | 
|  | if index < w.index { | 
|  | println("gcprog: Advance", index, "but index is", w.index) | 
|  | panic("gcprog: invalid Advance index") | 
|  | } | 
|  | skip := (index - w.index) | 
|  | if skip == 0 { | 
|  | return | 
|  | } | 
|  | if skip < 4*8 { | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "gcprog: advance to %d by literals\n", index) | 
|  | } | 
|  | for i := int64(0); i < skip; i++ { | 
|  | w.lit(0) | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "gcprog: advance to %d by repeat\n", index) | 
|  | } | 
|  | w.lit(0) | 
|  | w.flushlit() | 
|  | w.Repeat(1, skip-1) | 
|  | } | 
|  |  | 
|  | // Append emits the given GC program into the current output. | 
|  | // The caller asserts that the program emits n bits (describes n words), | 
|  | // and Append panics if that is not true. | 
|  | func (w *Writer) Append(prog []byte, n int64) { | 
|  | w.flushlit() | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "gcprog: append prog for %d ptrs\n", n) | 
|  | fmt.Fprintf(w.debug, "\t") | 
|  | } | 
|  | n1 := progbits(prog) | 
|  | if n1 != n { | 
|  | panic("gcprog: wrong bit count in append") | 
|  | } | 
|  | // The last byte of the prog terminates the program. | 
|  | // Don't emit that, or else our own program will end. | 
|  | for i, x := range prog[:len(prog)-1] { | 
|  | if w.debug != nil { | 
|  | if i > 0 { | 
|  | fmt.Fprintf(w.debug, " ") | 
|  | } | 
|  | fmt.Fprintf(w.debug, "%02x", x) | 
|  | } | 
|  | w.byte(x) | 
|  | } | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "\n") | 
|  | } | 
|  | w.index += n | 
|  | } | 
|  |  | 
|  | // progbits returns the length of the bit stream encoded by the program p. | 
|  | func progbits(p []byte) int64 { | 
|  | var n int64 | 
|  | for len(p) > 0 { | 
|  | x := p[0] | 
|  | p = p[1:] | 
|  | if x == 0 { | 
|  | break | 
|  | } | 
|  | if x&0x80 == 0 { | 
|  | count := x &^ 0x80 | 
|  | n += int64(count) | 
|  | p = p[(count+7)/8:] | 
|  | continue | 
|  | } | 
|  | nbit := int64(x &^ 0x80) | 
|  | if nbit == 0 { | 
|  | nbit, p = readvarint(p) | 
|  | } | 
|  | var count int64 | 
|  | count, p = readvarint(p) | 
|  | n += nbit * count | 
|  | } | 
|  | if len(p) > 0 { | 
|  | println("gcprog: found end instruction after", n, "ptrs, with", len(p), "bytes remaining") | 
|  | panic("gcprog: extra data at end of program") | 
|  | } | 
|  | return n | 
|  | } | 
|  |  | 
|  | // readvarint reads a varint from p, returning the value and the remainder of p. | 
|  | func readvarint(p []byte) (int64, []byte) { | 
|  | var v int64 | 
|  | var nb uint | 
|  | for { | 
|  | c := p[0] | 
|  | p = p[1:] | 
|  | v |= int64(c&^0x80) << nb | 
|  | nb += 7 | 
|  | if c&0x80 == 0 { | 
|  | break | 
|  | } | 
|  | } | 
|  | return v, p | 
|  | } | 
|  |  | 
|  | // lit adds a single literal bit to w. | 
|  | func (w *Writer) lit(x byte) { | 
|  | if w.nb == progMaxLiteral { | 
|  | w.flushlit() | 
|  | } | 
|  | w.b[w.nb] = x | 
|  | w.nb++ | 
|  | w.index++ | 
|  | } | 
|  |  | 
|  | // varint emits the varint encoding of x. | 
|  | func (w *Writer) varint(x int64) { | 
|  | if x < 0 { | 
|  | panic("gcprog: negative varint") | 
|  | } | 
|  | for x >= 0x80 { | 
|  | w.byte(byte(0x80 | x)) | 
|  | x >>= 7 | 
|  | } | 
|  | w.byte(byte(x)) | 
|  | } | 
|  |  | 
|  | // flushlit flushes any pending literal bits. | 
|  | func (w *Writer) flushlit() { | 
|  | if w.nb == 0 { | 
|  | return | 
|  | } | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "gcprog: flush %d literals\n", w.nb) | 
|  | fmt.Fprintf(w.debug, "\t%v\n", w.b[:w.nb]) | 
|  | fmt.Fprintf(w.debug, "\t%02x", byte(w.nb)) | 
|  | } | 
|  | w.byte(byte(w.nb)) | 
|  | var bits uint8 | 
|  | for i := 0; i < w.nb; i++ { | 
|  | bits |= w.b[i] << uint(i%8) | 
|  | if (i+1)%8 == 0 { | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, " %02x", bits) | 
|  | } | 
|  | w.byte(bits) | 
|  | bits = 0 | 
|  | } | 
|  | } | 
|  | if w.nb%8 != 0 { | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, " %02x", bits) | 
|  | } | 
|  | w.byte(bits) | 
|  | } | 
|  | if w.debug != nil { | 
|  | fmt.Fprintf(w.debug, "\n") | 
|  | } | 
|  | w.nb = 0 | 
|  | } |