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
	
	 Tõnis Tiigi
						Tõnis Tiigi