ogle/debug/dwarf: use existing I/O implementation to parse line table

LGTM=nigeltao
R=nigeltao
https://golang.org/cl/89300044
diff --git a/debug/dwarf/line.go b/debug/dwarf/line.go
index 5a23922..de095bd 100644
--- a/debug/dwarf/line.go
+++ b/debug/dwarf/line.go
@@ -11,7 +11,6 @@
 // TODO: Find a way to test this properly.
 
 import (
-	"encoding/binary"
 	"fmt"
 )
 
@@ -23,9 +22,14 @@
 		return
 	}
 	var m lineMachine
-	for offset := 0; offset < len(d.line); {
+	// Assume the first info unit is the same as us. Extremely likely. TODO?
+	if len(d.unit) == 0 {
+		return "", 0, fmt.Errorf("no info section")
+	}
+	buf := makeBuf(d, &d.unit[0], "line", 0, d.line)
+	for len(buf.data) > 0 {
 		var found bool
-		offset, found, err = m.evalCompilationUnit(d.line, offset, pc)
+		found, err = m.evalCompilationUnit(&buf, pc)
 		if err != nil {
 			return "", 0, err
 		}
@@ -165,79 +169,66 @@
 
 // parseLinePrologue parses the prologue/header describing the compilation
 // unit in the line table starting at the specified offset.
-func (m *lineMachine) parseLinePrologue(data []byte, offset int) (int, error) {
-	// TODO: Assumes little endian
+func (m *lineMachine) parseLinePrologue(b *buf) error {
 	m.prologue = linePrologue{}
-	m.prologue.unitLength = int(binary.LittleEndian.Uint32(data[offset:]))
-	if m.prologue.unitLength > len(data)-4 {
-		return 0, fmt.Errorf("DWARF: bad PC/line header length")
+	m.prologue.unitLength = int(b.uint32())
+	if m.prologue.unitLength > len(b.data) {
+		return fmt.Errorf("DWARF: bad PC/line header length")
 	}
-	offset += 4
-	m.prologue.version = int(binary.LittleEndian.Uint16(data[offset:]))
-	offset += 2
-	m.prologue.headerLength = int(binary.LittleEndian.Uint32(data[offset:]))
-	offset += 4
-	m.prologue.minInstructionLength = int(data[offset])
-	offset += 1
+	m.prologue.version = int(b.uint16())
+	m.prologue.headerLength = int(b.uint32())
+	m.prologue.minInstructionLength = int(b.uint8())
 	if m.prologue.version >= 4 {
-		m.prologue.maxOpsPerInstruction = int(data[offset])
-		offset += 1
+		m.prologue.maxOpsPerInstruction = int(b.uint8())
 	} else {
 		m.prologue.maxOpsPerInstruction = 1
 	}
-	m.prologue.defaultIsStmt = data[offset] != 0
-	offset += 1
-	m.prologue.lineBase = int(int8(data[offset]))
-	offset += 1
-	m.prologue.lineRange = int(data[offset])
-	offset += 1
-	m.prologue.opcodeBase = data[offset]
-	offset += 1
+	m.prologue.defaultIsStmt = b.uint8() != 0
+	m.prologue.lineBase = int(int8(b.uint8()))
+	m.prologue.lineRange = int(b.uint8())
+	m.prologue.opcodeBase = b.uint8()
 	m.prologue.stdOpcodeLengths = make([]byte, m.prologue.opcodeBase-1)
-	copy(m.prologue.stdOpcodeLengths, data[offset:])
-	offset += int(m.prologue.opcodeBase - 1)
+	copy(m.prologue.stdOpcodeLengths, b.bytes(int(m.prologue.opcodeBase-1)))
 	m.prologue.include = make([]string, 1) // First entry is empty; file index entries are 1-indexed.
 	// Includes
+	name := make([]byte, 0, 64)
+	zeroTerminatedString := func() string {
+		name = name[:0]
+		for {
+			c := b.uint8()
+			if c == 0 {
+				break
+			}
+			name = append(name, c)
+		}
+		return string(name)
+	}
 	for {
-		if data[offset] == 0 {
-			offset++
+		name := zeroTerminatedString()
+		if name == "" {
 			break
 		}
-		startOfName := offset
-		for data[offset] != 0 {
-			offset++
-		}
-		m.prologue.include = append(m.prologue.include, string(data[startOfName:offset]))
-		offset++ // terminal NUL
+		m.prologue.include = append(m.prologue.include, name)
 	}
 	// Files
 	m.prologue.file = make([]lineFile, 1, 10) // entries are 1-indexed in line number program.
 	for {
-		if data[offset] == 0 {
-			offset++
+		name := zeroTerminatedString()
+		if name == "" {
 			break
 		}
-		startOfName := offset
-		for data[offset] != 0 {
-			offset++
-		}
-		name := data[startOfName:offset]
-		offset++ // terminal NUL
-		index, w := uleb128(data[offset:])
-		offset += w
-		time, w := uleb128(data[offset:])
-		offset += w
-		length, w := uleb128(data[offset:])
-		offset += w
+		index := b.uint()
+		time := b.uint()
+		length := b.uint()
 		f := lineFile{
-			name:   string(name),
+			name:   name,
 			index:  int(index),
 			time:   int(time),
 			length: int(length),
 		}
 		m.prologue.file = append(m.prologue.file, f)
 	}
-	return offset, nil
+	return nil
 }
 
 // Special opcodes, page 117.
@@ -255,61 +246,46 @@
 	m.discriminator = 0
 }
 
-// evalCompilationUnit scans the compilation unit starting at the specified offset to see if it contains the PC.
-// It returns when it finds the PC or at the end of the compilation unit.
-// The return values are the offset where it stops and whether the PC was found; if so,
-// the machine's registers contain the relevant information.
-func (m *lineMachine) evalCompilationUnit(data []byte, startOffset int, pc uint64) (int, bool, error) {
-	offset, err := m.parseLinePrologue(data, startOffset)
+// evalCompilationUnit reads the next compilation unit to see if it contains the PC.
+// The return value reports whether the PC was found; if so, the machine's registers
+// contain the relevant information.
+func (m *lineMachine) evalCompilationUnit(b *buf, pc uint64) (bool, error) {
+	err := m.parseLinePrologue(b)
 	if err != nil {
-		return offset, false, err
+		return false, err
 	}
 	m.reset()
-	for offset < len(data) {
-		op := data[offset]
-		offset++
+	for len(b.data) > 0 {
+		op := b.uint8()
 		if op >= m.prologue.opcodeBase {
 			m.specialOpcode(op)
 			continue
 		}
 		switch op {
 		case lineStartExtendedOpcode:
-			if len(data) == 0 {
-				return offset, false, fmt.Errorf("DWARF: short extended opcode (1)")
+			if len(b.data) == 0 {
+				return false, fmt.Errorf("DWARF: short extended opcode (1)")
 			}
-			size, wid := uleb128(data[offset:])
-			if uint64(len(data)) < size {
-				return offset, false, fmt.Errorf("DWARF: short extended opcode (2)")
+			size := b.uint()
+			if uint64(len(b.data)) < size {
+				return false, fmt.Errorf("DWARF: short extended opcode (2)")
 			}
-			offset += int(wid)
-			op = data[offset]
-			offset++
+			op = b.uint8()
 			switch op {
 			case lineExtEndSequence:
 				m.endSequence = true
 				m.reset()
-				return offset, false, nil
+				return false, nil
 			case lineExtSetAddress:
-				var addr uint64
-				// TODO: Assumes little-endian.
-				switch size {
-				case 1 + 4: // TODO: How should we do this?
-					addr = uint64(binary.LittleEndian.Uint32(data[offset:]))
-					offset += 4
-				case 1 + 8:
-					addr = binary.LittleEndian.Uint64(data[offset:])
-					offset += 8
-				}
-				m.address = addr
+				m.address = b.addr()
 				m.opIndex = 0
 			case lineExtDefineFile:
-				return offset, false, fmt.Errorf("DWARF: unimplemented define_file op")
+				return false, fmt.Errorf("DWARF: unimplemented define_file op")
 			case lineExtSetDiscriminator:
-				discriminator, wid := uleb128(data[offset:])
+				discriminator := b.uint()
 				m.line = discriminator
-				offset += wid
 			default:
-				return offset, false, fmt.Errorf("DWARF: unknown extended opcode %#x", op)
+				return false, fmt.Errorf("DWARF: unknown extended opcode %#x", op)
 			}
 		case lineStdCopy:
 			m.discriminator = 0
@@ -318,47 +294,39 @@
 			m.epilogueBegin = false
 			if m.address >= pc {
 				// TODO: if m.address > pc, is this one step too far?
-				return offset, true, nil
+				return true, nil
 			}
 		case lineStdAdvancePC:
-			advance, wid := uleb128(data[offset:])
+			advance := b.uint()
 			delta := (int(m.opIndex) + int(advance)) / m.prologue.maxOpsPerInstruction
 			m.address += uint64(m.prologue.minInstructionLength * delta)
 			m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.prologue.maxOpsPerInstruction)
-			offset += wid
 			m.basicBlock = false
 			m.prologueEnd = false
 			m.epilogueBegin = false
 			m.discriminator = 0
 		case lineStdAdvanceLine:
-			advance, wid := sleb128(data[offset:])
+			advance := b.int()
 			m.line = uint64(int64(m.line) + advance)
-			offset += wid
 		case lineStdSetFile:
-			index, wid := uleb128(data[offset:])
+			index := b.uint()
 			m.file = index
-			offset += wid
 		case lineStdSetColumn:
-			column, wid := uleb128(data[offset:])
+			column := b.uint()
 			m.column = column
-			offset += wid
 		case lineStdNegateStmt:
 			m.isStmt = !m.isStmt
 		case lineStdSetBasicBlock:
 			m.basicBlock = true
 		case lineStdFixedAdvancePC:
-			delta := binary.LittleEndian.Uint16(data[offset:])
-			m.address += uint64(delta)
+			m.address += uint64(b.uint16())
 			m.opIndex = 0
-			offset += 2
 		case lineStdSetPrologueEnd:
 			m.prologueEnd = true
 		case lineStdSetEpilogueBegin:
 			m.epilogueBegin = true
 		case lineStdSetISA:
-			isa, wid := uleb128(data[offset:])
-			m.isa = isa
-			offset += wid
+			m.isa = b.uint()
 		case lineStdConstAddPC:
 			// TODO: Is this right? Seems crazy - why not just use 255 as a special opcode?
 			m.specialOpcode(255)
@@ -384,38 +352,3 @@
 	m.isa = 0
 	m.discriminator = 0
 }
-
-// uleb128 decodes a varint-encoded unsigned integer.
-// TODO: use the buffer interface.
-func uleb128(v []uint8) (u uint64, length int) {
-	var shift uint
-	var x byte
-	for length, x = range v {
-		u |= (uint64(x) & 0x7F) << shift
-		shift += 7
-		if x&0x80 == 0 {
-			break
-		}
-	}
-	return u, length + 1
-}
-
-// sleb128 decodes a varint-encoded signed integer.
-// TODO: use the buffer interface.
-func sleb128(v []uint8) (s int64, length int) {
-	var shift uint
-	var sign int64 = -1
-	var x byte
-	for length, x = range v {
-		s |= (int64(x) & 0x7F) << shift
-		shift += 7
-		sign <<= 7
-		if x&0x80 == 0 {
-			if x&0x40 != 0 {
-				s |= sign
-			}
-			break
-		}
-	}
-	return s, length + 1
-}