Merge pull request #1100 from tonistiigi/print-outline
Build: Support for printing outline/targets of the current buildpull/1266/head v0.9.0-rc2
						commit
						da1f4b8496
					
				@ -0,0 +1,48 @@
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/buildx/build"
 | 
			
		||||
	"github.com/docker/docker/api/types/versions"
 | 
			
		||||
	"github.com/moby/buildkit/frontend/subrequests"
 | 
			
		||||
	"github.com/moby/buildkit/frontend/subrequests/outline"
 | 
			
		||||
	"github.com/moby/buildkit/frontend/subrequests/targets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func printResult(f *build.PrintFunc, res map[string]string) error {
 | 
			
		||||
	switch f.Name {
 | 
			
		||||
	case "outline":
 | 
			
		||||
		return printValue(outline.PrintOutline, outline.SubrequestsOutlineDefinition.Version, f.Format, res)
 | 
			
		||||
	case "targets":
 | 
			
		||||
		return printValue(targets.PrintTargets, targets.SubrequestsTargetsDefinition.Version, f.Format, res)
 | 
			
		||||
	case "subrequests.describe":
 | 
			
		||||
		return printValue(subrequests.PrintDescribe, subrequests.SubrequestsDescribeDefinition.Version, f.Format, res)
 | 
			
		||||
	default:
 | 
			
		||||
		if dt, ok := res["result.txt"]; ok {
 | 
			
		||||
			fmt.Print(dt)
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Printf("%s %+v", f, res)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type printFunc func([]byte, io.Writer) error
 | 
			
		||||
 | 
			
		||||
func printValue(printer printFunc, version string, format string, res map[string]string) error {
 | 
			
		||||
	if format == "json" {
 | 
			
		||||
		fmt.Fprintln(os.Stdout, res["result.json"])
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res["version"] != "" && versions.LessThan(version, res["version"]) && res["result.txt"] != "" {
 | 
			
		||||
		// structure is too new and we don't know how to print it
 | 
			
		||||
		fmt.Fprint(os.Stdout, res["result.txt"])
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return printer([]byte(res["result.json"]), os.Stdout)
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +0,0 @@
 | 
			
		||||
package huff0
 | 
			
		||||
 | 
			
		||||
//go:generate go run generate.go
 | 
			
		||||
//go:generate asmfmt -w decompress_amd64.s
 | 
			
		||||
//go:generate asmfmt -w decompress_8b_amd64.s
 | 
			
		||||
@ -1,488 +0,0 @@
 | 
			
		||||
// +build !appengine
 | 
			
		||||
// +build gc
 | 
			
		||||
// +build !noasm
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
#include "funcdata.h"
 | 
			
		||||
#include "go_asm.h"
 | 
			
		||||
 | 
			
		||||
#define bufoff      256 // see decompress.go, we're using [4][256]byte table
 | 
			
		||||
 | 
			
		||||
// func decompress4x_main_loop_x86(pbr0, pbr1, pbr2, pbr3 *bitReaderShifted,
 | 
			
		||||
//	peekBits uint8, buf *byte, tbl *dEntrySingle) (int, bool)
 | 
			
		||||
TEXT ·decompress4x_8b_loop_x86(SB), NOSPLIT, $8
 | 
			
		||||
#define off             R8
 | 
			
		||||
#define buffer          DI
 | 
			
		||||
#define table           SI
 | 
			
		||||
 | 
			
		||||
#define br_bits_read    R9
 | 
			
		||||
#define br_value        R10
 | 
			
		||||
#define br_offset       R11
 | 
			
		||||
#define peek_bits       R12
 | 
			
		||||
#define exhausted       DX
 | 
			
		||||
 | 
			
		||||
#define br0             R13
 | 
			
		||||
#define br1             R14
 | 
			
		||||
#define br2             R15
 | 
			
		||||
#define br3             BP
 | 
			
		||||
 | 
			
		||||
	MOVQ BP, 0(SP)
 | 
			
		||||
 | 
			
		||||
	XORQ exhausted, exhausted // exhausted = false
 | 
			
		||||
	XORQ off, off             // off = 0
 | 
			
		||||
 | 
			
		||||
	MOVBQZX peekBits+32(FP), peek_bits
 | 
			
		||||
	MOVQ    buf+40(FP), buffer
 | 
			
		||||
	MOVQ    tbl+48(FP), table
 | 
			
		||||
 | 
			
		||||
	MOVQ pbr0+0(FP), br0
 | 
			
		||||
	MOVQ pbr1+8(FP), br1
 | 
			
		||||
	MOVQ pbr2+16(FP), br2
 | 
			
		||||
	MOVQ pbr3+24(FP), br3
 | 
			
		||||
 | 
			
		||||
main_loop:
 | 
			
		||||
 | 
			
		||||
	// const stream = 0
 | 
			
		||||
	// br0.fillFast()
 | 
			
		||||
	MOVBQZX bitReaderShifted_bitsRead(br0), br_bits_read
 | 
			
		||||
	MOVQ    bitReaderShifted_value(br0), br_value
 | 
			
		||||
	MOVQ    bitReaderShifted_off(br0), br_offset
 | 
			
		||||
 | 
			
		||||
	// if b.bitsRead >= 32 {
 | 
			
		||||
	CMPQ br_bits_read, $32
 | 
			
		||||
	JB   skip_fill0
 | 
			
		||||
 | 
			
		||||
	SUBQ $32, br_bits_read // b.bitsRead -= 32
 | 
			
		||||
	SUBQ $4, br_offset     // b.off -= 4
 | 
			
		||||
 | 
			
		||||
	// v := b.in[b.off-4 : b.off]
 | 
			
		||||
	// v = v[:4]
 | 
			
		||||
	// low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
 | 
			
		||||
	MOVQ bitReaderShifted_in(br0), AX
 | 
			
		||||
	MOVL 0(br_offset)(AX*1), AX       // AX = uint32(b.in[b.off:b.off+4])
 | 
			
		||||
 | 
			
		||||
	// b.value |= uint64(low) << (b.bitsRead & 63)
 | 
			
		||||
	MOVQ br_bits_read, CX
 | 
			
		||||
	SHLQ CL, AX
 | 
			
		||||
	ORQ  AX, br_value
 | 
			
		||||
 | 
			
		||||
	// exhausted = exhausted || (br0.off < 4)
 | 
			
		||||
	CMPQ  br_offset, $4
 | 
			
		||||
	SETLT DL
 | 
			
		||||
	ORB   DL, DH
 | 
			
		||||
 | 
			
		||||
	// }
 | 
			
		||||
skip_fill0:
 | 
			
		||||
 | 
			
		||||
	// val0 := br0.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v0 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br0.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val1 := br0.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v1 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br0.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off] = uint8(v0.entry >> 8)
 | 
			
		||||
	// buf[stream][off+1] = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVW BX, 0(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// SECOND PART:
 | 
			
		||||
	// val2 := br0.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v2 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br0.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val3 := br0.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v3 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br0.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off+2] = uint8(v2.entry >> 8)
 | 
			
		||||
	// buf[stream][off+3] = uint8(v3.entry >> 8)
 | 
			
		||||
	MOVW BX, 0+2(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// update the bitrader reader structure
 | 
			
		||||
	MOVB br_bits_read, bitReaderShifted_bitsRead(br0)
 | 
			
		||||
	MOVQ br_value, bitReaderShifted_value(br0)
 | 
			
		||||
	MOVQ br_offset, bitReaderShifted_off(br0)
 | 
			
		||||
 | 
			
		||||
	// const stream = 1
 | 
			
		||||
	// br1.fillFast()
 | 
			
		||||
	MOVBQZX bitReaderShifted_bitsRead(br1), br_bits_read
 | 
			
		||||
	MOVQ    bitReaderShifted_value(br1), br_value
 | 
			
		||||
	MOVQ    bitReaderShifted_off(br1), br_offset
 | 
			
		||||
 | 
			
		||||
	// if b.bitsRead >= 32 {
 | 
			
		||||
	CMPQ br_bits_read, $32
 | 
			
		||||
	JB   skip_fill1
 | 
			
		||||
 | 
			
		||||
	SUBQ $32, br_bits_read // b.bitsRead -= 32
 | 
			
		||||
	SUBQ $4, br_offset     // b.off -= 4
 | 
			
		||||
 | 
			
		||||
	// v := b.in[b.off-4 : b.off]
 | 
			
		||||
	// v = v[:4]
 | 
			
		||||
	// low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
 | 
			
		||||
	MOVQ bitReaderShifted_in(br1), AX
 | 
			
		||||
	MOVL 0(br_offset)(AX*1), AX       // AX = uint32(b.in[b.off:b.off+4])
 | 
			
		||||
 | 
			
		||||
	// b.value |= uint64(low) << (b.bitsRead & 63)
 | 
			
		||||
	MOVQ br_bits_read, CX
 | 
			
		||||
	SHLQ CL, AX
 | 
			
		||||
	ORQ  AX, br_value
 | 
			
		||||
 | 
			
		||||
	// exhausted = exhausted || (br1.off < 4)
 | 
			
		||||
	CMPQ  br_offset, $4
 | 
			
		||||
	SETLT DL
 | 
			
		||||
	ORB   DL, DH
 | 
			
		||||
 | 
			
		||||
	// }
 | 
			
		||||
skip_fill1:
 | 
			
		||||
 | 
			
		||||
	// val0 := br1.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v0 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br1.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val1 := br1.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v1 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br1.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off] = uint8(v0.entry >> 8)
 | 
			
		||||
	// buf[stream][off+1] = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVW BX, 256(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// SECOND PART:
 | 
			
		||||
	// val2 := br1.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v2 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br1.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val3 := br1.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v3 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br1.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off+2] = uint8(v2.entry >> 8)
 | 
			
		||||
	// buf[stream][off+3] = uint8(v3.entry >> 8)
 | 
			
		||||
	MOVW BX, 256+2(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// update the bitrader reader structure
 | 
			
		||||
	MOVB br_bits_read, bitReaderShifted_bitsRead(br1)
 | 
			
		||||
	MOVQ br_value, bitReaderShifted_value(br1)
 | 
			
		||||
	MOVQ br_offset, bitReaderShifted_off(br1)
 | 
			
		||||
 | 
			
		||||
	// const stream = 2
 | 
			
		||||
	// br2.fillFast()
 | 
			
		||||
	MOVBQZX bitReaderShifted_bitsRead(br2), br_bits_read
 | 
			
		||||
	MOVQ    bitReaderShifted_value(br2), br_value
 | 
			
		||||
	MOVQ    bitReaderShifted_off(br2), br_offset
 | 
			
		||||
 | 
			
		||||
	// if b.bitsRead >= 32 {
 | 
			
		||||
	CMPQ br_bits_read, $32
 | 
			
		||||
	JB   skip_fill2
 | 
			
		||||
 | 
			
		||||
	SUBQ $32, br_bits_read // b.bitsRead -= 32
 | 
			
		||||
	SUBQ $4, br_offset     // b.off -= 4
 | 
			
		||||
 | 
			
		||||
	// v := b.in[b.off-4 : b.off]
 | 
			
		||||
	// v = v[:4]
 | 
			
		||||
	// low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
 | 
			
		||||
	MOVQ bitReaderShifted_in(br2), AX
 | 
			
		||||
	MOVL 0(br_offset)(AX*1), AX       // AX = uint32(b.in[b.off:b.off+4])
 | 
			
		||||
 | 
			
		||||
	// b.value |= uint64(low) << (b.bitsRead & 63)
 | 
			
		||||
	MOVQ br_bits_read, CX
 | 
			
		||||
	SHLQ CL, AX
 | 
			
		||||
	ORQ  AX, br_value
 | 
			
		||||
 | 
			
		||||
	// exhausted = exhausted || (br2.off < 4)
 | 
			
		||||
	CMPQ  br_offset, $4
 | 
			
		||||
	SETLT DL
 | 
			
		||||
	ORB   DL, DH
 | 
			
		||||
 | 
			
		||||
	// }
 | 
			
		||||
skip_fill2:
 | 
			
		||||
 | 
			
		||||
	// val0 := br2.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v0 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br2.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val1 := br2.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v1 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br2.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off] = uint8(v0.entry >> 8)
 | 
			
		||||
	// buf[stream][off+1] = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVW BX, 512(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// SECOND PART:
 | 
			
		||||
	// val2 := br2.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v2 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br2.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val3 := br2.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v3 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br2.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off+2] = uint8(v2.entry >> 8)
 | 
			
		||||
	// buf[stream][off+3] = uint8(v3.entry >> 8)
 | 
			
		||||
	MOVW BX, 512+2(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// update the bitrader reader structure
 | 
			
		||||
	MOVB br_bits_read, bitReaderShifted_bitsRead(br2)
 | 
			
		||||
	MOVQ br_value, bitReaderShifted_value(br2)
 | 
			
		||||
	MOVQ br_offset, bitReaderShifted_off(br2)
 | 
			
		||||
 | 
			
		||||
	// const stream = 3
 | 
			
		||||
	// br3.fillFast()
 | 
			
		||||
	MOVBQZX bitReaderShifted_bitsRead(br3), br_bits_read
 | 
			
		||||
	MOVQ    bitReaderShifted_value(br3), br_value
 | 
			
		||||
	MOVQ    bitReaderShifted_off(br3), br_offset
 | 
			
		||||
 | 
			
		||||
	// if b.bitsRead >= 32 {
 | 
			
		||||
	CMPQ br_bits_read, $32
 | 
			
		||||
	JB   skip_fill3
 | 
			
		||||
 | 
			
		||||
	SUBQ $32, br_bits_read // b.bitsRead -= 32
 | 
			
		||||
	SUBQ $4, br_offset     // b.off -= 4
 | 
			
		||||
 | 
			
		||||
	// v := b.in[b.off-4 : b.off]
 | 
			
		||||
	// v = v[:4]
 | 
			
		||||
	// low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
 | 
			
		||||
	MOVQ bitReaderShifted_in(br3), AX
 | 
			
		||||
	MOVL 0(br_offset)(AX*1), AX       // AX = uint32(b.in[b.off:b.off+4])
 | 
			
		||||
 | 
			
		||||
	// b.value |= uint64(low) << (b.bitsRead & 63)
 | 
			
		||||
	MOVQ br_bits_read, CX
 | 
			
		||||
	SHLQ CL, AX
 | 
			
		||||
	ORQ  AX, br_value
 | 
			
		||||
 | 
			
		||||
	// exhausted = exhausted || (br3.off < 4)
 | 
			
		||||
	CMPQ  br_offset, $4
 | 
			
		||||
	SETLT DL
 | 
			
		||||
	ORB   DL, DH
 | 
			
		||||
 | 
			
		||||
	// }
 | 
			
		||||
skip_fill3:
 | 
			
		||||
 | 
			
		||||
	// val0 := br3.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v0 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br3.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val1 := br3.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v1 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br3.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off] = uint8(v0.entry >> 8)
 | 
			
		||||
	// buf[stream][off+1] = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVW BX, 768(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// SECOND PART:
 | 
			
		||||
	// val2 := br3.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v2 := table[val0&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v0
 | 
			
		||||
 | 
			
		||||
	// br3.advance(uint8(v0.entry))
 | 
			
		||||
	MOVB    AH, BL           // BL = uint8(v0.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CL, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// val3 := br3.peekTopBits(peekBits)
 | 
			
		||||
	MOVQ peek_bits, CX
 | 
			
		||||
	MOVQ br_value, AX
 | 
			
		||||
	SHRQ CL, AX        // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
	// v3 := table[val1&mask]
 | 
			
		||||
	MOVW 0(table)(AX*2), AX // AX - v1
 | 
			
		||||
 | 
			
		||||
	// br3.advance(uint8(v1.entry))
 | 
			
		||||
	MOVB    AH, BH           // BH = uint8(v1.entry >> 8)
 | 
			
		||||
	MOVBQZX AL, CX
 | 
			
		||||
	SHLQ    CX, br_value     // value <<= n
 | 
			
		||||
	ADDQ    CX, br_bits_read // bits_read += n
 | 
			
		||||
 | 
			
		||||
	// these two writes get coalesced
 | 
			
		||||
	// buf[stream][off+2] = uint8(v2.entry >> 8)
 | 
			
		||||
	// buf[stream][off+3] = uint8(v3.entry >> 8)
 | 
			
		||||
	MOVW BX, 768+2(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
	// update the bitrader reader structure
 | 
			
		||||
	MOVB br_bits_read, bitReaderShifted_bitsRead(br3)
 | 
			
		||||
	MOVQ br_value, bitReaderShifted_value(br3)
 | 
			
		||||
	MOVQ br_offset, bitReaderShifted_off(br3)
 | 
			
		||||
 | 
			
		||||
	ADDQ $4, off // off += 2
 | 
			
		||||
 | 
			
		||||
	TESTB DH, DH // any br[i].ofs < 4?
 | 
			
		||||
	JNZ   end
 | 
			
		||||
 | 
			
		||||
	CMPQ off, $bufoff
 | 
			
		||||
	JL   main_loop
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
	MOVQ 0(SP), BP
 | 
			
		||||
 | 
			
		||||
	MOVB off, ret+56(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
#undef off
 | 
			
		||||
#undef buffer
 | 
			
		||||
#undef table
 | 
			
		||||
 | 
			
		||||
#undef br_bits_read
 | 
			
		||||
#undef br_value
 | 
			
		||||
#undef br_offset
 | 
			
		||||
#undef peek_bits
 | 
			
		||||
#undef exhausted
 | 
			
		||||
 | 
			
		||||
#undef br0
 | 
			
		||||
#undef br1
 | 
			
		||||
#undef br2
 | 
			
		||||
#undef br3
 | 
			
		||||
@ -1,197 +0,0 @@
 | 
			
		||||
// +build !appengine
 | 
			
		||||
// +build gc
 | 
			
		||||
// +build !noasm
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
#include "funcdata.h"
 | 
			
		||||
#include "go_asm.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define bufoff      256     // see decompress.go, we're using [4][256]byte table
 | 
			
		||||
 | 
			
		||||
//func decompress4x_main_loop_x86(pbr0, pbr1, pbr2, pbr3 *bitReaderShifted,
 | 
			
		||||
//	peekBits uint8, buf *byte, tbl *dEntrySingle) (int, bool)
 | 
			
		||||
TEXT ·decompress4x_8b_loop_x86(SB), NOSPLIT, $8
 | 
			
		||||
#define off             R8
 | 
			
		||||
#define buffer          DI
 | 
			
		||||
#define table           SI
 | 
			
		||||
 | 
			
		||||
#define br_bits_read    R9
 | 
			
		||||
#define br_value        R10
 | 
			
		||||
#define br_offset       R11
 | 
			
		||||
#define peek_bits       R12
 | 
			
		||||
#define exhausted       DX
 | 
			
		||||
 | 
			
		||||
#define br0             R13
 | 
			
		||||
#define br1             R14
 | 
			
		||||
#define br2             R15
 | 
			
		||||
#define br3             BP
 | 
			
		||||
 | 
			
		||||
    MOVQ    BP, 0(SP)
 | 
			
		||||
 | 
			
		||||
    XORQ    exhausted, exhausted    // exhausted = false
 | 
			
		||||
    XORQ    off, off                // off = 0
 | 
			
		||||
 | 
			
		||||
    MOVBQZX peekBits+32(FP), peek_bits
 | 
			
		||||
    MOVQ    buf+40(FP), buffer
 | 
			
		||||
    MOVQ    tbl+48(FP), table
 | 
			
		||||
 | 
			
		||||
    MOVQ    pbr0+0(FP), br0
 | 
			
		||||
    MOVQ    pbr1+8(FP), br1
 | 
			
		||||
    MOVQ    pbr2+16(FP), br2
 | 
			
		||||
    MOVQ    pbr3+24(FP), br3
 | 
			
		||||
 | 
			
		||||
main_loop:
 | 
			
		||||
{{ define "decode_2_values_x86" }}
 | 
			
		||||
    // const stream = {{ var "id" }}
 | 
			
		||||
    // br{{ var "id"}}.fillFast()
 | 
			
		||||
    MOVBQZX bitReaderShifted_bitsRead(br{{ var "id" }}), br_bits_read
 | 
			
		||||
    MOVQ    bitReaderShifted_value(br{{ var "id" }}), br_value
 | 
			
		||||
    MOVQ    bitReaderShifted_off(br{{ var "id" }}), br_offset
 | 
			
		||||
 | 
			
		||||
	// if b.bitsRead >= 32 {
 | 
			
		||||
    CMPQ    br_bits_read, $32
 | 
			
		||||
    JB      skip_fill{{ var "id" }}
 | 
			
		||||
 | 
			
		||||
    SUBQ    $32, br_bits_read       // b.bitsRead -= 32
 | 
			
		||||
    SUBQ    $4, br_offset           // b.off -= 4
 | 
			
		||||
 | 
			
		||||
	// v := b.in[b.off-4 : b.off]
 | 
			
		||||
	// v = v[:4]
 | 
			
		||||
	// low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
 | 
			
		||||
    MOVQ    bitReaderShifted_in(br{{ var "id" }}), AX
 | 
			
		||||
    MOVL    0(br_offset)(AX*1), AX  // AX = uint32(b.in[b.off:b.off+4])
 | 
			
		||||
 | 
			
		||||
	// b.value |= uint64(low) << (b.bitsRead & 63)
 | 
			
		||||
    MOVQ    br_bits_read, CX
 | 
			
		||||
    SHLQ    CL, AX
 | 
			
		||||
    ORQ     AX, br_value
 | 
			
		||||
 | 
			
		||||
    // exhausted = exhausted || (br{{ var "id"}}.off < 4)
 | 
			
		||||
    CMPQ    br_offset, $4
 | 
			
		||||
    SETLT   DL
 | 
			
		||||
    ORB     DL, DH
 | 
			
		||||
    // }
 | 
			
		||||
skip_fill{{ var "id" }}:
 | 
			
		||||
 | 
			
		||||
    // val0 := br{{ var "id"}}.peekTopBits(peekBits)
 | 
			
		||||
    MOVQ    br_value, AX
 | 
			
		||||
    MOVQ    peek_bits, CX
 | 
			
		||||
    SHRQ    CL, AX                  // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
    // v0 := table[val0&mask]
 | 
			
		||||
    MOVW    0(table)(AX*2), AX      // AX - v0
 | 
			
		||||
 | 
			
		||||
    // br{{ var "id"}}.advance(uint8(v0.entry))
 | 
			
		||||
    MOVB    AH, BL                  // BL = uint8(v0.entry >> 8)
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLQ    CL, br_value            // value <<= n
 | 
			
		||||
    ADDQ    CX, br_bits_read        // bits_read += n
 | 
			
		||||
 | 
			
		||||
    // val1 := br{{ var "id"}}.peekTopBits(peekBits)
 | 
			
		||||
    MOVQ    peek_bits, CX
 | 
			
		||||
    MOVQ    br_value, AX
 | 
			
		||||
    SHRQ    CL, AX                  // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
    // v1 := table[val1&mask]
 | 
			
		||||
    MOVW    0(table)(AX*2), AX      // AX - v1
 | 
			
		||||
 | 
			
		||||
    // br{{ var "id"}}.advance(uint8(v1.entry))
 | 
			
		||||
    MOVB    AH, BH                  // BH = uint8(v1.entry >> 8)
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLQ    CX, br_value            // value <<= n
 | 
			
		||||
    ADDQ    CX, br_bits_read        // bits_read += n
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // these two writes get coalesced
 | 
			
		||||
    // buf[stream][off] = uint8(v0.entry >> 8)
 | 
			
		||||
    // buf[stream][off+1] = uint8(v1.entry >> 8)
 | 
			
		||||
    MOVW    BX, {{ var "bufofs" }}(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
    // SECOND PART:
 | 
			
		||||
    // val2 := br{{ var "id"}}.peekTopBits(peekBits)
 | 
			
		||||
    MOVQ    br_value, AX
 | 
			
		||||
    MOVQ    peek_bits, CX
 | 
			
		||||
    SHRQ    CL, AX                  // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
    // v2 := table[val0&mask]
 | 
			
		||||
    MOVW    0(table)(AX*2), AX      // AX - v0
 | 
			
		||||
 | 
			
		||||
    // br{{ var "id"}}.advance(uint8(v0.entry))
 | 
			
		||||
    MOVB    AH, BL                  // BL = uint8(v0.entry >> 8)
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLQ    CL, br_value            // value <<= n
 | 
			
		||||
    ADDQ    CX, br_bits_read        // bits_read += n
 | 
			
		||||
 | 
			
		||||
    // val3 := br{{ var "id"}}.peekTopBits(peekBits)
 | 
			
		||||
    MOVQ    peek_bits, CX
 | 
			
		||||
    MOVQ    br_value, AX
 | 
			
		||||
    SHRQ    CL, AX                  // AX = (value >> peek_bits) & mask
 | 
			
		||||
 | 
			
		||||
    // v3 := table[val1&mask]
 | 
			
		||||
    MOVW    0(table)(AX*2), AX      // AX - v1
 | 
			
		||||
 | 
			
		||||
    // br{{ var "id"}}.advance(uint8(v1.entry))
 | 
			
		||||
    MOVB    AH, BH                  // BH = uint8(v1.entry >> 8)
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLQ    CX, br_value            // value <<= n
 | 
			
		||||
    ADDQ    CX, br_bits_read        // bits_read += n
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // these two writes get coalesced
 | 
			
		||||
    // buf[stream][off+2] = uint8(v2.entry >> 8)
 | 
			
		||||
    // buf[stream][off+3] = uint8(v3.entry >> 8)
 | 
			
		||||
    MOVW    BX, {{ var "bufofs" }}+2(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
    // update the bitrader reader structure
 | 
			
		||||
    MOVB    br_bits_read, bitReaderShifted_bitsRead(br{{ var "id" }})
 | 
			
		||||
    MOVQ    br_value, bitReaderShifted_value(br{{ var "id" }})
 | 
			
		||||
    MOVQ    br_offset, bitReaderShifted_off(br{{ var "id" }})
 | 
			
		||||
{{ end }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "0" }}
 | 
			
		||||
    {{ set "ofs" "0" }}
 | 
			
		||||
    {{ set "bufofs" "0" }} {{/* id * bufoff */}}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "1" }}
 | 
			
		||||
    {{ set "ofs" "8" }}
 | 
			
		||||
    {{ set "bufofs" "256" }}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "2" }}
 | 
			
		||||
    {{ set "ofs" "16" }}
 | 
			
		||||
    {{ set "bufofs" "512" }}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "3" }}
 | 
			
		||||
    {{ set "ofs" "24" }}
 | 
			
		||||
    {{ set "bufofs" "768" }}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    ADDQ    $4, off     // off += 2
 | 
			
		||||
 | 
			
		||||
    TESTB   DH, DH      // any br[i].ofs < 4?
 | 
			
		||||
    JNZ     end
 | 
			
		||||
 | 
			
		||||
    CMPQ    off, $bufoff
 | 
			
		||||
    JL      main_loop
 | 
			
		||||
end:
 | 
			
		||||
    MOVQ    0(SP), BP
 | 
			
		||||
 | 
			
		||||
    MOVB    off, ret+56(FP)
 | 
			
		||||
    RET
 | 
			
		||||
#undef  off
 | 
			
		||||
#undef  buffer
 | 
			
		||||
#undef  table
 | 
			
		||||
 | 
			
		||||
#undef  br_bits_read
 | 
			
		||||
#undef  br_value
 | 
			
		||||
#undef  br_offset
 | 
			
		||||
#undef  peek_bits
 | 
			
		||||
#undef  exhausted
 | 
			
		||||
 | 
			
		||||
#undef  br0
 | 
			
		||||
#undef  br1
 | 
			
		||||
#undef  br2
 | 
			
		||||
#undef  br3
 | 
			
		||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								@ -1,195 +0,0 @@
 | 
			
		||||
// +build !appengine
 | 
			
		||||
// +build gc
 | 
			
		||||
// +build !noasm
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
#include "funcdata.h"
 | 
			
		||||
#include "go_asm.h"
 | 
			
		||||
 | 
			
		||||
#ifdef GOAMD64_v4
 | 
			
		||||
#ifndef GOAMD64_v3
 | 
			
		||||
#define GOAMD64_v3
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define bufoff      256     // see decompress.go, we're using [4][256]byte table
 | 
			
		||||
 | 
			
		||||
//func decompress4x_main_loop_x86(pbr0, pbr1, pbr2, pbr3 *bitReaderShifted,
 | 
			
		||||
//	peekBits uint8, buf *byte, tbl *dEntrySingle) (int, bool)
 | 
			
		||||
TEXT ·decompress4x_main_loop_x86(SB), NOSPLIT, $8
 | 
			
		||||
#define off             R8
 | 
			
		||||
#define buffer          DI
 | 
			
		||||
#define table           SI
 | 
			
		||||
 | 
			
		||||
#define br_bits_read    R9
 | 
			
		||||
#define br_value        R10
 | 
			
		||||
#define br_offset       R11
 | 
			
		||||
#define peek_bits       R12
 | 
			
		||||
#define exhausted       DX
 | 
			
		||||
 | 
			
		||||
#define br0             R13
 | 
			
		||||
#define br1             R14
 | 
			
		||||
#define br2             R15
 | 
			
		||||
#define br3             BP
 | 
			
		||||
 | 
			
		||||
    MOVQ    BP, 0(SP)
 | 
			
		||||
 | 
			
		||||
    XORQ    exhausted, exhausted    // exhausted = false
 | 
			
		||||
    XORQ    off, off                // off = 0
 | 
			
		||||
 | 
			
		||||
    MOVBQZX peekBits+32(FP), peek_bits
 | 
			
		||||
    MOVQ    buf+40(FP), buffer
 | 
			
		||||
    MOVQ    tbl+48(FP), table
 | 
			
		||||
 | 
			
		||||
    MOVQ    pbr0+0(FP), br0
 | 
			
		||||
    MOVQ    pbr1+8(FP), br1
 | 
			
		||||
    MOVQ    pbr2+16(FP), br2
 | 
			
		||||
    MOVQ    pbr3+24(FP), br3
 | 
			
		||||
 | 
			
		||||
main_loop:
 | 
			
		||||
{{ define "decode_2_values_x86" }}
 | 
			
		||||
    // const stream = {{ var "id" }}
 | 
			
		||||
    // br{{ var "id"}}.fillFast()
 | 
			
		||||
    MOVBQZX bitReaderShifted_bitsRead(br{{ var "id" }}), br_bits_read
 | 
			
		||||
    MOVQ    bitReaderShifted_value(br{{ var "id" }}), br_value
 | 
			
		||||
    MOVQ    bitReaderShifted_off(br{{ var "id" }}), br_offset
 | 
			
		||||
 | 
			
		||||
    // We must have at least 2 * max tablelog left
 | 
			
		||||
    CMPQ    br_bits_read, $64-22
 | 
			
		||||
    JBE     skip_fill{{ var "id" }}
 | 
			
		||||
 | 
			
		||||
    SUBQ    $32, br_bits_read       // b.bitsRead -= 32
 | 
			
		||||
    SUBQ    $4, br_offset           // b.off -= 4
 | 
			
		||||
 | 
			
		||||
	// v := b.in[b.off-4 : b.off]
 | 
			
		||||
	// v = v[:4]
 | 
			
		||||
	// low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
 | 
			
		||||
    MOVQ    bitReaderShifted_in(br{{ var "id" }}), AX
 | 
			
		||||
 | 
			
		||||
	// b.value |= uint64(low) << (b.bitsRead & 63)
 | 
			
		||||
#ifdef GOAMD64_v3
 | 
			
		||||
    SHLXQ   br_bits_read, 0(br_offset)(AX*1), AX // AX = uint32(b.in[b.off:b.off+4]) << (b.bitsRead & 63)
 | 
			
		||||
#else
 | 
			
		||||
    MOVL    0(br_offset)(AX*1), AX  // AX = uint32(b.in[b.off:b.off+4])
 | 
			
		||||
    MOVQ    br_bits_read, CX
 | 
			
		||||
    SHLQ    CL, AX
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ORQ     AX, br_value
 | 
			
		||||
 | 
			
		||||
    // exhausted = exhausted || (br{{ var "id"}}.off < 4)
 | 
			
		||||
    CMPQ    br_offset, $4
 | 
			
		||||
    SETLT   DL
 | 
			
		||||
    ORB     DL, DH
 | 
			
		||||
    // }
 | 
			
		||||
skip_fill{{ var "id" }}:
 | 
			
		||||
 | 
			
		||||
    // val0 := br{{ var "id"}}.peekTopBits(peekBits)
 | 
			
		||||
#ifdef GOAMD64_v3
 | 
			
		||||
    SHRXQ   peek_bits, br_value, AX // AX = (value >> peek_bits) & mask
 | 
			
		||||
#else
 | 
			
		||||
    MOVQ    br_value, AX
 | 
			
		||||
    MOVQ    peek_bits, CX
 | 
			
		||||
    SHRQ    CL, AX                  // AX = (value >> peek_bits) & mask
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // v0 := table[val0&mask]
 | 
			
		||||
    MOVW    0(table)(AX*2), AX      // AX - v0
 | 
			
		||||
 | 
			
		||||
    // br{{ var "id"}}.advance(uint8(v0.entry))
 | 
			
		||||
    MOVB    AH, BL                  // BL = uint8(v0.entry >> 8)
 | 
			
		||||
 | 
			
		||||
#ifdef GOAMD64_v3
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLXQ   AX, br_value, br_value // value <<= n
 | 
			
		||||
#else
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLQ    CL, br_value            // value <<= n
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ADDQ    CX, br_bits_read        // bits_read += n
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef GOAMD64_v3
 | 
			
		||||
    SHRXQ    peek_bits, br_value, AX  // AX = (value >> peek_bits) & mask
 | 
			
		||||
#else
 | 
			
		||||
    // val1 := br{{ var "id"}}.peekTopBits(peekBits)
 | 
			
		||||
    MOVQ    peek_bits, CX
 | 
			
		||||
    MOVQ    br_value, AX
 | 
			
		||||
    SHRQ    CL, AX                  // AX = (value >> peek_bits) & mask
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // v1 := table[val1&mask]
 | 
			
		||||
    MOVW    0(table)(AX*2), AX      // AX - v1
 | 
			
		||||
 | 
			
		||||
    // br{{ var "id"}}.advance(uint8(v1.entry))
 | 
			
		||||
    MOVB    AH, BH                  // BH = uint8(v1.entry >> 8)
 | 
			
		||||
 | 
			
		||||
#ifdef GOAMD64_v3
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLXQ   AX, br_value, br_value // value <<= n
 | 
			
		||||
#else
 | 
			
		||||
    MOVBQZX AL, CX
 | 
			
		||||
    SHLQ    CL, br_value            // value <<= n
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ADDQ    CX, br_bits_read        // bits_read += n
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // these two writes get coalesced
 | 
			
		||||
    // buf[stream][off] = uint8(v0.entry >> 8)
 | 
			
		||||
    // buf[stream][off+1] = uint8(v1.entry >> 8)
 | 
			
		||||
    MOVW    BX, {{ var "bufofs" }}(buffer)(off*1)
 | 
			
		||||
 | 
			
		||||
    // update the bitrader reader structure
 | 
			
		||||
    MOVB    br_bits_read, bitReaderShifted_bitsRead(br{{ var "id" }})
 | 
			
		||||
    MOVQ    br_value, bitReaderShifted_value(br{{ var "id" }})
 | 
			
		||||
    MOVQ    br_offset, bitReaderShifted_off(br{{ var "id" }})
 | 
			
		||||
{{ end }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "0" }}
 | 
			
		||||
    {{ set "ofs" "0" }}
 | 
			
		||||
    {{ set "bufofs" "0" }} {{/* id * bufoff */}}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "1" }}
 | 
			
		||||
    {{ set "ofs" "8" }}
 | 
			
		||||
    {{ set "bufofs" "256" }}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "2" }}
 | 
			
		||||
    {{ set "ofs" "16" }}
 | 
			
		||||
    {{ set "bufofs" "512" }}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    {{ set "id" "3" }}
 | 
			
		||||
    {{ set "ofs" "24" }}
 | 
			
		||||
    {{ set "bufofs" "768" }}
 | 
			
		||||
    {{ template "decode_2_values_x86" . }}
 | 
			
		||||
 | 
			
		||||
    ADDQ    $2, off     // off += 2
 | 
			
		||||
 | 
			
		||||
    TESTB   DH, DH      // any br[i].ofs < 4?
 | 
			
		||||
    JNZ     end
 | 
			
		||||
 | 
			
		||||
    CMPQ    off, $bufoff
 | 
			
		||||
    JL      main_loop
 | 
			
		||||
