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