package reflect

import (
	"unsafe"
)

// This stores a varint for each named type. Named types are identified by their
// name instead of by their type. The named types stored in this struct are
// non-basic types: pointer, struct, and channel.
//go:extern reflect.namedNonBasicTypesSidetable
var namedNonBasicTypesSidetable uintptr

//go:extern reflect.structTypesSidetable
var structTypesSidetable byte

//go:extern reflect.structNamesSidetable
var structNamesSidetable byte

//go:extern reflect.arrayTypesSidetable
var arrayTypesSidetable byte

// readStringSidetable reads a string from the given table (like
// structNamesSidetable) and returns this string. No heap allocation is
// necessary because it makes the string point directly to the raw bytes of the
// table.
func readStringSidetable(table unsafe.Pointer, index uintptr) string {
	nameLen, namePtr := readVarint(unsafe.Pointer(uintptr(table) + index))
	return *(*string)(unsafe.Pointer(&stringHeader{
		data: namePtr,
		len:  nameLen,
	}))
}

// readVarint decodes a varint as used in the encoding/binary package.
// It has an input pointer and returns the read varint and the pointer
// incremented to the next field in the data structure, just after the varint.
//
// Details:
// https://github.com/golang/go/blob/e37a1b1c/src/encoding/binary/varint.go#L7-L25
func readVarint(buf unsafe.Pointer) (uintptr, unsafe.Pointer) {
	var n uintptr
	shift := uintptr(0)
	for {
		// Read the next byte in the buffer.
		c := *(*byte)(buf)

		// Decode the bits from this byte and add them to the output number.
		n |= uintptr(c&0x7f) << shift
		shift += 7

		// Increment the buf pointer (pointer arithmetic!).
		buf = unsafe.Pointer(uintptr(buf) + 1)

		// Check whether this is the last byte of this varint. The upper bit
		// (msb) indicates whether any bytes follow.
		if c>>7 == 0 {
			return n, buf
		}
	}
}