end:
 | 
			
		||||
    MOVQ    0(SP), BP
 | 
			
		||||
 | 
			
		||||
    MOVB    off, ret+56(FP)
 | 
			
		||||
    RET
 | 
			
		||||
#undef  off
 | 
			
		||||
#undef  buffer
 | 
			
		||||
#undef  table
 | 
			
		||||
 | 
			
		||||
#undef  br_bits_read
 | 
			
		||||
#undef  br_value
 | 
			
		||||
#undef  br_offset
 | 
			
		||||
#undef  peek_bits
 | 
			
		||||
#undef  exhausted
 | 
			
		||||
 | 
			
		||||
#undef  br0
 | 
			
		||||
#undef  br1
 | 
			
		||||
#undef  br2
 | 
			
		||||
#undef  br3
 | 
			
		||||
@ -0,0 +1,34 @@
 | 
			
		||||
// Package cpuinfo gives runtime info about the current CPU.
 | 
			
		||||
//
 | 
			
		||||
// This is a very limited module meant for use internally
 | 
			
		||||
// in this project. For more versatile solution check
 | 
			
		||||
// https://github.com/klauspost/cpuid.
 | 
			
		||||
package cpuinfo
 | 
			
		||||
 | 
			
		||||
// HasBMI1 checks whether an x86 CPU supports the BMI1 extension.
 | 
			
		||||
