build: add experimental support for print flag
Print flag can be used to make additional information requests about the build and print their results. Currently Dockerfile supports: outline, targets, subrequests.describe Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>pull/1100/head
parent
17dc0e1108
commit
eefa8188e1
@ -0,0 +1,35 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/moby/buildkit/frontend/subrequests"
|
||||
"github.com/moby/buildkit/frontend/subrequests/outline"
|
||||
"github.com/moby/buildkit/frontend/subrequests/targets"
|
||||
)
|
||||
|
||||
func printResult(f string, res map[string]string) error {
|
||||
switch f {
|
||||
case "outline":
|
||||
if err := outline.PrintOutline([]byte(res["result.json"]), os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
case "targets":
|
||||
if err := targets.PrintTargets([]byte(res["result.json"]), os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
case "subrequests.describe":
|
||||
if err := subrequests.PrintDescribe([]byte(res["result.json"]), os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if dt, ok := res["result.txt"]; ok {
|
||||
fmt.Print(dt)
|
||||
} else {
|
||||
log.Printf("%s %+v", f, res)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -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