func HasBMI1() bool {
 | 
			
		||||
	return hasBMI1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasBMI2 checks whether an x86 CPU supports the BMI2 extension.
 | 
			
		||||
func HasBMI2() bool {
 | 
			
		||||
	return hasBMI2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisableBMI2 will disable BMI2, for testing purposes.
 | 
			
		||||
// Call returned function to restore previous state.
 | 
			
		||||
func DisableBMI2() func() {
 | 
			
		||||
	old := hasBMI2
 | 
			
		||||
	hasBMI2 = false
 | 
			
		||||
	return func() {
 | 
			
		||||
		hasBMI2 = old
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasBMI checks whether an x86 CPU supports both BMI1 and BMI2 extensions.
 | 
			
		||||
func HasBMI() bool {
 | 
			
		||||
	return HasBMI1() && HasBMI2()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var hasBMI1 bool
 | 
			
		||||
var hasBMI2 bool
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
//go:build amd64 && !appengine && !noasm && gc
 | 
			
		||||
// +build amd64,!appengine,!noasm,gc
 | 
			
		||||
 | 
			
		||||
package cpuinfo
 | 
			
		||||
 | 
			
		||||
// go:noescape
 | 
			
		||||
func x86extensions() (bmi1, bmi2 bool)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	hasBMI1, hasBMI2 = x86extensions()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,36 @@
 | 
			
		||||
// +build !appengine
 | 
			
		||||
// +build gc
 | 
			
		||||
// +build !noasm
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
#include "funcdata.h"
 | 
			
		||||
#include "go_asm.h"
 | 
			
		||||
 | 
			
		||||
TEXT ·x86extensions(SB), NOSPLIT, $0
 | 
			
		||||
	// 1. determine max EAX value
 | 
			
		||||
	XORQ AX, AX
 | 
			
		||||
	CPUID
 | 
			
		||||
 | 
			
		||||
	CMPQ AX, $7
 | 
			
		||||
	JB   unsupported
 | 
			
		||||
 | 
			
		||||
	// 2. EAX = 7, ECX = 0 --- see Table 3-8 "Information Returned by CPUID Instruction"
 | 
			
		||||
	MOVQ $7, AX
 | 
			
		||||
	MOVQ $0, CX
 | 
			
		||||
	CPUID
 | 
			
		||||
 | 
			
		||||
	BTQ   $3, BX // bit 3 = BMI1
 | 
			
		||||
	SETCS AL
 | 
			
		||||
 | 
			
		||||
	BTQ   $8, BX // bit 8 = BMI2
 | 
			
		||||
	SETCS AH
 | 
			
		||||
 | 
			
		||||
	MOVB AL, bmi1+0(FP)
 | 
			
		||||
	MOVB AH, bmi2+1(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
unsupported:
 | 
			
		||||
	XORQ AX, AX
 | 
			
		||||
	MOVB AL, bmi1+0(FP)
 | 
			
		||||
	MOVB AL, bmi2+1(FP)
 | 
			
		||||
	RET
 | 
			
		||||
@ -0,0 +1,64 @@
 | 
			
		||||
//go:build amd64 && !appengine && !noasm && gc
 | 
			
		||||
// +build amd64,!appengine,!noasm,gc
 | 
			
		||||
 | 
			
		||||
package zstd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type buildDtableAsmContext struct {
 | 
			
		||||
	// inputs
 | 
			
		||||
	stateTable *uint16
 | 
			
		||||
	norm       *int16
 | 
			
		||||
	dt         *uint64
 | 
			
		||||
 | 
			
		||||
	// outputs --- set by the procedure in the case of error;
 | 
			
		||||
	// for interpretation please see the error handling part below
 | 
			
		||||
	errParam1 uint64
 | 
			
		||||
	errParam2 uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable.
 | 
			
		||||
// Function returns non-zero exit code on error.
 | 
			
		||||
// go:noescape
 | 
			
		||||
func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int
 | 
			
		||||
 | 
			
		||||
// please keep in sync with _generate/gen_fse.go
 | 
			
		||||
const (
 | 
			
		||||
	errorCorruptedNormalizedCounter = 1
 | 
			
		||||
	errorNewStateTooBig             = 2
 | 
			
		||||
	errorNewStateNoBits             = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// buildDtable will build the decoding table.
 | 
			
		||||
func (s *fseDecoder) buildDtable() error {
 | 
			
		||||
	ctx := buildDtableAsmContext{
 | 
			
		||||
		stateTable: (*uint16)(&s.stateTable[0]),
 | 
			
		||||
		norm:       (*int16)(&s.norm[0]),
 | 
			
		||||
		dt:         (*uint64)(&s.dt[0]),
 | 
			
		||||
	}
 | 
			
		||||
	code := buildDtable_asm(s, &ctx)
 | 
			
		||||
 | 
			
		||||
	if code != 0 {
 | 
			
		||||
		switch code {
 | 
			
		||||
		case errorCorruptedNormalizedCounter:
 | 
			
		||||
			position := ctx.errParam1
 | 
			
		||||
			return fmt.Errorf("corrupted input (position=%d, expected 0)", position)
 | 
			
		||||
 | 
			
		||||
		case errorNewStateTooBig:
 | 
			
		||||
			newState := decSymbol(ctx.errParam1)
 | 
			
		||||
			size := ctx.errParam2
 | 
			
		||||
			return fmt.Errorf("newState (%d) outside table size (%d)", newState, size)
 | 
			
		||||
 | 
			
		||||
		case errorNewStateNoBits:
 | 
			
		||||
			newState := decSymbol(ctx.errParam1)
 | 
			
		||||
			oldState := decSymbol(ctx.errParam2)
 | 
			
		||||
			return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, oldState)
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("buildDtable_asm returned unhandled nonzero code = %d", code)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,127 @@
 | 
			
		||||
// Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
//go:build !appengine && !noasm && gc && !noasm
 | 
			
		||||
// +build !appengine,!noasm,gc,!noasm
 | 
			
		||||
 | 
			
		||||
// func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int
 | 
			
		||||
TEXT ·buildDtable_asm(SB), $0-24
 | 
			
		||||
	MOVQ ctx+8(FP), CX
 | 
			
		||||
	MOVQ s+0(FP), DI
 | 
			
		||||
 | 
			
		||||
	// Load values
 | 
			
		||||
	MOVBQZX 4098(DI), DX
 | 
			
		||||
	XORQ    AX, AX
 | 
			
		||||
	BTSQ    DX, AX
 | 
			
		||||
	MOVQ    (CX), BX
 | 
			
		||||
	MOVQ    16(CX), SI
 | 
			
		||||
	LEAQ    -1(AX), R8
 | 
			
		||||
	MOVQ    8(CX), CX
 | 
			
		||||
	MOVWQZX 4096(DI), DI
 | 
			
		||||
 | 
			
		||||
	// End load values
 | 
			
		||||
	// Init, lay down lowprob symbols
 | 
			
		||||
	XORQ R9, R9
 | 
			
		||||
	JMP  init_main_loop_condition
 | 
			
		||||
 | 
			
		||||
init_main_loop:
 | 
			
		||||
	MOVWQSX (CX)(R9*2), R10
 | 
			
		||||
	CMPW    R10, $-1
 | 
			
		||||
	JNE     do_not_update_high_threshold
 | 
			
		||||
	MOVB    R9, 1(SI)(R8*8)
 | 
			
		||||
	DECQ    R8
 | 
			
		||||
	MOVQ    $0x0000000000000001, R10
 | 
			
		||||
 | 
			
		||||
do_not_update_high_threshold:
 | 
			
		||||
	MOVW R10, (BX)(R9*2)
 | 
			
		||||
	INCQ R9
 | 
			
		||||
 | 
			
		||||
init_main_loop_condition:
 | 
			
		||||
	CMPQ R9, DI
 | 
			
		||||
	JL   init_main_loop
 | 
			
		||||
 | 
			
		||||
	// Spread symbols
 | 
			
		||||
	// Calculate table step
 | 
			
		||||
	MOVQ AX, R9
 | 
			
		||||
	SHRQ $0x01, R9
 | 
			
		||||
	MOVQ AX, R10
 | 
			
		||||
	SHRQ $0x03, R10
 | 
			
		||||
	LEAQ 3(R9)(R10*1), R9
 | 
			
		||||
 | 
			
		||||
	// Fill add bits values
 | 
			
		||||
	LEAQ -1(AX), R10
 | 
			
		||||
	XORQ R11, R11
 | 
			
		||||
	XORQ R12, R12
 | 
			
		||||
	JMP  spread_main_loop_condition
 | 
			
		||||
 | 
			
		||||
spread_main_loop:
 | 
			
		||||
	XORQ    R13, R13
 | 
			
		||||
	MOVWQSX (CX)(R12*2), R14
 | 
			
		||||
	JMP     spread_inner_loop_condition
 | 
			
		||||
 | 
			
		||||
spread_inner_loop:
 | 
			
		||||
	MOVB R12, 1(SI)(R11*8)
 | 
			
		||||
 | 
			
		||||
adjust_position:
 | 
			
		||||
	ADDQ R9, R11
 | 
			
		||||
	ANDQ R10, R11
 | 
			
		||||
	CMPQ R11, R8
 | 
			
		||||
	JG   adjust_position
 | 
			
		||||
	INCQ R13
 | 
			
		||||
 | 
			
		||||
spread_inner_loop_condition:
 | 
			
		||||
	CMPQ R13, R14
 | 
			
		||||
	JL   spread_inner_loop
 | 
			
		||||
	INCQ R12
 | 
			
		||||
 | 
			
		||||
spread_main_loop_condition:
 | 
			
		||||
	CMPQ  R12, DI
 | 
			
		||||
	JL    spread_main_loop
 | 
			
		||||
	TESTQ R11, R11
 | 
			
		||||
	JZ    spread_check_ok
 | 
			
		||||
	MOVQ  ctx+8(FP), AX
 | 
			
		||||
	MOVQ  R11, 24(AX)
 | 
			
		||||
	MOVQ  $+1, ret+16(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
spread_check_ok:
 | 
			
		||||
	// Build Decoding table
 | 
			
		||||
	XORQ DI, DI
 | 
			
		||||
 | 
			
		||||
build_table_main_table:
 | 
			
		||||
	MOVBQZX 1(SI)(DI*8), CX
 | 
			
		||||
	MOVWQZX (BX)(CX*2), R8
 | 
			
		||||
	LEAQ    1(R8), R9
 | 
			
		||||
	MOVW    R9, (BX)(CX*2)
 | 
			
		||||
	MOVQ    R8, R9
 | 
			
		||||
	BSRQ    R9, R9
 | 
			
		||||
	MOVQ    DX, CX
 | 
			
		||||
	SUBQ    R9, CX
 | 
			
		||||
	SHLQ    CL, R8
 | 
			
		||||
	SUBQ    AX, R8
 | 
			
		||||
	MOVB    CL, (SI)(DI*8)
 | 
			
		||||
	MOVW    R8, 2(SI)(DI*8)
 | 
			
		||||
	CMPQ    R8, AX
 | 
			
		||||
	JLE     build_table_check1_ok
 | 
			
		||||
	MOVQ    ctx+8(FP), CX
 | 
			
		||||
	MOVQ    R8, 24(CX)
 | 
			
		||||
	MOVQ    AX, 32(CX)
 | 
			
		||||
	MOVQ    $+2, ret+16(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
build_table_check1_ok:
 | 
			
		||||
	TESTB CL, CL
 | 
			
		||||
	JNZ   build_table_check2_ok
 | 
			
		||||
	CMPW  R8, DI
 | 
			
		||||
	JNE   build_table_check2_ok
 | 
			
		||||
	MOVQ  ctx+8(FP), AX
 | 
			
		||||
	MOVQ  R8, 24(AX)
 | 
			
		||||
	MOVQ  DI, 32(AX)
 | 
			
		||||
	MOVQ  $+3, ret+16(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
build_table_check2_ok:
 | 
			
		||||
	INCQ DI
 | 
			
		||||
	CMPQ DI, AX
 | 
			
		||||
	JL   build_table_main_table
 | 
			
		||||
	MOVQ $+0, ret+16(FP)
 | 
			
		||||
	RET
 | 
			
		||||
@ -0,0 +1,72 @@
 | 
			
		||||
//go:build !amd64 || appengine || !gc || noasm
 | 
			
		||||
// +build !amd64 appengine !gc noasm
 | 
			
		||||
 | 
			
		||||
package zstd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// buildDtable will build the decoding table.
 | 
			
		||||
func (s *fseDecoder) buildDtable() error {
 | 
			
		||||
	tableSize := uint32(1 << s.actualTableLog)
 | 
			
		||||
	highThreshold := tableSize - 1
 | 
			
		||||
	symbolNext := s.stateTable[:256]
 | 
			
		||||
 | 
			
		||||
	// Init, lay down lowprob symbols
 | 
			
		||||
	{
 | 
			
		||||
		for i, v := range s.norm[:s.symbolLen] {
 | 
			
		||||
			if v == -1 {
 | 
			
		||||
				s.dt[highThreshold].setAddBits(uint8(i))
 | 
			
		||||
				highThreshold--
 | 
			
		||||
				symbolNext[i] = 1
 | 
			
		||||
			} else {
 | 
			
		||||
				symbolNext[i] = uint16(v)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Spread symbols
 | 
			
		||||
	{
 | 
			
		||||
		tableMask := tableSize - 1
 | 
			
		||||
		step := tableStep(tableSize)
 | 
			
		||||
		position := uint32(0)
 | 
			
		||||
		for ss, v := range s.norm[:s.symbolLen] {
 | 
			
		||||
			for i := 0; i < int(v); i++ {
 | 
			
		||||
				s.dt[position].setAddBits(uint8(ss))
 | 
			
		||||
				position = (position + step) & tableMask
 | 
			
		||||
				for position > highThreshold {
 | 
			
		||||
					// lowprob area
 | 
			
		||||
					position = (position + step) & tableMask
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if position != 0 {
 | 
			
		||||
			// position must reach all cells once, otherwise normalizedCounter is incorrect
 | 
			
		||||
			return errors.New("corrupted input (position != 0)")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Build Decoding table
 | 
			
		||||
	{
 | 
			
		||||
		tableSize := uint16(1 << s.actualTableLog)
 | 
			
		||||
		for u, v := range s.dt[:tableSize] {
 | 
			
		||||
			symbol := v.addBits()
 | 
			
		||||
			nextState := symbolNext[symbol]
 | 
			
		||||
			symbolNext[symbol] = nextState + 1
 | 
			
		||||
			nBits := s.actualTableLog - byte(highBits(uint32(nextState)))
 | 
			
		||||
			s.dt[u&maxTableMask].setNBits(nBits)
 | 
			
		||||
			newState := (nextState << nBits) - tableSize
 | 
			
		||||
			if newState > tableSize {
 | 
			
		||||
				return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize)
 | 
			
		||||
			}
 | 
			
		||||
			if newState == uint16(u) && nBits == 0 {
 | 
			
		||||
				// Seems weird that this is possible with nbits > 0.
 | 
			
		||||
				return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u)
 | 
			
		||||
			}
 | 
			
		||||
			s.dt[u&maxTableMask].setNewState(newState)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +0,0 @@
 | 
			
		||||
//go:build ignorecrc
 | 
			
		||||
// +build ignorecrc
 | 
			
		||||
 | 
			
		||||
// Copyright 2019+ Klaus Post. All rights reserved.
 | 
			
		||||
// License information can be found in the LICENSE file.
 | 
			
		||||
// Based on work by Yann Collet, released under BSD License.
 | 
			
		||||
 | 
			
		||||
package zstd
 | 
			
		||||
 | 
			
		||||
// ignoreCRC can be used for fuzz testing to ignore CRC values...
 | 
			
		||||
const ignoreCRC = true
 | 
			
		||||
@ -1,11 +0,0 @@
 | 
			
		||||
//go:build !ignorecrc
 | 
			
		||||
// +build !ignorecrc
 | 
			
		||||
 | 
			
		||||
// Copyright 2019+ Klaus Post. All rights reserved.
 | 
			
		||||
// License information can be found in the LICENSE file.
 | 
			
		||||
// Based on work by Yann Collet, released under BSD License.
 | 
			
		||||
 | 
			
		||||
package zstd
 | 
			
		||||
 | 
			
		||||
// ignoreCRC can be used for fuzz testing to ignore CRC values...
 | 
			
		||||
const ignoreCRC = false
 | 
			
		||||
@ -0,0 +1,362 @@
 | 
			
		||||
//go:build amd64 && !appengine && !noasm && gc
 | 
			
		||||
// +build amd64,!appengine,!noasm,gc
 | 
			
		||||
 | 
			
		||||
package zstd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/klauspost/compress/internal/cpuinfo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type decodeSyncAsmContext struct {
 | 
			
		||||
	llTable     []decSymbol
 | 
			
		||||
	mlTable     []decSymbol
 | 
			
		||||
	ofTable     []decSymbol
 | 
			
		||||
	llState     uint64
 | 
			
		||||
	mlState     uint64
 | 
			
		||||
	ofState     uint64
 | 
			
		||||
	iteration   int
 | 
			
		||||
	litRemain   int
 | 
			
		||||
	out         []byte
 | 
			
		||||
	outPosition int
 | 
			
		||||
	literals    []byte
 | 
			
		||||
	litPosition int
 | 
			
		||||
	history     []byte
 | 
			
		||||
	windowSize  int
 | 
			
		||||
	ll          int // set on error (not for all errors, please refer to _generate/gen.go)
 | 
			
		||||
	ml          int // set on error (not for all errors, please refer to _generate/gen.go)
 | 
			
		||||
	mo          int // set on error (not for all errors, please refer to _generate/gen.go)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm.
 | 
			
		||||
//
 | 
			
		||||
// Please refer to seqdec_generic.go for the reference implementation.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
 | 
			
		||||
 | 
			
		||||
// decode sequences from the stream with the provided history but without a dictionary.
 | 
			
		||||
func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) {
 | 
			
		||||
	if len(s.dict) > 0 {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	useSafe := false
 | 
			
		||||
	if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc {
 | 
			
		||||
		useSafe = true
 | 
			
		||||
	}
 | 
			
		||||
	if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) {
 | 
			
		||||
		useSafe = true
 | 
			
		||||
	}
 | 
			
		||||
	if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc {
 | 
			
		||||
		useSafe = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	br := s.br
 | 
			
		||||
 | 
			
		||||
	maxBlockSize := maxCompressedBlockSize
 | 
			
		||||
	if s.windowSize < maxBlockSize {
 | 
			
		||||
		maxBlockSize = s.windowSize
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := decodeSyncAsmContext{
 | 
			
		||||
		llTable:     s.litLengths.fse.dt[:maxTablesize],
 | 
			
		||||
		mlTable:     s.matchLengths.fse.dt[:maxTablesize],
 | 
			
		||||
		ofTable:     s.offsets.fse.dt[:maxTablesize],
 | 
			
		||||
		llState:     uint64(s.litLengths.state.state),
 | 
			
		||||
		mlState:     uint64(s.matchLengths.state.state),
 | 
			
		||||
		ofState:     uint64(s.offsets.state.state),
 | 
			
		||||
		iteration:   s.nSeqs - 1,
 | 
			
		||||
		litRemain:   len(s.literals),
 | 
			
		||||
		out:         s.out,
 | 
			
		||||
		outPosition: len(s.out),
 | 
			
		||||
		literals:    s.literals,
 | 
			
		||||
		windowSize:  s.windowSize,
 | 
			
		||||
		history:     hist,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.seqSize = 0
 | 
			
		||||
	startSize := len(s.out)
 | 
			
		||||
 | 
			
		||||
	var errCode int
 | 
			
		||||
	if cpuinfo.HasBMI2() {
 | 
			
		||||
		if useSafe {
 | 
			
		||||
			errCode = sequenceDecs_decodeSync_safe_bmi2(s, br, &ctx)
 | 
			
		||||
		} else {
 | 
			
		||||
			errCode = sequenceDecs_decodeSync_bmi2(s, br, &ctx)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if useSafe {
 | 
			
		||||
			errCode = sequenceDecs_decodeSync_safe_amd64(s, br, &ctx)
 | 
			
		||||
		} else {
 | 
			
		||||
			errCode = sequenceDecs_decodeSync_amd64(s, br, &ctx)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch errCode {
 | 
			
		||||
	case noError:
 | 
			
		||||
		break
 | 
			
		||||
 | 
			
		||||
	case errorMatchLenOfsMismatch:
 | 
			
		||||
		return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml)
 | 
			
		||||
 | 
			
		||||
	case errorMatchLenTooBig:
 | 
			
		||||
		return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml)
 | 
			
		||||
 | 
			
		||||
	case errorMatchOffTooBig:
 | 
			
		||||
		return true, fmt.Errorf("match offset (%d) bigger than current history (%d)",
 | 
			
		||||
			ctx.mo, ctx.outPosition+len(hist)-startSize)
 | 
			
		||||
 | 
			
		||||
	case errorNotEnoughLiterals:
 | 
			
		||||
		return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available",
 | 
			
		||||
			ctx.ll, ctx.litRemain+ctx.ll)
 | 
			
		||||
 | 
			
		||||
	case errorNotEnoughSpace:
 | 
			
		||||
		size := ctx.outPosition + ctx.ll + ctx.ml
 | 
			
		||||
		if debugDecoder {
 | 
			
		||||
			println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize)
 | 
			
		||||
		}
 | 
			
		||||
		return true, fmt.Errorf("output (%d) bigger than max block size (%d)", size-startSize, maxBlockSize)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return true, fmt.Errorf("sequenceDecs_decode returned erronous code %d", errCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.seqSize += ctx.litRemain
 | 
			
		||||
	if s.seqSize > maxBlockSize {
 | 
			
		||||
		return true, fmt.Errorf("output (%d) bigger than max block size (%d)", s.seqSize, maxBlockSize)
 | 
			
		||||
	}
 | 
			
		||||
	err := br.close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		printf("Closing sequences: %v, %+v\n", err, *br)
 | 
			
		||||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.literals = s.literals[ctx.litPosition:]
 | 
			
		||||
	t := ctx.outPosition
 | 
			
		||||
	s.out = s.out[:t]
 | 
			
		||||
 | 
			
		||||
	// Add final literals
 | 
			
		||||
	s.out = append(s.out, s.literals...)
 | 
			
		||||
	if debugDecoder {
 | 
			
		||||
		t += len(s.literals)
 | 
			
		||||
		if t != len(s.out) {
 | 
			
		||||
			panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
type decodeAsmContext struct {
 | 
			
		||||
	llTable   []decSymbol
 | 
			
		||||
	mlTable   []decSymbol
 | 
			
		||||
	ofTable   []decSymbol
 | 
			
		||||
	llState   uint64
 | 
			
		||||
	mlState   uint64
 | 
			
		||||
	ofState   uint64
 | 
			
		||||
	iteration int
 | 
			
		||||
	seqs      []seqVals
 | 
			
		||||
	litRemain int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const noError = 0
 | 
			
		||||
 | 
			
		||||
// error reported when mo == 0 && ml > 0
 | 
			
		||||
const errorMatchLenOfsMismatch = 1
 | 
			
		||||
 | 
			
		||||
// error reported when ml > maxMatchLen
 | 
			
		||||
const errorMatchLenTooBig = 2
 | 
			
		||||
 | 
			
		||||
// error reported when mo > available history or mo > s.windowSize
 | 
			
		||||
const errorMatchOffTooBig = 3
 | 
			
		||||
 | 
			
		||||
// error reported when the sum of literal lengths exeeceds the literal buffer size
 | 
			
		||||
const errorNotEnoughLiterals = 4
 | 
			
		||||
 | 
			
		||||
// error reported when capacity of `out` is too small
 | 
			
		||||
const errorNotEnoughSpace = 5
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm.
 | 
			
		||||
//
 | 
			
		||||
// Please refer to seqdec_generic.go for the reference implementation.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm.
 | 
			
		||||
//
 | 
			
		||||
// Please refer to seqdec_generic.go for the reference implementation.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
 | 
			
		||||
 | 
			
		||||
// decode sequences from the stream without the provided history.
 | 
			
		||||
func (s *sequenceDecs) decode(seqs []seqVals) error {
 | 
			
		||||
	br := s.br
 | 
			
		||||
 | 
			
		||||
	maxBlockSize := maxCompressedBlockSize
 | 
			
		||||
	if s.windowSize < maxBlockSize {
 | 
			
		||||
		maxBlockSize = s.windowSize
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := decodeAsmContext{
 | 
			
		||||
		llTable:   s.litLengths.fse.dt[:maxTablesize],
 | 
			
		||||
		mlTable:   s.matchLengths.fse.dt[:maxTablesize],
 | 
			
		||||
		ofTable:   s.offsets.fse.dt[:maxTablesize],
 | 
			
		||||
		llState:   uint64(s.litLengths.state.state),
 | 
			
		||||
		mlState:   uint64(s.matchLengths.state.state),
 | 
			
		||||
		ofState:   uint64(s.offsets.state.state),
 | 
			
		||||
		seqs:      seqs,
 | 
			
		||||
		iteration: len(seqs) - 1,
 | 
			
		||||
		litRemain: len(s.literals),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.seqSize = 0
 | 
			
		||||
	lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56
 | 
			
		||||
	var errCode int
 | 
			
		||||
	if cpuinfo.HasBMI2() {
 | 
			
		||||
		if lte56bits {
 | 
			
		||||
			errCode = sequenceDecs_decode_56_bmi2(s, br, &ctx)
 | 
			
		||||
		} else {
 | 
			
		||||
			errCode = sequenceDecs_decode_bmi2(s, br, &ctx)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if lte56bits {
 | 
			
		||||
			errCode = sequenceDecs_decode_56_amd64(s, br, &ctx)
 | 
			
		||||
		} else {
 | 
			
		||||
			errCode = sequenceDecs_decode_amd64(s, br, &ctx)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if errCode != 0 {
 | 
			
		||||
		i := len(seqs) - ctx.iteration - 1
 | 
			
		||||
		switch errCode {
 | 
			
		||||
		case errorMatchLenOfsMismatch:
 | 
			
		||||
			ml := ctx.seqs[i].ml
 | 
			
		||||
			return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml)
 | 
			
		||||
 | 
			
		||||
		case errorMatchLenTooBig:
 | 
			
		||||
			ml := ctx.seqs[i].ml
 | 
			
		||||
			return fmt.Errorf("match len (%d) bigger than max allowed length", ml)
 | 
			
		||||
 | 
			
		||||
		case errorNotEnoughLiterals:
 | 
			
		||||
			ll := ctx.seqs[i].ll
 | 
			
		||||
			return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return fmt.Errorf("sequenceDecs_decode_amd64 returned erronous code %d", errCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctx.litRemain < 0 {
 | 
			
		||||
		return fmt.Errorf("literal count is too big: total available %d, total requested %d",
 | 
			
		||||
			len(s.literals), len(s.literals)-ctx.litRemain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.seqSize += ctx.litRemain
 | 
			
		||||
	if s.seqSize > maxBlockSize {
 | 
			
		||||
		return fmt.Errorf("output (%d) bigger than max block size (%d)", s.seqSize, maxBlockSize)
 | 
			
		||||
	}
 | 
			
		||||
	err := br.close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		printf("Closing sequences: %v, %+v\n", err, *br)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
type executeAsmContext struct {
 | 
			
		||||
	seqs        []seqVals
 | 
			
		||||
	seqIndex    int
 | 
			
		||||
	out         []byte
 | 
			
		||||
	history     []byte
 | 
			
		||||
	literals    []byte
 | 
			
		||||
	outPosition int
 | 
			
		||||
	litPosition int
 | 
			
		||||
	windowSize  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sequenceDecs_executeSimple_amd64 implements the main loop of sequenceDecs.executeSimple in x86 asm.
 | 
			
		||||
//
 | 
			
		||||
// Returns false if a match offset is too big.
 | 
			
		||||
//
 | 
			
		||||
// Please refer to seqdec_generic.go for the reference implementation.
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool
 | 
			
		||||
 | 
			
		||||
// Same as above, but with safe memcopies
 | 
			
		||||
//go:noescape
 | 
			
		||||
func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool
 | 
			
		||||
 | 
			
		||||
// executeSimple handles cases when dictionary is not used.
 | 
			
		||||
func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error {
 | 
			
		||||
	// Ensure we have enough output size...
 | 
			
		||||
	if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) {
 | 
			
		||||
		addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc
 | 
			
		||||
		s.out = append(s.out, make([]byte, addBytes)...)
 | 
			
		||||
		s.out = s.out[:len(s.out)-addBytes]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if debugDecoder {
 | 
			
		||||
		printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var t = len(s.out)
 | 
			
		||||
	out := s.out[:t+s.seqSize]
 | 
			
		||||
 | 
			
		||||
	ctx := executeAsmContext{
 | 
			
		||||
		seqs:        seqs,
 | 
			
		||||
		seqIndex:    0,
 | 
			
		||||
		out:         out,
 | 
			
		||||
		history:     hist,
 | 
			
		||||
		outPosition: t,
 | 
			
		||||
		litPosition: 0,
 | 
			
		||||
		literals:    s.literals,
 | 
			
		||||
		windowSize:  s.windowSize,
 | 
			
		||||
	}
 | 
			
		||||
	var ok bool
 | 
			
		||||
	if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc {
 | 
			
		||||
		ok = sequenceDecs_executeSimple_safe_amd64(&ctx)
 | 
			
		||||
	} else {
 | 
			
		||||
		ok = sequenceDecs_executeSimple_amd64(&ctx)
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("match offset (%d) bigger than current history (%d)",
 | 
			
		||||
			seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist))
 | 
			
		||||
	}
 | 
			
		||||
	s.literals = s.literals[ctx.litPosition:]
 | 
			
		||||
	t = ctx.outPosition
 | 
			
		||||
 | 
			
		||||
	// Add final literals
 | 
			
		||||
	copy(out[t:], s.literals)
 | 
			
		||||
	if debugDecoder {
 | 
			
		||||
		t += len(s.literals)
 | 
			
		||||
		if t != len(out) {
 | 
			
		||||
			panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	s.out = out
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								@ -0,0 +1,237 @@
 | 
			
		||||
//go:build !amd64 || appengine || !gc || noasm
 | 
			
		||||
// +build !amd64 appengine !gc noasm
 | 
			
		||||
 | 
			
		||||
package zstd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// decode sequences from the stream with the provided history but without dictionary.
 | 
			
		||||
func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) {
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decode sequences from the stream without the provided history.
 | 
			
		||||
func (s *sequenceDecs) decode(seqs []seqVals) error {
 | 
			
		||||
	br := s.br
 | 
			
		||||
 | 
			
		||||
	// Grab full sizes tables, to avoid bounds checks.
 | 
			
		||||
	llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize]
 | 
			
		||||
	llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state
 | 
			
		||||
	s.seqSize = 0
 | 
			
		||||
	litRemain := len(s.literals)
 | 
			
		||||
 | 
			
		||||
	maxBlockSize := maxCompressedBlockSize
 | 
			
		||||
	if s.windowSize < maxBlockSize {
 | 
			
		||||
		maxBlockSize = s.windowSize
 | 
			
		||||
	}
 | 
			
		||||
	for i := range seqs {
 | 
			
		||||
		var ll, mo, ml int
 | 
			
		||||
		if br.off > 4+((maxOffsetBits+16+16)>>3) {
 | 
			
		||||
			// inlined function:
 | 
			
		||||
			// ll, mo, ml = s.nextFast(br, llState, mlState, ofState)
 | 
			
		||||
 | 
			
		||||
			// Final will not read from stream.
 | 
			
		||||
			var llB, mlB, moB uint8
 | 
			
		||||
			ll, llB = llState.final()
 | 
			
		||||
			ml, mlB = mlState.final()
 | 
			
		||||
			mo, moB = ofState.final()
 | 
			
		||||
 | 
			
		||||
			// extra bits are stored in reverse order.
 | 
			
		||||
			br.fillFast()
 | 
			
		||||
			mo += br.getBits(moB)
 | 
			
		||||
			if s.maxBits > 32 {
 | 
			
		||||
				br.fillFast()
 | 
			
		||||
			}
 | 
			
		||||
			ml += br.getBits(mlB)
 | 
			
		||||
			ll += br.getBits(llB)
 | 
			
		||||
 | 
			
		||||
			if moB > 1 {
 | 
			
		||||
				s.prevOffset[2] = s.prevOffset[1]
 | 
			
		||||
				s.prevOffset[1] = s.prevOffset[0]
 | 
			
		||||
				s.prevOffset[0] = mo
 | 
			
		||||
			} else {
 | 
			
		||||
				// mo = s.adjustOffset(mo, ll, moB)
 | 
			
		||||
				// Inlined for rather big speedup
 | 
			
		||||
				if ll == 0 {
 | 
			
		||||
					// There is an exception though, when current sequence's literals_length = 0.
 | 
			
		||||
					// In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2,
 | 
			
		||||
					// an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte.
 | 
			
		||||
					mo++
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if mo == 0 {
 | 
			
		||||
					mo = s.prevOffset[0]
 | 
			
		||||
				} else {
 | 
			
		||||
					var temp int
 | 
			
		||||
					if mo == 3 {
 | 
			
		||||
						temp = s.prevOffset[0] - 1
 | 
			
		||||
					} else {
 | 
			
		||||
						temp = s.prevOffset[mo]
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if temp == 0 {
 | 
			
		||||
						// 0 is not valid; input is corrupted; force offset to 1
 | 
			
		||||
						println("WARNING: temp was 0")
 | 
			
		||||
						temp = 1
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if mo != 1 {
 | 
			
		||||
						s.prevOffset[2] = s.prevOffset[1]
 | 
			
		||||
					}
 | 
			
		||||
					s.prevOffset[1] = s.prevOffset[0]
 | 
			
		||||
					s.prevOffset[0] = temp
 | 
			
		||||
					mo = temp
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			br.fillFast()
 | 
			
		||||
		} else {
 | 
			
		||||
			if br.overread() {
 | 
			
		||||
				if debugDecoder {
 | 
			
		||||
					printf("reading sequence %d, exceeded available data\n", i)
 | 
			
		||||
				}
 | 
			
		||||
				return io.ErrUnexpectedEOF
 | 
			
		||||
			}
 | 
			
		||||
			ll, mo, ml = s.next(br, llState, mlState, ofState)
 | 
			
		||||
			br.fill()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if debugSequences {
 | 
			
		||||
			println("Seq", i, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml)
 | 
			
		||||
		}
 | 
			
		||||
		// Evaluate.
 | 
			
		||||
		// We might be doing this async, so do it early.
 | 
			
		||||
		if mo == 0 && ml > 0 {
 | 
			
		||||
			return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml)
 | 
			
		||||
		}
 | 
			
		||||
		if ml > maxMatchLen {
 | 
			
		||||
			return fmt.Errorf("match len (%d) bigger than max allowed length", ml)
 | 
			
		||||
		}
 | 
			
		||||
		s.seqSize += ll + ml
 | 
			
		||||
		if s.seqSize > maxBlockSize {
 | 
			
		||||
			return fmt.Errorf("output (%d) bigger than max block size (%d)", s.seqSize, maxBlockSize)
 | 
			
		||||
		}
 | 
			
		||||
		litRemain -= ll
 | 
			
		||||
		if litRemain < 0 {
 | 
			
		||||
			return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, litRemain+ll)
 | 
			
		||||
		}
 | 
			
		||||
		seqs[i] = seqVals{
 | 
			
		||||
			ll: ll,
 | 
			
		||||
			ml: ml,
 | 
			
		||||
			mo: mo,
 | 
			
		||||
		}
 | 
			
		||||
		if i == len(seqs)-1 {
 | 
			
		||||
			// This is the last sequence, so we shouldn't update state.
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Manually inlined, ~ 5-20% faster
 | 
			
		||||
		// Update all 3 states at once. Approx 20% faster.
 | 
			
		||||
		nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits()
 | 
			
		||||
		if nBits == 0 {
 | 
			
		||||
			llState = llTable[llState.newState()&maxTableMask]
 | 
			
		||||
			mlState = mlTable[mlState.newState()&maxTableMask]
 | 
			
		||||
			ofState = ofTable[ofState.newState()&maxTableMask]
 | 
			
		||||
		} else {
 | 
			
		||||
			bits := br.get32BitsFast(nBits)
 | 
			
		||||
			lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31))
 | 
			
		||||
			llState = llTable[(llState.newState()+lowBits)&maxTableMask]
 | 
			
		||||
 | 
			
		||||
			lowBits = uint16(bits >> (ofState.nbBits() & 31))
 | 
			
		||||
			lowBits &= bitMask[mlState.nbBits()&15]
 | 
			
		||||
			mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask]
 | 
			
		||||
 | 
			
		||||
			lowBits = uint16(bits) & bitMask[ofState.nbBits()&15]
 | 
			
		||||
			ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	s.seqSize += litRemain
 | 
			
		||||
	if s.seqSize > maxBlockSize {
 | 
			
		||||
		return fmt.Errorf("output (%d) bigger than max block size (%d)", s.seqSize, maxBlockSize)
 | 
			
		||||
	}
 | 
			
		||||
	err := br.close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		printf("Closing sequences: %v, %+v\n", err, *br)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// executeSimple handles cases when a dictionary is not used.
 | 
			
		||||
func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error {
 | 
			
		||||
	// Ensure we have enough output size...
 | 
			
		||||
	if len(s.out)+s.seqSize > cap(s.out) {
 | 
			
		||||
		addBytes := s.seqSize + len(s.out)
 | 
			
		||||
		s.out = append(s.out, make([]byte, addBytes)...)
 | 
			
		||||
		s.out = s.out[:len(s.out)-addBytes]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if debugDecoder {
 | 
			
		||||
		printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var t = len(s.out)
 | 
			
		||||
	out := s.out[:t+s.seqSize]
 | 
			
		||||
 | 
			
		||||
	for _, seq := range seqs {
 | 
			
		||||
		// Add literals
 | 
			
		||||
		copy(out[t:], s.literals[:seq.ll])
 | 
			
		||||
		t += seq.ll
 | 
			
		||||
		s.literals = s.literals[seq.ll:]
 | 
			
		||||
 | 
			
		||||
		// Malformed input
 | 
			
		||||
		if seq.mo > t+len(hist) || seq.mo > s.windowSize {
 | 
			
		||||
			return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Copy from history.
 | 
			
		||||
		if v := seq.mo - t; v > 0 {
 | 
			
		||||
			// v is the start position in history from end.
 | 
			
		||||
			start := len(hist) - v
 | 
			
		||||
			if seq.ml > v {
 | 
			
		||||
				// Some goes into the current block.
 | 
			
		||||
				// Copy remainder of history
 | 
			
		||||
				copy(out[t:], hist[start:])
 | 
			
		||||
				t += v
 | 
			
		||||
				seq.ml -= v
 | 
			
		||||
			} else {
 | 
			
		||||
				copy(out[t:], hist[start:start+seq.ml])
 | 
			
		||||
				t += seq.ml
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We must be in the current buffer now
 | 
			
		||||
		if seq.ml > 0 {
 | 
			
		||||
			start := t - seq.mo
 | 
			
		||||
			if seq.ml <= t-start {
 | 
			
		||||
				// No overlap
 | 
			
		||||
				copy(out[t:], out[start:start+seq.ml])
 | 
			
		||||
				t += seq.ml
 | 
			
		||||
			} else {
 | 
			
		||||
				// Overlapping copy
 | 
			
		||||
				// Extend destination slice and copy one byte at the time.
 | 
			
		||||
				src := out[start : start+seq.ml]
 | 
			
		||||
				dst := out[t:]
 | 
			
		||||
				dst = dst[:len(src)]
 | 
			
		||||
				t += len(src)
 | 
			
		||||
				// Destination is the space we just added.
 | 
			
		||||
				for i := range src {
 | 
			
		||||
					dst[i] = src[i]
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Add final literals
 | 
			
		||||
	copy(out[t:], s.literals)
 | 
			
		||||
	if debugDecoder {
 | 
			
		||||
		t += len(s.literals)
 | 
			
		||||
		if t != len(out) {
 | 
			
		||||
			panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	s.out = out
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,81 @@
 | 
			
		||||
package subrequests
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/moby/buildkit/frontend/gateway/client"
 | 
			
		||||
	gwpb "github.com/moby/buildkit/frontend/gateway/pb"
 | 
			
		||||
	"github.com/moby/buildkit/solver/errdefs"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const RequestSubrequestsDescribe = "frontend.subrequests.describe"
 | 
			
		||||
 | 
			
		||||
var SubrequestsDescribeDefinition = Request{
 | 
			
		||||
	Name:        RequestSubrequestsDescribe,
 | 
			
		||||
	Version:     "1.0.0",
 | 
			
		||||
	Type:        TypeRPC,
 | 
			
		||||
	Description: "List available subrequest types",
 | 
			
		||||
	Metadata: []Named{
 | 
			
		||||
		{Name: "result.json"},
 | 
			
		||||
		{Name: "result.txt"},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Describe(ctx context.Context, c client.Client) ([]Request, error) {
 | 
			
		||||
	gwcaps := c.BuildOpts().Caps
 | 
			
		||||
 | 
			
		||||
	if err := (&gwcaps).Supports(gwpb.CapFrontendCaps); err != nil {
 | 
			
		||||
		return nil, errdefs.NewUnsupportedSubrequestError(RequestSubrequestsDescribe)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res, err := c.Solve(ctx, client.SolveRequest{
 | 
			
		||||
		FrontendOpt: map[string]string{
 | 
			
		||||
			"requestid":     RequestSubrequestsDescribe,
 | 
			
		||||
			"frontend.caps": "moby.buildkit.frontend.subrequests",
 | 
			
		||||
		},
 | 
			
		||||
		Frontend: "dockerfile.v0",
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		var reqErr *errdefs.UnsupportedSubrequestError
 | 
			
		||||
		if errors.As(err, &reqErr) {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		var capErr *errdefs.UnsupportedFrontendCapError
 | 
			
		||||
		if errors.As(err, &capErr) {
 | 
			
		||||
			return nil, errdefs.NewUnsupportedSubrequestError(RequestSubrequestsDescribe)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dt, ok := res.Metadata["result.json"]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.Errorf("no result.json metadata in response")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reqs []Request
 | 
			
		||||
	if err := json.Unmarshal(dt, &reqs); err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to parse describe result")
 | 
			
		||||
	}
 | 
			
		||||
	return reqs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PrintDescribe(dt []byte, w io.Writer) error {
 | 
			
		||||
	var d []Request
 | 
			
		||||
	if err := json.Unmarshal(dt, &d); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
 | 
			
		||||
	fmt.Fprintf(tw, "NAME\tVERSION\tDESCRIPTION\n")
 | 
			
		||||
 | 
			
		||||
	for _, r := range d {
 | 
			
		||||
		fmt.Fprintf(tw, "%s\t%s\t%s\n", strings.TrimPrefix(r.Name, "frontend."), r.Version, r.Description)
 | 
			
		||||
	}
 | 
			
		||||
	return tw.Flush()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,146 @@
 | 
			
		||||
package outline
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/moby/buildkit/frontend/gateway/client"
 | 
			
		||||
	"github.com/moby/buildkit/frontend/subrequests"
 | 
			
		||||
	"github.com/moby/buildkit/solver/pb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const RequestSubrequestsOutline = "frontend.outline"
 | 
			
		||||
 | 
			
		||||
var SubrequestsOutlineDefinition = subrequests.Request{
 | 
			
		||||
	Name:        RequestSubrequestsOutline,
 | 
			
		||||
	Version:     "1.0.0",
 | 
			
		||||
	Type:        subrequests.TypeRPC,
 | 
			
		||||
	Description: "List all parameters current build target supports",
 | 
			
		||||
	Opts: []subrequests.Named{
 | 
			
		||||
		{
 | 
			
		||||
			Name:        "target",
 | 
			
		||||
			Description: "Target build stage",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Metadata: []subrequests.Named{
 | 
			
		||||
		{Name: "result.json"},
 | 
			
		||||
		{Name: "result.txt"},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Outline struct {
 | 
			
		||||
	Name        string       `json:"name,omitempty"`
 | 
			
		||||
	Description string       `json:"description,omitempty"`
 | 
			
		||||
	Args        []Arg        `json:"args,omitempty"`
 | 
			
		||||
	Secrets     []Secret     `json:"secrets,omitempty"`
 | 
			
		||||
	SSH         []SSH        `json:"ssh,omitempty"`
 | 
			
		||||
	Cache       []CacheMount `json:"cache,omitempty"`
 | 
			
		||||
	Sources     [][]byte     `json:"sources,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Outline) ToResult() (*client.Result, error) {
 | 
			
		||||
	res := client.NewResult()
 | 
			
		||||
	dt, err := json.MarshalIndent(o, "", "  ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	res.AddMeta("result.json", dt)
 | 
			
		||||
 | 
			
		||||
	b := bytes.NewBuffer(nil)
 | 
			
		||||
	if err := PrintOutline(dt, b); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	res.AddMeta("result.txt", b.Bytes())
 | 
			
		||||
 | 
			
		||||
	res.AddMeta("version", []byte(SubrequestsOutlineDefinition.Version))
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Arg struct {
 | 
			
		||||
	Name        string       `json:"name"`
 | 
			
		||||
	Description string       `json:"description,omitempty"`
 | 
			
		||||
	Value       string       `json:"value,omitempty"`
 | 
			
		||||
	Location    *pb.Location `json:"location,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Secret struct {
 | 
			
		||||
	Name     string       `json:"name"`
 | 
			
		||||
	Required bool         `json:"required,omitempty"`
 | 
			
		||||
	Location *pb.Location `json:"location,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SSH struct {
 | 
			
		||||
	Name     string       `json:"name"`
 | 
			
		||||
	Required bool         `json:"required,omitempty"`
 | 
			
		||||
	Location *pb.Location `json:"location,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CacheMount struct {
 | 
			
		||||
	ID       string       `json:"ID"`
 | 
			
		||||
	Location *pb.Location `json:"location,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PrintOutline(dt []byte, w io.Writer) error {
 | 
			
		||||
	var o Outline
 | 
			
		||||
 | 
			
		||||
	if err := json.Unmarshal(dt, &o); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.Name != "" || o.Description != "" {
 | 
			
		||||
		tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
 | 
			
		||||
		name := o.Name
 | 
			
		||||
		if o.Name == "" {
 | 
			
		||||
			name = "(default)"
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(tw, "TARGET:\t%s\n", name)
 | 
			
		||||
		if o.Description != "" {
 | 
			
		||||
			fmt.Fprintf(tw, "DESCRIPTION:\t%s\n", o.Description)
 | 
			
		||||
		}
 | 
			
		||||
		tw.Flush()
 | 
			
		||||
		fmt.Println()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.Args) > 0 {
 | 
			
		||||
		tw := tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
 | 
			
		||||
		fmt.Fprintf(tw, "BUILD ARG\tVALUE\tDESCRIPTION\n")
 | 
			
		||||
		for _, a := range o.Args {
 | 
			
		||||
			fmt.Fprintf(tw, "%s\t%s\t%s\n", a.Name, a.Value, a.Description)
 | 
			
		||||
		}
 | 
			
		||||
		tw.Flush()
 | 
			
		||||
		fmt.Println()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.Secrets) > 0 {
 | 
			
		||||
		tw := tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
 | 
			
		||||
		fmt.Fprintf(tw, "SECRET\tREQUIRED\n")
 | 
			
		||||
		for _, s := range o.Secrets {
 | 
			
		||||
			b := ""
 | 
			
		||||
			if s.Required {
 | 
			
		||||
				b = "true"
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Fprintf(tw, "%s\t%s\n", s.Name, b)
 | 
			
		||||
		}
 | 
			
		||||
		tw.Flush()
 | 
			
		||||
		fmt.Println()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.SSH) > 0 {
 | 
			
		||||
		tw := tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
 | 
			
		||||
		fmt.Fprintf(tw, "SSH\tREQUIRED\n")
 | 
			
		||||
		for _, s := range o.SSH {
 | 
			
		||||
			b := ""
 | 
			
		||||
			if s.Required {
 | 
			
		||||
				b = "true"
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Fprintf(tw, "%s\t%s\n", s.Name, b)
 | 
			
		||||
		}
 | 
			
		||||
		tw.Flush()
 | 
			
		||||
		fmt.Println()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,84 @@
 | 
			
		||||
package targets
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/moby/buildkit/frontend/gateway/client"
 | 
			
		||||
	"github.com/moby/buildkit/frontend/subrequests"
 | 
			
		||||
	"github.com/moby/buildkit/solver/pb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const RequestTargets = "frontend.targets"
 | 
			
		||||
 | 
			
		||||
var SubrequestsTargetsDefinition = subrequests.Request{
 | 
			
		||||
	Name:        RequestTargets,
 | 
			
		||||
	Version:     "1.0.0",
 | 
			
		||||
	Type:        subrequests.TypeRPC,
 | 
			
		||||
	Description: "List all targets current build supports",
 | 
			
		||||
	Opts:        []subrequests.Named{},
 | 
			
		||||
	Metadata: []subrequests.Named{
 | 
			
		||||
		{Name: "result.json"},
 | 
			
		||||
		{Name: "result.txt"},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type List struct {
 | 
			
		||||
	Targets []Target `json:"targets"`
 | 
			
		||||
	Sources [][]byte `json:"sources"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l List) ToResult() (*client.Result, error) {
 | 
			
		||||
	res := client.NewResult()
 | 
			
		||||
	dt, err := json.MarshalIndent(l, "", "  ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	res.AddMeta("result.json", dt)
 | 
			
		||||
 | 
			
		||||
	b := bytes.NewBuffer(nil)
 | 
			
		||||
	if err := PrintTargets(dt, b); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	res.AddMeta("result.txt", b.Bytes())
 | 
			
		||||
 | 
			
		||||
	res.AddMeta("version", []byte(SubrequestsTargetsDefinition.Version))
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Target struct {
 | 
			
		||||
	Name        string       `json:"name,omitempty"`
 | 
			
		||||
	Default     bool         `json:"default,omitempty"`
 | 
			
		||||
	Description string       `json:"description,omitempty"`
 | 
			
		||||
	Base        string       `json:"base,omitempty"`
 | 
			
		||||
	Platform    string       `json:"platform,omitempty"`
 | 
			
		||||
	Location    *pb.Location `json:"location,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PrintTargets(dt []byte, w io.Writer) error {
 | 
			
		||||
	var l List
 | 
			
		||||
 | 
			
		||||
	if err := json.Unmarshal(dt, &l); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
 | 
			
		||||
	fmt.Fprintf(tw, "TARGET\tDESCRIPTION\n")
 | 
			
		||||
 | 
			
		||||
	for _, t := range l.Targets {
 | 
			
		||||
		name := t.Name
 | 
			
		||||
		if name == "" && t.Default {
 | 
			
		||||
			name = "(default)"
 | 
			
		||||
		} else {
 | 
			
		||||
			if t.Default {
 | 
			
		||||
				name = fmt.Sprintf("%s (default)", name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(tw, "%s\t%s\n", name, t.Description)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return tw.Flush()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
package subrequests
 | 
			
		||||
 | 
			
		||||
type Request struct {
 | 
			
		||||
	Name        string      `json:"name"`
 | 
			
		||||
	Version     string      `json:"version"`
 | 
			
		||||
	Type        RequestType `json:"type"`
 | 
			
		||||
	Description string      `json:"description"`
 | 
			
		||||
	Opts        []Named     `json:"opts"`
 | 
			
		||||
	Inputs      []Named     `json:"inputs"`
 | 
			
		||||
	Metadata    []Named     `json:"metadata"`
 | 
			
		||||
	Refs        []Named     `json:"refs"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Named struct {
 | 
			
		||||
	Name        string `json:"name"`
 | 
			
		||||
	Description string `json:"description"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RequestType string
 | 
			
		||||
 | 
			
		||||
const TypeRPC RequestType = "rpc"
 | 
			
		||||
@ -0,0 +1,95 @@
 | 
			
		||||
package gitutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/errdefs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GitRef represents a git ref.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
// - "https://github.com/foo/bar.git#baz/qux:quux/quuz" is parsed into:
 | 
			
		||||
//   {Remote: "https://github.com/foo/bar.git", ShortName: "bar", Commit:"baz/qux", SubDir: "quux/quuz"}.
 | 
			
		||||
type GitRef struct {
 | 
			
		||||
	// Remote is the remote repository path.
 | 
			
		||||
	Remote string
 | 
			
		||||
 | 
			
		||||
	// ShortName is the directory name of the repo.
 | 
			
		||||
	// e.g., "bar" for "https://github.com/foo/bar.git"
 | 
			
		||||
	ShortName string
 | 
			
		||||
 | 
			
		||||
	// Commit is a commit hash, a tag, or branch name.
 | 
			
		||||
	// Commit is optional.
 | 
			
		||||
	Commit string
 | 
			
		||||
 | 
			
		||||
	// SubDir is a directory path inside the repo.
 | 
			
		||||
	// SubDir is optional.
 | 
			
		||||
	SubDir string
 | 
			
		||||
 | 
			
		||||
	// IndistinguishableFromLocal is true for a ref that is indistinguishable from a local file path,
 | 
			
		||||
	// e.g., "github.com/foo/bar".
 | 
			
		||||
	//
 | 
			
		||||
	// Deprecated.
 | 
			
		||||
	// Instead, use a distinguishable form such as "https://github.com/foo/bar.git".
 | 
			
		||||
	//
 | 
			
		||||
	// The dockerfile frontend still accepts this form only for build contexts.
 | 
			
		||||
	IndistinguishableFromLocal bool
 | 
			
		||||
 | 
			
		||||
	// UnencryptedTCP is true for a ref that needs an unencrypted TCP connection,
 | 
			
		||||
	// e.g., "git://..." and "http://..." .
 | 
			
		||||
	//
 | 
			
		||||
	// Discouraged, although not deprecated.
 | 
			
		||||
	// Instead, consider using an encrypted TCP connection such as "git@github.com/foo/bar.git" or "https://github.com/foo/bar.git".
 | 
			
		||||
	UnencryptedTCP bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
 | 
			
		||||
 | 
			
		||||
// ParseGitRef parses a git ref.
 | 
			
		||||
func ParseGitRef(ref string) (*GitRef, error) {
 | 
			
		||||
	res := &GitRef{}
 | 
			
		||||
 | 
			
		||||
	if strings.HasPrefix(ref, "github.com/") {
 | 
			
		||||
		res.IndistinguishableFromLocal = true // Deprecated
 | 
			
		||||
	} else {
 | 
			
		||||
		_, proto := ParseProtocol(ref)
 | 
			
		||||
		switch proto {
 | 
			
		||||
		case UnknownProtocol:
 | 
			
		||||
			return nil, errdefs.ErrInvalidArgument
 | 
			
		||||
		}
 | 
			
		||||
		switch proto {
 | 
			
		||||
		case HTTPProtocol, GitProtocol:
 | 
			
		||||
			res.UnencryptedTCP = true // Discouraged, but not deprecated
 | 
			
		||||
		}
 | 
			
		||||
		switch proto {
 | 
			
		||||
		// An HTTP(S) URL is considered to be a valid git ref only when it has the ".git[...]" suffix.
 | 
			
		||||
		case HTTPProtocol, HTTPSProtocol:
 | 
			
		||||
			var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
 | 
			
		||||
			if !gitURLPathWithFragmentSuffix.MatchString(ref) {
 | 
			
		||||
				return nil, errdefs.ErrInvalidArgument
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refSplitBySharp := strings.SplitN(ref, "#", 2)
 | 
			
		||||
	res.Remote = refSplitBySharp[0]
 | 
			
		||||
	if len(res.Remote) == 0 {
 | 
			
		||||
		return res, errdefs.ErrInvalidArgument
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(refSplitBySharp) > 1 {
 | 
			
		||||
		refSplitBySharpSplitByColon := strings.SplitN(refSplitBySharp[1], ":", 2)
 | 
			
		||||
		res.Commit = refSplitBySharpSplitByColon[0]
 | 
			
		||||
		if len(res.Commit) == 0 {
 | 
			
		||||
			return res, errdefs.ErrInvalidArgument
 | 
			
		||||
		}
 | 
			
		||||
		if len(refSplitBySharpSplitByColon) > 1 {
 | 
			
		||||
			res.SubDir = refSplitBySharpSplitByColon[1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	repoSplitBySlash := strings.Split(res.Remote, "/")
 | 
			
		||||
	res.ShortName = strings.TrimSuffix(repoSplitBySlash[len(repoSplitBySlash)-1], ".git")
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
								
									
										
											
										
									
									
										
											14
										
									
									vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go
									
										generated
									
									
										vendored
									
								
								
							
							
										
											14
										
									
									vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go
									
										generated
									
									
										vendored
									
								@ -0,0 +1,36 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2020 gRPC authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Package channelz exports internals of the channelz implementation as required
 | 
			
		||||
// by other gRPC packages.
 | 
			
		||||
//
 | 
			
		||||
// The implementation of the channelz spec as defined in
 | 
			
		||||
// https://github.com/grpc/proposal/blob/master/A14-channelz.md, is provided by
 | 
			
		||||
// the `internal/channelz` package.
 | 
			
		||||
//
 | 
			
		||||
// Experimental
 | 
			
		||||
//
 | 
			
		||||
// Notice: All APIs in this package are experimental and may be removed in a
 | 
			
		||||
// later release.
 | 
			
		||||
package channelz
 | 
			
		||||
 | 
			
		||||
import "google.golang.org/grpc/internal/channelz"
 | 
			
		||||
 | 
			
		||||
// Identifier is an opaque identifier which uniquely identifies an entity in the
 | 
			
		||||
// channelz database.
 | 
			
		||||
type Identifier = channelz.Identifier
 | 
			
		||||
								
									
										
											
										
									
									
										
											382
										
									
									vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go
									
										generated
									
									
										vendored
									
								
								
							
							
										
											382
										
									
									vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go
									
										generated
									
									
										vendored
									
								@ -0,0 +1,382 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2022 gRPC authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Package gracefulswitch implements a graceful switch load balancer.
 | 
			
		||||
package gracefulswitch
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/grpc/balancer"
 | 
			
		||||
	"google.golang.org/grpc/balancer/base"
 | 
			
		||||
	"google.golang.org/grpc/connectivity"
 | 
			
		||||
	"google.golang.org/grpc/resolver"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var errBalancerClosed = errors.New("gracefulSwitchBalancer is closed")
 | 
			
		||||
var _ balancer.Balancer = (*Balancer)(nil)
 | 
			
		||||
 | 
			
		||||
// NewBalancer returns a graceful switch Balancer.
 | 
			
		||||
func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions) *Balancer {
 | 
			
		||||
	return &Balancer{
 | 
			
		||||
		cc:    cc,
 | 
			
		||||
		bOpts: opts,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Balancer is a utility to gracefully switch from one balancer to
 | 
			
		||||
// a new balancer. It implements the balancer.Balancer interface.
 | 
			
		||||
type Balancer struct {
 | 
			
		||||
	bOpts balancer.BuildOptions
 | 
			
		||||
	cc    balancer.ClientConn
 | 
			
		||||
 | 
			
		||||
	// mu protects the following fields and all fields within balancerCurrent
 | 
			
		||||
	// and balancerPending. mu does not need to be held when calling into the
 | 
			
		||||
	// child balancers, as all calls into these children happen only as a direct
 | 
			
		||||
	// result of a call into the gracefulSwitchBalancer, which are also
 | 
			
		||||
	// guaranteed to be synchronous. There is one exception: an UpdateState call
 | 
			
		||||
	// from a child balancer when current and pending are populated can lead to
 | 
			
		||||
	// calling Close() on the current. To prevent that racing with an
 | 
			
		||||
	// UpdateSubConnState from the channel, we hold currentMu during Close and
 | 
			
		||||
	// UpdateSubConnState calls.
 | 
			
		||||
	mu              sync.Mutex
 | 
			
		||||
	balancerCurrent *balancerWrapper
 | 
			
		||||
	balancerPending *balancerWrapper
 | 
			
		||||
	closed          bool // set to true when this balancer is closed
 | 
			
		||||
 | 
			
		||||
	// currentMu must be locked before mu. This mutex guards against this
 | 
			
		||||
	// sequence of events: UpdateSubConnState() called, finds the
 | 
			
		||||
	// balancerCurrent, gives up lock, updateState comes in, causes Close() on
 | 
			
		||||
	// balancerCurrent before the UpdateSubConnState is called on the
 | 
			
		||||
	// balancerCurrent.
 | 
			
		||||
	currentMu sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// swap swaps out the current lb with the pending lb and updates the ClientConn.
 | 
			
		||||
// The caller must hold gsb.mu.
 | 
			
		||||
func (gsb *Balancer) swap() {
 | 
			
		||||
	gsb.cc.UpdateState(gsb.balancerPending.lastState)
 | 
			
		||||
	cur := gsb.balancerCurrent
 | 
			
		||||
	gsb.balancerCurrent = gsb.balancerPending
 | 
			
		||||
	gsb.balancerPending = nil
 | 
			
		||||
	go func() {
 | 
			
		||||
		gsb.currentMu.Lock()
 | 
			
		||||
		defer gsb.currentMu.Unlock()
 | 
			
		||||
		cur.Close()
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper function that checks if the balancer passed in is current or pending.
 | 
			
		||||
// The caller must hold gsb.mu.
 | 
			
		||||
func (gsb *Balancer) balancerCurrentOrPending(bw *balancerWrapper) bool {
 | 
			
		||||
	return bw == gsb.balancerCurrent || bw == gsb.balancerPending
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchTo initializes the graceful switch process, which completes based on
 | 
			
		||||
// connectivity state changes on the current/pending balancer. Thus, the switch
 | 
			
		||||
// process is not complete when this method returns. This method must be called
 | 
			
		||||
// synchronously alongside the rest of the balancer.Balancer methods this
 | 
			
		||||
// Graceful Switch Balancer implements.
 | 
			
		||||
func (gsb *Balancer) SwitchTo(builder balancer.Builder) error {
 | 
			
		||||
	gsb.mu.Lock()
 | 
			
		||||
	if gsb.closed {
 | 
			
		||||
		gsb.mu.Unlock()
 | 
			
		||||
		return errBalancerClosed
 | 
			
		||||
	}
 | 
			
		||||
	bw := &balancerWrapper{
 | 
			
		||||
		gsb: gsb,
 | 
			
		||||
		lastState: balancer.State{
 | 
			
		||||
			ConnectivityState: connectivity.Connecting,
 | 
			
		||||
			Picker:            base.NewErrPicker(balancer.ErrNoSubConnAvailable),
 | 
			
		||||
		},
 | 
			
		||||
		subconns: make(map[balancer.SubConn]bool),
 | 
			
		||||
	}
 | 
			
		||||
	balToClose := gsb.balancerPending // nil if there is no pending balancer
 | 
			
		||||
	if gsb.balancerCurrent == nil {
 | 
			
		||||
		gsb.balancerCurrent = bw
 | 
			
		||||
	} else {
 | 
			
		||||
		gsb.balancerPending = bw
 | 
			
		||||
	}
 | 
			
		||||
	gsb.mu.Unlock()
 | 
			
		||||
	balToClose.Close()
 | 
			
		||||
	// This function takes a builder instead of a balancer because builder.Build
 | 
			
		||||
	// can call back inline, and this utility needs to handle the callbacks.
 | 
			
		||||
	newBalancer := builder.Build(bw, gsb.bOpts)
 | 
			
		||||
	if newBalancer == nil {
 | 
			
		||||
		// This is illegal and should never happen; we clear the balancerWrapper
 | 
			
		||||
		// we were constructing if it happens to avoid a potential panic.
 | 
			
		||||
		gsb.mu.Lock()
 | 
			
		||||
		if gsb.balancerPending != nil {
 | 
			
		||||
			gsb.balancerPending = nil
 | 
			
		||||
		} else {
 | 
			
		||||
			gsb.balancerCurrent = nil
 | 
			
		||||
		}
 | 
			
		||||
		gsb.mu.Unlock()
 | 
			
		||||
		return balancer.ErrBadResolverState
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// This write doesn't need to take gsb.mu because this field never gets read
 | 
			
		||||
	// or written to on any calls from the current or pending. Calls from grpc
 | 
			
		||||
	// to this balancer are guaranteed to be called synchronously, so this
 | 
			
		||||
	// bw.Balancer field will never be forwarded to until this SwitchTo()
 | 
			
		||||
	// function returns.
 | 
			
		||||
	bw.Balancer = newBalancer
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns nil if the graceful switch balancer is closed.
 | 
			
		||||
func (gsb *Balancer) latestBalancer() *balancerWrapper {
 | 
			
		||||
	gsb.mu.Lock()
 | 
			
		||||
	defer gsb.mu.Unlock()
 | 
			
		||||
	if gsb.balancerPending != nil {
 | 
			
		||||
		return gsb.balancerPending
 | 
			
		||||
	}
 | 
			
		||||
	return gsb.balancerCurrent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateClientConnState forwards the update to the latest balancer created.
 | 
			
		||||
func (gsb *Balancer) UpdateClientConnState(state balancer.ClientConnState) error {
 | 
			
		||||
	// The resolver data is only relevant to the most recent LB Policy.
 | 
			
		||||
	balToUpdate := gsb.latestBalancer()
 | 
			
		||||
	if balToUpdate == nil {
 | 
			
		||||
		return errBalancerClosed
 | 
			
		||||
	}
 | 
			
		||||
	// Perform this call without gsb.mu to prevent deadlocks if the child calls
 | 
			
		||||
	// back into the channel. The latest balancer can never be closed during a
 | 
			
		||||
	// call from the channel, even without gsb.mu held.
 | 
			
		||||
	return balToUpdate.UpdateClientConnState(state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResolverError forwards the error to the latest balancer created.
 | 
			
		||||
func (gsb *Balancer) ResolverError(err error) {
 | 
			
		||||
	// The resolver data is only relevant to the most recent LB Policy.
 | 
			
		||||
	balToUpdate := gsb.latestBalancer()
 | 
			
		||||
	if balToUpdate == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// Perform this call without gsb.mu to prevent deadlocks if the child calls
 | 
			
		||||
	// back into the channel. The latest balancer can never be closed during a
 | 
			
		||||
	// call from the channel, even without gsb.mu held.
 | 
			
		||||
	balToUpdate.ResolverError(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExitIdle forwards the call to the latest balancer created.
 | 
			
		||||
//
 | 
			
		||||
// If the latest balancer does not support ExitIdle, the subConns are
 | 
			
		||||
// re-connected to manually.
 | 
			
		||||
func (gsb *Balancer) ExitIdle() {
 | 
			
		||||
	balToUpdate := gsb.latestBalancer()
 | 
			
		||||
	if balToUpdate == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// There is no need to protect this read with a mutex, as the write to the
 | 
			
		||||
	// Balancer field happens in SwitchTo, which completes before this can be
 | 
			
		||||
	// called.
 | 
			
		||||
	if ei, ok := balToUpdate.Balancer.(balancer.ExitIdler); ok {
 | 
			
		||||
		ei.ExitIdle()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for sc := range balToUpdate.subconns {
 | 
			
		||||
		sc.Connect()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateSubConnState forwards the update to the appropriate child.
 | 
			
		||||
func (gsb *Balancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
 | 
			
		||||
	gsb.currentMu.Lock()
 | 
			
		||||
	defer gsb.currentMu.Unlock()
 | 
			
		||||
	gsb.mu.Lock()
 | 
			
		||||
	// Forward update to the appropriate child.  Even if there is a pending
 | 
			
		||||
	// balancer, the current balancer should continue to get SubConn updates to
 | 
			
		||||
	// maintain the proper state while the pending is still connecting.
 | 
			
		||||
	var balToUpdate *balancerWrapper
 | 
			
		||||
	if gsb.balancerCurrent != nil && gsb.balancerCurrent.subconns[sc] {
 | 
			
		||||
		balToUpdate = gsb.balancerCurrent
 | 
			
		||||
	} else if gsb.balancerPending != nil && gsb.balancerPending.subconns[sc] {
 | 
			
		||||
		balToUpdate = gsb.balancerPending
 | 
			
		||||
	}
 | 
			
		||||
	gsb.mu.Unlock()
 | 
			
		||||
	if balToUpdate == nil {
 | 
			
		||||
		// SubConn belonged to a stale lb policy that has not yet fully closed,
 | 
			
		||||
		// or the balancer was already closed.
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	balToUpdate.UpdateSubConnState(sc, state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes any active child balancers.
 | 
			
		||||
func (gsb *Balancer) Close() {
 | 
			
		||||
	gsb.mu.Lock()
 | 
			
		||||
	gsb.closed = true
 | 
			
		||||
	currentBalancerToClose := gsb.balancerCurrent
 | 
			
		||||
	gsb.balancerCurrent = nil
 | 
			
		||||
	pendingBalancerToClose := gsb.balancerPending
 | 
			
		||||
	gsb.balancerPending = nil
 | 
			
		||||
	gsb.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	currentBalancerToClose.Close()
 | 
			
		||||
	pendingBalancerToClose.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// balancerWrapper wraps a balancer.Balancer, and overrides some Balancer
 | 
			
		||||
// methods to help cleanup SubConns created by the wrapped balancer.
 | 
			
		||||
//
 | 
			
		||||
// It implements the balancer.ClientConn interface and is passed down in that
 | 
			
		||||
// capacity to the wrapped balancer. It maintains a set of subConns created by
 | 
			
		||||
// the wrapped balancer and calls from the latter to create/update/remove
 | 
			
		||||
// SubConns update this set before being forwarded to the parent ClientConn.
 | 
			
		||||
// State updates from the wrapped balancer can result in invocation of the
 | 
			
		||||
// graceful switch logic.
 | 
			
		||||
type balancerWrapper struct {
 | 
			
		||||
	balancer.Balancer
 | 
			
		||||
	gsb *Balancer
 | 
			
		||||
 | 
			
		||||
	lastState balancer.State
 | 
			
		||||
	subconns  map[balancer.SubConn]bool // subconns created by this balancer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
 | 
			
		||||
	if state.ConnectivityState == connectivity.Shutdown {
 | 
			
		||||
		bw.gsb.mu.Lock()
 | 
			
		||||
		delete(bw.subconns, sc)
 | 
			
		||||
		bw.gsb.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
	// There is no need to protect this read with a mutex, as the write to the
 | 
			
		||||
	// Balancer field happens in SwitchTo, which completes before this can be
 | 
			
		||||
	// called.
 | 
			
		||||
	bw.Balancer.UpdateSubConnState(sc, state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the underlying LB policy and removes the subconns it created. bw
 | 
			
		||||
// must not be referenced via balancerCurrent or balancerPending in gsb when
 | 
			
		||||
// called. gsb.mu must not be held.  Does not panic with a nil receiver.
 | 
			
		||||
func (bw *balancerWrapper) Close() {
 | 
			
		||||
	// before Close is called.
 | 
			
		||||
	if bw == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// There is no need to protect this read with a mutex, as Close() is
 | 
			
		||||
	// impossible to be called concurrently with the write in SwitchTo(). The
 | 
			
		||||
	// callsites of Close() for this balancer in Graceful Switch Balancer will
 | 
			
		||||
	// never be called until SwitchTo() returns.
 | 
			
		||||
	bw.Balancer.Close()
 | 
			
		||||
	bw.gsb.mu.Lock()
 | 
			
		||||
	for sc := range bw.subconns {
 | 
			
		||||
		bw.gsb.cc.RemoveSubConn(sc)
 | 
			
		||||
	}
 | 
			
		||||
	bw.gsb.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) UpdateState(state balancer.State) {
 | 
			
		||||
	// Hold the mutex for this entire call to ensure it cannot occur
 | 
			
		||||
	// concurrently with other updateState() calls. This causes updates to
 | 
			
		||||
	// lastState and calls to cc.UpdateState to happen atomically.
 | 
			
		||||
	bw.gsb.mu.Lock()
 | 
			
		||||
	defer bw.gsb.mu.Unlock()
 | 
			
		||||
	bw.lastState = state
 | 
			
		||||
 | 
			
		||||
	if !bw.gsb.balancerCurrentOrPending(bw) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bw == bw.gsb.balancerCurrent {
 | 
			
		||||
		// In the case that the current balancer exits READY, and there is a pending
 | 
			
		||||
		// balancer, you can forward the pending balancer's cached State up to
 | 
			
		||||
		// ClientConn and swap the pending into the current. This is because there
 | 
			
		||||
		// is no reason to gracefully switch from and keep using the old policy as
 | 
			
		||||
		// the ClientConn is not connected to any backends.
 | 
			
		||||
		if state.ConnectivityState != connectivity.Ready && bw.gsb.balancerPending != nil {
 | 
			
		||||
			bw.gsb.swap()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// Even if there is a pending balancer waiting to be gracefully switched to,
 | 
			
		||||
		// continue to forward current balancer updates to the Client Conn. Ignoring
 | 
			
		||||
		// state + picker from the current would cause undefined behavior/cause the
 | 
			
		||||
		// system to behave incorrectly from the current LB policies perspective.
 | 
			
		||||
		// Also, the current LB is still being used by grpc to choose SubConns per
 | 
			
		||||
		// RPC, and thus should use the most updated form of the current balancer.
 | 
			
		||||
		bw.gsb.cc.UpdateState(state)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// This method is now dealing with a state update from the pending balancer.
 | 
			
		||||
	// If the current balancer is currently in a state other than READY, the new
 | 
			
		||||
	// policy can be swapped into place immediately. This is because there is no
 | 
			
		||||
	// reason to gracefully switch from and keep using the old policy as the
 | 
			
		||||
	// ClientConn is not connected to any backends.
 | 
			
		||||
	if state.ConnectivityState != connectivity.Connecting || bw.gsb.balancerCurrent.lastState.ConnectivityState != connectivity.Ready {
 | 
			
		||||
		bw.gsb.swap()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
 | 
			
		||||
	bw.gsb.mu.Lock()
 | 
			
		||||
	if !bw.gsb.balancerCurrentOrPending(bw) {
 | 
			
		||||
		bw.gsb.mu.Unlock()
 | 
			
		||||
		return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw)
 | 
			
		||||
	}
 | 
			
		||||
	bw.gsb.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	sc, err := bw.gsb.cc.NewSubConn(addrs, opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	bw.gsb.mu.Lock()
 | 
			
		||||
	if !bw.gsb.balancerCurrentOrPending(bw) { // balancer was closed during this call
 | 
			
		||||
		bw.gsb.cc.RemoveSubConn(sc)
 | 
			
		||||
		bw.gsb.mu.Unlock()
 | 
			
		||||
		return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw)
 | 
			
		||||
	}
 | 
			
		||||
	bw.subconns[sc] = true
 | 
			
		||||
	bw.gsb.mu.Unlock()
 | 
			
		||||
	return sc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) ResolveNow(opts resolver.ResolveNowOptions) {
 | 
			
		||||
	// Ignore ResolveNow requests from anything other than the most recent
 | 
			
		||||
	// balancer, because older balancers were already removed from the config.
 | 
			
		||||
	if bw != bw.gsb.latestBalancer() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bw.gsb.cc.ResolveNow(opts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) RemoveSubConn(sc balancer.SubConn) {
 | 
			
		||||
	bw.gsb.mu.Lock()
 | 
			
		||||
	if !bw.gsb.balancerCurrentOrPending(bw) {
 | 
			
		||||
		bw.gsb.mu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bw.gsb.mu.Unlock()
 | 
			
		||||
	bw.gsb.cc.RemoveSubConn(sc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
 | 
			
		||||
	bw.gsb.mu.Lock()
 | 
			
		||||
	if !bw.gsb.balancerCurrentOrPending(bw) {
 | 
			
		||||
		bw.gsb.mu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bw.gsb.mu.Unlock()
 | 
			
		||||
	bw.gsb.cc.UpdateAddresses(sc, addrs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bw *balancerWrapper) Target() string {
 | 
			
		||||
	return bw.gsb.cc.Target()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2022 gRPC authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package channelz
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
// Identifier is an opaque identifier which uniquely identifies an entity in the
 | 
			
		||||
// channelz database.
 | 
			
		||||
type Identifier struct {
 | 
			
		||||
	typ RefChannelType
 | 
			
		||||
	id  int64
 | 
			
		||||
	str string
 | 
			
		||||
	pid *Identifier
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the entity type corresponding to id.
 | 
			
		||||
func (id *Identifier) Type() RefChannelType {
 | 
			
		||||
	return id.typ
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Int returns the integer identifier corresponding to id.
 | 
			
		||||
func (id *Identifier) Int() int64 {
 | 
			
		||||
	return id.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns a string representation of the entity corresponding to id.
 | 
			
		||||
//
 | 
			
		||||
// This includes some information about the parent as well. Examples:
 | 
			
		||||
// Top-level channel: [Channel #channel-number]
 | 
			
		||||
// Nested channel:    [Channel #parent-channel-number Channel #channel-number]
 | 
			
		||||
// Sub channel:       [Channel #parent-channel SubChannel #subchannel-number]
 | 
			
		||||
func (id *Identifier) String() string {
 | 
			
		||||
	return id.str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal returns true if other is the same as id.
 | 
			
		||||
func (id *Identifier) Equal(other *Identifier) bool {
 | 
			
		||||
	if (id != nil) != (other != nil) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if id == nil && other == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return id.typ == other.typ && id.id == other.id && id.pid == other.pid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewIdentifierForTesting returns a new opaque identifier to be used only for
 | 
			
		||||
// testing purposes.
 | 
			
		||||
func NewIdentifierForTesting(typ RefChannelType, id int64, pid *Identifier) *Identifier {
 | 
			
		||||
	return newIdentifer(typ, id, pid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newIdentifer(typ RefChannelType, id int64, pid *Identifier) *Identifier {
 | 
			
		||||
	str := fmt.Sprintf("%s #%d", typ, id)
 | 
			
		||||
	if pid != nil {
 | 
			
		||||
		str = fmt.Sprintf("%s %s", pid, str)
 | 
			
		||||
	}
 | 
			
		||||
	return &Identifier{typ: typ, id: id, str: str, pid: pid}
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,82 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2021 gRPC authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Package pretty defines helper functions to pretty-print structs for logging.
 | 
			
		||||
package pretty
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/jsonpb"
 | 
			
		||||
	protov1 "github.com/golang/protobuf/proto"
 | 
			
		||||
	"google.golang.org/protobuf/encoding/protojson"
 | 
			
		||||
	protov2 "google.golang.org/protobuf/proto"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const jsonIndent = "  "
 | 
			
		||||
 | 
			
		||||
// ToJSON marshals the input into a json string.
 | 
			
		||||
//
 | 
			
		||||
// If marshal fails, it falls back to fmt.Sprintf("%+v").
 | 
			
		||||
func ToJSON(e interface{}) string {
 | 
			
		||||
	switch ee := e.(type) {
 | 
			
		||||
	case protov1.Message:
 | 
			
		||||
		mm := jsonpb.Marshaler{Indent: jsonIndent}
 | 
			
		||||
		ret, err := mm.MarshalToString(ee)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// This may fail for proto.Anys, e.g. for xDS v2, LDS, the v2
 | 
			
		||||
			// messages are not imported, and this will fail because the message
 | 
			
		||||
			// is not found.
 | 
			
		||||
			return fmt.Sprintf("%+v", ee)
 | 
			
		||||
		}
 | 
			
		||||
		return ret
 | 
			
		||||
	case protov2.Message:
 | 
			
		||||
		mm := protojson.MarshalOptions{
 | 
			
		||||
			Multiline: true,
 | 
			
		||||
			Indent:    jsonIndent,
 | 
			
		||||
		}
 | 
			
		||||
		ret, err := mm.Marshal(ee)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// This may fail for proto.Anys, e.g. for xDS v2, LDS, the v2
 | 
			
		||||
			// messages are not imported, and this will fail because the message
 | 
			
		||||
			// is not found.
 | 
			
		||||
			return fmt.Sprintf("%+v", ee)
 | 
			
		||||
		}
 | 
			
		||||
		return string(ret)
 | 
			
		||||
	default:
 | 
			
		||||
		ret, err := json.MarshalIndent(ee, "", jsonIndent)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Sprintf("%+v", ee)
 | 
			
		||||
		}
 | 
			
		||||
		return string(ret)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatJSON formats the input json bytes with indentation.
 | 
			
		||||
//
 | 
			
		||||
// If Indent fails, it returns the unchanged input as string.
 | 
			
		||||
func FormatJSON(b []byte) string {
 | 
			
		||||
	var out bytes.Buffer
 | 
			
		||||
	err := json.Indent(&out, b, "", jsonIndent)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
	return out.String()
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue