chore: upgrade go-waku to v0.5 (#3213)
* chore: upgrade go-waku to v0.5 * chore: add println and logs to check what's being stored in the enr, and preemptively delete the multiaddr field (#3219) * feat: add wakuv2 test (#3218)
This commit is contained in:
parent
cc283bfaab
commit
0babdad17b
|
@ -6,6 +6,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/status-im/status-go/connection"
|
"github.com/status-im/status-go/connection"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -57,12 +59,12 @@ func (w *gethWakuWrapper) StopDiscV5() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerCount function only added for compatibility with waku V2
|
// PeerCount function only added for compatibility with waku V2
|
||||||
func (w *gethWakuWrapper) AddStorePeer(address string) (string, error) {
|
func (w *gethWakuWrapper) AddStorePeer(address string) (peer.ID, error) {
|
||||||
return "", errors.New("not available in WakuV1")
|
return "", errors.New("not available in WakuV1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRelayPeer function only added for compatibility with waku V2
|
// AddRelayPeer function only added for compatibility with waku V2
|
||||||
func (w *gethWakuWrapper) AddRelayPeer(address string) (string, error) {
|
func (w *gethWakuWrapper) AddRelayPeer(address string) (peer.ID, error) {
|
||||||
return "", errors.New("not available in WakuV1")
|
return "", errors.New("not available in WakuV1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
||||||
|
storepb "github.com/waku-org/go-waku/waku/v2/protocol/store/pb"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/status-im/status-go/connection"
|
"github.com/status-im/status-go/connection"
|
||||||
|
@ -191,7 +191,7 @@ func (w *gethWakuV2Wrapper) RequestStoreMessages(ctx context.Context, peerID []b
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.StoreCursor != nil {
|
if r.StoreCursor != nil {
|
||||||
options = append(options, store.WithCursor(&pb.Index{
|
options = append(options, store.WithCursor(&storepb.Index{
|
||||||
Digest: r.StoreCursor.Digest,
|
Digest: r.StoreCursor.Digest,
|
||||||
ReceiverTime: r.StoreCursor.ReceiverTime,
|
ReceiverTime: r.StoreCursor.ReceiverTime,
|
||||||
SenderTime: r.StoreCursor.SenderTime,
|
SenderTime: r.StoreCursor.SenderTime,
|
||||||
|
@ -234,11 +234,11 @@ func (w *gethWakuV2Wrapper) StopDiscV5() error {
|
||||||
return w.waku.StopDiscV5()
|
return w.waku.StopDiscV5()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gethWakuV2Wrapper) AddStorePeer(address string) (string, error) {
|
func (w *gethWakuV2Wrapper) AddStorePeer(address string) (peer.ID, error) {
|
||||||
return w.waku.AddStorePeer(address)
|
return w.waku.AddStorePeer(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gethWakuV2Wrapper) AddRelayPeer(address string) (string, error) {
|
func (w *gethWakuV2Wrapper) AddRelayPeer(address string) (peer.ID, error) {
|
||||||
return w.waku.AddRelayPeer(address)
|
return w.waku.AddRelayPeer(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -19,8 +21,8 @@ type ConnStatus struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WakuV2Peer struct {
|
type WakuV2Peer struct {
|
||||||
Protocols []string `json:"protocols"`
|
Protocols []protocol.ID `json:"protocols"`
|
||||||
Addresses []string `json:"addresses"`
|
Addresses []string `json:"addresses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnStatusSubscription struct {
|
type ConnStatusSubscription struct {
|
||||||
|
@ -86,9 +88,9 @@ type Waku interface {
|
||||||
|
|
||||||
StopDiscV5() error
|
StopDiscV5() error
|
||||||
|
|
||||||
AddStorePeer(address string) (string, error)
|
AddStorePeer(address string) (peer.ID, error)
|
||||||
|
|
||||||
AddRelayPeer(address string) (string, error)
|
AddRelayPeer(address string) (peer.ID, error)
|
||||||
|
|
||||||
DialPeer(address string) error
|
DialPeer(address string) error
|
||||||
|
|
||||||
|
|
82
go.mod
82
go.mod
|
@ -29,22 +29,22 @@ require (
|
||||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||||
github.com/keighl/metabolize v0.0.0-20150915210303-97ab655d4034
|
github.com/keighl/metabolize v0.0.0-20150915210303-97ab655d4034
|
||||||
github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f
|
github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f
|
||||||
github.com/lib/pq v1.10.0
|
github.com/lib/pq v1.10.4
|
||||||
github.com/libp2p/go-libp2p v0.23.3
|
github.com/libp2p/go-libp2p v0.25.1
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.8.1
|
github.com/libp2p/go-libp2p-pubsub v0.9.0
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3
|
github.com/lucasb-eyer/go-colorful v1.0.3
|
||||||
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7
|
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7
|
||||||
github.com/multiformats/go-multiaddr v0.7.0
|
github.com/multiformats/go-multiaddr v0.8.0
|
||||||
github.com/multiformats/go-multibase v0.1.1
|
github.com/multiformats/go-multibase v0.1.1
|
||||||
github.com/multiformats/go-multihash v0.2.1
|
github.com/multiformats/go-multihash v0.2.1
|
||||||
github.com/multiformats/go-varint v0.0.6
|
github.com/multiformats/go-varint v0.0.7
|
||||||
github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f
|
github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f
|
||||||
github.com/nfnt/resize v0.0.0-00010101000000-000000000000
|
github.com/nfnt/resize v0.0.0-00010101000000-000000000000
|
||||||
github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd
|
github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd
|
||||||
github.com/oliamb/cutter v0.2.2
|
github.com/oliamb/cutter v0.2.2
|
||||||
github.com/pborman/uuid v1.2.0
|
github.com/pborman/uuid v1.2.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.13.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a
|
github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a
|
||||||
github.com/status-im/doubleratchet v3.0.0+incompatible
|
github.com/status-im/doubleratchet v3.0.0+incompatible
|
||||||
github.com/status-im/markdown v0.0.0-20221220095528-8f1babe09d1e
|
github.com/status-im/markdown v0.0.0-20221220095528-8f1babe09d1e
|
||||||
|
@ -61,8 +61,8 @@ require (
|
||||||
github.com/wealdtech/go-multicodec v1.4.0
|
github.com/wealdtech/go-multicodec v1.4.0
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
github.com/zenthangplus/goccm v0.0.0-20211005163543-2f2e522aca15
|
github.com/zenthangplus/goccm v0.0.0-20211005163543-2f2e522aca15
|
||||||
go.uber.org/zap v1.23.0
|
go.uber.org/zap v1.24.0
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
golang.org/x/crypto v0.4.0
|
||||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
|
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||||
|
@ -78,7 +78,7 @@ require (
|
||||||
github.com/ipfs/go-log/v2 v2.5.1
|
github.com/ipfs/go-log/v2 v2.5.1
|
||||||
github.com/ladydascalie/currency v1.6.0
|
github.com/ladydascalie/currency v1.6.0
|
||||||
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8
|
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8
|
||||||
github.com/waku-org/go-waku v0.4.1-0.20230213133834-a3f08943acb4
|
github.com/waku-org/go-waku v0.5.2-0.20230221221637-74b734c337e9
|
||||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
||||||
)
|
)
|
||||||
|
@ -112,9 +112,9 @@ require (
|
||||||
github.com/btcsuite/btcd v0.22.1 // indirect
|
github.com/btcsuite/btcd v0.22.1 // indirect
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.0 // indirect
|
github.com/btcsuite/btcd/btcec/v2 v2.3.0 // indirect
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/containerd/cgroups v1.0.4 // indirect
|
github.com/containerd/cgroups v1.0.4 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.4.0 // indirect
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/cruxic/go-hmac-drbg v0.0.0-20170206035330-84c46983886d // indirect
|
github.com/cruxic/go-hmac-drbg v0.0.0-20170206035330-84c46983886d // indirect
|
||||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||||
|
@ -123,10 +123,10 @@ require (
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||||
github.com/elastic/gosigar v0.14.2 // indirect
|
github.com/elastic/gosigar v0.14.2 // indirect
|
||||||
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
|
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
|
||||||
github.com/flynn/noise v1.0.0 // indirect
|
github.com/flynn/noise v1.0.0 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
|
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
|
@ -142,20 +142,22 @@ require (
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/google/gopacket v1.1.19 // indirect
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-bexpr v0.1.10 // indirect
|
github.com/hashicorp/go-bexpr v0.1.10 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
|
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
|
||||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||||
github.com/holiman/uint256 v1.2.0 // indirect
|
github.com/holiman/uint256 v1.2.0 // indirect
|
||||||
github.com/huandu/xstrings v1.3.2 // indirect
|
github.com/huandu/xstrings v1.3.2 // indirect
|
||||||
github.com/huin/goupnp v1.0.3 // indirect
|
github.com/huin/goupnp v1.0.3 // indirect
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.15.10 // indirect
|
github.com/klauspost/compress v1.15.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||||
github.com/koron/go-ssdp v0.0.3 // indirect
|
github.com/koron/go-ssdp v0.0.3 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||||
|
@ -163,21 +165,16 @@ require (
|
||||||
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
|
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
|
||||||
github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
|
github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
|
||||||
github.com/libp2p/go-mplex v0.7.0 // indirect
|
github.com/libp2p/go-mplex v0.7.0 // indirect
|
||||||
github.com/libp2p/go-msgio v0.2.0 // indirect
|
github.com/libp2p/go-msgio v0.3.0 // indirect
|
||||||
github.com/libp2p/go-nat v0.1.0 // indirect
|
github.com/libp2p/go-nat v0.1.0 // indirect
|
||||||
github.com/libp2p/go-netroute v0.2.0 // indirect
|
github.com/libp2p/go-netroute v0.2.1 // indirect
|
||||||
github.com/libp2p/go-openssl v0.1.0 // indirect
|
|
||||||
github.com/libp2p/go-reuseport v0.2.0 // indirect
|
github.com/libp2p/go-reuseport v0.2.0 // indirect
|
||||||
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
|
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
|
||||||
github.com/lucas-clemente/quic-go v0.29.1 // indirect
|
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
|
|
||||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-pointer v0.0.1 // indirect
|
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/miekg/dns v1.1.50 // indirect
|
github.com/miekg/dns v1.1.50 // indirect
|
||||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||||
|
@ -187,14 +184,13 @@ require (
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||||
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
|
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
|
||||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||||
github.com/multiformats/go-multicodec v0.6.0 // indirect
|
github.com/multiformats/go-multicodec v0.7.0 // indirect
|
||||||
github.com/multiformats/go-multistream v0.3.3 // indirect
|
github.com/multiformats/go-multistream v0.4.1 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
github.com/onsi/ginkgo/v2 v2.5.1 // indirect
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
|
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||||
|
@ -216,10 +212,16 @@ require (
|
||||||
github.com/pion/udp v0.1.1 // indirect
|
github.com/pion/udp v0.1.1 // indirect
|
||||||
github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38 // indirect
|
github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
github.com/prometheus/tsdb v0.10.0 // indirect
|
github.com/prometheus/tsdb v0.10.0 // indirect
|
||||||
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||||
|
github.com/quic-go/webtransport-go v0.5.1 // indirect
|
||||||
github.com/raulk/go-watchdog v1.3.0 // indirect
|
github.com/raulk/go-watchdog v1.3.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
@ -231,42 +233,42 @@ require (
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/shirou/gopsutil v3.21.5+incompatible // indirect
|
github.com/shirou/gopsutil v3.21.5+incompatible // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
github.com/status-im/go-multiaddr-ethv4 v1.2.4 // indirect
|
github.com/status-im/go-multiaddr-ethv4 v1.2.4 // indirect
|
||||||
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect
|
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
||||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||||
github.com/urfave/cli/v2 v2.23.7 // indirect
|
github.com/urfave/cli/v2 v2.24.4 // indirect
|
||||||
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect
|
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg // indirect
|
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg // indirect
|
||||||
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
github.com/yeqown/reedsolomon v1.0.0 // indirect
|
github.com/yeqown/reedsolomon v1.0.0 // indirect
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
go.etcd.io/bbolt v1.3.6 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
|
go.uber.org/dig v1.15.0 // indirect
|
||||||
|
go.uber.org/fx v1.18.2 // indirect
|
||||||
go.uber.org/multierr v1.8.0 // indirect
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b // indirect
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
golang.org/x/mod v0.7.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 // indirect
|
golang.org/x/net v0.4.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
|
golang.org/x/sys v0.3.0 // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/term v0.3.0 // indirect
|
||||||
golang.org/x/text v0.4.0 // indirect
|
golang.org/x/text v0.7.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
golang.org/x/tools v0.3.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/blake3 v1.1.7 // indirect
|
lukechampine.com/blake3 v1.1.7 // indirect
|
||||||
modernc.org/libc v1.11.82 // indirect
|
modernc.org/libc v1.11.82 // indirect
|
||||||
modernc.org/mathutil v1.4.1 // indirect
|
modernc.org/mathutil v1.4.1 // indirect
|
||||||
modernc.org/memory v1.0.5 // indirect
|
modernc.org/memory v1.0.5 // indirect
|
||||||
modernc.org/sqlite v1.14.2-0.20211125151325-d4ed92c0a70f // indirect
|
modernc.org/sqlite v1.14.2-0.20211125151325-d4ed92c0a70f // indirect
|
||||||
|
nhooyr.io/websocket v1.8.7 // indirect
|
||||||
zombiezen.com/go/sqlite v0.8.0 // indirect
|
zombiezen.com/go/sqlite v0.8.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
182
go.sum
182
go.sum
|
@ -456,8 +456,9 @@ github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
|
||||||
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||||
|
@ -612,8 +613,8 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
|
||||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||||
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU=
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
@ -720,6 +721,8 @@ github.com/elliotchance/orderedmap v1.3.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51R
|
||||||
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
@ -779,6 +782,10 @@ github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW
|
||||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||||
|
@ -815,6 +822,7 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
|
||||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||||
|
@ -834,10 +842,15 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
|
||||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
@ -872,6 +885,12 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
|
||||||
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
|
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||||
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
|
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||||
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
|
github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
|
||||||
github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
|
github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
|
||||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
|
@ -973,7 +992,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
|
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
|
||||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
|
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
|
||||||
|
@ -1005,6 +1024,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM=
|
||||||
|
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
@ -1041,6 +1062,7 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
@ -1091,6 +1113,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
|
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
|
||||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
@ -1230,6 +1254,7 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
@ -1261,19 +1286,20 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||||
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
|
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
||||||
github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
@ -1304,6 +1330,7 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
|
||||||
github.com/ladydascalie/currency v1.6.0 h1:r5s/TMCYcpn6jPRHLV3F8nI7YjpY8trvstfuixxiHns=
|
github.com/ladydascalie/currency v1.6.0 h1:r5s/TMCYcpn6jPRHLV3F8nI7YjpY8trvstfuixxiHns=
|
||||||
github.com/ladydascalie/currency v1.6.0/go.mod h1:C9eil8e6tthhBb5yhwoH1U0LT5hm1BP/g+v/V82KYjY=
|
github.com/ladydascalie/currency v1.6.0/go.mod h1:C9eil8e6tthhBb5yhwoH1U0LT5hm1BP/g+v/V82KYjY=
|
||||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
@ -1311,33 +1338,32 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
|
|
||||||
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||||
|
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||||
github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=
|
github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=
|
||||||
github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
|
github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
|
||||||
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
|
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
|
||||||
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
|
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
|
||||||
github.com/libp2p/go-libp2p v0.23.3 h1:/n3i0VtJF0iZ9YMUxl/teOY3h+M8NfgaCjOSYr9D+uI=
|
github.com/libp2p/go-libp2p v0.25.1 h1:YK+YDCHpYyTvitKWVxa5PfElgIpOONU01X5UcLEwJGA=
|
||||||
github.com/libp2p/go-libp2p v0.23.3/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
|
github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g=
|
||||||
github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
|
github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
|
||||||
github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI=
|
github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI=
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY=
|
github.com/libp2p/go-libp2p-pubsub v0.9.0 h1:mcLb4WzwhUG4OKb0rp1/bYMd/DYhvMyzJheQH3LMd1s=
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw=
|
github.com/libp2p/go-libp2p-pubsub v0.9.0/go.mod h1:OEsj0Cc/BpkqikXRTrVspWU/Hx7bMZwHP+6vNMd+c7I=
|
||||||
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
||||||
github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU=
|
github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU=
|
||||||
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
|
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
|
||||||
github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU=
|
github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU=
|
||||||
github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU=
|
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
|
||||||
github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY=
|
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
|
||||||
github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg=
|
github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg=
|
||||||
github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM=
|
github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM=
|
||||||
github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
|
github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
|
||||||
github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE=
|
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
|
||||||
github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI=
|
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
|
||||||
github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo=
|
|
||||||
github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc=
|
|
||||||
github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560=
|
github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560=
|
||||||
github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k=
|
github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k=
|
||||||
github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
|
github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
|
||||||
|
@ -1349,8 +1375,6 @@ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q
|
||||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||||
github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
||||||
github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8=
|
github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8=
|
||||||
github.com/lucas-clemente/quic-go v0.29.1 h1:Z+WMJ++qMLhvpFkRZA+jl3BTxUjm415YBmWanXB8zP0=
|
|
||||||
github.com/lucas-clemente/quic-go v0.29.1/go.mod h1:CTcNfLYJS2UuRNB+zcNlgvkjBhxX6Hm3WUxxAQx2mgE=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/lukechampine/stm v0.0.0-20191022212748-05486c32d236/go.mod h1:wTLsd5FC9rts7GkMpsPGk64CIuea+03yaLAp19Jmlg8=
|
github.com/lukechampine/stm v0.0.0-20191022212748-05486c32d236/go.mod h1:wTLsd5FC9rts7GkMpsPGk64CIuea+03yaLAp19Jmlg8=
|
||||||
|
@ -1370,19 +1394,13 @@ github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQ
|
||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||||
github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
|
|
||||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||||
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
|
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
|
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
|
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
|
||||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
||||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
||||||
github.com/marten-seemann/webtransport-go v0.1.1 h1:TnyKp3pEXcDooTaNn4s9dYpMJ7kMnTp7k5h+SgYP/mc=
|
|
||||||
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7 h1:nxEXqXZcqWABJ8BudZJwrkdfKqCsSsGeo5IPQCbDKTw=
|
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7 h1:nxEXqXZcqWABJ8BudZJwrkdfKqCsSsGeo5IPQCbDKTw=
|
||||||
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7/go.mod h1:RucRlHZcJbj2wwfsa5Vf6sZW5oIYv9ID65evx+CzUrw=
|
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7/go.mod h1:RucRlHZcJbj2wwfsa5Vf6sZW5oIYv9ID65evx+CzUrw=
|
||||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||||
|
@ -1409,8 +1427,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
|
|
||||||
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
@ -1432,8 +1448,9 @@ github.com/mattn/go-sqlite3 v2.0.2+incompatible h1:qzw9c2GNT8UFrgWNDhCTqRqYUSmu/
|
||||||
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
|
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
|
||||||
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8 h1:7TJiWD1knYDpOAPyFBoKqoyvlsa+UwDw0kv0jVN5Mrk=
|
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8 h1:7TJiWD1knYDpOAPyFBoKqoyvlsa+UwDw0kv0jVN5Mrk=
|
||||||
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8/go.mod h1:Uz8uoD6o+eQN19hr6Yro/qKvW+KP6olFq+PK/Nn7gCE=
|
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8/go.mod h1:Uz8uoD6o+eQN19hr6Yro/qKvW+KP6olFq+PK/Nn7gCE=
|
||||||
|
@ -1480,9 +1497,11 @@ github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/f
|
||||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
|
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
@ -1500,14 +1519,15 @@ github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOl
|
||||||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
||||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||||
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
|
|
||||||
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
|
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
|
||||||
|
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||||
|
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||||
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
||||||
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
|
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
|
||||||
github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=
|
github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=
|
||||||
github.com/multiformats/go-multiaddr v0.3.2/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0=
|
github.com/multiformats/go-multiaddr v0.3.2/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0=
|
||||||
github.com/multiformats/go-multiaddr v0.7.0 h1:gskHcdaCyPtp9XskVwtvEeQOG465sCohbQIirSyqxrc=
|
github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
|
||||||
github.com/multiformats/go-multiaddr v0.7.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
|
github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
|
||||||
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
|
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
|
||||||
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
|
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
|
||||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
||||||
|
@ -1515,20 +1535,21 @@ github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDu
|
||||||
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
|
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
|
||||||
github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI=
|
github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI=
|
||||||
github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8=
|
github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8=
|
||||||
github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE=
|
github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ=
|
||||||
github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
|
github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
|
||||||
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
||||||
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
|
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
|
||||||
github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
|
github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
|
||||||
github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg=
|
github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg=
|
||||||
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
|
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
|
||||||
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
|
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
|
||||||
github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o=
|
github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo=
|
||||||
github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg=
|
github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q=
|
||||||
github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||||
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||||
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
|
|
||||||
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||||
|
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||||
|
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f h1:hd3r+uv9DNLScbOrnlj82rBldHQf3XWmCeXAWbw8euQ=
|
github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f h1:hd3r+uv9DNLScbOrnlj82rBldHQf3XWmCeXAWbw8euQ=
|
||||||
|
@ -1583,6 +1604,8 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
|
||||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
@ -1596,8 +1619,8 @@ github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDs
|
||||||
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
|
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
|
||||||
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
|
||||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||||
|
github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
|
@ -1778,16 +1801,17 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP
|
||||||
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||||
|
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
@ -1827,6 +1851,18 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
|
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
|
||||||
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
|
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
|
||||||
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
|
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||||
|
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||||
|
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||||
|
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
|
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||||
|
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||||
|
github.com/quic-go/webtransport-go v0.5.1 h1:1eVb7WDWCRoaeTtFHpFBJ6WDN1bSrPrRoW6tZgSw0Ow=
|
||||||
|
github.com/quic-go/webtransport-go v0.5.1/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU=
|
||||||
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
|
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
|
||||||
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
|
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
|
@ -1934,8 +1970,6 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE
|
||||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
|
|
||||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
@ -2043,13 +2077,17 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
|
github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU=
|
||||||
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||||
github.com/vacp2p/mvds v0.0.24-0.20201124060106-26d8e94130d8 h1:aSQuY64yglPb7I6lZRXt/xWD4ExM1DZo8Gpb7xCz6zk=
|
github.com/vacp2p/mvds v0.0.24-0.20201124060106-26d8e94130d8 h1:aSQuY64yglPb7I6lZRXt/xWD4ExM1DZo8Gpb7xCz6zk=
|
||||||
github.com/vacp2p/mvds v0.0.24-0.20201124060106-26d8e94130d8/go.mod h1:uUmtiahU7efOVl/5w5yk9jOze5xYpDZDrSrT8TvHXjQ=
|
github.com/vacp2p/mvds v0.0.24-0.20201124060106-26d8e94130d8/go.mod h1:uUmtiahU7efOVl/5w5yk9jOze5xYpDZDrSrT8TvHXjQ=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
@ -2067,8 +2105,8 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZFimdqfZb9cZwT1S3VJP9j3AE6bdNd9boXM=
|
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZFimdqfZb9cZwT1S3VJP9j3AE6bdNd9boXM=
|
||||||
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
|
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
|
||||||
github.com/waku-org/go-waku v0.4.1-0.20230213133834-a3f08943acb4 h1:juvZsi9AXnZrUjRw1nt8K6aK++kY0I60HJxFBaYe7kI=
|
github.com/waku-org/go-waku v0.5.2-0.20230221221637-74b734c337e9 h1:9AiqlpVucwUx+Lm1gBLOwk1VUgozGYmnJ4KM9mH/Irk=
|
||||||
github.com/waku-org/go-waku v0.4.1-0.20230213133834-a3f08943acb4/go.mod h1:sI14mN/sM8inIb2x2b462wydSEFyOyuDKI1cjiVIIpM=
|
github.com/waku-org/go-waku v0.5.2-0.20230221221637-74b734c337e9/go.mod h1:hupqK7cKFAmY+r/EW7LLaPnMLsPrm0fW1OuR6KrO8a8=
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
|
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
|
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
|
||||||
github.com/wealdtech/go-ens/v3 v3.5.0 h1:Huc9GxBgiGweCOGTYomvsg07K2QggAqZpZ5SuiZdC8o=
|
github.com/wealdtech/go-ens/v3 v3.5.0 h1:Huc9GxBgiGweCOGTYomvsg07K2QggAqZpZ5SuiZdC8o=
|
||||||
|
@ -2077,8 +2115,6 @@ github.com/wealdtech/go-multicodec v1.4.0 h1:iq5PgxwssxnXGGPTIK1srvt6U5bJwIp7k6k
|
||||||
github.com/wealdtech/go-multicodec v1.4.0/go.mod h1:aedGMaTeYkIqi/KCPre1ho5rTb3hGpu/snBOS3GQLw4=
|
github.com/wealdtech/go-multicodec v1.4.0/go.mod h1:aedGMaTeYkIqi/KCPre1ho5rTb3hGpu/snBOS3GQLw4=
|
||||||
github.com/wealdtech/go-string2eth v1.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw=
|
github.com/wealdtech/go-string2eth v1.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw=
|
||||||
github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU=
|
github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU=
|
||||||
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow=
|
|
||||||
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
|
|
||||||
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
|
@ -2150,8 +2186,9 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
|
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w=
|
||||||
|
@ -2180,6 +2217,10 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
|
go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE=
|
||||||
|
go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM=
|
||||||
|
go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU=
|
||||||
|
go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY=
|
||||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
|
@ -2197,8 +2238,8 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||||
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
@ -2246,8 +2287,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -2262,8 +2303,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
@ -2300,8 +2341,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -2372,7 +2413,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
||||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
|
||||||
golang.org/x/net v0.0.0-20210427231257-85d9c07bbe3a/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210427231257-85d9c07bbe3a/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
@ -2392,8 +2432,8 @@ golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 h1:KafLifaRFIuSJ5C+7CyFJOF9haxKNC1CEIDk8GX6X0k=
|
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||||
golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -2556,7 +2596,6 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -2590,14 +2629,15 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -2607,8 +2647,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -2707,8 +2747,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -3149,6 +3189,8 @@ modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||||
modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=
|
modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=
|
||||||
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
|
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
|
||||||
modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
|
modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
|
||||||
|
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||||
|
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
|
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
|
||||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=
|
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import "github.com/status-im/status-go/eth-node/types"
|
import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
func (m *Messenger) AddStorePeer(address string) (string, error) {
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Messenger) AddStorePeer(address string) (peer.ID, error) {
|
||||||
return m.transport.AddStorePeer(address)
|
return m.transport.AddStorePeer(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) AddRelayPeer(address string) (string, error) {
|
func (m *Messenger) AddRelayPeer(address string) (peer.ID, error) {
|
||||||
return m.transport.AddStorePeer(address)
|
return m.transport.AddStorePeer(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
@ -653,11 +654,11 @@ func (t *Transport) ListenAddresses() ([]string, error) {
|
||||||
return t.waku.ListenAddresses()
|
return t.waku.ListenAddresses()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) AddStorePeer(address string) (string, error) {
|
func (t *Transport) AddStorePeer(address string) (peer.ID, error) {
|
||||||
return t.waku.AddStorePeer(address)
|
return t.waku.AddStorePeer(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) AddRelayPeer(address string) (string, error) {
|
func (t *Transport) AddRelayPeer(address string) (peer.ID, error) {
|
||||||
return t.waku.AddRelayPeer(address)
|
return t.waku.AddRelayPeer(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1186,11 +1186,13 @@ func (api *PublicAPI) DisableCommunityHistoryArchiveProtocol() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *PublicAPI) AddStorePeer(address string) (string, error) {
|
func (api *PublicAPI) AddStorePeer(address string) (string, error) {
|
||||||
return api.service.messenger.AddStorePeer(address)
|
peerID, err := api.service.messenger.AddStorePeer(address)
|
||||||
|
return string(peerID), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *PublicAPI) AddRelayPeer(address string) (string, error) {
|
func (api *PublicAPI) AddRelayPeer(address string) (string, error) {
|
||||||
return api.service.messenger.AddRelayPeer(address)
|
peerID, err := api.service.messenger.AddRelayPeer(address)
|
||||||
|
return string(peerID), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *PublicAPI) DialPeer(address string) error {
|
func (api *PublicAPI) DialPeer(address string) error {
|
||||||
|
|
31
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
31
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
|
@ -3,8 +3,7 @@
|
||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2)
|
[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2)
|
||||||
[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml)
|
[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml)
|
||||||
|
|
||||||
xxhash is a Go implementation of the 64-bit
|
xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a
|
||||||
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
|
|
||||||
high-quality hashing algorithm that is much faster than anything in the Go
|
high-quality hashing algorithm that is much faster than anything in the Go
|
||||||
standard library.
|
standard library.
|
||||||
|
|
||||||
|
@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error)
|
||||||
func (*Digest) Sum64() uint64
|
func (*Digest) Sum64() uint64
|
||||||
```
|
```
|
||||||
|
|
||||||
This implementation provides a fast pure-Go implementation and an even faster
|
The package is written with optimized pure Go and also contains even faster
|
||||||
assembly implementation for amd64.
|
assembly implementations for amd64 and arm64. If desired, the `purego` build tag
|
||||||
|
opts into using the Go code even on those architectures.
|
||||||
|
|
||||||
|
[xxHash]: http://cyan4973.github.io/xxHash/
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
|
@ -45,19 +47,20 @@ I recommend using the latest release of Go.
|
||||||
Here are some quick benchmarks comparing the pure-Go and assembly
|
Here are some quick benchmarks comparing the pure-Go and assembly
|
||||||
implementations of Sum64.
|
implementations of Sum64.
|
||||||
|
|
||||||
| input size | purego | asm |
|
| input size | purego | asm |
|
||||||
| --- | --- | --- |
|
| ---------- | --------- | --------- |
|
||||||
| 5 B | 979.66 MB/s | 1291.17 MB/s |
|
| 4 B | 1.3 GB/s | 1.2 GB/s |
|
||||||
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
|
| 16 B | 2.9 GB/s | 3.5 GB/s |
|
||||||
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
|
| 100 B | 6.9 GB/s | 8.1 GB/s |
|
||||||
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
|
| 4 KB | 11.7 GB/s | 16.7 GB/s |
|
||||||
|
| 10 MB | 12.0 GB/s | 17.3 GB/s |
|
||||||
|
|
||||||
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
|
These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C
|
||||||
the following commands under Go 1.11.2:
|
CPU using the following commands under Go 1.19.2:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
|
benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||||
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
|
benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Projects using this package
|
## Projects using this package
|
||||||
|
|
10
vendor/github.com/cespare/xxhash/v2/testall.sh
generated
vendored
Normal file
10
vendor/github.com/cespare/xxhash/v2/testall.sh
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -eu -o pipefail
|
||||||
|
|
||||||
|
# Small convenience script for running the tests with various combinations of
|
||||||
|
# arch/tags. This assumes we're running on amd64 and have qemu available.
|
||||||
|
|
||||||
|
go test ./...
|
||||||
|
go test -tags purego ./...
|
||||||
|
GOARCH=arm64 go test
|
||||||
|
GOARCH=arm64 go test -tags purego
|
47
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
47
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
|
@ -16,19 +16,11 @@ const (
|
||||||
prime5 uint64 = 2870177450012600261
|
prime5 uint64 = 2870177450012600261
|
||||||
)
|
)
|
||||||
|
|
||||||
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
|
// Store the primes in an array as well.
|
||||||
// possible in the Go code is worth a small (but measurable) performance boost
|
//
|
||||||
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for
|
// The consts are used when possible in Go code to avoid MOVs but we need a
|
||||||
// convenience in the Go code in a few places where we need to intentionally
|
// contiguous array of the assembly code.
|
||||||
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
|
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
|
||||||
// result overflows a uint64).
|
|
||||||
var (
|
|
||||||
prime1v = prime1
|
|
||||||
prime2v = prime2
|
|
||||||
prime3v = prime3
|
|
||||||
prime4v = prime4
|
|
||||||
prime5v = prime5
|
|
||||||
)
|
|
||||||
|
|
||||||
// Digest implements hash.Hash64.
|
// Digest implements hash.Hash64.
|
||||||
type Digest struct {
|
type Digest struct {
|
||||||
|
@ -50,10 +42,10 @@ func New() *Digest {
|
||||||
|
|
||||||
// Reset clears the Digest's state so that it can be reused.
|
// Reset clears the Digest's state so that it can be reused.
|
||||||
func (d *Digest) Reset() {
|
func (d *Digest) Reset() {
|
||||||
d.v1 = prime1v + prime2
|
d.v1 = primes[0] + prime2
|
||||||
d.v2 = prime2
|
d.v2 = prime2
|
||||||
d.v3 = 0
|
d.v3 = 0
|
||||||
d.v4 = -prime1v
|
d.v4 = -primes[0]
|
||||||
d.total = 0
|
d.total = 0
|
||||||
d.n = 0
|
d.n = 0
|
||||||
}
|
}
|
||||||
|
@ -69,21 +61,23 @@ func (d *Digest) Write(b []byte) (n int, err error) {
|
||||||
n = len(b)
|
n = len(b)
|
||||||
d.total += uint64(n)
|
d.total += uint64(n)
|
||||||
|
|
||||||
|
memleft := d.mem[d.n&(len(d.mem)-1):]
|
||||||
|
|
||||||
if d.n+n < 32 {
|
if d.n+n < 32 {
|
||||||
// This new data doesn't even fill the current block.
|
// This new data doesn't even fill the current block.
|
||||||
copy(d.mem[d.n:], b)
|
copy(memleft, b)
|
||||||
d.n += n
|
d.n += n
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.n > 0 {
|
if d.n > 0 {
|
||||||
// Finish off the partial block.
|
// Finish off the partial block.
|
||||||
copy(d.mem[d.n:], b)
|
c := copy(memleft, b)
|
||||||
d.v1 = round(d.v1, u64(d.mem[0:8]))
|
d.v1 = round(d.v1, u64(d.mem[0:8]))
|
||||||
d.v2 = round(d.v2, u64(d.mem[8:16]))
|
d.v2 = round(d.v2, u64(d.mem[8:16]))
|
||||||
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
||||||
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
||||||
b = b[32-d.n:]
|
b = b[c:]
|
||||||
d.n = 0
|
d.n = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,21 +127,20 @@ func (d *Digest) Sum64() uint64 {
|
||||||
|
|
||||||
h += d.total
|
h += d.total
|
||||||
|
|
||||||
i, end := 0, d.n
|
b := d.mem[:d.n&(len(d.mem)-1)]
|
||||||
for ; i+8 <= end; i += 8 {
|
for ; len(b) >= 8; b = b[8:] {
|
||||||
k1 := round(0, u64(d.mem[i:i+8]))
|
k1 := round(0, u64(b[:8]))
|
||||||
h ^= k1
|
h ^= k1
|
||||||
h = rol27(h)*prime1 + prime4
|
h = rol27(h)*prime1 + prime4
|
||||||
}
|
}
|
||||||
if i+4 <= end {
|
if len(b) >= 4 {
|
||||||
h ^= uint64(u32(d.mem[i:i+4])) * prime1
|
h ^= uint64(u32(b[:4])) * prime1
|
||||||
h = rol23(h)*prime2 + prime3
|
h = rol23(h)*prime2 + prime3
|
||||||
i += 4
|
b = b[4:]
|
||||||
}
|
}
|
||||||
for i < end {
|
for ; len(b) > 0; b = b[1:] {
|
||||||
h ^= uint64(d.mem[i]) * prime5
|
h ^= uint64(b[0]) * prime5
|
||||||
h = rol11(h) * prime1
|
h = rol11(h) * prime1
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h ^= h >> 33
|
h ^= h >> 33
|
||||||
|
|
13
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go
generated
vendored
13
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go
generated
vendored
|
@ -1,13 +0,0 @@
|
||||||
// +build !appengine
|
|
||||||
// +build gc
|
|
||||||
// +build !purego
|
|
||||||
|
|
||||||
package xxhash
|
|
||||||
|
|
||||||
// Sum64 computes the 64-bit xxHash digest of b.
|
|
||||||
//
|
|
||||||
//go:noescape
|
|
||||||
func Sum64(b []byte) uint64
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
func writeBlocks(d *Digest, b []byte) int
|
|
308
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
308
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
|
@ -1,215 +1,209 @@
|
||||||
|
//go:build !appengine && gc && !purego
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !purego
|
// +build !purego
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
// Register allocation:
|
// Registers:
|
||||||
// AX h
|
#define h AX
|
||||||
// SI pointer to advance through b
|
#define d AX
|
||||||
// DX n
|
#define p SI // pointer to advance through b
|
||||||
// BX loop end
|
#define n DX
|
||||||
// R8 v1, k1
|
#define end BX // loop end
|
||||||
// R9 v2
|
#define v1 R8
|
||||||
// R10 v3
|
#define v2 R9
|
||||||
// R11 v4
|
#define v3 R10
|
||||||
// R12 tmp
|
#define v4 R11
|
||||||
// R13 prime1v
|
#define x R12
|
||||||
// R14 prime2v
|
#define prime1 R13
|
||||||
// DI prime4v
|
#define prime2 R14
|
||||||
|
#define prime4 DI
|
||||||
|
|
||||||
// round reads from and advances the buffer pointer in SI.
|
#define round(acc, x) \
|
||||||
// It assumes that R13 has prime1v and R14 has prime2v.
|
IMULQ prime2, x \
|
||||||
#define round(r) \
|
ADDQ x, acc \
|
||||||
MOVQ (SI), R12 \
|
ROLQ $31, acc \
|
||||||
ADDQ $8, SI \
|
IMULQ prime1, acc
|
||||||
IMULQ R14, R12 \
|
|
||||||
ADDQ R12, r \
|
|
||||||
ROLQ $31, r \
|
|
||||||
IMULQ R13, r
|
|
||||||
|
|
||||||
// mergeRound applies a merge round on the two registers acc and val.
|
// round0 performs the operation x = round(0, x).
|
||||||
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v.
|
#define round0(x) \
|
||||||
#define mergeRound(acc, val) \
|
IMULQ prime2, x \
|
||||||
IMULQ R14, val \
|
ROLQ $31, x \
|
||||||
ROLQ $31, val \
|
IMULQ prime1, x
|
||||||
IMULQ R13, val \
|
|
||||||
XORQ val, acc \
|
// mergeRound applies a merge round on the two registers acc and x.
|
||||||
IMULQ R13, acc \
|
// It assumes that prime1, prime2, and prime4 have been loaded.
|
||||||
ADDQ DI, acc
|
#define mergeRound(acc, x) \
|
||||||
|
round0(x) \
|
||||||
|
XORQ x, acc \
|
||||||
|
IMULQ prime1, acc \
|
||||||
|
ADDQ prime4, acc
|
||||||
|
|
||||||
|
// blockLoop processes as many 32-byte blocks as possible,
|
||||||
|
// updating v1, v2, v3, and v4. It assumes that there is at least one block
|
||||||
|
// to process.
|
||||||
|
#define blockLoop() \
|
||||||
|
loop: \
|
||||||
|
MOVQ +0(p), x \
|
||||||
|
round(v1, x) \
|
||||||
|
MOVQ +8(p), x \
|
||||||
|
round(v2, x) \
|
||||||
|
MOVQ +16(p), x \
|
||||||
|
round(v3, x) \
|
||||||
|
MOVQ +24(p), x \
|
||||||
|
round(v4, x) \
|
||||||
|
ADDQ $32, p \
|
||||||
|
CMPQ p, end \
|
||||||
|
JLE loop
|
||||||
|
|
||||||
// func Sum64(b []byte) uint64
|
// func Sum64(b []byte) uint64
|
||||||
TEXT ·Sum64(SB), NOSPLIT, $0-32
|
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||||
// Load fixed primes.
|
// Load fixed primes.
|
||||||
MOVQ ·prime1v(SB), R13
|
MOVQ ·primes+0(SB), prime1
|
||||||
MOVQ ·prime2v(SB), R14
|
MOVQ ·primes+8(SB), prime2
|
||||||
MOVQ ·prime4v(SB), DI
|
MOVQ ·primes+24(SB), prime4
|
||||||
|
|
||||||
// Load slice.
|
// Load slice.
|
||||||
MOVQ b_base+0(FP), SI
|
MOVQ b_base+0(FP), p
|
||||||
MOVQ b_len+8(FP), DX
|
MOVQ b_len+8(FP), n
|
||||||
LEAQ (SI)(DX*1), BX
|
LEAQ (p)(n*1), end
|
||||||
|
|
||||||
// The first loop limit will be len(b)-32.
|
// The first loop limit will be len(b)-32.
|
||||||
SUBQ $32, BX
|
SUBQ $32, end
|
||||||
|
|
||||||
// Check whether we have at least one block.
|
// Check whether we have at least one block.
|
||||||
CMPQ DX, $32
|
CMPQ n, $32
|
||||||
JLT noBlocks
|
JLT noBlocks
|
||||||
|
|
||||||
// Set up initial state (v1, v2, v3, v4).
|
// Set up initial state (v1, v2, v3, v4).
|
||||||
MOVQ R13, R8
|
MOVQ prime1, v1
|
||||||
ADDQ R14, R8
|
ADDQ prime2, v1
|
||||||
MOVQ R14, R9
|
MOVQ prime2, v2
|
||||||
XORQ R10, R10
|
XORQ v3, v3
|
||||||
XORQ R11, R11
|
XORQ v4, v4
|
||||||
SUBQ R13, R11
|
SUBQ prime1, v4
|
||||||
|
|
||||||
// Loop until SI > BX.
|
blockLoop()
|
||||||
blockLoop:
|
|
||||||
round(R8)
|
|
||||||
round(R9)
|
|
||||||
round(R10)
|
|
||||||
round(R11)
|
|
||||||
|
|
||||||
CMPQ SI, BX
|
MOVQ v1, h
|
||||||
JLE blockLoop
|
ROLQ $1, h
|
||||||
|
MOVQ v2, x
|
||||||
|
ROLQ $7, x
|
||||||
|
ADDQ x, h
|
||||||
|
MOVQ v3, x
|
||||||
|
ROLQ $12, x
|
||||||
|
ADDQ x, h
|
||||||
|
MOVQ v4, x
|
||||||
|
ROLQ $18, x
|
||||||
|
ADDQ x, h
|
||||||
|
|
||||||
MOVQ R8, AX
|
mergeRound(h, v1)
|
||||||
ROLQ $1, AX
|
mergeRound(h, v2)
|
||||||
MOVQ R9, R12
|
mergeRound(h, v3)
|
||||||
ROLQ $7, R12
|
mergeRound(h, v4)
|
||||||
ADDQ R12, AX
|
|
||||||
MOVQ R10, R12
|
|
||||||
ROLQ $12, R12
|
|
||||||
ADDQ R12, AX
|
|
||||||
MOVQ R11, R12
|
|
||||||
ROLQ $18, R12
|
|
||||||
ADDQ R12, AX
|
|
||||||
|
|
||||||
mergeRound(AX, R8)
|
|
||||||
mergeRound(AX, R9)
|
|
||||||
mergeRound(AX, R10)
|
|
||||||
mergeRound(AX, R11)
|
|
||||||
|
|
||||||
JMP afterBlocks
|
JMP afterBlocks
|
||||||
|
|
||||||
noBlocks:
|
noBlocks:
|
||||||
MOVQ ·prime5v(SB), AX
|
MOVQ ·primes+32(SB), h
|
||||||
|
|
||||||
afterBlocks:
|
afterBlocks:
|
||||||
ADDQ DX, AX
|
ADDQ n, h
|
||||||
|
|
||||||
// Right now BX has len(b)-32, and we want to loop until SI > len(b)-8.
|
ADDQ $24, end
|
||||||
ADDQ $24, BX
|
CMPQ p, end
|
||||||
|
JG try4
|
||||||
|
|
||||||
CMPQ SI, BX
|
loop8:
|
||||||
JG fourByte
|
MOVQ (p), x
|
||||||
|
ADDQ $8, p
|
||||||
|
round0(x)
|
||||||
|
XORQ x, h
|
||||||
|
ROLQ $27, h
|
||||||
|
IMULQ prime1, h
|
||||||
|
ADDQ prime4, h
|
||||||
|
|
||||||
wordLoop:
|
CMPQ p, end
|
||||||
// Calculate k1.
|
JLE loop8
|
||||||
MOVQ (SI), R8
|
|
||||||
ADDQ $8, SI
|
|
||||||
IMULQ R14, R8
|
|
||||||
ROLQ $31, R8
|
|
||||||
IMULQ R13, R8
|
|
||||||
|
|
||||||
XORQ R8, AX
|
try4:
|
||||||
ROLQ $27, AX
|
ADDQ $4, end
|
||||||
IMULQ R13, AX
|
CMPQ p, end
|
||||||
ADDQ DI, AX
|
JG try1
|
||||||
|
|
||||||
CMPQ SI, BX
|
MOVL (p), x
|
||||||
JLE wordLoop
|
ADDQ $4, p
|
||||||
|
IMULQ prime1, x
|
||||||
|
XORQ x, h
|
||||||
|
|
||||||
fourByte:
|
ROLQ $23, h
|
||||||
ADDQ $4, BX
|
IMULQ prime2, h
|
||||||
CMPQ SI, BX
|
ADDQ ·primes+16(SB), h
|
||||||
JG singles
|
|
||||||
|
|
||||||
MOVL (SI), R8
|
try1:
|
||||||
ADDQ $4, SI
|
ADDQ $4, end
|
||||||
IMULQ R13, R8
|
CMPQ p, end
|
||||||
XORQ R8, AX
|
|
||||||
|
|
||||||
ROLQ $23, AX
|
|
||||||
IMULQ R14, AX
|
|
||||||
ADDQ ·prime3v(SB), AX
|
|
||||||
|
|
||||||
singles:
|
|
||||||
ADDQ $4, BX
|
|
||||||
CMPQ SI, BX
|
|
||||||
JGE finalize
|
JGE finalize
|
||||||
|
|
||||||
singlesLoop:
|
loop1:
|
||||||
MOVBQZX (SI), R12
|
MOVBQZX (p), x
|
||||||
ADDQ $1, SI
|
ADDQ $1, p
|
||||||
IMULQ ·prime5v(SB), R12
|
IMULQ ·primes+32(SB), x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
|
ROLQ $11, h
|
||||||
|
IMULQ prime1, h
|
||||||
|
|
||||||
ROLQ $11, AX
|
CMPQ p, end
|
||||||
IMULQ R13, AX
|
JL loop1
|
||||||
|
|
||||||
CMPQ SI, BX
|
|
||||||
JL singlesLoop
|
|
||||||
|
|
||||||
finalize:
|
finalize:
|
||||||
MOVQ AX, R12
|
MOVQ h, x
|
||||||
SHRQ $33, R12
|
SHRQ $33, x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
IMULQ R14, AX
|
IMULQ prime2, h
|
||||||
MOVQ AX, R12
|
MOVQ h, x
|
||||||
SHRQ $29, R12
|
SHRQ $29, x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
IMULQ ·prime3v(SB), AX
|
IMULQ ·primes+16(SB), h
|
||||||
MOVQ AX, R12
|
MOVQ h, x
|
||||||
SHRQ $32, R12
|
SHRQ $32, x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
|
|
||||||
MOVQ AX, ret+24(FP)
|
MOVQ h, ret+24(FP)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// writeBlocks uses the same registers as above except that it uses AX to store
|
|
||||||
// the d pointer.
|
|
||||||
|
|
||||||
// func writeBlocks(d *Digest, b []byte) int
|
// func writeBlocks(d *Digest, b []byte) int
|
||||||
TEXT ·writeBlocks(SB), NOSPLIT, $0-40
|
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||||
// Load fixed primes needed for round.
|
// Load fixed primes needed for round.
|
||||||
MOVQ ·prime1v(SB), R13
|
MOVQ ·primes+0(SB), prime1
|
||||||
MOVQ ·prime2v(SB), R14
|
MOVQ ·primes+8(SB), prime2
|
||||||
|
|
||||||
// Load slice.
|
// Load slice.
|
||||||
MOVQ b_base+8(FP), SI
|
MOVQ b_base+8(FP), p
|
||||||
MOVQ b_len+16(FP), DX
|
MOVQ b_len+16(FP), n
|
||||||
LEAQ (SI)(DX*1), BX
|
LEAQ (p)(n*1), end
|
||||||
SUBQ $32, BX
|
SUBQ $32, end
|
||||||
|
|
||||||
// Load vN from d.
|
// Load vN from d.
|
||||||
MOVQ d+0(FP), AX
|
MOVQ s+0(FP), d
|
||||||
MOVQ 0(AX), R8 // v1
|
MOVQ 0(d), v1
|
||||||
MOVQ 8(AX), R9 // v2
|
MOVQ 8(d), v2
|
||||||
MOVQ 16(AX), R10 // v3
|
MOVQ 16(d), v3
|
||||||
MOVQ 24(AX), R11 // v4
|
MOVQ 24(d), v4
|
||||||
|
|
||||||
// We don't need to check the loop condition here; this function is
|
// We don't need to check the loop condition here; this function is
|
||||||
// always called with at least one block of data to process.
|
// always called with at least one block of data to process.
|
||||||
blockLoop:
|
blockLoop()
|
||||||
round(R8)
|
|
||||||
round(R9)
|
|
||||||
round(R10)
|
|
||||||
round(R11)
|
|
||||||
|
|
||||||
CMPQ SI, BX
|
|
||||||
JLE blockLoop
|
|
||||||
|
|
||||||
// Copy vN back to d.
|
// Copy vN back to d.
|
||||||
MOVQ R8, 0(AX)
|
MOVQ v1, 0(d)
|
||||||
MOVQ R9, 8(AX)
|
MOVQ v2, 8(d)
|
||||||
MOVQ R10, 16(AX)
|
MOVQ v3, 16(d)
|
||||||
MOVQ R11, 24(AX)
|
MOVQ v4, 24(d)
|
||||||
|
|
||||||
// The number of bytes written is SI minus the old base pointer.
|
// The number of bytes written is p minus the old base pointer.
|
||||||
SUBQ b_base+8(FP), SI
|
SUBQ b_base+8(FP), p
|
||||||
MOVQ SI, ret+32(FP)
|
MOVQ p, ret+32(FP)
|
||||||
|
|
||||||
RET
|
RET
|
||||||
|
|
183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s
generated
vendored
Normal file
183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s
generated
vendored
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
//go:build !appengine && gc && !purego
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// Registers:
|
||||||
|
#define digest R1
|
||||||
|
#define h R2 // return value
|
||||||
|
#define p R3 // input pointer
|
||||||
|
#define n R4 // input length
|
||||||
|
#define nblocks R5 // n / 32
|
||||||
|
#define prime1 R7
|
||||||
|
#define prime2 R8
|
||||||
|
#define prime3 R9
|
||||||
|
#define prime4 R10
|
||||||
|
#define prime5 R11
|
||||||
|
#define v1 R12
|
||||||
|
#define v2 R13
|
||||||
|
#define v3 R14
|
||||||
|
#define v4 R15
|
||||||
|
#define x1 R20
|
||||||
|
#define x2 R21
|
||||||
|
#define x3 R22
|
||||||
|
#define x4 R23
|
||||||
|
|
||||||
|
#define round(acc, x) \
|
||||||
|
MADD prime2, acc, x, acc \
|
||||||
|
ROR $64-31, acc \
|
||||||
|
MUL prime1, acc
|
||||||
|
|
||||||
|
// round0 performs the operation x = round(0, x).
|
||||||
|
#define round0(x) \
|
||||||
|
MUL prime2, x \
|
||||||
|
ROR $64-31, x \
|
||||||
|
MUL prime1, x
|
||||||
|
|
||||||
|
#define mergeRound(acc, x) \
|
||||||
|
round0(x) \
|
||||||
|
EOR x, acc \
|
||||||
|
MADD acc, prime4, prime1, acc
|
||||||
|
|
||||||
|
// blockLoop processes as many 32-byte blocks as possible,
|
||||||
|
// updating v1, v2, v3, and v4. It assumes that n >= 32.
|
||||||
|
#define blockLoop() \
|
||||||
|
LSR $5, n, nblocks \
|
||||||
|
PCALIGN $16 \
|
||||||
|
loop: \
|
||||||
|
LDP.P 16(p), (x1, x2) \
|
||||||
|
LDP.P 16(p), (x3, x4) \
|
||||||
|
round(v1, x1) \
|
||||||
|
round(v2, x2) \
|
||||||
|
round(v3, x3) \
|
||||||
|
round(v4, x4) \
|
||||||
|
SUB $1, nblocks \
|
||||||
|
CBNZ nblocks, loop
|
||||||
|
|
||||||
|
// func Sum64(b []byte) uint64
|
||||||
|
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||||
|
LDP b_base+0(FP), (p, n)
|
||||||
|
|
||||||
|
LDP ·primes+0(SB), (prime1, prime2)
|
||||||
|
LDP ·primes+16(SB), (prime3, prime4)
|
||||||
|
MOVD ·primes+32(SB), prime5
|
||||||
|
|
||||||
|
CMP $32, n
|
||||||
|
CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 }
|
||||||
|
BLT afterLoop
|
||||||
|
|
||||||
|
ADD prime1, prime2, v1
|
||||||
|
MOVD prime2, v2
|
||||||
|
MOVD $0, v3
|
||||||
|
NEG prime1, v4
|
||||||
|
|
||||||
|
blockLoop()
|
||||||
|
|
||||||
|
ROR $64-1, v1, x1
|
||||||
|
ROR $64-7, v2, x2
|
||||||
|
ADD x1, x2
|
||||||
|
ROR $64-12, v3, x3
|
||||||
|
ROR $64-18, v4, x4
|
||||||
|
ADD x3, x4
|
||||||
|
ADD x2, x4, h
|
||||||
|
|
||||||
|
mergeRound(h, v1)
|
||||||
|
mergeRound(h, v2)
|
||||||
|
mergeRound(h, v3)
|
||||||
|
mergeRound(h, v4)
|
||||||
|
|
||||||
|
afterLoop:
|
||||||
|
ADD n, h
|
||||||
|
|
||||||
|
TBZ $4, n, try8
|
||||||
|
LDP.P 16(p), (x1, x2)
|
||||||
|
|
||||||
|
round0(x1)
|
||||||
|
|
||||||
|
// NOTE: here and below, sequencing the EOR after the ROR (using a
|
||||||
|
// rotated register) is worth a small but measurable speedup for small
|
||||||
|
// inputs.
|
||||||
|
ROR $64-27, h
|
||||||
|
EOR x1 @> 64-27, h, h
|
||||||
|
MADD h, prime4, prime1, h
|
||||||
|
|
||||||
|
round0(x2)
|
||||||
|
ROR $64-27, h
|
||||||
|
EOR x2 @> 64-27, h, h
|
||||||
|
MADD h, prime4, prime1, h
|
||||||
|
|
||||||
|
try8:
|
||||||
|
TBZ $3, n, try4
|
||||||
|
MOVD.P 8(p), x1
|
||||||
|
|
||||||
|
round0(x1)
|
||||||
|
ROR $64-27, h
|
||||||
|
EOR x1 @> 64-27, h, h
|
||||||
|
MADD h, prime4, prime1, h
|
||||||
|
|
||||||
|
try4:
|
||||||
|
TBZ $2, n, try2
|
||||||
|
MOVWU.P 4(p), x2
|
||||||
|
|
||||||
|
MUL prime1, x2
|
||||||
|
ROR $64-23, h
|
||||||
|
EOR x2 @> 64-23, h, h
|
||||||
|
MADD h, prime3, prime2, h
|
||||||
|
|
||||||
|
try2:
|
||||||
|
TBZ $1, n, try1
|
||||||
|
MOVHU.P 2(p), x3
|
||||||
|
AND $255, x3, x1
|
||||||
|
LSR $8, x3, x2
|
||||||
|
|
||||||
|
MUL prime5, x1
|
||||||
|
ROR $64-11, h
|
||||||
|
EOR x1 @> 64-11, h, h
|
||||||
|
MUL prime1, h
|
||||||
|
|
||||||
|
MUL prime5, x2
|
||||||
|
ROR $64-11, h
|
||||||
|
EOR x2 @> 64-11, h, h
|
||||||
|
MUL prime1, h
|
||||||
|
|
||||||
|
try1:
|
||||||
|
TBZ $0, n, finalize
|
||||||
|
MOVBU (p), x4
|
||||||
|
|
||||||
|
MUL prime5, x4
|
||||||
|
ROR $64-11, h
|
||||||
|
EOR x4 @> 64-11, h, h
|
||||||
|
MUL prime1, h
|
||||||
|
|
||||||
|
finalize:
|
||||||
|
EOR h >> 33, h
|
||||||
|
MUL prime2, h
|
||||||
|
EOR h >> 29, h
|
||||||
|
MUL prime3, h
|
||||||
|
EOR h >> 32, h
|
||||||
|
|
||||||
|
MOVD h, ret+24(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func writeBlocks(d *Digest, b []byte) int
|
||||||
|
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||||
|
LDP ·primes+0(SB), (prime1, prime2)
|
||||||
|
|
||||||
|
// Load state. Assume v[1-4] are stored contiguously.
|
||||||
|
MOVD d+0(FP), digest
|
||||||
|
LDP 0(digest), (v1, v2)
|
||||||
|
LDP 16(digest), (v3, v4)
|
||||||
|
|
||||||
|
LDP b_base+8(FP), (p, n)
|
||||||
|
|
||||||
|
blockLoop()
|
||||||
|
|
||||||
|
// Store updated state.
|
||||||
|
STP (v1, v2), 0(digest)
|
||||||
|
STP (v3, v4), 16(digest)
|
||||||
|
|
||||||
|
BIC $31, n
|
||||||
|
MOVD n, ret+32(FP)
|
||||||
|
RET
|
15
vendor/github.com/cespare/xxhash/v2/xxhash_asm.go
generated
vendored
Normal file
15
vendor/github.com/cespare/xxhash/v2/xxhash_asm.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//go:build (amd64 || arm64) && !appengine && gc && !purego
|
||||||
|
// +build amd64 arm64
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !purego
|
||||||
|
|
||||||
|
package xxhash
|
||||||
|
|
||||||
|
// Sum64 computes the 64-bit xxHash digest of b.
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func Sum64(b []byte) uint64
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func writeBlocks(d *Digest, b []byte) int
|
22
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
22
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
|
@ -1,4 +1,5 @@
|
||||||
// +build !amd64 appengine !gc purego
|
//go:build (!amd64 && !arm64) || appengine || !gc || purego
|
||||||
|
// +build !amd64,!arm64 appengine !gc purego
|
||||||
|
|
||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
|
@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 {
|
||||||
var h uint64
|
var h uint64
|
||||||
|
|
||||||
if n >= 32 {
|
if n >= 32 {
|
||||||
v1 := prime1v + prime2
|
v1 := primes[0] + prime2
|
||||||
v2 := prime2
|
v2 := prime2
|
||||||
v3 := uint64(0)
|
v3 := uint64(0)
|
||||||
v4 := -prime1v
|
v4 := -primes[0]
|
||||||
for len(b) >= 32 {
|
for len(b) >= 32 {
|
||||||
v1 = round(v1, u64(b[0:8:len(b)]))
|
v1 = round(v1, u64(b[0:8:len(b)]))
|
||||||
v2 = round(v2, u64(b[8:16:len(b)]))
|
v2 = round(v2, u64(b[8:16:len(b)]))
|
||||||
|
@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 {
|
||||||
|
|
||||||
h += uint64(n)
|
h += uint64(n)
|
||||||
|
|
||||||
i, end := 0, len(b)
|
for ; len(b) >= 8; b = b[8:] {
|
||||||
for ; i+8 <= end; i += 8 {
|
k1 := round(0, u64(b[:8]))
|
||||||
k1 := round(0, u64(b[i:i+8:len(b)]))
|
|
||||||
h ^= k1
|
h ^= k1
|
||||||
h = rol27(h)*prime1 + prime4
|
h = rol27(h)*prime1 + prime4
|
||||||
}
|
}
|
||||||
if i+4 <= end {
|
if len(b) >= 4 {
|
||||||
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
|
h ^= uint64(u32(b[:4])) * prime1
|
||||||
h = rol23(h)*prime2 + prime3
|
h = rol23(h)*prime2 + prime3
|
||||||
i += 4
|
b = b[4:]
|
||||||
}
|
}
|
||||||
for ; i < end; i++ {
|
for ; len(b) > 0; b = b[1:] {
|
||||||
h ^= uint64(b[i]) * prime5
|
h ^= uint64(b[0]) * prime5
|
||||||
h = rol11(h) * prime1
|
h = rol11(h) * prime1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
1
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build appengine
|
||||||
// +build appengine
|
// +build appengine
|
||||||
|
|
||||||
// This file contains the safe implementations of otherwise unsafe-using code.
|
// This file contains the safe implementations of otherwise unsafe-using code.
|
||||||
|
|
3
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
3
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !appengine
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
|
||||||
// This file encapsulates usage of unsafe.
|
// This file encapsulates usage of unsafe.
|
||||||
|
@ -11,7 +12,7 @@ import (
|
||||||
|
|
||||||
// In the future it's possible that compiler optimizations will make these
|
// In the future it's possible that compiler optimizations will make these
|
||||||
// XxxString functions unnecessary by realizing that calls such as
|
// XxxString functions unnecessary by realizing that calls such as
|
||||||
// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205.
|
// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205.
|
||||||
// If that happens, even if we keep these functions they can be replaced with
|
// If that happens, even if we keep these functions they can be replaced with
|
||||||
// the trivial safe code.
|
// the trivial safe code.
|
||||||
|
|
||||||
|
|
41
vendor/github.com/emirpasic/gods/LICENSE
generated
vendored
Normal file
41
vendor/github.com/emirpasic/gods/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
Copyright (c) 2015, Emir Pasic
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
AVL Tree:
|
||||||
|
|
||||||
|
Copyright (c) 2017 Benjamin Scher Purcell <benjapurcell@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
36
vendor/github.com/emirpasic/gods/containers/containers.go
generated
vendored
Normal file
36
vendor/github.com/emirpasic/gods/containers/containers.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package containers provides core interfaces and functions for data structures.
|
||||||
|
//
|
||||||
|
// Container is the base interface for all data structures to implement.
|
||||||
|
//
|
||||||
|
// Iterators provide stateful iterators.
|
||||||
|
//
|
||||||
|
// Enumerable provides Ruby inspired (each, select, map, find, any?, etc.) container functions.
|
||||||
|
//
|
||||||
|
// Serialization provides serializers (marshalers) and deserializers (unmarshalers).
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import "github.com/emirpasic/gods/utils"
|
||||||
|
|
||||||
|
// Container is base interface that all data structures implement.
|
||||||
|
type Container interface {
|
||||||
|
Empty() bool
|
||||||
|
Size() int
|
||||||
|
Clear()
|
||||||
|
Values() []interface{}
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSortedValues returns sorted container's elements with respect to the passed comparator.
|
||||||
|
// Does not affect the ordering of elements within the container.
|
||||||
|
func GetSortedValues(container Container, comparator utils.Comparator) []interface{} {
|
||||||
|
values := container.Values()
|
||||||
|
if len(values) < 2 {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
utils.Sort(values, comparator)
|
||||||
|
return values
|
||||||
|
}
|
57
vendor/github.com/emirpasic/gods/containers/enumerable.go
generated
vendored
Normal file
57
vendor/github.com/emirpasic/gods/containers/enumerable.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package containers
|
||||||
|
|
||||||
|
// EnumerableWithIndex provides functions for ordered containers whose values can be fetched by an index.
|
||||||
|
type EnumerableWithIndex interface {
|
||||||
|
// Each calls the given function once for each element, passing that element's index and value.
|
||||||
|
Each(func(index int, value interface{}))
|
||||||
|
|
||||||
|
// Map invokes the given function once for each element and returns a
|
||||||
|
// container containing the values returned by the given function.
|
||||||
|
// Map(func(index int, value interface{}) interface{}) Container
|
||||||
|
|
||||||
|
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||||
|
// Select(func(index int, value interface{}) bool) Container
|
||||||
|
|
||||||
|
// Any passes each element of the container to the given function and
|
||||||
|
// returns true if the function ever returns true for any element.
|
||||||
|
Any(func(index int, value interface{}) bool) bool
|
||||||
|
|
||||||
|
// All passes each element of the container to the given function and
|
||||||
|
// returns true if the function returns true for all elements.
|
||||||
|
All(func(index int, value interface{}) bool) bool
|
||||||
|
|
||||||
|
// Find passes each element of the container to the given function and returns
|
||||||
|
// the first (index,value) for which the function is true or -1,nil otherwise
|
||||||
|
// if no element matches the criteria.
|
||||||
|
Find(func(index int, value interface{}) bool) (int, interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumerableWithKey provides functions for ordered containers whose values whose elements are key/value pairs.
|
||||||
|
type EnumerableWithKey interface {
|
||||||
|
// Each calls the given function once for each element, passing that element's key and value.
|
||||||
|
Each(func(key interface{}, value interface{}))
|
||||||
|
|
||||||
|
// Map invokes the given function once for each element and returns a container
|
||||||
|
// containing the values returned by the given function as key/value pairs.
|
||||||
|
// Map(func(key interface{}, value interface{}) (interface{}, interface{})) Container
|
||||||
|
|
||||||
|
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||||
|
// Select(func(key interface{}, value interface{}) bool) Container
|
||||||
|
|
||||||
|
// Any passes each element of the container to the given function and
|
||||||
|
// returns true if the function ever returns true for any element.
|
||||||
|
Any(func(key interface{}, value interface{}) bool) bool
|
||||||
|
|
||||||
|
// All passes each element of the container to the given function and
|
||||||
|
// returns true if the function returns true for all elements.
|
||||||
|
All(func(key interface{}, value interface{}) bool) bool
|
||||||
|
|
||||||
|
// Find passes each element of the container to the given function and returns
|
||||||
|
// the first (key,value) for which the function is true or nil,nil otherwise if no element
|
||||||
|
// matches the criteria.
|
||||||
|
Find(func(key interface{}, value interface{}) bool) (interface{}, interface{})
|
||||||
|
}
|
133
vendor/github.com/emirpasic/gods/containers/iterator.go
generated
vendored
Normal file
133
vendor/github.com/emirpasic/gods/containers/iterator.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package containers
|
||||||
|
|
||||||
|
// IteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
|
||||||
|
type IteratorWithIndex interface {
|
||||||
|
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||||
|
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||||
|
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
Next() bool
|
||||||
|
|
||||||
|
// Value returns the current element's value.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
Value() interface{}
|
||||||
|
|
||||||
|
// Index returns the current element's index.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
Index() int
|
||||||
|
|
||||||
|
// Begin resets the iterator to its initial state (one-before-first)
|
||||||
|
// Call Next() to fetch the first element if any.
|
||||||
|
Begin()
|
||||||
|
|
||||||
|
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||||
|
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
First() bool
|
||||||
|
|
||||||
|
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
NextTo(func(index int, value interface{}) bool) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
|
||||||
|
type IteratorWithKey interface {
|
||||||
|
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||||
|
// If Next() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||||
|
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
Next() bool
|
||||||
|
|
||||||
|
// Value returns the current element's value.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
Value() interface{}
|
||||||
|
|
||||||
|
// Key returns the current element's key.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
Key() interface{}
|
||||||
|
|
||||||
|
// Begin resets the iterator to its initial state (one-before-first)
|
||||||
|
// Call Next() to fetch the first element if any.
|
||||||
|
Begin()
|
||||||
|
|
||||||
|
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||||
|
// If First() returns true, then first element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
First() bool
|
||||||
|
|
||||||
|
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
NextTo(func(key interface{}, value interface{}) bool) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseIteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
|
||||||
|
//
|
||||||
|
// Essentially it is the same as IteratorWithIndex, but provides additional:
|
||||||
|
//
|
||||||
|
// Prev() function to enable traversal in reverse
|
||||||
|
//
|
||||||
|
// Last() function to move the iterator to the last element.
|
||||||
|
//
|
||||||
|
// End() function to move the iterator past the last element (one-past-the-end).
|
||||||
|
type ReverseIteratorWithIndex interface {
|
||||||
|
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||||
|
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
Prev() bool
|
||||||
|
|
||||||
|
// End moves the iterator past the last element (one-past-the-end).
|
||||||
|
// Call Prev() to fetch the last element if any.
|
||||||
|
End()
|
||||||
|
|
||||||
|
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||||
|
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
Last() bool
|
||||||
|
|
||||||
|
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
PrevTo(func(index int, value interface{}) bool) bool
|
||||||
|
|
||||||
|
IteratorWithIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseIteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
|
||||||
|
//
|
||||||
|
// Essentially it is the same as IteratorWithKey, but provides additional:
|
||||||
|
//
|
||||||
|
// Prev() function to enable traversal in reverse
|
||||||
|
//
|
||||||
|
// Last() function to move the iterator to the last element.
|
||||||
|
type ReverseIteratorWithKey interface {
|
||||||
|
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||||
|
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
Prev() bool
|
||||||
|
|
||||||
|
// End moves the iterator past the last element (one-past-the-end).
|
||||||
|
// Call Prev() to fetch the last element if any.
|
||||||
|
End()
|
||||||
|
|
||||||
|
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||||
|
// If Last() returns true, then last element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
Last() bool
|
||||||
|
|
||||||
|
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
PrevTo(func(key interface{}, value interface{}) bool) bool
|
||||||
|
|
||||||
|
IteratorWithKey
|
||||||
|
}
|
21
vendor/github.com/emirpasic/gods/containers/serialization.go
generated
vendored
Normal file
21
vendor/github.com/emirpasic/gods/containers/serialization.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package containers
|
||||||
|
|
||||||
|
// JSONSerializer provides JSON serialization
|
||||||
|
type JSONSerializer interface {
|
||||||
|
// ToJSON outputs the JSON representation of containers's elements.
|
||||||
|
ToJSON() ([]byte, error)
|
||||||
|
// MarshalJSON @implements json.Marshaler
|
||||||
|
MarshalJSON() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONDeserializer provides JSON deserialization
|
||||||
|
type JSONDeserializer interface {
|
||||||
|
// FromJSON populates containers's elements from the input JSON representation.
|
||||||
|
FromJSON([]byte) error
|
||||||
|
// UnmarshalJSON @implements json.Unmarshaler
|
||||||
|
UnmarshalJSON([]byte) error
|
||||||
|
}
|
344
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/doublylinkedlist.go
generated
vendored
Normal file
344
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/doublylinkedlist.go
generated
vendored
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package doublylinkedlist implements the doubly-linked list.
|
||||||
|
//
|
||||||
|
// Structure is not thread safe.
|
||||||
|
//
|
||||||
|
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
|
||||||
|
package doublylinkedlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/emirpasic/gods/lists"
|
||||||
|
"github.com/emirpasic/gods/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert List implementation
|
||||||
|
var _ lists.List = (*List)(nil)
|
||||||
|
|
||||||
|
// List holds the elements, where each element points to the next and previous element
|
||||||
|
type List struct {
|
||||||
|
first *element
|
||||||
|
last *element
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
type element struct {
|
||||||
|
value interface{}
|
||||||
|
prev *element
|
||||||
|
next *element
|
||||||
|
}
|
||||||
|
|
||||||
|
// New instantiates a new list and adds the passed values, if any, to the list
|
||||||
|
func New(values ...interface{}) *List {
|
||||||
|
list := &List{}
|
||||||
|
if len(values) > 0 {
|
||||||
|
list.Add(values...)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add appends a value (one or more) at the end of the list (same as Append())
|
||||||
|
func (list *List) Add(values ...interface{}) {
|
||||||
|
for _, value := range values {
|
||||||
|
newElement := &element{value: value, prev: list.last}
|
||||||
|
if list.size == 0 {
|
||||||
|
list.first = newElement
|
||||||
|
list.last = newElement
|
||||||
|
} else {
|
||||||
|
list.last.next = newElement
|
||||||
|
list.last = newElement
|
||||||
|
}
|
||||||
|
list.size++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends a value (one or more) at the end of the list (same as Add())
|
||||||
|
func (list *List) Append(values ...interface{}) {
|
||||||
|
list.Add(values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend prepends a values (or more)
|
||||||
|
func (list *List) Prepend(values ...interface{}) {
|
||||||
|
// in reverse to keep passed order i.e. ["c","d"] -> Prepend(["a","b"]) -> ["a","b","c",d"]
|
||||||
|
for v := len(values) - 1; v >= 0; v-- {
|
||||||
|
newElement := &element{value: values[v], next: list.first}
|
||||||
|
if list.size == 0 {
|
||||||
|
list.first = newElement
|
||||||
|
list.last = newElement
|
||||||
|
} else {
|
||||||
|
list.first.prev = newElement
|
||||||
|
list.first = newElement
|
||||||
|
}
|
||||||
|
list.size++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the element at index.
|
||||||
|
// Second return parameter is true if index is within bounds of the array and array is not empty, otherwise false.
|
||||||
|
func (list *List) Get(index int) (interface{}, bool) {
|
||||||
|
|
||||||
|
if !list.withinRange(index) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine traveral direction, last to first or first to last
|
||||||
|
if list.size-index < index {
|
||||||
|
element := list.last
|
||||||
|
for e := list.size - 1; e != index; e, element = e-1, element.prev {
|
||||||
|
}
|
||||||
|
return element.value, true
|
||||||
|
}
|
||||||
|
element := list.first
|
||||||
|
for e := 0; e != index; e, element = e+1, element.next {
|
||||||
|
}
|
||||||
|
return element.value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the element at the given index from the list.
|
||||||
|
func (list *List) Remove(index int) {
|
||||||
|
|
||||||
|
if !list.withinRange(index) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.size == 1 {
|
||||||
|
list.Clear()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var element *element
|
||||||
|
// determine traversal direction, last to first or first to last
|
||||||
|
if list.size-index < index {
|
||||||
|
element = list.last
|
||||||
|
for e := list.size - 1; e != index; e, element = e-1, element.prev {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
element = list.first
|
||||||
|
for e := 0; e != index; e, element = e+1, element.next {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if element == list.first {
|
||||||
|
list.first = element.next
|
||||||
|
}
|
||||||
|
if element == list.last {
|
||||||
|
list.last = element.prev
|
||||||
|
}
|
||||||
|
if element.prev != nil {
|
||||||
|
element.prev.next = element.next
|
||||||
|
}
|
||||||
|
if element.next != nil {
|
||||||
|
element.next.prev = element.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
element = nil
|
||||||
|
|
||||||
|
list.size--
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains check if values (one or more) are present in the set.
|
||||||
|
// All values have to be present in the set for the method to return true.
|
||||||
|
// Performance time complexity of n^2.
|
||||||
|
// Returns true if no arguments are passed at all, i.e. set is always super-set of empty set.
|
||||||
|
func (list *List) Contains(values ...interface{}) bool {
|
||||||
|
|
||||||
|
if len(values) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if list.size == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
found := false
|
||||||
|
for element := list.first; element != nil; element = element.next {
|
||||||
|
if element.value == value {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns all elements in the list.
|
||||||
|
func (list *List) Values() []interface{} {
|
||||||
|
values := make([]interface{}, list.size, list.size)
|
||||||
|
for e, element := 0, list.first; element != nil; e, element = e+1, element.next {
|
||||||
|
values[e] = element.value
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
//IndexOf returns index of provided element
|
||||||
|
func (list *List) IndexOf(value interface{}) int {
|
||||||
|
if list.size == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
for index, element := range list.Values() {
|
||||||
|
if element == value {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns true if list does not contain any elements.
|
||||||
|
func (list *List) Empty() bool {
|
||||||
|
return list.size == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns number of elements within the list.
|
||||||
|
func (list *List) Size() int {
|
||||||
|
return list.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes all elements from the list.
|
||||||
|
func (list *List) Clear() {
|
||||||
|
list.size = 0
|
||||||
|
list.first = nil
|
||||||
|
list.last = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts values (in-place) using.
|
||||||
|
func (list *List) Sort(comparator utils.Comparator) {
|
||||||
|
|
||||||
|
if list.size < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values := list.Values()
|
||||||
|
utils.Sort(values, comparator)
|
||||||
|
|
||||||
|
list.Clear()
|
||||||
|
|
||||||
|
list.Add(values...)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps values of two elements at the given indices.
|
||||||
|
func (list *List) Swap(i, j int) {
|
||||||
|
if list.withinRange(i) && list.withinRange(j) && i != j {
|
||||||
|
var element1, element2 *element
|
||||||
|
for e, currentElement := 0, list.first; element1 == nil || element2 == nil; e, currentElement = e+1, currentElement.next {
|
||||||
|
switch e {
|
||||||
|
case i:
|
||||||
|
element1 = currentElement
|
||||||
|
case j:
|
||||||
|
element2 = currentElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element1.value, element2.value = element2.value, element1.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts values at specified index position shifting the value at that position (if any) and any subsequent elements to the right.
|
||||||
|
// Does not do anything if position is negative or bigger than list's size
|
||||||
|
// Note: position equal to list's size is valid, i.e. append.
|
||||||
|
func (list *List) Insert(index int, values ...interface{}) {
|
||||||
|
|
||||||
|
if !list.withinRange(index) {
|
||||||
|
// Append
|
||||||
|
if index == list.size {
|
||||||
|
list.Add(values...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list.size += len(values)
|
||||||
|
|
||||||
|
var beforeElement *element
|
||||||
|
var foundElement *element
|
||||||
|
// determine traversal direction, last to first or first to last
|
||||||
|
if list.size-index < index {
|
||||||
|
foundElement = list.last
|
||||||
|
for e := list.size - 1; e != index; e, foundElement = e-1, foundElement.prev {
|
||||||
|
beforeElement = foundElement.prev
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foundElement = list.first
|
||||||
|
for e := 0; e != index; e, foundElement = e+1, foundElement.next {
|
||||||
|
beforeElement = foundElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundElement == list.first {
|
||||||
|
oldNextElement := list.first
|
||||||
|
for i, value := range values {
|
||||||
|
newElement := &element{value: value}
|
||||||
|
if i == 0 {
|
||||||
|
list.first = newElement
|
||||||
|
} else {
|
||||||
|
newElement.prev = beforeElement
|
||||||
|
beforeElement.next = newElement
|
||||||
|
}
|
||||||
|
beforeElement = newElement
|
||||||
|
}
|
||||||
|
oldNextElement.prev = beforeElement
|
||||||
|
beforeElement.next = oldNextElement
|
||||||
|
} else {
|
||||||
|
oldNextElement := beforeElement.next
|
||||||
|
for _, value := range values {
|
||||||
|
newElement := &element{value: value}
|
||||||
|
newElement.prev = beforeElement
|
||||||
|
beforeElement.next = newElement
|
||||||
|
beforeElement = newElement
|
||||||
|
}
|
||||||
|
oldNextElement.prev = beforeElement
|
||||||
|
beforeElement.next = oldNextElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set value at specified index position
|
||||||
|
// Does not do anything if position is negative or bigger than list's size
|
||||||
|
// Note: position equal to list's size is valid, i.e. append.
|
||||||
|
func (list *List) Set(index int, value interface{}) {
|
||||||
|
|
||||||
|
if !list.withinRange(index) {
|
||||||
|
// Append
|
||||||
|
if index == list.size {
|
||||||
|
list.Add(value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundElement *element
|
||||||
|
// determine traversal direction, last to first or first to last
|
||||||
|
if list.size-index < index {
|
||||||
|
foundElement = list.last
|
||||||
|
for e := list.size - 1; e != index; {
|
||||||
|
fmt.Println("Set last", index, value, foundElement, foundElement.prev)
|
||||||
|
e, foundElement = e-1, foundElement.prev
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foundElement = list.first
|
||||||
|
for e := 0; e != index; {
|
||||||
|
e, foundElement = e+1, foundElement.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foundElement.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of container
|
||||||
|
func (list *List) String() string {
|
||||||
|
str := "DoublyLinkedList\n"
|
||||||
|
values := []string{}
|
||||||
|
for element := list.first; element != nil; element = element.next {
|
||||||
|
values = append(values, fmt.Sprintf("%v", element.value))
|
||||||
|
}
|
||||||
|
str += strings.Join(values, ", ")
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the index is within bounds of the list
|
||||||
|
func (list *List) withinRange(index int) bool {
|
||||||
|
return index >= 0 && index < list.size
|
||||||
|
}
|
78
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/enumerable.go
generated
vendored
Normal file
78
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/enumerable.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package doublylinkedlist
|
||||||
|
|
||||||
|
import "github.com/emirpasic/gods/containers"
|
||||||
|
|
||||||
|
// Assert Enumerable implementation
|
||||||
|
var _ containers.EnumerableWithIndex = (*List)(nil)
|
||||||
|
|
||||||
|
// Each calls the given function once for each element, passing that element's index and value.
|
||||||
|
func (list *List) Each(f func(index int, value interface{})) {
|
||||||
|
iterator := list.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
f(iterator.Index(), iterator.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map invokes the given function once for each element and returns a
|
||||||
|
// container containing the values returned by the given function.
|
||||||
|
func (list *List) Map(f func(index int, value interface{}) interface{}) *List {
|
||||||
|
newList := &List{}
|
||||||
|
iterator := list.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
newList.Add(f(iterator.Index(), iterator.Value()))
|
||||||
|
}
|
||||||
|
return newList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||||
|
func (list *List) Select(f func(index int, value interface{}) bool) *List {
|
||||||
|
newList := &List{}
|
||||||
|
iterator := list.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if f(iterator.Index(), iterator.Value()) {
|
||||||
|
newList.Add(iterator.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any passes each element of the container to the given function and
|
||||||
|
// returns true if the function ever returns true for any element.
|
||||||
|
func (list *List) Any(f func(index int, value interface{}) bool) bool {
|
||||||
|
iterator := list.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if f(iterator.Index(), iterator.Value()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// All passes each element of the container to the given function and
|
||||||
|
// returns true if the function returns true for all elements.
|
||||||
|
func (list *List) All(f func(index int, value interface{}) bool) bool {
|
||||||
|
iterator := list.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if !f(iterator.Index(), iterator.Value()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find passes each element of the container to the given function and returns
|
||||||
|
// the first (index,value) for which the function is true or -1,nil otherwise
|
||||||
|
// if no element matches the criteria.
|
||||||
|
func (list *List) Find(f func(index int, value interface{}) bool) (index int, value interface{}) {
|
||||||
|
iterator := list.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if f(iterator.Index(), iterator.Value()) {
|
||||||
|
return iterator.Index(), iterator.Value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, nil
|
||||||
|
}
|
131
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/iterator.go
generated
vendored
Normal file
131
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/iterator.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package doublylinkedlist
|
||||||
|
|
||||||
|
import "github.com/emirpasic/gods/containers"
|
||||||
|
|
||||||
|
// Assert Iterator implementation
|
||||||
|
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
|
||||||
|
|
||||||
|
// Iterator holding the iterator's state
|
||||||
|
type Iterator struct {
|
||||||
|
list *List
|
||||||
|
index int
|
||||||
|
element *element
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator returns a stateful iterator whose values can be fetched by an index.
|
||||||
|
func (list *List) Iterator() Iterator {
|
||||||
|
return Iterator{list: list, index: -1, element: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||||
|
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||||
|
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) Next() bool {
|
||||||
|
if iterator.index < iterator.list.size {
|
||||||
|
iterator.index++
|
||||||
|
}
|
||||||
|
if !iterator.list.withinRange(iterator.index) {
|
||||||
|
iterator.element = nil
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if iterator.index != 0 {
|
||||||
|
iterator.element = iterator.element.next
|
||||||
|
} else {
|
||||||
|
iterator.element = iterator.list.first
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||||
|
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) Prev() bool {
|
||||||
|
if iterator.index >= 0 {
|
||||||
|
iterator.index--
|
||||||
|
}
|
||||||
|
if !iterator.list.withinRange(iterator.index) {
|
||||||
|
iterator.element = nil
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if iterator.index == iterator.list.size-1 {
|
||||||
|
iterator.element = iterator.list.last
|
||||||
|
} else {
|
||||||
|
iterator.element = iterator.element.prev
|
||||||
|
}
|
||||||
|
return iterator.list.withinRange(iterator.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the current element's value.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
func (iterator *Iterator) Value() interface{} {
|
||||||
|
return iterator.element.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index returns the current element's index.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
func (iterator *Iterator) Index() int {
|
||||||
|
return iterator.index
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin resets the iterator to its initial state (one-before-first)
|
||||||
|
// Call Next() to fetch the first element if any.
|
||||||
|
func (iterator *Iterator) Begin() {
|
||||||
|
iterator.index = -1
|
||||||
|
iterator.element = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// End moves the iterator past the last element (one-past-the-end).
|
||||||
|
// Call Prev() to fetch the last element if any.
|
||||||
|
func (iterator *Iterator) End() {
|
||||||
|
iterator.index = iterator.list.size
|
||||||
|
iterator.element = iterator.list.last
|
||||||
|
}
|
||||||
|
|
||||||
|
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||||
|
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) First() bool {
|
||||||
|
iterator.Begin()
|
||||||
|
return iterator.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||||
|
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) Last() bool {
|
||||||
|
iterator.End()
|
||||||
|
return iterator.Prev()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool {
|
||||||
|
for iterator.Next() {
|
||||||
|
index, value := iterator.Index(), iterator.Value()
|
||||||
|
if f(index, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool {
|
||||||
|
for iterator.Prev() {
|
||||||
|
index, value := iterator.Index(), iterator.Value()
|
||||||
|
if f(index, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
40
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/serialization.go
generated
vendored
Normal file
40
vendor/github.com/emirpasic/gods/lists/doublylinkedlist/serialization.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package doublylinkedlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/emirpasic/gods/containers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert Serialization implementation
|
||||||
|
var _ containers.JSONSerializer = (*List)(nil)
|
||||||
|
var _ containers.JSONDeserializer = (*List)(nil)
|
||||||
|
|
||||||
|
// ToJSON outputs the JSON representation of list's elements.
|
||||||
|
func (list *List) ToJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(list.Values())
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromJSON populates list's elements from the input JSON representation.
|
||||||
|
func (list *List) FromJSON(data []byte) error {
|
||||||
|
elements := []interface{}{}
|
||||||
|
err := json.Unmarshal(data, &elements)
|
||||||
|
if err == nil {
|
||||||
|
list.Clear()
|
||||||
|
list.Add(elements...)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON @implements json.Unmarshaler
|
||||||
|
func (list *List) UnmarshalJSON(bytes []byte) error {
|
||||||
|
return list.FromJSON(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON @implements json.Marshaler
|
||||||
|
func (list *List) MarshalJSON() ([]byte, error) {
|
||||||
|
return list.ToJSON()
|
||||||
|
}
|
34
vendor/github.com/emirpasic/gods/lists/lists.go
generated
vendored
Normal file
34
vendor/github.com/emirpasic/gods/lists/lists.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package lists provides an abstract List interface.
|
||||||
|
//
|
||||||
|
// In computer science, a list or sequence is an abstract data type that represents an ordered sequence of values, where the same value may occur more than once. An instance of a list is a computer representation of the mathematical concept of a finite sequence; the (potentially) infinite analog of a list is a stream. Lists are a basic example of containers, as they contain other values. If the same value occurs multiple times, each occurrence is considered a distinct item.
|
||||||
|
//
|
||||||
|
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
|
||||||
|
package lists
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emirpasic/gods/containers"
|
||||||
|
"github.com/emirpasic/gods/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List interface that all lists implement
|
||||||
|
type List interface {
|
||||||
|
Get(index int) (interface{}, bool)
|
||||||
|
Remove(index int)
|
||||||
|
Add(values ...interface{})
|
||||||
|
Contains(values ...interface{}) bool
|
||||||
|
Sort(comparator utils.Comparator)
|
||||||
|
Swap(index1, index2 int)
|
||||||
|
Insert(index int, values ...interface{})
|
||||||
|
Set(index int, value interface{})
|
||||||
|
|
||||||
|
containers.Container
|
||||||
|
// Empty() bool
|
||||||
|
// Size() int
|
||||||
|
// Clear()
|
||||||
|
// Values() []interface{}
|
||||||
|
// String() string
|
||||||
|
}
|
79
vendor/github.com/emirpasic/gods/maps/linkedhashmap/enumerable.go
generated
vendored
Normal file
79
vendor/github.com/emirpasic/gods/maps/linkedhashmap/enumerable.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package linkedhashmap
|
||||||
|
|
||||||
|
import "github.com/emirpasic/gods/containers"
|
||||||
|
|
||||||
|
// Assert Enumerable implementation
|
||||||
|
var _ containers.EnumerableWithKey = (*Map)(nil)
|
||||||
|
|
||||||
|
// Each calls the given function once for each element, passing that element's key and value.
|
||||||
|
func (m *Map) Each(f func(key interface{}, value interface{})) {
|
||||||
|
iterator := m.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
f(iterator.Key(), iterator.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map invokes the given function once for each element and returns a container
|
||||||
|
// containing the values returned by the given function as key/value pairs.
|
||||||
|
func (m *Map) Map(f func(key1 interface{}, value1 interface{}) (interface{}, interface{})) *Map {
|
||||||
|
newMap := New()
|
||||||
|
iterator := m.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
key2, value2 := f(iterator.Key(), iterator.Value())
|
||||||
|
newMap.Put(key2, value2)
|
||||||
|
}
|
||||||
|
return newMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||||
|
func (m *Map) Select(f func(key interface{}, value interface{}) bool) *Map {
|
||||||
|
newMap := New()
|
||||||
|
iterator := m.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if f(iterator.Key(), iterator.Value()) {
|
||||||
|
newMap.Put(iterator.Key(), iterator.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any passes each element of the container to the given function and
|
||||||
|
// returns true if the function ever returns true for any element.
|
||||||
|
func (m *Map) Any(f func(key interface{}, value interface{}) bool) bool {
|
||||||
|
iterator := m.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if f(iterator.Key(), iterator.Value()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// All passes each element of the container to the given function and
|
||||||
|
// returns true if the function returns true for all elements.
|
||||||
|
func (m *Map) All(f func(key interface{}, value interface{}) bool) bool {
|
||||||
|
iterator := m.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if !f(iterator.Key(), iterator.Value()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find passes each element of the container to the given function and returns
|
||||||
|
// the first (key,value) for which the function is true or nil,nil otherwise if no element
|
||||||
|
// matches the criteria.
|
||||||
|
func (m *Map) Find(f func(key interface{}, value interface{}) bool) (interface{}, interface{}) {
|
||||||
|
iterator := m.Iterator()
|
||||||
|
for iterator.Next() {
|
||||||
|
if f(iterator.Key(), iterator.Value()) {
|
||||||
|
return iterator.Key(), iterator.Value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
108
vendor/github.com/emirpasic/gods/maps/linkedhashmap/iterator.go
generated
vendored
Normal file
108
vendor/github.com/emirpasic/gods/maps/linkedhashmap/iterator.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package linkedhashmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emirpasic/gods/containers"
|
||||||
|
"github.com/emirpasic/gods/lists/doublylinkedlist"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert Iterator implementation
|
||||||
|
var _ containers.ReverseIteratorWithKey = (*Iterator)(nil)
|
||||||
|
|
||||||
|
// Iterator holding the iterator's state
|
||||||
|
type Iterator struct {
|
||||||
|
iterator doublylinkedlist.Iterator
|
||||||
|
table map[interface{}]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator returns a stateful iterator whose elements are key/value pairs.
|
||||||
|
func (m *Map) Iterator() Iterator {
|
||||||
|
return Iterator{
|
||||||
|
iterator: m.ordering.Iterator(),
|
||||||
|
table: m.table}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||||
|
// If Next() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||||
|
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) Next() bool {
|
||||||
|
return iterator.iterator.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||||
|
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) Prev() bool {
|
||||||
|
return iterator.iterator.Prev()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the current element's value.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
func (iterator *Iterator) Value() interface{} {
|
||||||
|
key := iterator.iterator.Value()
|
||||||
|
return iterator.table[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the current element's key.
|
||||||
|
// Does not modify the state of the iterator.
|
||||||
|
func (iterator *Iterator) Key() interface{} {
|
||||||
|
return iterator.iterator.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin resets the iterator to its initial state (one-before-first)
|
||||||
|
// Call Next() to fetch the first element if any.
|
||||||
|
func (iterator *Iterator) Begin() {
|
||||||
|
iterator.iterator.Begin()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End moves the iterator past the last element (one-past-the-end).
|
||||||
|
// Call Prev() to fetch the last element if any.
|
||||||
|
func (iterator *Iterator) End() {
|
||||||
|
iterator.iterator.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||||
|
// If First() returns true, then first element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator
|
||||||
|
func (iterator *Iterator) First() bool {
|
||||||
|
return iterator.iterator.First()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||||
|
// If Last() returns true, then last element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) Last() bool {
|
||||||
|
return iterator.iterator.Last()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool {
|
||||||
|
for iterator.Next() {
|
||||||
|
key, value := iterator.Key(), iterator.Value()
|
||||||
|
if f(key, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||||
|
// passed function, and returns true if there was a next element in the container.
|
||||||
|
// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||||
|
// Modifies the state of the iterator.
|
||||||
|
func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool {
|
||||||
|
for iterator.Prev() {
|
||||||
|
key, value := iterator.Key(), iterator.Value()
|
||||||
|
if f(key, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
108
vendor/github.com/emirpasic/gods/maps/linkedhashmap/linkedhashmap.go
generated
vendored
Normal file
108
vendor/github.com/emirpasic/gods/maps/linkedhashmap/linkedhashmap.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package linkedhashmap is a map that preserves insertion-order.
|
||||||
|
//
|
||||||
|
// It is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||||
|
//
|
||||||
|
// Structure is not thread safe.
|
||||||
|
//
|
||||||
|
// Reference: http://en.wikipedia.org/wiki/Associative_array
|
||||||
|
package linkedhashmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/emirpasic/gods/lists/doublylinkedlist"
|
||||||
|
"github.com/emirpasic/gods/maps"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert Map implementation
|
||||||
|
var _ maps.Map = (*Map)(nil)
|
||||||
|
|
||||||
|
// Map holds the elements in a regular hash table, and uses doubly-linked list to store key ordering.
|
||||||
|
type Map struct {
|
||||||
|
table map[interface{}]interface{}
|
||||||
|
ordering *doublylinkedlist.List
|
||||||
|
}
|
||||||
|
|
||||||
|
// New instantiates a linked-hash-map.
|
||||||
|
func New() *Map {
|
||||||
|
return &Map{
|
||||||
|
table: make(map[interface{}]interface{}),
|
||||||
|
ordering: doublylinkedlist.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put inserts key-value pair into the map.
|
||||||
|
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||||
|
func (m *Map) Put(key interface{}, value interface{}) {
|
||||||
|
if _, contains := m.table[key]; !contains {
|
||||||
|
m.ordering.Append(key)
|
||||||
|
}
|
||||||
|
m.table[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get searches the element in the map by key and returns its value or nil if key is not found in tree.
|
||||||
|
// Second return parameter is true if key was found, otherwise false.
|
||||||
|
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||||
|
func (m *Map) Get(key interface{}) (value interface{}, found bool) {
|
||||||
|
value = m.table[key]
|
||||||
|
found = value != nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the element from the map by key.
|
||||||
|
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||||
|
func (m *Map) Remove(key interface{}) {
|
||||||
|
if _, contains := m.table[key]; contains {
|
||||||
|
delete(m.table, key)
|
||||||
|
index := m.ordering.IndexOf(key)
|
||||||
|
m.ordering.Remove(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns true if map does not contain any elements
|
||||||
|
func (m *Map) Empty() bool {
|
||||||
|
return m.Size() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns number of elements in the map.
|
||||||
|
func (m *Map) Size() int {
|
||||||
|
return m.ordering.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns all keys in-order
|
||||||
|
func (m *Map) Keys() []interface{} {
|
||||||
|
return m.ordering.Values()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns all values in-order based on the key.
|
||||||
|
func (m *Map) Values() []interface{} {
|
||||||
|
values := make([]interface{}, m.Size())
|
||||||
|
count := 0
|
||||||
|
it := m.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
values[count] = it.Value()
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes all elements from the map.
|
||||||
|
func (m *Map) Clear() {
|
||||||
|
m.table = make(map[interface{}]interface{})
|
||||||
|
m.ordering.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of container
|
||||||
|
func (m *Map) String() string {
|
||||||
|
str := "LinkedHashMap\nmap["
|
||||||
|
it := m.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
str += fmt.Sprintf("%v:%v ", it.Key(), it.Value())
|
||||||
|
}
|
||||||
|
return strings.TrimRight(str, " ") + "]"
|
||||||
|
|
||||||
|
}
|
112
vendor/github.com/emirpasic/gods/maps/linkedhashmap/serialization.go
generated
vendored
Normal file
112
vendor/github.com/emirpasic/gods/maps/linkedhashmap/serialization.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package linkedhashmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/emirpasic/gods/containers"
|
||||||
|
"github.com/emirpasic/gods/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert Serialization implementation
|
||||||
|
var _ containers.JSONSerializer = (*Map)(nil)
|
||||||
|
var _ containers.JSONDeserializer = (*Map)(nil)
|
||||||
|
|
||||||
|
// ToJSON outputs the JSON representation of map.
|
||||||
|
func (m *Map) ToJSON() ([]byte, error) {
|
||||||
|
var b []byte
|
||||||
|
buf := bytes.NewBuffer(b)
|
||||||
|
|
||||||
|
buf.WriteRune('{')
|
||||||
|
|
||||||
|
it := m.Iterator()
|
||||||
|
lastIndex := m.Size() - 1
|
||||||
|
index := 0
|
||||||
|
|
||||||
|
for it.Next() {
|
||||||
|
km, err := json.Marshal(it.Key())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf.Write(km)
|
||||||
|
|
||||||
|
buf.WriteRune(':')
|
||||||
|
|
||||||
|
vm, err := json.Marshal(it.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf.Write(vm)
|
||||||
|
|
||||||
|
if index != lastIndex {
|
||||||
|
buf.WriteRune(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune('}')
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromJSON populates map from the input JSON representation.
|
||||||
|
//func (m *Map) FromJSON(data []byte) error {
|
||||||
|
// elements := make(map[string]interface{})
|
||||||
|
// err := json.Unmarshal(data, &elements)
|
||||||
|
// if err == nil {
|
||||||
|
// m.Clear()
|
||||||
|
// for key, value := range elements {
|
||||||
|
// m.Put(key, value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
|
||||||
|
// FromJSON populates map from the input JSON representation.
|
||||||
|
func (m *Map) FromJSON(data []byte) error {
|
||||||
|
elements := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(data, &elements)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
index := make(map[string]int)
|
||||||
|
var keys []interface{}
|
||||||
|
for key := range elements {
|
||||||
|
keys = append(keys, key)
|
||||||
|
esc, _ := json.Marshal(key)
|
||||||
|
index[key] = bytes.Index(data, esc)
|
||||||
|
}
|
||||||
|
|
||||||
|
byIndex := func(a, b interface{}) int {
|
||||||
|
key1 := a.(string)
|
||||||
|
key2 := b.(string)
|
||||||
|
index1 := index[key1]
|
||||||
|
index2 := index[key2]
|
||||||
|
return index1 - index2
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Sort(keys, byIndex)
|
||||||
|
|
||||||
|
m.Clear()
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
m.Put(key, elements[key.(string)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON @implements json.Unmarshaler
|
||||||
|
func (m *Map) UnmarshalJSON(bytes []byte) error {
|
||||||
|
return m.FromJSON(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON @implements json.Marshaler
|
||||||
|
func (m *Map) MarshalJSON() ([]byte, error) {
|
||||||
|
return m.ToJSON()
|
||||||
|
}
|
40
vendor/github.com/emirpasic/gods/maps/maps.go
generated
vendored
Normal file
40
vendor/github.com/emirpasic/gods/maps/maps.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package maps provides an abstract Map interface.
|
||||||
|
//
|
||||||
|
// In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears just once in the collection.
|
||||||
|
//
|
||||||
|
// Operations associated with this data type allow:
|
||||||
|
// - the addition of a pair to the collection
|
||||||
|
// - the removal of a pair from the collection
|
||||||
|
// - the modification of an existing pair
|
||||||
|
// - the lookup of a value associated with a particular key
|
||||||
|
//
|
||||||
|
// Reference: https://en.wikipedia.org/wiki/Associative_array
|
||||||
|
package maps
|
||||||
|
|
||||||
|
import "github.com/emirpasic/gods/containers"
|
||||||
|
|
||||||
|
// Map interface that all maps implement
|
||||||
|
type Map interface {
|
||||||
|
Put(key interface{}, value interface{})
|
||||||
|
Get(key interface{}) (value interface{}, found bool)
|
||||||
|
Remove(key interface{})
|
||||||
|
Keys() []interface{}
|
||||||
|
|
||||||
|
containers.Container
|
||||||
|
// Empty() bool
|
||||||
|
// Size() int
|
||||||
|
// Clear()
|
||||||
|
// Values() []interface{}
|
||||||
|
// String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BidiMap interface that all bidirectional maps implement (extends the Map interface)
|
||||||
|
type BidiMap interface {
|
||||||
|
GetKey(value interface{}) (key interface{}, found bool)
|
||||||
|
|
||||||
|
Map
|
||||||
|
}
|
251
vendor/github.com/emirpasic/gods/utils/comparator.go
generated
vendored
Normal file
251
vendor/github.com/emirpasic/gods/utils/comparator.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Comparator will make type assertion (see IntComparator for example),
|
||||||
|
// which will panic if a or b are not of the asserted type.
|
||||||
|
//
|
||||||
|
// Should return a number:
|
||||||
|
// negative , if a < b
|
||||||
|
// zero , if a == b
|
||||||
|
// positive , if a > b
|
||||||
|
type Comparator func(a, b interface{}) int
|
||||||
|
|
||||||
|
// StringComparator provides a fast comparison on strings
|
||||||
|
func StringComparator(a, b interface{}) int {
|
||||||
|
s1 := a.(string)
|
||||||
|
s2 := b.(string)
|
||||||
|
min := len(s2)
|
||||||
|
if len(s1) < len(s2) {
|
||||||
|
min = len(s1)
|
||||||
|
}
|
||||||
|
diff := 0
|
||||||
|
for i := 0; i < min && diff == 0; i++ {
|
||||||
|
diff = int(s1[i]) - int(s2[i])
|
||||||
|
}
|
||||||
|
if diff == 0 {
|
||||||
|
diff = len(s1) - len(s2)
|
||||||
|
}
|
||||||
|
if diff < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if diff > 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntComparator provides a basic comparison on int
|
||||||
|
func IntComparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(int)
|
||||||
|
bAsserted := b.(int)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8Comparator provides a basic comparison on int8
|
||||||
|
func Int8Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(int8)
|
||||||
|
bAsserted := b.(int8)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int16Comparator provides a basic comparison on int16
|
||||||
|
func Int16Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(int16)
|
||||||
|
bAsserted := b.(int16)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32Comparator provides a basic comparison on int32
|
||||||
|
func Int32Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(int32)
|
||||||
|
bAsserted := b.(int32)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Comparator provides a basic comparison on int64
|
||||||
|
func Int64Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(int64)
|
||||||
|
bAsserted := b.(int64)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UIntComparator provides a basic comparison on uint
|
||||||
|
func UIntComparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(uint)
|
||||||
|
bAsserted := b.(uint)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UInt8Comparator provides a basic comparison on uint8
|
||||||
|
func UInt8Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(uint8)
|
||||||
|
bAsserted := b.(uint8)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UInt16Comparator provides a basic comparison on uint16
|
||||||
|
func UInt16Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(uint16)
|
||||||
|
bAsserted := b.(uint16)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UInt32Comparator provides a basic comparison on uint32
|
||||||
|
func UInt32Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(uint32)
|
||||||
|
bAsserted := b.(uint32)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UInt64Comparator provides a basic comparison on uint64
|
||||||
|
func UInt64Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(uint64)
|
||||||
|
bAsserted := b.(uint64)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32Comparator provides a basic comparison on float32
|
||||||
|
func Float32Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(float32)
|
||||||
|
bAsserted := b.(float32)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Comparator provides a basic comparison on float64
|
||||||
|
func Float64Comparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(float64)
|
||||||
|
bAsserted := b.(float64)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByteComparator provides a basic comparison on byte
|
||||||
|
func ByteComparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(byte)
|
||||||
|
bAsserted := b.(byte)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuneComparator provides a basic comparison on rune
|
||||||
|
func RuneComparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(rune)
|
||||||
|
bAsserted := b.(rune)
|
||||||
|
switch {
|
||||||
|
case aAsserted > bAsserted:
|
||||||
|
return 1
|
||||||
|
case aAsserted < bAsserted:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeComparator provides a basic comparison on time.Time
|
||||||
|
func TimeComparator(a, b interface{}) int {
|
||||||
|
aAsserted := a.(time.Time)
|
||||||
|
bAsserted := b.(time.Time)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case aAsserted.After(bAsserted):
|
||||||
|
return 1
|
||||||
|
case aAsserted.Before(bAsserted):
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
29
vendor/github.com/emirpasic/gods/utils/sort.go
generated
vendored
Normal file
29
vendor/github.com/emirpasic/gods/utils/sort.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
// Sort sorts values (in-place) with respect to the given comparator.
|
||||||
|
//
|
||||||
|
// Uses Go's sort (hybrid of quicksort for large and then insertion sort for smaller slices).
|
||||||
|
func Sort(values []interface{}, comparator Comparator) {
|
||||||
|
sort.Sort(sortable{values, comparator})
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortable struct {
|
||||||
|
values []interface{}
|
||||||
|
comparator Comparator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sortable) Len() int {
|
||||||
|
return len(s.values)
|
||||||
|
}
|
||||||
|
func (s sortable) Swap(i, j int) {
|
||||||
|
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||||
|
}
|
||||||
|
func (s sortable) Less(i, j int) bool {
|
||||||
|
return s.comparator(s.values[i], s.values[j]) < 0
|
||||||
|
}
|
47
vendor/github.com/emirpasic/gods/utils/utils.go
generated
vendored
Normal file
47
vendor/github.com/emirpasic/gods/utils/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package utils provides common utility functions.
|
||||||
|
//
|
||||||
|
// Provided functionalities:
|
||||||
|
// - sorting
|
||||||
|
// - comparators
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToString converts a value to string.
|
||||||
|
func ToString(value interface{}) string {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case string:
|
||||||
|
return value
|
||||||
|
case int8:
|
||||||
|
return strconv.FormatInt(int64(value), 10)
|
||||||
|
case int16:
|
||||||
|
return strconv.FormatInt(int64(value), 10)
|
||||||
|
case int32:
|
||||||
|
return strconv.FormatInt(int64(value), 10)
|
||||||
|
case int64:
|
||||||
|
return strconv.FormatInt(value, 10)
|
||||||
|
case uint8:
|
||||||
|
return strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint16:
|
||||||
|
return strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint32:
|
||||||
|
return strconv.FormatUint(uint64(value), 10)
|
||||||
|
case uint64:
|
||||||
|
return strconv.FormatUint(value, 10)
|
||||||
|
case float32:
|
||||||
|
return strconv.FormatFloat(float64(value), 'g', -1, 64)
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(value, 'g', -1, 64)
|
||||||
|
case bool:
|
||||||
|
return strconv.FormatBool(value)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%+v", value)
|
||||||
|
}
|
||||||
|
}
|
12
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
12
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*.go]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 4
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
|
@ -1 +0,0 @@
|
||||||
go.sum linguist-generated
|
|
6
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
6
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
|
@ -1,6 +0,0 @@
|
||||||
# Setup a Global .gitignore for OS and editor generated files:
|
|
||||||
# https://help.github.com/articles/ignoring-files
|
|
||||||
# git config --global core.excludesfile ~/.gitignore_global
|
|
||||||
|
|
||||||
.vagrant
|
|
||||||
*.sublime-project
|
|
2
vendor/github.com/fsnotify/fsnotify/.mailmap
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/.mailmap
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
Chris Howey <howeyc@gmail.com> <chris@howey.me>
|
|
||||||
Nathan Youngman <git@nathany.com> <4566+nathany@users.noreply.github.com>
|
|
62
vendor/github.com/fsnotify/fsnotify/AUTHORS
generated
vendored
62
vendor/github.com/fsnotify/fsnotify/AUTHORS
generated
vendored
|
@ -1,62 +0,0 @@
|
||||||
# Names should be added to this file as
|
|
||||||
# Name or Organization <email address>
|
|
||||||
# The email address is not required for organizations.
|
|
||||||
|
|
||||||
# You can update this list using the following command:
|
|
||||||
#
|
|
||||||
# $ (head -n10 AUTHORS && git shortlog -se | sed -E 's/^\s+[0-9]+\t//') | tee AUTHORS
|
|
||||||
|
|
||||||
# Please keep the list sorted.
|
|
||||||
|
|
||||||
Aaron L <aaron@bettercoder.net>
|
|
||||||
Adrien Bustany <adrien@bustany.org>
|
|
||||||
Alexey Kazakov <alkazako@redhat.com>
|
|
||||||
Amit Krishnan <amit.krishnan@oracle.com>
|
|
||||||
Anmol Sethi <me@anmol.io>
|
|
||||||
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
|
|
||||||
Brian Goff <cpuguy83@gmail.com>
|
|
||||||
Bruno Bigras <bigras.bruno@gmail.com>
|
|
||||||
Caleb Spare <cespare@gmail.com>
|
|
||||||
Case Nelson <case@teammating.com>
|
|
||||||
Chris Howey <howeyc@gmail.com>
|
|
||||||
Christoffer Buchholz <christoffer.buchholz@gmail.com>
|
|
||||||
Daniel Wagner-Hall <dawagner@gmail.com>
|
|
||||||
Dave Cheney <dave@cheney.net>
|
|
||||||
Eric Lin <linxiulei@gmail.com>
|
|
||||||
Evan Phoenix <evan@fallingsnow.net>
|
|
||||||
Francisco Souza <f@souza.cc>
|
|
||||||
Gautam Dey <gautam.dey77@gmail.com>
|
|
||||||
Hari haran <hariharan.uno@gmail.com>
|
|
||||||
Ichinose Shogo <shogo82148@gmail.com>
|
|
||||||
Johannes Ebke <johannes@ebke.org>
|
|
||||||
John C Barstow <jbowtie@amathaine.com>
|
|
||||||
Kelvin Fo <vmirage@gmail.com>
|
|
||||||
Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
|
|
||||||
Matt Layher <mdlayher@gmail.com>
|
|
||||||
Matthias Stone <matthias@bellstone.ca>
|
|
||||||
Nathan Youngman <git@nathany.com>
|
|
||||||
Nickolai Zeldovich <nickolai@csail.mit.edu>
|
|
||||||
Oliver Bristow <evilumbrella+github@gmail.com>
|
|
||||||
Patrick <patrick@dropbox.com>
|
|
||||||
Paul Hammond <paul@paulhammond.org>
|
|
||||||
Pawel Knap <pawelknap88@gmail.com>
|
|
||||||
Pieter Droogendijk <pieter@binky.org.uk>
|
|
||||||
Pratik Shinde <pratikshinde320@gmail.com>
|
|
||||||
Pursuit92 <JoshChase@techpursuit.net>
|
|
||||||
Riku Voipio <riku.voipio@linaro.org>
|
|
||||||
Rob Figueiredo <robfig@gmail.com>
|
|
||||||
Rodrigo Chiossi <rodrigochiossi@gmail.com>
|
|
||||||
Slawek Ligus <root@ooz.ie>
|
|
||||||
Soge Zhang <zhssoge@gmail.com>
|
|
||||||
Tiffany Jernigan <tiffany.jernigan@intel.com>
|
|
||||||
Tilak Sharma <tilaks@google.com>
|
|
||||||
Tobias Klauser <tobias.klauser@gmail.com>
|
|
||||||
Tom Payne <twpayne@gmail.com>
|
|
||||||
Travis Cline <travis.cline@gmail.com>
|
|
||||||
Tudor Golubenco <tudor.g@gmail.com>
|
|
||||||
Vahe Khachikyan <vahe@live.ca>
|
|
||||||
Yukang <moorekang@gmail.com>
|
|
||||||
bronze1man <bronze1man@gmail.com>
|
|
||||||
debrando <denis.brandolini@gmail.com>
|
|
||||||
henrikedwards <henrik.edwards@gmail.com>
|
|
||||||
铁哥 <guotie.9@gmail.com>
|
|
357
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
357
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
|
@ -1,357 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [1.5.4] - 2022-04-25
|
|
||||||
|
|
||||||
* Windows: add missing defer to `Watcher.WatchList` [#447](https://github.com/fsnotify/fsnotify/pull/447)
|
|
||||||
* go.mod: use latest x/sys [#444](https://github.com/fsnotify/fsnotify/pull/444)
|
|
||||||
* Fix compilation for OpenBSD [#443](https://github.com/fsnotify/fsnotify/pull/443)
|
|
||||||
|
|
||||||
## [1.5.3] - 2022-04-22
|
|
||||||
|
|
||||||
* This version is retracted. An incorrect branch is published accidentally [#445](https://github.com/fsnotify/fsnotify/issues/445)
|
|
||||||
|
|
||||||
## [1.5.2] - 2022-04-21
|
|
||||||
|
|
||||||
* Add a feature to return the directories and files that are being monitored [#374](https://github.com/fsnotify/fsnotify/pull/374)
|
|
||||||
* Fix potential crash on windows if `raw.FileNameLength` exceeds `syscall.MAX_PATH` [#361](https://github.com/fsnotify/fsnotify/pull/361)
|
|
||||||
* Allow build on unsupported GOOS [#424](https://github.com/fsnotify/fsnotify/pull/424)
|
|
||||||
* Don't set `poller.fd` twice in `newFdPoller` [#406](https://github.com/fsnotify/fsnotify/pull/406)
|
|
||||||
* fix go vet warnings: call to `(*T).Fatalf` from a non-test goroutine [#416](https://github.com/fsnotify/fsnotify/pull/416)
|
|
||||||
|
|
||||||
## [1.5.1] - 2021-08-24
|
|
||||||
|
|
||||||
* Revert Add AddRaw to not follow symlinks [#394](https://github.com/fsnotify/fsnotify/pull/394)
|
|
||||||
|
|
||||||
## [1.5.0] - 2021-08-20
|
|
||||||
|
|
||||||
* Go: Increase minimum required version to Go 1.12 [#381](https://github.com/fsnotify/fsnotify/pull/381)
|
|
||||||
* Feature: Add AddRaw method which does not follow symlinks when adding a watch [#289](https://github.com/fsnotify/fsnotify/pull/298)
|
|
||||||
* Windows: Follow symlinks by default like on all other systems [#289](https://github.com/fsnotify/fsnotify/pull/289)
|
|
||||||
* CI: Use GitHub Actions for CI and cover go 1.12-1.17
|
|
||||||
[#378](https://github.com/fsnotify/fsnotify/pull/378)
|
|
||||||
[#381](https://github.com/fsnotify/fsnotify/pull/381)
|
|
||||||
[#385](https://github.com/fsnotify/fsnotify/pull/385)
|
|
||||||
* Go 1.14+: Fix unsafe pointer conversion [#325](https://github.com/fsnotify/fsnotify/pull/325)
|
|
||||||
|
|
||||||
## [1.4.7] - 2018-01-09
|
|
||||||
|
|
||||||
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
|
|
||||||
* Tests: Fix missing verb on format string (thanks @rchiossi)
|
|
||||||
* Linux: Fix deadlock in Remove (thanks @aarondl)
|
|
||||||
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
|
|
||||||
* Docs: Moved FAQ into the README (thanks @vahe)
|
|
||||||
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
|
|
||||||
* Docs: replace references to OS X with macOS
|
|
||||||
|
|
||||||
## [1.4.2] - 2016-10-10
|
|
||||||
|
|
||||||
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
|
|
||||||
|
|
||||||
## [1.4.1] - 2016-10-04
|
|
||||||
|
|
||||||
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
|
|
||||||
|
|
||||||
## [1.4.0] - 2016-10-01
|
|
||||||
|
|
||||||
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
|
|
||||||
|
|
||||||
## [1.3.1] - 2016-06-28
|
|
||||||
|
|
||||||
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
|
|
||||||
|
|
||||||
## [1.3.0] - 2016-04-19
|
|
||||||
|
|
||||||
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
|
|
||||||
|
|
||||||
## [1.2.10] - 2016-03-02
|
|
||||||
|
|
||||||
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
|
|
||||||
|
|
||||||
## [1.2.9] - 2016-01-13
|
|
||||||
|
|
||||||
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
|
|
||||||
|
|
||||||
## [1.2.8] - 2015-12-17
|
|
||||||
|
|
||||||
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
|
|
||||||
* inotify: fix race in test
|
|
||||||
* enable race detection for continuous integration (Linux, Mac, Windows)
|
|
||||||
|
|
||||||
## [1.2.5] - 2015-10-17
|
|
||||||
|
|
||||||
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
|
|
||||||
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
|
|
||||||
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
|
|
||||||
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
|
|
||||||
|
|
||||||
## [1.2.1] - 2015-10-14
|
|
||||||
|
|
||||||
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
|
|
||||||
|
|
||||||
## [1.2.0] - 2015-02-08
|
|
||||||
|
|
||||||
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
|
|
||||||
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
|
|
||||||
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
|
|
||||||
|
|
||||||
## [1.1.1] - 2015-02-05
|
|
||||||
|
|
||||||
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
|
|
||||||
|
|
||||||
## [1.1.0] - 2014-12-12
|
|
||||||
|
|
||||||
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
|
|
||||||
* add low-level functions
|
|
||||||
* only need to store flags on directories
|
|
||||||
* less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
|
|
||||||
* done can be an unbuffered channel
|
|
||||||
* remove calls to os.NewSyscallError
|
|
||||||
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
|
|
||||||
* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
|
|
||||||
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
|
||||||
|
|
||||||
## [1.0.4] - 2014-09-07
|
|
||||||
|
|
||||||
* kqueue: add dragonfly to the build tags.
|
|
||||||
* Rename source code files, rearrange code so exported APIs are at the top.
|
|
||||||
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
|
|
||||||
|
|
||||||
## [1.0.3] - 2014-08-19
|
|
||||||
|
|
||||||
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
|
|
||||||
|
|
||||||
## [1.0.2] - 2014-08-17
|
|
||||||
|
|
||||||
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
|
||||||
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
|
|
||||||
|
|
||||||
## [1.0.0] - 2014-08-15
|
|
||||||
|
|
||||||
* [API] Remove AddWatch on Windows, use Add.
|
|
||||||
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
|
|
||||||
* Minor updates based on feedback from golint.
|
|
||||||
|
|
||||||
## dev / 2014-07-09
|
|
||||||
|
|
||||||
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
|
|
||||||
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
|
|
||||||
|
|
||||||
## dev / 2014-07-04
|
|
||||||
|
|
||||||
* kqueue: fix incorrect mutex used in Close()
|
|
||||||
* Update example to demonstrate usage of Op.
|
|
||||||
|
|
||||||
## dev / 2014-06-28
|
|
||||||
|
|
||||||
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
|
|
||||||
* Fix for String() method on Event (thanks Alex Brainman)
|
|
||||||
* Don't build on Plan 9 or Solaris (thanks @4ad)
|
|
||||||
|
|
||||||
## dev / 2014-06-21
|
|
||||||
|
|
||||||
* Events channel of type Event rather than *Event.
|
|
||||||
* [internal] use syscall constants directly for inotify and kqueue.
|
|
||||||
* [internal] kqueue: rename events to kevents and fileEvent to event.
|
|
||||||
|
|
||||||
## dev / 2014-06-19
|
|
||||||
|
|
||||||
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
|
|
||||||
* [internal] remove cookie from Event struct (unused).
|
|
||||||
* [internal] Event struct has the same definition across every OS.
|
|
||||||
* [internal] remove internal watch and removeWatch methods.
|
|
||||||
|
|
||||||
## dev / 2014-06-12
|
|
||||||
|
|
||||||
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
|
|
||||||
* [API] Pluralized channel names: Events and Errors.
|
|
||||||
* [API] Renamed FileEvent struct to Event.
|
|
||||||
* [API] Op constants replace methods like IsCreate().
|
|
||||||
|
|
||||||
## dev / 2014-06-12
|
|
||||||
|
|
||||||
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
|
||||||
|
|
||||||
## dev / 2014-05-23
|
|
||||||
|
|
||||||
* [API] Remove current implementation of WatchFlags.
|
|
||||||
* current implementation doesn't take advantage of OS for efficiency
|
|
||||||
* provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
|
|
||||||
* no tests for the current implementation
|
|
||||||
* not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
|
|
||||||
|
|
||||||
## [0.9.3] - 2014-12-31
|
|
||||||
|
|
||||||
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
|
||||||
|
|
||||||
## [0.9.2] - 2014-08-17
|
|
||||||
|
|
||||||
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
|
||||||
|
|
||||||
## [0.9.1] - 2014-06-12
|
|
||||||
|
|
||||||
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
|
||||||
|
|
||||||
## [0.9.0] - 2014-01-17
|
|
||||||
|
|
||||||
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
|
|
||||||
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
|
|
||||||
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
|
|
||||||
|
|
||||||
## [0.8.12] - 2013-11-13
|
|
||||||
|
|
||||||
* [API] Remove FD_SET and friends from Linux adapter
|
|
||||||
|
|
||||||
## [0.8.11] - 2013-11-02
|
|
||||||
|
|
||||||
* [Doc] Add Changelog [#72][] (thanks @nathany)
|
|
||||||
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
|
|
||||||
|
|
||||||
## [0.8.10] - 2013-10-19
|
|
||||||
|
|
||||||
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
|
|
||||||
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
|
|
||||||
* [Doc] specify OS-specific limits in README (thanks @debrando)
|
|
||||||
|
|
||||||
## [0.8.9] - 2013-09-08
|
|
||||||
|
|
||||||
* [Doc] Contributing (thanks @nathany)
|
|
||||||
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
|
|
||||||
* [Doc] GoCI badge in README (Linux only) [#60][]
|
|
||||||
* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
|
|
||||||
|
|
||||||
## [0.8.8] - 2013-06-17
|
|
||||||
|
|
||||||
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
|
|
||||||
|
|
||||||
## [0.8.7] - 2013-06-03
|
|
||||||
|
|
||||||
* [API] Make syscall flags internal
|
|
||||||
* [Fix] inotify: ignore event changes
|
|
||||||
* [Fix] race in symlink test [#45][] (reported by @srid)
|
|
||||||
* [Fix] tests on Windows
|
|
||||||
* lower case error messages
|
|
||||||
|
|
||||||
## [0.8.6] - 2013-05-23
|
|
||||||
|
|
||||||
* kqueue: Use EVT_ONLY flag on Darwin
|
|
||||||
* [Doc] Update README with full example
|
|
||||||
|
|
||||||
## [0.8.5] - 2013-05-09
|
|
||||||
|
|
||||||
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
|
|
||||||
|
|
||||||
## [0.8.4] - 2013-04-07
|
|
||||||
|
|
||||||
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
|
|
||||||
|
|
||||||
## [0.8.3] - 2013-03-13
|
|
||||||
|
|
||||||
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
|
|
||||||
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
|
|
||||||
|
|
||||||
## [0.8.2] - 2013-02-07
|
|
||||||
|
|
||||||
* [Doc] add Authors
|
|
||||||
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
|
|
||||||
|
|
||||||
## [0.8.1] - 2013-01-09
|
|
||||||
|
|
||||||
* [Fix] Windows path separators
|
|
||||||
* [Doc] BSD License
|
|
||||||
|
|
||||||
## [0.8.0] - 2012-11-09
|
|
||||||
|
|
||||||
* kqueue: directory watching improvements (thanks @vmirage)
|
|
||||||
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
|
|
||||||
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
|
|
||||||
|
|
||||||
## [0.7.4] - 2012-10-09
|
|
||||||
|
|
||||||
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
|
|
||||||
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
|
|
||||||
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
|
|
||||||
* [Fix] kqueue: modify after recreation of file
|
|
||||||
|
|
||||||
## [0.7.3] - 2012-09-27
|
|
||||||
|
|
||||||
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
|
|
||||||
* [Fix] kqueue: no longer get duplicate CREATE events
|
|
||||||
|
|
||||||
## [0.7.2] - 2012-09-01
|
|
||||||
|
|
||||||
* kqueue: events for created directories
|
|
||||||
|
|
||||||
## [0.7.1] - 2012-07-14
|
|
||||||
|
|
||||||
* [Fix] for renaming files
|
|
||||||
|
|
||||||
## [0.7.0] - 2012-07-02
|
|
||||||
|
|
||||||
* [Feature] FSNotify flags
|
|
||||||
* [Fix] inotify: Added file name back to event path
|
|
||||||
|
|
||||||
## [0.6.0] - 2012-06-06
|
|
||||||
|
|
||||||
* kqueue: watch files after directory created (thanks @tmc)
|
|
||||||
|
|
||||||
## [0.5.1] - 2012-05-22
|
|
||||||
|
|
||||||
* [Fix] inotify: remove all watches before Close()
|
|
||||||
|
|
||||||
## [0.5.0] - 2012-05-03
|
|
||||||
|
|
||||||
* [API] kqueue: return errors during watch instead of sending over channel
|
|
||||||
* kqueue: match symlink behavior on Linux
|
|
||||||
* inotify: add `DELETE_SELF` (requested by @taralx)
|
|
||||||
* [Fix] kqueue: handle EINTR (reported by @robfig)
|
|
||||||
* [Doc] Godoc example [#1][] (thanks @davecheney)
|
|
||||||
|
|
||||||
## [0.4.0] - 2012-03-30
|
|
||||||
|
|
||||||
* Go 1 released: build with go tool
|
|
||||||
* [Feature] Windows support using winfsnotify
|
|
||||||
* Windows does not have attribute change notifications
|
|
||||||
* Roll attribute notifications into IsModify
|
|
||||||
|
|
||||||
## [0.3.0] - 2012-02-19
|
|
||||||
|
|
||||||
* kqueue: add files when watch directory
|
|
||||||
|
|
||||||
## [0.2.0] - 2011-12-30
|
|
||||||
|
|
||||||
* update to latest Go weekly code
|
|
||||||
|
|
||||||
## [0.1.0] - 2011-10-19
|
|
||||||
|
|
||||||
* kqueue: add watch on file creation to match inotify
|
|
||||||
* kqueue: create file event
|
|
||||||
* inotify: ignore `IN_IGNORED` events
|
|
||||||
* event String()
|
|
||||||
* linux: common FileEvent functions
|
|
||||||
* initial commit
|
|
||||||
|
|
||||||
[#79]: https://github.com/howeyc/fsnotify/pull/79
|
|
||||||
[#77]: https://github.com/howeyc/fsnotify/pull/77
|
|
||||||
[#72]: https://github.com/howeyc/fsnotify/issues/72
|
|
||||||
[#71]: https://github.com/howeyc/fsnotify/issues/71
|
|
||||||
[#70]: https://github.com/howeyc/fsnotify/issues/70
|
|
||||||
[#63]: https://github.com/howeyc/fsnotify/issues/63
|
|
||||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
|
||||||
[#60]: https://github.com/howeyc/fsnotify/issues/60
|
|
||||||
[#59]: https://github.com/howeyc/fsnotify/issues/59
|
|
||||||
[#49]: https://github.com/howeyc/fsnotify/issues/49
|
|
||||||
[#45]: https://github.com/howeyc/fsnotify/issues/45
|
|
||||||
[#40]: https://github.com/howeyc/fsnotify/issues/40
|
|
||||||
[#36]: https://github.com/howeyc/fsnotify/issues/36
|
|
||||||
[#33]: https://github.com/howeyc/fsnotify/issues/33
|
|
||||||
[#29]: https://github.com/howeyc/fsnotify/issues/29
|
|
||||||
[#25]: https://github.com/howeyc/fsnotify/issues/25
|
|
||||||
[#24]: https://github.com/howeyc/fsnotify/issues/24
|
|
||||||
[#21]: https://github.com/howeyc/fsnotify/issues/21
|
|
60
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
60
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
|
@ -1,60 +0,0 @@
|
||||||
# Contributing
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
|
|
||||||
* Please indicate the platform you are using fsnotify on.
|
|
||||||
* A code example to reproduce the problem is appreciated.
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
### Contributor License Agreement
|
|
||||||
|
|
||||||
fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
|
|
||||||
|
|
||||||
Please indicate that you have signed the CLA in your pull request.
|
|
||||||
|
|
||||||
### How fsnotify is Developed
|
|
||||||
|
|
||||||
* Development is done on feature branches.
|
|
||||||
* Tests are run on BSD, Linux, macOS and Windows.
|
|
||||||
* Pull requests are reviewed and [applied to master][am] using [hub][].
|
|
||||||
* Maintainers may modify or squash commits rather than asking contributors to.
|
|
||||||
* To issue a new release, the maintainers will:
|
|
||||||
* Update the CHANGELOG
|
|
||||||
* Tag a version, which will become available through gopkg.in.
|
|
||||||
|
|
||||||
### How to Fork
|
|
||||||
|
|
||||||
For smooth sailing, always use the original import path. Installing with `go get` makes this easy.
|
|
||||||
|
|
||||||
1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
|
|
||||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
||||||
3. Ensure everything works and the tests pass (see below)
|
|
||||||
4. Commit your changes (`git commit -am 'Add some feature'`)
|
|
||||||
|
|
||||||
Contribute upstream:
|
|
||||||
|
|
||||||
1. Fork fsnotify on GitHub
|
|
||||||
2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
|
|
||||||
3. Push to the branch (`git push fork my-new-feature`)
|
|
||||||
4. Create a new Pull Request on GitHub
|
|
||||||
|
|
||||||
This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
|
|
||||||
|
|
||||||
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
|
|
||||||
|
|
||||||
### Maintainers
|
|
||||||
|
|
||||||
Help maintaining fsnotify is welcome. To be a maintainer:
|
|
||||||
|
|
||||||
* Submit a pull request and sign the CLA as above.
|
|
||||||
* You must be able to run the test suite on Mac, Windows, Linux and BSD.
|
|
||||||
|
|
||||||
All code changes should be internal pull requests.
|
|
||||||
|
|
||||||
Releases are tagged using [Semantic Versioning](http://semver.org/).
|
|
28
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
28
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
|
@ -1,28 +0,0 @@
|
||||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
|
||||||
Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
120
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
120
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
|
@ -1,120 +0,0 @@
|
||||||
# File system notifications for Go
|
|
||||||
|
|
||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/fsnotify/fsnotify.svg)](https://pkg.go.dev/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) [![Maintainers Wanted](https://img.shields.io/badge/maintainers-wanted-red.svg)](https://github.com/fsnotify/fsnotify/issues/413)
|
|
||||||
|
|
||||||
fsnotify utilizes [`golang.org/x/sys`](https://pkg.go.dev/golang.org/x/sys) rather than [`syscall`](https://pkg.go.dev/syscall) from the standard library.
|
|
||||||
|
|
||||||
Cross platform: Windows, Linux, BSD and macOS.
|
|
||||||
|
|
||||||
| Adapter | OS | Status |
|
|
||||||
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| inotify | Linux 2.6.27 or later, Android\* | Supported |
|
|
||||||
| kqueue | BSD, macOS, iOS\* | Supported |
|
|
||||||
| ReadDirectoryChangesW | Windows | Supported |
|
|
||||||
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
|
|
||||||
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) |
|
|
||||||
| fanotify | Linux 2.6.37+ | [Maybe](https://github.com/fsnotify/fsnotify/issues/114) |
|
|
||||||
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
|
|
||||||
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
|
|
||||||
|
|
||||||
\* Android and iOS are untested.
|
|
||||||
|
|
||||||
Please see [the documentation](https://pkg.go.dev/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
|
|
||||||
|
|
||||||
## API stability
|
|
||||||
|
|
||||||
fsnotify is a fork of [howeyc/fsnotify](https://github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).
|
|
||||||
|
|
||||||
All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer watcher.Close()
|
|
||||||
|
|
||||||
done := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event, ok := <-watcher.Events:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("event:", event)
|
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
||||||
log.Println("modified file:", event.Name)
|
|
||||||
}
|
|
||||||
case err, ok := <-watcher.Errors:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("error:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = watcher.Add("/tmp/foo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
**When a file is moved to another directory is it still being watched?**
|
|
||||||
|
|
||||||
No (it shouldn't be, unless you are watching where it was moved to).
|
|
||||||
|
|
||||||
**When I watch a directory, are all subdirectories watched as well?**
|
|
||||||
|
|
||||||
No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
|
|
||||||
|
|
||||||
**Do I have to watch the Error and Event channels in a separate goroutine?**
|
|
||||||
|
|
||||||
As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
|
|
||||||
|
|
||||||
**Why am I receiving multiple events for the same file on OS X?**
|
|
||||||
|
|
||||||
Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
|
|
||||||
|
|
||||||
**How many files can be watched at once?**
|
|
||||||
|
|
||||||
There are OS-specific limits as to how many watches can be created:
|
|
||||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
|
|
||||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
|
|
||||||
|
|
||||||
**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
|
|
||||||
|
|
||||||
fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
|
|
||||||
|
|
||||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
|
||||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
|
||||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
|
||||||
[#7]: https://github.com/howeyc/fsnotify/issues/7
|
|
||||||
|
|
||||||
[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
|
|
||||||
|
|
||||||
## Related Projects
|
|
||||||
|
|
||||||
* [notify](https://github.com/rjeczalik/notify)
|
|
||||||
* [fsevents](https://github.com/fsnotify/fsevents)
|
|
||||||
|
|
38
vendor/github.com/fsnotify/fsnotify/fen.go
generated
vendored
38
vendor/github.com/fsnotify/fsnotify/fen.go
generated
vendored
|
@ -1,38 +0,0 @@
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build solaris
|
|
||||||
// +build solaris
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher watches a set of files, delivering events to a channel.
|
|
||||||
type Watcher struct {
|
|
||||||
Events chan Event
|
|
||||||
Errors chan error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close removes all watches and closes the events channel.
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add starts watching the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Add(name string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove stops watching the the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Remove(name string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
69
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
69
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
|
@ -1,69 +0,0 @@
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !plan9
|
|
||||||
// +build !plan9
|
|
||||||
|
|
||||||
// Package fsnotify provides a platform-independent interface for file system notifications.
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event represents a single file system notification.
|
|
||||||
type Event struct {
|
|
||||||
Name string // Relative path to the file or directory.
|
|
||||||
Op Op // File operation that triggered the event.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Op describes a set of file operations.
|
|
||||||
type Op uint32
|
|
||||||
|
|
||||||
// These are the generalized file operations that can trigger a notification.
|
|
||||||
const (
|
|
||||||
Create Op = 1 << iota
|
|
||||||
Write
|
|
||||||
Remove
|
|
||||||
Rename
|
|
||||||
Chmod
|
|
||||||
)
|
|
||||||
|
|
||||||
func (op Op) String() string {
|
|
||||||
// Use a buffer for efficient string concatenation
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
|
|
||||||
if op&Create == Create {
|
|
||||||
buffer.WriteString("|CREATE")
|
|
||||||
}
|
|
||||||
if op&Remove == Remove {
|
|
||||||
buffer.WriteString("|REMOVE")
|
|
||||||
}
|
|
||||||
if op&Write == Write {
|
|
||||||
buffer.WriteString("|WRITE")
|
|
||||||
}
|
|
||||||
if op&Rename == Rename {
|
|
||||||
buffer.WriteString("|RENAME")
|
|
||||||
}
|
|
||||||
if op&Chmod == Chmod {
|
|
||||||
buffer.WriteString("|CHMOD")
|
|
||||||
}
|
|
||||||
if buffer.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return buffer.String()[1:] // Strip leading pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the event in the form
|
|
||||||
// "file: REMOVE|WRITE|..."
|
|
||||||
func (e Event) String() string {
|
|
||||||
return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common errors that can be reported by a watcher
|
|
||||||
var (
|
|
||||||
ErrEventOverflow = errors.New("fsnotify queue overflow")
|
|
||||||
)
|
|
36
vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go
generated
vendored
36
vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go
generated
vendored
|
@ -1,36 +0,0 @@
|
||||||
// Copyright 2022 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows
|
|
||||||
// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher watches a set of files, delivering events to a channel.
|
|
||||||
type Watcher struct{}
|
|
||||||
|
|
||||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close removes all watches and closes the events channel.
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add starts watching the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Add(name string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove stops watching the the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Remove(name string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
351
vendor/github.com/fsnotify/fsnotify/inotify.go
generated
vendored
351
vendor/github.com/fsnotify/fsnotify/inotify.go
generated
vendored
|
@ -1,351 +0,0 @@
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher watches a set of files, delivering events to a channel.
|
|
||||||
type Watcher struct {
|
|
||||||
Events chan Event
|
|
||||||
Errors chan error
|
|
||||||
mu sync.Mutex // Map access
|
|
||||||
fd int
|
|
||||||
poller *fdPoller
|
|
||||||
watches map[string]*watch // Map of inotify watches (key: path)
|
|
||||||
paths map[int]string // Map of watched paths (key: watch descriptor)
|
|
||||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
|
||||||
doneResp chan struct{} // Channel to respond to Close
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
// Create inotify fd
|
|
||||||
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
|
|
||||||
if fd == -1 {
|
|
||||||
return nil, errno
|
|
||||||
}
|
|
||||||
// Create epoll
|
|
||||||
poller, err := newFdPoller(fd)
|
|
||||||
if err != nil {
|
|
||||||
unix.Close(fd)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
w := &Watcher{
|
|
||||||
fd: fd,
|
|
||||||
poller: poller,
|
|
||||||
watches: make(map[string]*watch),
|
|
||||||
paths: make(map[int]string),
|
|
||||||
Events: make(chan Event),
|
|
||||||
Errors: make(chan error),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
doneResp: make(chan struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
go w.readEvents()
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Watcher) isClosed() bool {
|
|
||||||
select {
|
|
||||||
case <-w.done:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close removes all watches and closes the events channel.
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
if w.isClosed() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send 'close' signal to goroutine, and set the Watcher to closed.
|
|
||||||
close(w.done)
|
|
||||||
|
|
||||||
// Wake up goroutine
|
|
||||||
w.poller.wake()
|
|
||||||
|
|
||||||
// Wait for goroutine to close
|
|
||||||
<-w.doneResp
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add starts watching the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Add(name string) error {
|
|
||||||
name = filepath.Clean(name)
|
|
||||||
if w.isClosed() {
|
|
||||||
return errors.New("inotify instance already closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
|
|
||||||
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
|
|
||||||
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
|
|
||||||
|
|
||||||
var flags uint32 = agnosticEvents
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
watchEntry := w.watches[name]
|
|
||||||
if watchEntry != nil {
|
|
||||||
flags |= watchEntry.flags | unix.IN_MASK_ADD
|
|
||||||
}
|
|
||||||
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
|
|
||||||
if wd == -1 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
|
|
||||||
if watchEntry == nil {
|
|
||||||
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
|
||||||
w.paths[wd] = name
|
|
||||||
} else {
|
|
||||||
watchEntry.wd = uint32(wd)
|
|
||||||
watchEntry.flags = flags
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove stops watching the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Remove(name string) error {
|
|
||||||
name = filepath.Clean(name)
|
|
||||||
|
|
||||||
// Fetch the watch.
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
watch, ok := w.watches[name]
|
|
||||||
|
|
||||||
// Remove it from inotify.
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We successfully removed the watch if InotifyRmWatch doesn't return an
|
|
||||||
// error, we need to clean up our internal state to ensure it matches
|
|
||||||
// inotify's kernel state.
|
|
||||||
delete(w.paths, int(watch.wd))
|
|
||||||
delete(w.watches, name)
|
|
||||||
|
|
||||||
// inotify_rm_watch will return EINVAL if the file has been deleted;
|
|
||||||
// the inotify will already have been removed.
|
|
||||||
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
|
|
||||||
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
|
|
||||||
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
|
|
||||||
// by another thread and we have not received IN_IGNORE event.
|
|
||||||
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
|
|
||||||
if success == -1 {
|
|
||||||
// TODO: Perhaps it's not helpful to return an error here in every case.
|
|
||||||
// the only two possible errors are:
|
|
||||||
// EBADF, which happens when w.fd is not a valid file descriptor of any kind.
|
|
||||||
// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
|
|
||||||
// Watch descriptors are invalidated when they are removed explicitly or implicitly;
|
|
||||||
// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchList returns the directories and files that are being monitered.
|
|
||||||
func (w *Watcher) WatchList() []string {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
entries := make([]string, 0, len(w.watches))
|
|
||||||
for pathname := range w.watches {
|
|
||||||
entries = append(entries, pathname)
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries
|
|
||||||
}
|
|
||||||
|
|
||||||
type watch struct {
|
|
||||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
|
||||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// readEvents reads from the inotify file descriptor, converts the
|
|
||||||
// received events into Event objects and sends them via the Events channel
|
|
||||||
func (w *Watcher) readEvents() {
|
|
||||||
var (
|
|
||||||
buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
|
|
||||||
n int // Number of bytes read with read()
|
|
||||||
errno error // Syscall errno
|
|
||||||
ok bool // For poller.wait
|
|
||||||
)
|
|
||||||
|
|
||||||
defer close(w.doneResp)
|
|
||||||
defer close(w.Errors)
|
|
||||||
defer close(w.Events)
|
|
||||||
defer unix.Close(w.fd)
|
|
||||||
defer w.poller.close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
// See if we have been closed.
|
|
||||||
if w.isClosed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, errno = w.poller.wait()
|
|
||||||
if errno != nil {
|
|
||||||
select {
|
|
||||||
case w.Errors <- errno:
|
|
||||||
case <-w.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
n, errno = unix.Read(w.fd, buf[:])
|
|
||||||
// If a signal interrupted execution, see if we've been asked to close, and try again.
|
|
||||||
// http://man7.org/linux/man-pages/man7/signal.7.html :
|
|
||||||
// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
|
|
||||||
if errno == unix.EINTR {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// unix.Read might have been woken up by Close. If so, we're done.
|
|
||||||
if w.isClosed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < unix.SizeofInotifyEvent {
|
|
||||||
var err error
|
|
||||||
if n == 0 {
|
|
||||||
// If EOF is received. This should really never happen.
|
|
||||||
err = io.EOF
|
|
||||||
} else if n < 0 {
|
|
||||||
// If an error occurred while reading.
|
|
||||||
err = errno
|
|
||||||
} else {
|
|
||||||
// Read was too short.
|
|
||||||
err = errors.New("notify: short read in readEvents()")
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case w.Errors <- err:
|
|
||||||
case <-w.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset uint32
|
|
||||||
// We don't know how many events we just read into the buffer
|
|
||||||
// While the offset points to at least one whole event...
|
|
||||||
for offset <= uint32(n-unix.SizeofInotifyEvent) {
|
|
||||||
// Point "raw" to the event in the buffer
|
|
||||||
raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
|
||||||
|
|
||||||
mask := uint32(raw.Mask)
|
|
||||||
nameLen := uint32(raw.Len)
|
|
||||||
|
|
||||||
if mask&unix.IN_Q_OVERFLOW != 0 {
|
|
||||||
select {
|
|
||||||
case w.Errors <- ErrEventOverflow:
|
|
||||||
case <-w.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the event happened to the watched directory or the watched file, the kernel
|
|
||||||
// doesn't append the filename to the event, but we would like to always fill the
|
|
||||||
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
|
||||||
// the "paths" map.
|
|
||||||
w.mu.Lock()
|
|
||||||
name, ok := w.paths[int(raw.Wd)]
|
|
||||||
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
|
|
||||||
// This is a sign to clean up the maps, otherwise we are no longer in sync
|
|
||||||
// with the inotify kernel state which has already deleted the watch
|
|
||||||
// automatically.
|
|
||||||
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
|
|
||||||
delete(w.paths, int(raw.Wd))
|
|
||||||
delete(w.watches, name)
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
if nameLen > 0 {
|
|
||||||
// Point "bytes" at the first byte of the filename
|
|
||||||
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
|
|
||||||
// The filename is padded with NULL bytes. TrimRight() gets rid of those.
|
|
||||||
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
|
|
||||||
}
|
|
||||||
|
|
||||||
event := newEvent(name, mask)
|
|
||||||
|
|
||||||
// Send the events that are not ignored on the events channel
|
|
||||||
if !event.ignoreLinux(mask) {
|
|
||||||
select {
|
|
||||||
case w.Events <- event:
|
|
||||||
case <-w.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the next event in the buffer
|
|
||||||
offset += unix.SizeofInotifyEvent + nameLen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Certain types of events can be "ignored" and not sent over the Events
|
|
||||||
// channel. Such as events marked ignore by the kernel, or MODIFY events
|
|
||||||
// against files that do not exist.
|
|
||||||
func (e *Event) ignoreLinux(mask uint32) bool {
|
|
||||||
// Ignore anything the inotify API says to ignore
|
|
||||||
if mask&unix.IN_IGNORED == unix.IN_IGNORED {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the event is not a DELETE or RENAME, the file must exist.
|
|
||||||
// Otherwise the event is ignored.
|
|
||||||
// *Note*: this was put in place because it was seen that a MODIFY
|
|
||||||
// event was sent after the DELETE. This ignores that MODIFY and
|
|
||||||
// assumes a DELETE will come or has come if the file doesn't exist.
|
|
||||||
if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
|
|
||||||
_, statErr := os.Lstat(e.Name)
|
|
||||||
return os.IsNotExist(statErr)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// newEvent returns an platform-independent Event based on an inotify mask.
|
|
||||||
func newEvent(name string, mask uint32) Event {
|
|
||||||
e := Event{Name: name}
|
|
||||||
if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
|
|
||||||
e.Op |= Create
|
|
||||||
}
|
|
||||||
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
|
|
||||||
e.Op |= Remove
|
|
||||||
}
|
|
||||||
if mask&unix.IN_MODIFY == unix.IN_MODIFY {
|
|
||||||
e.Op |= Write
|
|
||||||
}
|
|
||||||
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
|
|
||||||
e.Op |= Rename
|
|
||||||
}
|
|
||||||
if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
|
|
||||||
e.Op |= Chmod
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
187
vendor/github.com/fsnotify/fsnotify/inotify_poller.go
generated
vendored
187
vendor/github.com/fsnotify/fsnotify/inotify_poller.go
generated
vendored
|
@ -1,187 +0,0 @@
|
||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fdPoller struct {
|
|
||||||
fd int // File descriptor (as returned by the inotify_init() syscall)
|
|
||||||
epfd int // Epoll file descriptor
|
|
||||||
pipe [2]int // Pipe for waking up
|
|
||||||
}
|
|
||||||
|
|
||||||
func emptyPoller(fd int) *fdPoller {
|
|
||||||
poller := new(fdPoller)
|
|
||||||
poller.fd = fd
|
|
||||||
poller.epfd = -1
|
|
||||||
poller.pipe[0] = -1
|
|
||||||
poller.pipe[1] = -1
|
|
||||||
return poller
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new inotify poller.
|
|
||||||
// This creates an inotify handler, and an epoll handler.
|
|
||||||
func newFdPoller(fd int) (*fdPoller, error) {
|
|
||||||
var errno error
|
|
||||||
poller := emptyPoller(fd)
|
|
||||||
defer func() {
|
|
||||||
if errno != nil {
|
|
||||||
poller.close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create epoll fd
|
|
||||||
poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
|
|
||||||
if poller.epfd == -1 {
|
|
||||||
return nil, errno
|
|
||||||
}
|
|
||||||
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
|
|
||||||
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
|
|
||||||
if errno != nil {
|
|
||||||
return nil, errno
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register inotify fd with epoll
|
|
||||||
event := unix.EpollEvent{
|
|
||||||
Fd: int32(poller.fd),
|
|
||||||
Events: unix.EPOLLIN,
|
|
||||||
}
|
|
||||||
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
|
|
||||||
if errno != nil {
|
|
||||||
return nil, errno
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register pipe fd with epoll
|
|
||||||
event = unix.EpollEvent{
|
|
||||||
Fd: int32(poller.pipe[0]),
|
|
||||||
Events: unix.EPOLLIN,
|
|
||||||
}
|
|
||||||
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
|
|
||||||
if errno != nil {
|
|
||||||
return nil, errno
|
|
||||||
}
|
|
||||||
|
|
||||||
return poller, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait using epoll.
|
|
||||||
// Returns true if something is ready to be read,
|
|
||||||
// false if there is not.
|
|
||||||
func (poller *fdPoller) wait() (bool, error) {
|
|
||||||
// 3 possible events per fd, and 2 fds, makes a maximum of 6 events.
|
|
||||||
// I don't know whether epoll_wait returns the number of events returned,
|
|
||||||
// or the total number of events ready.
|
|
||||||
// I decided to catch both by making the buffer one larger than the maximum.
|
|
||||||
events := make([]unix.EpollEvent, 7)
|
|
||||||
for {
|
|
||||||
n, errno := unix.EpollWait(poller.epfd, events, -1)
|
|
||||||
if n == -1 {
|
|
||||||
if errno == unix.EINTR {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return false, errno
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
// If there are no events, try again.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if n > 6 {
|
|
||||||
// This should never happen. More events were returned than should be possible.
|
|
||||||
return false, errors.New("epoll_wait returned more events than I know what to do with")
|
|
||||||
}
|
|
||||||
ready := events[:n]
|
|
||||||
epollhup := false
|
|
||||||
epollerr := false
|
|
||||||
epollin := false
|
|
||||||
for _, event := range ready {
|
|
||||||
if event.Fd == int32(poller.fd) {
|
|
||||||
if event.Events&unix.EPOLLHUP != 0 {
|
|
||||||
// This should not happen, but if it does, treat it as a wakeup.
|
|
||||||
epollhup = true
|
|
||||||
}
|
|
||||||
if event.Events&unix.EPOLLERR != 0 {
|
|
||||||
// If an error is waiting on the file descriptor, we should pretend
|
|
||||||
// something is ready to read, and let unix.Read pick up the error.
|
|
||||||
epollerr = true
|
|
||||||
}
|
|
||||||
if event.Events&unix.EPOLLIN != 0 {
|
|
||||||
// There is data to read.
|
|
||||||
epollin = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if event.Fd == int32(poller.pipe[0]) {
|
|
||||||
if event.Events&unix.EPOLLHUP != 0 {
|
|
||||||
// Write pipe descriptor was closed, by us. This means we're closing down the
|
|
||||||
// watcher, and we should wake up.
|
|
||||||
}
|
|
||||||
if event.Events&unix.EPOLLERR != 0 {
|
|
||||||
// If an error is waiting on the pipe file descriptor.
|
|
||||||
// This is an absolute mystery, and should never ever happen.
|
|
||||||
return false, errors.New("Error on the pipe descriptor.")
|
|
||||||
}
|
|
||||||
if event.Events&unix.EPOLLIN != 0 {
|
|
||||||
// This is a regular wakeup, so we have to clear the buffer.
|
|
||||||
err := poller.clearWake()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if epollhup || epollerr || epollin {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the write end of the poller.
|
|
||||||
func (poller *fdPoller) wake() error {
|
|
||||||
buf := make([]byte, 1)
|
|
||||||
n, errno := unix.Write(poller.pipe[1], buf)
|
|
||||||
if n == -1 {
|
|
||||||
if errno == unix.EAGAIN {
|
|
||||||
// Buffer is full, poller will wake.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (poller *fdPoller) clearWake() error {
|
|
||||||
// You have to be woken up a LOT in order to get to 100!
|
|
||||||
buf := make([]byte, 100)
|
|
||||||
n, errno := unix.Read(poller.pipe[0], buf)
|
|
||||||
if n == -1 {
|
|
||||||
if errno == unix.EAGAIN {
|
|
||||||
// Buffer is empty, someone else cleared our wake.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close all poller file descriptors, but not the one passed to it.
|
|
||||||
func (poller *fdPoller) close() {
|
|
||||||
if poller.pipe[1] != -1 {
|
|
||||||
unix.Close(poller.pipe[1])
|
|
||||||
}
|
|
||||||
if poller.pipe[0] != -1 {
|
|
||||||
unix.Close(poller.pipe[0])
|
|
||||||
}
|
|
||||||
if poller.epfd != -1 {
|
|
||||||
unix.Close(poller.epfd)
|
|
||||||
}
|
|
||||||
}
|
|
535
vendor/github.com/fsnotify/fsnotify/kqueue.go
generated
vendored
535
vendor/github.com/fsnotify/fsnotify/kqueue.go
generated
vendored
|
@ -1,535 +0,0 @@
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build freebsd || openbsd || netbsd || dragonfly || darwin
|
|
||||||
// +build freebsd openbsd netbsd dragonfly darwin
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher watches a set of files, delivering events to a channel.
|
|
||||||
type Watcher struct {
|
|
||||||
Events chan Event
|
|
||||||
Errors chan error
|
|
||||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
|
||||||
|
|
||||||
kq int // File descriptor (as returned by the kqueue() syscall).
|
|
||||||
|
|
||||||
mu sync.Mutex // Protects access to watcher data
|
|
||||||
watches map[string]int // Map of watched file descriptors (key: path).
|
|
||||||
externalWatches map[string]bool // Map of watches added by user of the library.
|
|
||||||
dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue.
|
|
||||||
paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events.
|
|
||||||
fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events).
|
|
||||||
isClosed bool // Set to true when Close() is first called
|
|
||||||
}
|
|
||||||
|
|
||||||
type pathInfo struct {
|
|
||||||
name string
|
|
||||||
isDir bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
kq, err := kqueue()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &Watcher{
|
|
||||||
kq: kq,
|
|
||||||
watches: make(map[string]int),
|
|
||||||
dirFlags: make(map[string]uint32),
|
|
||||||
paths: make(map[int]pathInfo),
|
|
||||||
fileExists: make(map[string]bool),
|
|
||||||
externalWatches: make(map[string]bool),
|
|
||||||
Events: make(chan Event),
|
|
||||||
Errors: make(chan error),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
go w.readEvents()
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close removes all watches and closes the events channel.
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
w.mu.Lock()
|
|
||||||
if w.isClosed {
|
|
||||||
w.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.isClosed = true
|
|
||||||
|
|
||||||
// copy paths to remove while locked
|
|
||||||
var pathsToRemove = make([]string, 0, len(w.watches))
|
|
||||||
for name := range w.watches {
|
|
||||||
pathsToRemove = append(pathsToRemove, name)
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
// unlock before calling Remove, which also locks
|
|
||||||
|
|
||||||
for _, name := range pathsToRemove {
|
|
||||||
w.Remove(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a "quit" message to the reader goroutine
|
|
||||||
close(w.done)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add starts watching the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Add(name string) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
w.externalWatches[name] = true
|
|
||||||
w.mu.Unlock()
|
|
||||||
_, err := w.addWatch(name, noteAllEvents)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove stops watching the the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Remove(name string) error {
|
|
||||||
name = filepath.Clean(name)
|
|
||||||
w.mu.Lock()
|
|
||||||
watchfd, ok := w.watches[name]
|
|
||||||
w.mu.Unlock()
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerRemove = unix.EV_DELETE
|
|
||||||
if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
unix.Close(watchfd)
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
isDir := w.paths[watchfd].isDir
|
|
||||||
delete(w.watches, name)
|
|
||||||
delete(w.paths, watchfd)
|
|
||||||
delete(w.dirFlags, name)
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
// Find all watched paths that are in this directory that are not external.
|
|
||||||
if isDir {
|
|
||||||
var pathsToRemove []string
|
|
||||||
w.mu.Lock()
|
|
||||||
for _, path := range w.paths {
|
|
||||||
wdir, _ := filepath.Split(path.name)
|
|
||||||
if filepath.Clean(wdir) == name {
|
|
||||||
if !w.externalWatches[path.name] {
|
|
||||||
pathsToRemove = append(pathsToRemove, path.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
for _, name := range pathsToRemove {
|
|
||||||
// Since these are internal, not much sense in propagating error
|
|
||||||
// to the user, as that will just confuse them with an error about
|
|
||||||
// a path they did not explicitly watch themselves.
|
|
||||||
w.Remove(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchList returns the directories and files that are being monitered.
|
|
||||||
func (w *Watcher) WatchList() []string {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
entries := make([]string, 0, len(w.watches))
|
|
||||||
for pathname := range w.watches {
|
|
||||||
entries = append(entries, pathname)
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
|
|
||||||
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
|
|
||||||
|
|
||||||
// keventWaitTime to block on each read from kevent
|
|
||||||
var keventWaitTime = durationToTimespec(100 * time.Millisecond)
|
|
||||||
|
|
||||||
// addWatch adds name to the watched file set.
|
|
||||||
// The flags are interpreted as described in kevent(2).
|
|
||||||
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
|
|
||||||
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
|
||||||
var isDir bool
|
|
||||||
// Make ./name and name equivalent
|
|
||||||
name = filepath.Clean(name)
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
if w.isClosed {
|
|
||||||
w.mu.Unlock()
|
|
||||||
return "", errors.New("kevent instance already closed")
|
|
||||||
}
|
|
||||||
watchfd, alreadyWatching := w.watches[name]
|
|
||||||
// We already have a watch, but we can still override flags.
|
|
||||||
if alreadyWatching {
|
|
||||||
isDir = w.paths[watchfd].isDir
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
if !alreadyWatching {
|
|
||||||
fi, err := os.Lstat(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't watch sockets.
|
|
||||||
if fi.Mode()&os.ModeSocket == os.ModeSocket {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't watch named pipes.
|
|
||||||
if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Follow Symlinks
|
|
||||||
// Unfortunately, Linux can add bogus symlinks to watch list without
|
|
||||||
// issue, and Windows can't do symlinks period (AFAIK). To maintain
|
|
||||||
// consistency, we will act like everything is fine. There will simply
|
|
||||||
// be no file events for broken symlinks.
|
|
||||||
// Hence the returns of nil on errors.
|
|
||||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
|
||||||
name, err = filepath.EvalSymlinks(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
_, alreadyWatching = w.watches[name]
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
if alreadyWatching {
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err = os.Lstat(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watchfd, err = unix.Open(name, openMode, 0700)
|
|
||||||
if watchfd == -1 {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
isDir = fi.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
|
|
||||||
if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
|
|
||||||
unix.Close(watchfd)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !alreadyWatching {
|
|
||||||
w.mu.Lock()
|
|
||||||
w.watches[name] = watchfd
|
|
||||||
w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
|
|
||||||
w.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if isDir {
|
|
||||||
// Watch the directory if it has not been watched before,
|
|
||||||
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
|
|
||||||
w.mu.Lock()
|
|
||||||
|
|
||||||
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
|
|
||||||
(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
|
|
||||||
// Store flags so this watch can be updated later
|
|
||||||
w.dirFlags[name] = flags
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
if watchDir {
|
|
||||||
if err := w.watchDirectoryFiles(name); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readEvents reads from kqueue and converts the received kevents into
|
|
||||||
// Event values that it sends down the Events channel.
|
|
||||||
func (w *Watcher) readEvents() {
|
|
||||||
eventBuffer := make([]unix.Kevent_t, 10)
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
// See if there is a message on the "done" channel
|
|
||||||
select {
|
|
||||||
case <-w.done:
|
|
||||||
break loop
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get new events
|
|
||||||
kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
|
|
||||||
// EINTR is okay, the syscall was interrupted before timeout expired.
|
|
||||||
if err != nil && err != unix.EINTR {
|
|
||||||
select {
|
|
||||||
case w.Errors <- err:
|
|
||||||
case <-w.done:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush the events we received to the Events channel
|
|
||||||
for len(kevents) > 0 {
|
|
||||||
kevent := &kevents[0]
|
|
||||||
watchfd := int(kevent.Ident)
|
|
||||||
mask := uint32(kevent.Fflags)
|
|
||||||
w.mu.Lock()
|
|
||||||
path := w.paths[watchfd]
|
|
||||||
w.mu.Unlock()
|
|
||||||
event := newEvent(path.name, mask)
|
|
||||||
|
|
||||||
if path.isDir && !(event.Op&Remove == Remove) {
|
|
||||||
// Double check to make sure the directory exists. This can happen when
|
|
||||||
// we do a rm -fr on a recursively watched folders and we receive a
|
|
||||||
// modification event first but the folder has been deleted and later
|
|
||||||
// receive the delete event
|
|
||||||
if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
|
|
||||||
// mark is as delete event
|
|
||||||
event.Op |= Remove
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.Op&Rename == Rename || event.Op&Remove == Remove {
|
|
||||||
w.Remove(event.Name)
|
|
||||||
w.mu.Lock()
|
|
||||||
delete(w.fileExists, event.Name)
|
|
||||||
w.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
|
|
||||||
w.sendDirectoryChangeEvents(event.Name)
|
|
||||||
} else {
|
|
||||||
// Send the event on the Events channel.
|
|
||||||
select {
|
|
||||||
case w.Events <- event:
|
|
||||||
case <-w.done:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.Op&Remove == Remove {
|
|
||||||
// Look for a file that may have overwritten this.
|
|
||||||
// For example, mv f1 f2 will delete f2, then create f2.
|
|
||||||
if path.isDir {
|
|
||||||
fileDir := filepath.Clean(event.Name)
|
|
||||||
w.mu.Lock()
|
|
||||||
_, found := w.watches[fileDir]
|
|
||||||
w.mu.Unlock()
|
|
||||||
if found {
|
|
||||||
// make sure the directory exists before we watch for changes. When we
|
|
||||||
// do a recursive watch and perform rm -fr, the parent directory might
|
|
||||||
// have gone missing, ignore the missing directory and let the
|
|
||||||
// upcoming delete event remove the watch from the parent directory.
|
|
||||||
if _, err := os.Lstat(fileDir); err == nil {
|
|
||||||
w.sendDirectoryChangeEvents(fileDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
filePath := filepath.Clean(event.Name)
|
|
||||||
if fileInfo, err := os.Lstat(filePath); err == nil {
|
|
||||||
w.sendFileCreatedEventIfNew(filePath, fileInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to next event
|
|
||||||
kevents = kevents[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
err := unix.Close(w.kq)
|
|
||||||
if err != nil {
|
|
||||||
// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
|
|
||||||
select {
|
|
||||||
case w.Errors <- err:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(w.Events)
|
|
||||||
close(w.Errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newEvent returns an platform-independent Event based on kqueue Fflags.
|
|
||||||
func newEvent(name string, mask uint32) Event {
|
|
||||||
e := Event{Name: name}
|
|
||||||
if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
|
|
||||||
e.Op |= Remove
|
|
||||||
}
|
|
||||||
if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
|
|
||||||
e.Op |= Write
|
|
||||||
}
|
|
||||||
if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
|
|
||||||
e.Op |= Rename
|
|
||||||
}
|
|
||||||
if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
|
|
||||||
e.Op |= Chmod
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCreateEvent(name string) Event {
|
|
||||||
return Event{Name: name, Op: Create}
|
|
||||||
}
|
|
||||||
|
|
||||||
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
|
|
||||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
|
||||||
// Get all files
|
|
||||||
files, err := ioutil.ReadDir(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fileInfo := range files {
|
|
||||||
filePath := filepath.Join(dirPath, fileInfo.Name())
|
|
||||||
filePath, err = w.internalWatch(filePath, fileInfo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
w.fileExists[filePath] = true
|
|
||||||
w.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendDirectoryEvents searches the directory for newly created files
|
|
||||||
// and sends them over the event channel. This functionality is to have
|
|
||||||
// the BSD version of fsnotify match Linux inotify which provides a
|
|
||||||
// create event for files created in a watched directory.
|
|
||||||
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
|
|
||||||
// Get all files
|
|
||||||
files, err := ioutil.ReadDir(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
select {
|
|
||||||
case w.Errors <- err:
|
|
||||||
case <-w.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for new files
|
|
||||||
for _, fileInfo := range files {
|
|
||||||
filePath := filepath.Join(dirPath, fileInfo.Name())
|
|
||||||
err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
|
|
||||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
|
|
||||||
w.mu.Lock()
|
|
||||||
_, doesExist := w.fileExists[filePath]
|
|
||||||
w.mu.Unlock()
|
|
||||||
if !doesExist {
|
|
||||||
// Send create event
|
|
||||||
select {
|
|
||||||
case w.Events <- newCreateEvent(filePath):
|
|
||||||
case <-w.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// like watchDirectoryFiles (but without doing another ReadDir)
|
|
||||||
filePath, err = w.internalWatch(filePath, fileInfo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.mu.Lock()
|
|
||||||
w.fileExists[filePath] = true
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
|
|
||||||
if fileInfo.IsDir() {
|
|
||||||
// mimic Linux providing delete events for subdirectories
|
|
||||||
// but preserve the flags used if currently watching subdirectory
|
|
||||||
w.mu.Lock()
|
|
||||||
flags := w.dirFlags[name]
|
|
||||||
w.mu.Unlock()
|
|
||||||
|
|
||||||
flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
|
|
||||||
return w.addWatch(name, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// watch file to mimic Linux inotify
|
|
||||||
return w.addWatch(name, noteAllEvents)
|
|
||||||
}
|
|
||||||
|
|
||||||
// kqueue creates a new kernel event queue and returns a descriptor.
|
|
||||||
func kqueue() (kq int, err error) {
|
|
||||||
kq, err = unix.Kqueue()
|
|
||||||
if kq == -1 {
|
|
||||||
return kq, err
|
|
||||||
}
|
|
||||||
return kq, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// register events with the queue
|
|
||||||
func register(kq int, fds []int, flags int, fflags uint32) error {
|
|
||||||
changes := make([]unix.Kevent_t, len(fds))
|
|
||||||
|
|
||||||
for i, fd := range fds {
|
|
||||||
// SetKevent converts int to the platform-specific types:
|
|
||||||
unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
|
|
||||||
changes[i].Fflags = fflags
|
|
||||||
}
|
|
||||||
|
|
||||||
// register the events
|
|
||||||
success, err := unix.Kevent(kq, changes, nil, nil)
|
|
||||||
if success == -1 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// read retrieves pending events, or waits until an event occurs.
|
|
||||||
// A timeout of nil blocks indefinitely, while 0 polls the queue.
|
|
||||||
func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
|
|
||||||
n, err := unix.Kevent(kq, nil, events, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return events[0:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// durationToTimespec prepares a timeout value
|
|
||||||
func durationToTimespec(d time.Duration) unix.Timespec {
|
|
||||||
return unix.NsecToTimespec(d.Nanoseconds())
|
|
||||||
}
|
|
12
vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
generated
vendored
12
vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build freebsd || openbsd || netbsd || dragonfly
|
|
||||||
// +build freebsd openbsd netbsd dragonfly
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC
|
|
13
vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
generated
vendored
13
vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
generated
vendored
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build darwin
|
|
||||||
// +build darwin
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
// note: this constant is not defined on BSD
|
|
||||||
const openMode = unix.O_EVTONLY | unix.O_CLOEXEC
|
|
586
vendor/github.com/fsnotify/fsnotify/windows.go
generated
vendored
586
vendor/github.com/fsnotify/fsnotify/windows.go
generated
vendored
|
@ -1,586 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fsnotify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher watches a set of files, delivering events to a channel.
|
|
||||||
type Watcher struct {
|
|
||||||
Events chan Event
|
|
||||||
Errors chan error
|
|
||||||
isClosed bool // Set to true when Close() is first called
|
|
||||||
mu sync.Mutex // Map access
|
|
||||||
port syscall.Handle // Handle to completion port
|
|
||||||
watches watchMap // Map of watches (key: i-number)
|
|
||||||
input chan *input // Inputs to the reader are sent on this channel
|
|
||||||
quit chan chan<- error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
|
||||||
func NewWatcher() (*Watcher, error) {
|
|
||||||
port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
|
|
||||||
if e != nil {
|
|
||||||
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
|
|
||||||
}
|
|
||||||
w := &Watcher{
|
|
||||||
port: port,
|
|
||||||
watches: make(watchMap),
|
|
||||||
input: make(chan *input, 1),
|
|
||||||
Events: make(chan Event, 50),
|
|
||||||
Errors: make(chan error),
|
|
||||||
quit: make(chan chan<- error, 1),
|
|
||||||
}
|
|
||||||
go w.readEvents()
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close removes all watches and closes the events channel.
|
|
||||||
func (w *Watcher) Close() error {
|
|
||||||
if w.isClosed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.isClosed = true
|
|
||||||
|
|
||||||
// Send "quit" message to the reader goroutine
|
|
||||||
ch := make(chan error)
|
|
||||||
w.quit <- ch
|
|
||||||
if err := w.wakeupReader(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return <-ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add starts watching the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Add(name string) error {
|
|
||||||
if w.isClosed {
|
|
||||||
return errors.New("watcher already closed")
|
|
||||||
}
|
|
||||||
in := &input{
|
|
||||||
op: opAddWatch,
|
|
||||||
path: filepath.Clean(name),
|
|
||||||
flags: sysFSALLEVENTS,
|
|
||||||
reply: make(chan error),
|
|
||||||
}
|
|
||||||
w.input <- in
|
|
||||||
if err := w.wakeupReader(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return <-in.reply
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove stops watching the the named file or directory (non-recursively).
|
|
||||||
func (w *Watcher) Remove(name string) error {
|
|
||||||
in := &input{
|
|
||||||
op: opRemoveWatch,
|
|
||||||
path: filepath.Clean(name),
|
|
||||||
reply: make(chan error),
|
|
||||||
}
|
|
||||||
w.input <- in
|
|
||||||
if err := w.wakeupReader(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return <-in.reply
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchList returns the directories and files that are being monitered.
|
|
||||||
func (w *Watcher) WatchList() []string {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
entries := make([]string, 0, len(w.watches))
|
|
||||||
for _, entry := range w.watches {
|
|
||||||
for _, watchEntry := range entry {
|
|
||||||
entries = append(entries, watchEntry.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Options for AddWatch
|
|
||||||
sysFSONESHOT = 0x80000000
|
|
||||||
sysFSONLYDIR = 0x1000000
|
|
||||||
|
|
||||||
// Events
|
|
||||||
sysFSACCESS = 0x1
|
|
||||||
sysFSALLEVENTS = 0xfff
|
|
||||||
sysFSATTRIB = 0x4
|
|
||||||
sysFSCLOSE = 0x18
|
|
||||||
sysFSCREATE = 0x100
|
|
||||||
sysFSDELETE = 0x200
|
|
||||||
sysFSDELETESELF = 0x400
|
|
||||||
sysFSMODIFY = 0x2
|
|
||||||
sysFSMOVE = 0xc0
|
|
||||||
sysFSMOVEDFROM = 0x40
|
|
||||||
sysFSMOVEDTO = 0x80
|
|
||||||
sysFSMOVESELF = 0x800
|
|
||||||
|
|
||||||
// Special events
|
|
||||||
sysFSIGNORED = 0x8000
|
|
||||||
sysFSQOVERFLOW = 0x4000
|
|
||||||
)
|
|
||||||
|
|
||||||
func newEvent(name string, mask uint32) Event {
|
|
||||||
e := Event{Name: name}
|
|
||||||
if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
|
|
||||||
e.Op |= Create
|
|
||||||
}
|
|
||||||
if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
|
|
||||||
e.Op |= Remove
|
|
||||||
}
|
|
||||||
if mask&sysFSMODIFY == sysFSMODIFY {
|
|
||||||
e.Op |= Write
|
|
||||||
}
|
|
||||||
if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
|
|
||||||
e.Op |= Rename
|
|
||||||
}
|
|
||||||
if mask&sysFSATTRIB == sysFSATTRIB {
|
|
||||||
e.Op |= Chmod
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
opAddWatch = iota
|
|
||||||
opRemoveWatch
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
provisional uint64 = 1 << (32 + iota)
|
|
||||||
)
|
|
||||||
|
|
||||||
type input struct {
|
|
||||||
op int
|
|
||||||
path string
|
|
||||||
flags uint32
|
|
||||||
reply chan error
|
|
||||||
}
|
|
||||||
|
|
||||||
type inode struct {
|
|
||||||
handle syscall.Handle
|
|
||||||
volume uint32
|
|
||||||
index uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type watch struct {
|
|
||||||
ov syscall.Overlapped
|
|
||||||
ino *inode // i-number
|
|
||||||
path string // Directory path
|
|
||||||
mask uint64 // Directory itself is being watched with these notify flags
|
|
||||||
names map[string]uint64 // Map of names being watched and their notify flags
|
|
||||||
rename string // Remembers the old name while renaming a file
|
|
||||||
buf [4096]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type indexMap map[uint64]*watch
|
|
||||||
type watchMap map[uint32]indexMap
|
|
||||||
|
|
||||||
func (w *Watcher) wakeupReader() error {
|
|
||||||
e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
|
|
||||||
if e != nil {
|
|
||||||
return os.NewSyscallError("PostQueuedCompletionStatus", e)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDir(pathname string) (dir string, err error) {
|
|
||||||
attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
|
|
||||||
if e != nil {
|
|
||||||
return "", os.NewSyscallError("GetFileAttributes", e)
|
|
||||||
}
|
|
||||||
if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
|
||||||
dir = pathname
|
|
||||||
} else {
|
|
||||||
dir, _ = filepath.Split(pathname)
|
|
||||||
dir = filepath.Clean(dir)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIno(path string) (ino *inode, err error) {
|
|
||||||
h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
|
|
||||||
syscall.FILE_LIST_DIRECTORY,
|
|
||||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
|
||||||
nil, syscall.OPEN_EXISTING,
|
|
||||||
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
|
|
||||||
if e != nil {
|
|
||||||
return nil, os.NewSyscallError("CreateFile", e)
|
|
||||||
}
|
|
||||||
var fi syscall.ByHandleFileInformation
|
|
||||||
if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
|
|
||||||
syscall.CloseHandle(h)
|
|
||||||
return nil, os.NewSyscallError("GetFileInformationByHandle", e)
|
|
||||||
}
|
|
||||||
ino = &inode{
|
|
||||||
handle: h,
|
|
||||||
volume: fi.VolumeSerialNumber,
|
|
||||||
index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
|
|
||||||
}
|
|
||||||
return ino, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (m watchMap) get(ino *inode) *watch {
|
|
||||||
if i := m[ino.volume]; i != nil {
|
|
||||||
return i[ino.index]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (m watchMap) set(ino *inode, watch *watch) {
|
|
||||||
i := m[ino.volume]
|
|
||||||
if i == nil {
|
|
||||||
i = make(indexMap)
|
|
||||||
m[ino.volume] = i
|
|
||||||
}
|
|
||||||
i[ino.index] = watch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
|
||||||
dir, err := getDir(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if flags&sysFSONLYDIR != 0 && pathname != dir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ino, err := getIno(dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
watchEntry := w.watches.get(ino)
|
|
||||||
w.mu.Unlock()
|
|
||||||
if watchEntry == nil {
|
|
||||||
if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
|
|
||||||
syscall.CloseHandle(ino.handle)
|
|
||||||
return os.NewSyscallError("CreateIoCompletionPort", e)
|
|
||||||
}
|
|
||||||
watchEntry = &watch{
|
|
||||||
ino: ino,
|
|
||||||
path: dir,
|
|
||||||
names: make(map[string]uint64),
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
w.watches.set(ino, watchEntry)
|
|
||||||
w.mu.Unlock()
|
|
||||||
flags |= provisional
|
|
||||||
} else {
|
|
||||||
syscall.CloseHandle(ino.handle)
|
|
||||||
}
|
|
||||||
if pathname == dir {
|
|
||||||
watchEntry.mask |= flags
|
|
||||||
} else {
|
|
||||||
watchEntry.names[filepath.Base(pathname)] |= flags
|
|
||||||
}
|
|
||||||
if err = w.startRead(watchEntry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pathname == dir {
|
|
||||||
watchEntry.mask &= ^provisional
|
|
||||||
} else {
|
|
||||||
watchEntry.names[filepath.Base(pathname)] &= ^provisional
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) remWatch(pathname string) error {
|
|
||||||
dir, err := getDir(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ino, err := getIno(dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
watch := w.watches.get(ino)
|
|
||||||
w.mu.Unlock()
|
|
||||||
if watch == nil {
|
|
||||||
return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
|
|
||||||
}
|
|
||||||
if pathname == dir {
|
|
||||||
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
|
|
||||||
watch.mask = 0
|
|
||||||
} else {
|
|
||||||
name := filepath.Base(pathname)
|
|
||||||
w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
return w.startRead(watch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) deleteWatch(watch *watch) {
|
|
||||||
for name, mask := range watch.names {
|
|
||||||
if mask&provisional == 0 {
|
|
||||||
w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
|
|
||||||
}
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
if watch.mask != 0 {
|
|
||||||
if watch.mask&provisional == 0 {
|
|
||||||
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
|
|
||||||
}
|
|
||||||
watch.mask = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must run within the I/O thread.
|
|
||||||
func (w *Watcher) startRead(watch *watch) error {
|
|
||||||
if e := syscall.CancelIo(watch.ino.handle); e != nil {
|
|
||||||
w.Errors <- os.NewSyscallError("CancelIo", e)
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
}
|
|
||||||
mask := toWindowsFlags(watch.mask)
|
|
||||||
for _, m := range watch.names {
|
|
||||||
mask |= toWindowsFlags(m)
|
|
||||||
}
|
|
||||||
if mask == 0 {
|
|
||||||
if e := syscall.CloseHandle(watch.ino.handle); e != nil {
|
|
||||||
w.Errors <- os.NewSyscallError("CloseHandle", e)
|
|
||||||
}
|
|
||||||
w.mu.Lock()
|
|
||||||
delete(w.watches[watch.ino.volume], watch.ino.index)
|
|
||||||
w.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
|
|
||||||
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
|
|
||||||
if e != nil {
|
|
||||||
err := os.NewSyscallError("ReadDirectoryChanges", e)
|
|
||||||
if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
|
|
||||||
// Watched directory was probably removed
|
|
||||||
if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
|
|
||||||
if watch.mask&sysFSONESHOT != 0 {
|
|
||||||
watch.mask = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
w.startRead(watch)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readEvents reads from the I/O completion port, converts the
|
|
||||||
// received events into Event objects and sends them via the Events channel.
|
|
||||||
// Entry point to the I/O thread.
|
|
||||||
func (w *Watcher) readEvents() {
|
|
||||||
var (
|
|
||||||
n, key uint32
|
|
||||||
ov *syscall.Overlapped
|
|
||||||
)
|
|
||||||
runtime.LockOSThread()
|
|
||||||
|
|
||||||
for {
|
|
||||||
e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
|
|
||||||
watch := (*watch)(unsafe.Pointer(ov))
|
|
||||||
|
|
||||||
if watch == nil {
|
|
||||||
select {
|
|
||||||
case ch := <-w.quit:
|
|
||||||
w.mu.Lock()
|
|
||||||
var indexes []indexMap
|
|
||||||
for _, index := range w.watches {
|
|
||||||
indexes = append(indexes, index)
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
for _, index := range indexes {
|
|
||||||
for _, watch := range index {
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
w.startRead(watch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if e := syscall.CloseHandle(w.port); e != nil {
|
|
||||||
err = os.NewSyscallError("CloseHandle", e)
|
|
||||||
}
|
|
||||||
close(w.Events)
|
|
||||||
close(w.Errors)
|
|
||||||
ch <- err
|
|
||||||
return
|
|
||||||
case in := <-w.input:
|
|
||||||
switch in.op {
|
|
||||||
case opAddWatch:
|
|
||||||
in.reply <- w.addWatch(in.path, uint64(in.flags))
|
|
||||||
case opRemoveWatch:
|
|
||||||
in.reply <- w.remWatch(in.path)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch e {
|
|
||||||
case syscall.ERROR_MORE_DATA:
|
|
||||||
if watch == nil {
|
|
||||||
w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
|
|
||||||
} else {
|
|
||||||
// The i/o succeeded but the buffer is full.
|
|
||||||
// In theory we should be building up a full packet.
|
|
||||||
// In practice we can get away with just carrying on.
|
|
||||||
n = uint32(unsafe.Sizeof(watch.buf))
|
|
||||||
}
|
|
||||||
case syscall.ERROR_ACCESS_DENIED:
|
|
||||||
// Watched directory was probably removed
|
|
||||||
w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
|
|
||||||
w.deleteWatch(watch)
|
|
||||||
w.startRead(watch)
|
|
||||||
continue
|
|
||||||
case syscall.ERROR_OPERATION_ABORTED:
|
|
||||||
// CancelIo was called on this handle
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
|
|
||||||
continue
|
|
||||||
case nil:
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset uint32
|
|
||||||
for {
|
|
||||||
if n == 0 {
|
|
||||||
w.Events <- newEvent("", sysFSQOVERFLOW)
|
|
||||||
w.Errors <- errors.New("short read in readEvents()")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point "raw" to the event in the buffer
|
|
||||||
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
|
|
||||||
// TODO: Consider using unsafe.Slice that is available from go1.17
|
|
||||||
// https://stackoverflow.com/questions/51187973/how-to-create-an-array-or-a-slice-from-an-array-unsafe-pointer-in-golang
|
|
||||||
// instead of using a fixed syscall.MAX_PATH buf, we create a buf that is the size of the path name
|
|
||||||
size := int(raw.FileNameLength / 2)
|
|
||||||
var buf []uint16
|
|
||||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
|
||||||
sh.Data = uintptr(unsafe.Pointer(&raw.FileName))
|
|
||||||
sh.Len = size
|
|
||||||
sh.Cap = size
|
|
||||||
name := syscall.UTF16ToString(buf)
|
|
||||||
fullname := filepath.Join(watch.path, name)
|
|
||||||
|
|
||||||
var mask uint64
|
|
||||||
switch raw.Action {
|
|
||||||
case syscall.FILE_ACTION_REMOVED:
|
|
||||||
mask = sysFSDELETESELF
|
|
||||||
case syscall.FILE_ACTION_MODIFIED:
|
|
||||||
mask = sysFSMODIFY
|
|
||||||
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
|
|
||||||
watch.rename = name
|
|
||||||
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
|
|
||||||
if watch.names[watch.rename] != 0 {
|
|
||||||
watch.names[name] |= watch.names[watch.rename]
|
|
||||||
delete(watch.names, watch.rename)
|
|
||||||
mask = sysFSMOVESELF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendNameEvent := func() {
|
|
||||||
if w.sendEvent(fullname, watch.names[name]&mask) {
|
|
||||||
if watch.names[name]&sysFSONESHOT != 0 {
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
|
|
||||||
sendNameEvent()
|
|
||||||
}
|
|
||||||
if raw.Action == syscall.FILE_ACTION_REMOVED {
|
|
||||||
w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
|
|
||||||
delete(watch.names, name)
|
|
||||||
}
|
|
||||||
if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
|
|
||||||
if watch.mask&sysFSONESHOT != 0 {
|
|
||||||
watch.mask = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
|
|
||||||
fullname = filepath.Join(watch.path, watch.rename)
|
|
||||||
sendNameEvent()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the next event in the buffer
|
|
||||||
if raw.NextEntryOffset == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
offset += raw.NextEntryOffset
|
|
||||||
|
|
||||||
// Error!
|
|
||||||
if offset >= n {
|
|
||||||
w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.startRead(watch); err != nil {
|
|
||||||
w.Errors <- err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
|
|
||||||
if mask == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
event := newEvent(name, uint32(mask))
|
|
||||||
select {
|
|
||||||
case ch := <-w.quit:
|
|
||||||
w.quit <- ch
|
|
||||||
case w.Events <- event:
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func toWindowsFlags(mask uint64) uint32 {
|
|
||||||
var m uint32
|
|
||||||
if mask&sysFSACCESS != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
|
|
||||||
}
|
|
||||||
if mask&sysFSMODIFY != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
|
|
||||||
}
|
|
||||||
if mask&sysFSATTRIB != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
|
|
||||||
}
|
|
||||||
if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
|
|
||||||
m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func toFSnotifyFlags(action uint32) uint64 {
|
|
||||||
switch action {
|
|
||||||
case syscall.FILE_ACTION_ADDED:
|
|
||||||
return sysFSCREATE
|
|
||||||
case syscall.FILE_ACTION_REMOVED:
|
|
||||||
return sysFSDELETE
|
|
||||||
case syscall.FILE_ACTION_MODIFIED:
|
|
||||||
return sysFSMODIFY
|
|
||||||
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
|
|
||||||
return sysFSMOVEDFROM
|
|
||||||
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
|
|
||||||
return sysFSMOVEDTO
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
7
vendor/github.com/google/pprof/AUTHORS
generated
vendored
Normal file
7
vendor/github.com/google/pprof/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This is the official list of pprof authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS files.
|
||||||
|
# See the latter for an explanation.
|
||||||
|
# Names should be added to this file as:
|
||||||
|
# Name or Organization <email address>
|
||||||
|
# The email address is not required for organizations.
|
||||||
|
Google Inc.
|
16
vendor/github.com/google/pprof/CONTRIBUTORS
generated
vendored
Normal file
16
vendor/github.com/google/pprof/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# People who have agreed to one of the CLAs and can contribute patches.
|
||||||
|
# The AUTHORS file lists the copyright holders; this file
|
||||||
|
# lists people. For example, Google employees are listed here
|
||||||
|
# but not in AUTHORS, because Google holds the copyright.
|
||||||
|
#
|
||||||
|
# https://developers.google.com/open-source/cla/individual
|
||||||
|
# https://developers.google.com/open-source/cla/corporate
|
||||||
|
#
|
||||||
|
# Names should be added to this file as:
|
||||||
|
# Name <email address>
|
||||||
|
Raul Silvera <rsilvera@google.com>
|
||||||
|
Tipp Moseley <tipp@google.com>
|
||||||
|
Hyoun Kyu Cho <netforce@google.com>
|
||||||
|
Martin Spier <spiermar@gmail.com>
|
||||||
|
Taco de Wolff <tacodewolff@gmail.com>
|
||||||
|
Andrew Hunter <andrewhhunter@gmail.com>
|
202
vendor/github.com/google/pprof/LICENSE
generated
vendored
Normal file
202
vendor/github.com/google/pprof/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
588
vendor/github.com/google/pprof/profile/encode.go
generated
vendored
Normal file
588
vendor/github.com/google/pprof/profile/encode.go
generated
vendored
Normal file
|
@ -0,0 +1,588 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Profile) decoder() []decoder {
|
||||||
|
return profileDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// preEncode populates the unexported fields to be used by encode
|
||||||
|
// (with suffix X) from the corresponding exported fields. The
|
||||||
|
// exported fields are cleared up to facilitate testing.
|
||||||
|
func (p *Profile) preEncode() {
|
||||||
|
strings := make(map[string]int)
|
||||||
|
addString(strings, "")
|
||||||
|
|
||||||
|
for _, st := range p.SampleType {
|
||||||
|
st.typeX = addString(strings, st.Type)
|
||||||
|
st.unitX = addString(strings, st.Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
s.labelX = nil
|
||||||
|
var keys []string
|
||||||
|
for k := range s.Label {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, k := range keys {
|
||||||
|
vs := s.Label[k]
|
||||||
|
for _, v := range vs {
|
||||||
|
s.labelX = append(s.labelX,
|
||||||
|
label{
|
||||||
|
keyX: addString(strings, k),
|
||||||
|
strX: addString(strings, v),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var numKeys []string
|
||||||
|
for k := range s.NumLabel {
|
||||||
|
numKeys = append(numKeys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(numKeys)
|
||||||
|
for _, k := range numKeys {
|
||||||
|
keyX := addString(strings, k)
|
||||||
|
vs := s.NumLabel[k]
|
||||||
|
units := s.NumUnit[k]
|
||||||
|
for i, v := range vs {
|
||||||
|
var unitX int64
|
||||||
|
if len(units) != 0 {
|
||||||
|
unitX = addString(strings, units[i])
|
||||||
|
}
|
||||||
|
s.labelX = append(s.labelX,
|
||||||
|
label{
|
||||||
|
keyX: keyX,
|
||||||
|
numX: v,
|
||||||
|
unitX: unitX,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.locationIDX = make([]uint64, len(s.Location))
|
||||||
|
for i, loc := range s.Location {
|
||||||
|
s.locationIDX[i] = loc.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range p.Mapping {
|
||||||
|
m.fileX = addString(strings, m.File)
|
||||||
|
m.buildIDX = addString(strings, m.BuildID)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range p.Location {
|
||||||
|
for i, ln := range l.Line {
|
||||||
|
if ln.Function != nil {
|
||||||
|
l.Line[i].functionIDX = ln.Function.ID
|
||||||
|
} else {
|
||||||
|
l.Line[i].functionIDX = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.Mapping != nil {
|
||||||
|
l.mappingIDX = l.Mapping.ID
|
||||||
|
} else {
|
||||||
|
l.mappingIDX = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, f := range p.Function {
|
||||||
|
f.nameX = addString(strings, f.Name)
|
||||||
|
f.systemNameX = addString(strings, f.SystemName)
|
||||||
|
f.filenameX = addString(strings, f.Filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.dropFramesX = addString(strings, p.DropFrames)
|
||||||
|
p.keepFramesX = addString(strings, p.KeepFrames)
|
||||||
|
|
||||||
|
if pt := p.PeriodType; pt != nil {
|
||||||
|
pt.typeX = addString(strings, pt.Type)
|
||||||
|
pt.unitX = addString(strings, pt.Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.commentX = nil
|
||||||
|
for _, c := range p.Comments {
|
||||||
|
p.commentX = append(p.commentX, addString(strings, c))
|
||||||
|
}
|
||||||
|
|
||||||
|
p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
|
||||||
|
|
||||||
|
p.stringTable = make([]string, len(strings))
|
||||||
|
for s, i := range strings {
|
||||||
|
p.stringTable[i] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Profile) encode(b *buffer) {
|
||||||
|
for _, x := range p.SampleType {
|
||||||
|
encodeMessage(b, 1, x)
|
||||||
|
}
|
||||||
|
for _, x := range p.Sample {
|
||||||
|
encodeMessage(b, 2, x)
|
||||||
|
}
|
||||||
|
for _, x := range p.Mapping {
|
||||||
|
encodeMessage(b, 3, x)
|
||||||
|
}
|
||||||
|
for _, x := range p.Location {
|
||||||
|
encodeMessage(b, 4, x)
|
||||||
|
}
|
||||||
|
for _, x := range p.Function {
|
||||||
|
encodeMessage(b, 5, x)
|
||||||
|
}
|
||||||
|
encodeStrings(b, 6, p.stringTable)
|
||||||
|
encodeInt64Opt(b, 7, p.dropFramesX)
|
||||||
|
encodeInt64Opt(b, 8, p.keepFramesX)
|
||||||
|
encodeInt64Opt(b, 9, p.TimeNanos)
|
||||||
|
encodeInt64Opt(b, 10, p.DurationNanos)
|
||||||
|
if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) {
|
||||||
|
encodeMessage(b, 11, p.PeriodType)
|
||||||
|
}
|
||||||
|
encodeInt64Opt(b, 12, p.Period)
|
||||||
|
encodeInt64s(b, 13, p.commentX)
|
||||||
|
encodeInt64(b, 14, p.defaultSampleTypeX)
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
// repeated ValueType sample_type = 1
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
x := new(ValueType)
|
||||||
|
pp := m.(*Profile)
|
||||||
|
pp.SampleType = append(pp.SampleType, x)
|
||||||
|
return decodeMessage(b, x)
|
||||||
|
},
|
||||||
|
// repeated Sample sample = 2
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
x := new(Sample)
|
||||||
|
pp := m.(*Profile)
|
||||||
|
pp.Sample = append(pp.Sample, x)
|
||||||
|
return decodeMessage(b, x)
|
||||||
|
},
|
||||||
|
// repeated Mapping mapping = 3
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
x := new(Mapping)
|
||||||
|
pp := m.(*Profile)
|
||||||
|
pp.Mapping = append(pp.Mapping, x)
|
||||||
|
return decodeMessage(b, x)
|
||||||
|
},
|
||||||
|
// repeated Location location = 4
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
x := new(Location)
|
||||||
|
x.Line = b.tmpLines[:0] // Use shared space temporarily
|
||||||
|
pp := m.(*Profile)
|
||||||
|
pp.Location = append(pp.Location, x)
|
||||||
|
err := decodeMessage(b, x)
|
||||||
|
b.tmpLines = x.Line[:0]
|
||||||
|
// Copy to shrink size and detach from shared space.
|
||||||
|
x.Line = append([]Line(nil), x.Line...)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
// repeated Function function = 5
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
x := new(Function)
|
||||||
|
pp := m.(*Profile)
|
||||||
|
pp.Function = append(pp.Function, x)
|
||||||
|
return decodeMessage(b, x)
|
||||||
|
},
|
||||||
|
// repeated string string_table = 6
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
err := decodeStrings(b, &m.(*Profile).stringTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if m.(*Profile).stringTable[0] != "" {
|
||||||
|
return errors.New("string_table[0] must be ''")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// int64 drop_frames = 7
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) },
|
||||||
|
// int64 keep_frames = 8
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) },
|
||||||
|
// int64 time_nanos = 9
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
if m.(*Profile).TimeNanos != 0 {
|
||||||
|
return errConcatProfile
|
||||||
|
}
|
||||||
|
return decodeInt64(b, &m.(*Profile).TimeNanos)
|
||||||
|
},
|
||||||
|
// int64 duration_nanos = 10
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) },
|
||||||
|
// ValueType period_type = 11
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
x := new(ValueType)
|
||||||
|
pp := m.(*Profile)
|
||||||
|
pp.PeriodType = x
|
||||||
|
return decodeMessage(b, x)
|
||||||
|
},
|
||||||
|
// int64 period = 12
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) },
|
||||||
|
// repeated int64 comment = 13
|
||||||
|
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
|
||||||
|
// int64 defaultSampleType = 14
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) },
|
||||||
|
}
|
||||||
|
|
||||||
|
// postDecode takes the unexported fields populated by decode (with
|
||||||
|
// suffix X) and populates the corresponding exported fields.
|
||||||
|
// The unexported fields are cleared up to facilitate testing.
|
||||||
|
func (p *Profile) postDecode() error {
|
||||||
|
var err error
|
||||||
|
mappings := make(map[uint64]*Mapping, len(p.Mapping))
|
||||||
|
mappingIds := make([]*Mapping, len(p.Mapping)+1)
|
||||||
|
for _, m := range p.Mapping {
|
||||||
|
m.File, err = getString(p.stringTable, &m.fileX, err)
|
||||||
|
m.BuildID, err = getString(p.stringTable, &m.buildIDX, err)
|
||||||
|
if m.ID < uint64(len(mappingIds)) {
|
||||||
|
mappingIds[m.ID] = m
|
||||||
|
} else {
|
||||||
|
mappings[m.ID] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this a main linux kernel mapping with a relocation symbol suffix
|
||||||
|
// ("[kernel.kallsyms]_text"), extract said suffix.
|
||||||
|
// It is fairly hacky to handle at this level, but the alternatives appear even worse.
|
||||||
|
if strings.HasPrefix(m.File, "[kernel.kallsyms]") {
|
||||||
|
m.KernelRelocationSymbol = strings.ReplaceAll(m.File, "[kernel.kallsyms]", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
functions := make(map[uint64]*Function, len(p.Function))
|
||||||
|
functionIds := make([]*Function, len(p.Function)+1)
|
||||||
|
for _, f := range p.Function {
|
||||||
|
f.Name, err = getString(p.stringTable, &f.nameX, err)
|
||||||
|
f.SystemName, err = getString(p.stringTable, &f.systemNameX, err)
|
||||||
|
f.Filename, err = getString(p.stringTable, &f.filenameX, err)
|
||||||
|
if f.ID < uint64(len(functionIds)) {
|
||||||
|
functionIds[f.ID] = f
|
||||||
|
} else {
|
||||||
|
functions[f.ID] = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locations := make(map[uint64]*Location, len(p.Location))
|
||||||
|
locationIds := make([]*Location, len(p.Location)+1)
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if id := l.mappingIDX; id < uint64(len(mappingIds)) {
|
||||||
|
l.Mapping = mappingIds[id]
|
||||||
|
} else {
|
||||||
|
l.Mapping = mappings[id]
|
||||||
|
}
|
||||||
|
l.mappingIDX = 0
|
||||||
|
for i, ln := range l.Line {
|
||||||
|
if id := ln.functionIDX; id != 0 {
|
||||||
|
l.Line[i].functionIDX = 0
|
||||||
|
if id < uint64(len(functionIds)) {
|
||||||
|
l.Line[i].Function = functionIds[id]
|
||||||
|
} else {
|
||||||
|
l.Line[i].Function = functions[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.ID < uint64(len(locationIds)) {
|
||||||
|
locationIds[l.ID] = l
|
||||||
|
} else {
|
||||||
|
locations[l.ID] = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, st := range p.SampleType {
|
||||||
|
st.Type, err = getString(p.stringTable, &st.typeX, err)
|
||||||
|
st.Unit, err = getString(p.stringTable, &st.unitX, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-allocate space for all locations.
|
||||||
|
numLocations := 0
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
numLocations += len(s.locationIDX)
|
||||||
|
}
|
||||||
|
locBuffer := make([]*Location, numLocations)
|
||||||
|
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
if len(s.labelX) > 0 {
|
||||||
|
labels := make(map[string][]string, len(s.labelX))
|
||||||
|
numLabels := make(map[string][]int64, len(s.labelX))
|
||||||
|
numUnits := make(map[string][]string, len(s.labelX))
|
||||||
|
for _, l := range s.labelX {
|
||||||
|
var key, value string
|
||||||
|
key, err = getString(p.stringTable, &l.keyX, err)
|
||||||
|
if l.strX != 0 {
|
||||||
|
value, err = getString(p.stringTable, &l.strX, err)
|
||||||
|
labels[key] = append(labels[key], value)
|
||||||
|
} else if l.numX != 0 || l.unitX != 0 {
|
||||||
|
numValues := numLabels[key]
|
||||||
|
units := numUnits[key]
|
||||||
|
if l.unitX != 0 {
|
||||||
|
var unit string
|
||||||
|
unit, err = getString(p.stringTable, &l.unitX, err)
|
||||||
|
units = padStringArray(units, len(numValues))
|
||||||
|
numUnits[key] = append(units, unit)
|
||||||
|
}
|
||||||
|
numLabels[key] = append(numLabels[key], l.numX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(labels) > 0 {
|
||||||
|
s.Label = labels
|
||||||
|
}
|
||||||
|
if len(numLabels) > 0 {
|
||||||
|
s.NumLabel = numLabels
|
||||||
|
for key, units := range numUnits {
|
||||||
|
if len(units) > 0 {
|
||||||
|
numUnits[key] = padStringArray(units, len(numLabels[key]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.NumUnit = numUnits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Location = locBuffer[:len(s.locationIDX)]
|
||||||
|
locBuffer = locBuffer[len(s.locationIDX):]
|
||||||
|
for i, lid := range s.locationIDX {
|
||||||
|
if lid < uint64(len(locationIds)) {
|
||||||
|
s.Location[i] = locationIds[lid]
|
||||||
|
} else {
|
||||||
|
s.Location[i] = locations[lid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.locationIDX = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err)
|
||||||
|
p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err)
|
||||||
|
|
||||||
|
if pt := p.PeriodType; pt == nil {
|
||||||
|
p.PeriodType = &ValueType{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pt := p.PeriodType; pt != nil {
|
||||||
|
pt.Type, err = getString(p.stringTable, &pt.typeX, err)
|
||||||
|
pt.Unit, err = getString(p.stringTable, &pt.unitX, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range p.commentX {
|
||||||
|
var c string
|
||||||
|
c, err = getString(p.stringTable, &i, err)
|
||||||
|
p.Comments = append(p.Comments, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.commentX = nil
|
||||||
|
p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
|
||||||
|
p.stringTable = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// padStringArray pads arr with enough empty strings to make arr
|
||||||
|
// length l when arr's length is less than l.
|
||||||
|
func padStringArray(arr []string, l int) []string {
|
||||||
|
if l <= len(arr) {
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return append(arr, make([]string, l-len(arr))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ValueType) decoder() []decoder {
|
||||||
|
return valueTypeDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ValueType) encode(b *buffer) {
|
||||||
|
encodeInt64Opt(b, 1, p.typeX)
|
||||||
|
encodeInt64Opt(b, 2, p.unitX)
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueTypeDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
// optional int64 type = 1
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) },
|
||||||
|
// optional int64 unit = 2
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Sample) decoder() []decoder {
|
||||||
|
return sampleDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Sample) encode(b *buffer) {
|
||||||
|
encodeUint64s(b, 1, p.locationIDX)
|
||||||
|
encodeInt64s(b, 2, p.Value)
|
||||||
|
for _, x := range p.labelX {
|
||||||
|
encodeMessage(b, 3, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sampleDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
// repeated uint64 location = 1
|
||||||
|
func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) },
|
||||||
|
// repeated int64 value = 2
|
||||||
|
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) },
|
||||||
|
// repeated Label label = 3
|
||||||
|
func(b *buffer, m message) error {
|
||||||
|
s := m.(*Sample)
|
||||||
|
n := len(s.labelX)
|
||||||
|
s.labelX = append(s.labelX, label{})
|
||||||
|
return decodeMessage(b, &s.labelX[n])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p label) decoder() []decoder {
|
||||||
|
return labelDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p label) encode(b *buffer) {
|
||||||
|
encodeInt64Opt(b, 1, p.keyX)
|
||||||
|
encodeInt64Opt(b, 2, p.strX)
|
||||||
|
encodeInt64Opt(b, 3, p.numX)
|
||||||
|
encodeInt64Opt(b, 4, p.unitX)
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
// optional int64 key = 1
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).keyX) },
|
||||||
|
// optional int64 str = 2
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).strX) },
|
||||||
|
// optional int64 num = 3
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).numX) },
|
||||||
|
// optional int64 num = 4
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).unitX) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Mapping) decoder() []decoder {
|
||||||
|
return mappingDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Mapping) encode(b *buffer) {
|
||||||
|
encodeUint64Opt(b, 1, p.ID)
|
||||||
|
encodeUint64Opt(b, 2, p.Start)
|
||||||
|
encodeUint64Opt(b, 3, p.Limit)
|
||||||
|
encodeUint64Opt(b, 4, p.Offset)
|
||||||
|
encodeInt64Opt(b, 5, p.fileX)
|
||||||
|
encodeInt64Opt(b, 6, p.buildIDX)
|
||||||
|
encodeBoolOpt(b, 7, p.HasFunctions)
|
||||||
|
encodeBoolOpt(b, 8, p.HasFilenames)
|
||||||
|
encodeBoolOpt(b, 9, p.HasLineNumbers)
|
||||||
|
encodeBoolOpt(b, 10, p.HasInlineFrames)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mappingDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6
|
||||||
|
func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7
|
||||||
|
func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8
|
||||||
|
func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9
|
||||||
|
func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Location) decoder() []decoder {
|
||||||
|
return locationDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Location) encode(b *buffer) {
|
||||||
|
encodeUint64Opt(b, 1, p.ID)
|
||||||
|
encodeUint64Opt(b, 2, p.mappingIDX)
|
||||||
|
encodeUint64Opt(b, 3, p.Address)
|
||||||
|
for i := range p.Line {
|
||||||
|
encodeMessage(b, 4, &p.Line[i])
|
||||||
|
}
|
||||||
|
encodeBoolOpt(b, 5, p.IsFolded)
|
||||||
|
}
|
||||||
|
|
||||||
|
var locationDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1;
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2;
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3;
|
||||||
|
func(b *buffer, m message) error { // repeated Line line = 4
|
||||||
|
pp := m.(*Location)
|
||||||
|
n := len(pp.Line)
|
||||||
|
pp.Line = append(pp.Line, Line{})
|
||||||
|
return decodeMessage(b, &pp.Line[n])
|
||||||
|
},
|
||||||
|
func(b *buffer, m message) error { return decodeBool(b, &m.(*Location).IsFolded) }, // optional bool is_folded = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Line) decoder() []decoder {
|
||||||
|
return lineDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Line) encode(b *buffer) {
|
||||||
|
encodeUint64Opt(b, 1, p.functionIDX)
|
||||||
|
encodeInt64Opt(b, 2, p.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lineDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
// optional uint64 function_id = 1
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) },
|
||||||
|
// optional int64 line = 2
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Function) decoder() []decoder {
|
||||||
|
return functionDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Function) encode(b *buffer) {
|
||||||
|
encodeUint64Opt(b, 1, p.ID)
|
||||||
|
encodeInt64Opt(b, 2, p.nameX)
|
||||||
|
encodeInt64Opt(b, 3, p.systemNameX)
|
||||||
|
encodeInt64Opt(b, 4, p.filenameX)
|
||||||
|
encodeInt64Opt(b, 5, p.StartLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
var functionDecoder = []decoder{
|
||||||
|
nil, // 0
|
||||||
|
// optional uint64 id = 1
|
||||||
|
func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) },
|
||||||
|
// optional int64 function_name = 2
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) },
|
||||||
|
// optional int64 function_system_name = 3
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) },
|
||||||
|
// repeated int64 filename = 4
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) },
|
||||||
|
// optional int64 start_line = 5
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func addString(strings map[string]int, s string) int64 {
|
||||||
|
i, ok := strings[s]
|
||||||
|
if !ok {
|
||||||
|
i = len(strings)
|
||||||
|
strings[s] = i
|
||||||
|
}
|
||||||
|
return int64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getString(strings []string, strng *int64, err error) (string, error) {
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s := int(*strng)
|
||||||
|
if s < 0 || s >= len(strings) {
|
||||||
|
return "", errMalformed
|
||||||
|
}
|
||||||
|
*strng = 0
|
||||||
|
return strings[s], nil
|
||||||
|
}
|
274
vendor/github.com/google/pprof/profile/filter.go
generated
vendored
Normal file
274
vendor/github.com/google/pprof/profile/filter.go
generated
vendored
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 profile
|
||||||
|
|
||||||
|
// Implements methods to filter samples from profiles.
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// FilterSamplesByName filters the samples in a profile and only keeps
|
||||||
|
// samples where at least one frame matches focus but none match ignore.
|
||||||
|
// Returns true is the corresponding regexp matched at least one sample.
|
||||||
|
func (p *Profile) FilterSamplesByName(focus, ignore, hide, show *regexp.Regexp) (fm, im, hm, hnm bool) {
|
||||||
|
if focus == nil && ignore == nil && hide == nil && show == nil {
|
||||||
|
fm = true // Missing focus implies a match
|
||||||
|
return
|
||||||
|
}
|
||||||
|
focusOrIgnore := make(map[uint64]bool)
|
||||||
|
hidden := make(map[uint64]bool)
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if ignore != nil && l.matchesName(ignore) {
|
||||||
|
im = true
|
||||||
|
focusOrIgnore[l.ID] = false
|
||||||
|
} else if focus == nil || l.matchesName(focus) {
|
||||||
|
fm = true
|
||||||
|
focusOrIgnore[l.ID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if hide != nil && l.matchesName(hide) {
|
||||||
|
hm = true
|
||||||
|
l.Line = l.unmatchedLines(hide)
|
||||||
|
if len(l.Line) == 0 {
|
||||||
|
hidden[l.ID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if show != nil {
|
||||||
|
l.Line = l.matchedLines(show)
|
||||||
|
if len(l.Line) == 0 {
|
||||||
|
hidden[l.ID] = true
|
||||||
|
} else {
|
||||||
|
hnm = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := make([]*Sample, 0, len(p.Sample))
|
||||||
|
for _, sample := range p.Sample {
|
||||||
|
if focusedAndNotIgnored(sample.Location, focusOrIgnore) {
|
||||||
|
if len(hidden) > 0 {
|
||||||
|
var locs []*Location
|
||||||
|
for _, loc := range sample.Location {
|
||||||
|
if !hidden[loc.ID] {
|
||||||
|
locs = append(locs, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(locs) == 0 {
|
||||||
|
// Remove sample with no locations (by not adding it to s).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sample.Location = locs
|
||||||
|
}
|
||||||
|
s = append(s, sample)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Sample = s
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowFrom drops all stack frames above the highest matching frame and returns
|
||||||
|
// whether a match was found. If showFrom is nil it returns false and does not
|
||||||
|
// modify the profile.
|
||||||
|
//
|
||||||
|
// Example: consider a sample with frames [A, B, C, B], where A is the root.
|
||||||
|
// ShowFrom(nil) returns false and has frames [A, B, C, B].
|
||||||
|
// ShowFrom(A) returns true and has frames [A, B, C, B].
|
||||||
|
// ShowFrom(B) returns true and has frames [B, C, B].
|
||||||
|
// ShowFrom(C) returns true and has frames [C, B].
|
||||||
|
// ShowFrom(D) returns false and drops the sample because no frames remain.
|
||||||
|
func (p *Profile) ShowFrom(showFrom *regexp.Regexp) (matched bool) {
|
||||||
|
if showFrom == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// showFromLocs stores location IDs that matched ShowFrom.
|
||||||
|
showFromLocs := make(map[uint64]bool)
|
||||||
|
// Apply to locations.
|
||||||
|
for _, loc := range p.Location {
|
||||||
|
if filterShowFromLocation(loc, showFrom) {
|
||||||
|
showFromLocs[loc.ID] = true
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For all samples, strip locations after the highest matching one.
|
||||||
|
s := make([]*Sample, 0, len(p.Sample))
|
||||||
|
for _, sample := range p.Sample {
|
||||||
|
for i := len(sample.Location) - 1; i >= 0; i-- {
|
||||||
|
if showFromLocs[sample.Location[i].ID] {
|
||||||
|
sample.Location = sample.Location[:i+1]
|
||||||
|
s = append(s, sample)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Sample = s
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterShowFromLocation tests a showFrom regex against a location, removes
|
||||||
|
// lines after the last match and returns whether a match was found. If the
|
||||||
|
// mapping is matched, then all lines are kept.
|
||||||
|
func filterShowFromLocation(loc *Location, showFrom *regexp.Regexp) bool {
|
||||||
|
if m := loc.Mapping; m != nil && showFrom.MatchString(m.File) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if i := loc.lastMatchedLineIndex(showFrom); i >= 0 {
|
||||||
|
loc.Line = loc.Line[:i+1]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastMatchedLineIndex returns the index of the last line that matches a regex,
|
||||||
|
// or -1 if no match is found.
|
||||||
|
func (loc *Location) lastMatchedLineIndex(re *regexp.Regexp) int {
|
||||||
|
for i := len(loc.Line) - 1; i >= 0; i-- {
|
||||||
|
if fn := loc.Line[i].Function; fn != nil {
|
||||||
|
if re.MatchString(fn.Name) || re.MatchString(fn.Filename) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterTagsByName filters the tags in a profile and only keeps
|
||||||
|
// tags that match show and not hide.
|
||||||
|
func (p *Profile) FilterTagsByName(show, hide *regexp.Regexp) (sm, hm bool) {
|
||||||
|
matchRemove := func(name string) bool {
|
||||||
|
matchShow := show == nil || show.MatchString(name)
|
||||||
|
matchHide := hide != nil && hide.MatchString(name)
|
||||||
|
|
||||||
|
if matchShow {
|
||||||
|
sm = true
|
||||||
|
}
|
||||||
|
if matchHide {
|
||||||
|
hm = true
|
||||||
|
}
|
||||||
|
return !matchShow || matchHide
|
||||||
|
}
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
for lab := range s.Label {
|
||||||
|
if matchRemove(lab) {
|
||||||
|
delete(s.Label, lab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for lab := range s.NumLabel {
|
||||||
|
if matchRemove(lab) {
|
||||||
|
delete(s.NumLabel, lab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchesName returns whether the location matches the regular
|
||||||
|
// expression. It checks any available function names, file names, and
|
||||||
|
// mapping object filename.
|
||||||
|
func (loc *Location) matchesName(re *regexp.Regexp) bool {
|
||||||
|
for _, ln := range loc.Line {
|
||||||
|
if fn := ln.Function; fn != nil {
|
||||||
|
if re.MatchString(fn.Name) || re.MatchString(fn.Filename) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m := loc.Mapping; m != nil && re.MatchString(m.File) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmatchedLines returns the lines in the location that do not match
|
||||||
|
// the regular expression.
|
||||||
|
func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line {
|
||||||
|
if m := loc.Mapping; m != nil && re.MatchString(m.File) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var lines []Line
|
||||||
|
for _, ln := range loc.Line {
|
||||||
|
if fn := ln.Function; fn != nil {
|
||||||
|
if re.MatchString(fn.Name) || re.MatchString(fn.Filename) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines = append(lines, ln)
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchedLines returns the lines in the location that match
|
||||||
|
// the regular expression.
|
||||||
|
func (loc *Location) matchedLines(re *regexp.Regexp) []Line {
|
||||||
|
if m := loc.Mapping; m != nil && re.MatchString(m.File) {
|
||||||
|
return loc.Line
|
||||||
|
}
|
||||||
|
var lines []Line
|
||||||
|
for _, ln := range loc.Line {
|
||||||
|
if fn := ln.Function; fn != nil {
|
||||||
|
if !re.MatchString(fn.Name) && !re.MatchString(fn.Filename) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines = append(lines, ln)
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// focusedAndNotIgnored looks up a slice of ids against a map of
|
||||||
|
// focused/ignored locations. The map only contains locations that are
|
||||||
|
// explicitly focused or ignored. Returns whether there is at least
|
||||||
|
// one focused location but no ignored locations.
|
||||||
|
func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool {
|
||||||
|
var f bool
|
||||||
|
for _, loc := range locs {
|
||||||
|
if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore {
|
||||||
|
if focus {
|
||||||
|
// Found focused location. Must keep searching in case there
|
||||||
|
// is an ignored one as well.
|
||||||
|
f = true
|
||||||
|
} else {
|
||||||
|
// Found ignored location. Can return false right away.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagMatch selects tags for filtering
|
||||||
|
type TagMatch func(s *Sample) bool
|
||||||
|
|
||||||
|
// FilterSamplesByTag removes all samples from the profile, except
|
||||||
|
// those that match focus and do not match the ignore regular
|
||||||
|
// expression.
|
||||||
|
func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) {
|
||||||
|
samples := make([]*Sample, 0, len(p.Sample))
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
focused, ignored := true, false
|
||||||
|
if focus != nil {
|
||||||
|
focused = focus(s)
|
||||||
|
}
|
||||||
|
if ignore != nil {
|
||||||
|
ignored = ignore(s)
|
||||||
|
}
|
||||||
|
fm = fm || focused
|
||||||
|
im = im || ignored
|
||||||
|
if focused && !ignored {
|
||||||
|
samples = append(samples, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Sample = samples
|
||||||
|
return
|
||||||
|
}
|
64
vendor/github.com/google/pprof/profile/index.go
generated
vendored
Normal file
64
vendor/github.com/google/pprof/profile/index.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SampleIndexByName returns the appropriate index for a value of sample index.
|
||||||
|
// If numeric, it returns the number, otherwise it looks up the text in the
|
||||||
|
// profile sample types.
|
||||||
|
func (p *Profile) SampleIndexByName(sampleIndex string) (int, error) {
|
||||||
|
if sampleIndex == "" {
|
||||||
|
if dst := p.DefaultSampleType; dst != "" {
|
||||||
|
for i, t := range sampleTypes(p) {
|
||||||
|
if t == dst {
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// By default select the last sample value
|
||||||
|
return len(p.SampleType) - 1, nil
|
||||||
|
}
|
||||||
|
if i, err := strconv.Atoi(sampleIndex); err == nil {
|
||||||
|
if i < 0 || i >= len(p.SampleType) {
|
||||||
|
return 0, fmt.Errorf("sample_index %s is outside the range [0..%d]", sampleIndex, len(p.SampleType)-1)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the inuse_ prefix to support legacy pprof options
|
||||||
|
// "inuse_space" and "inuse_objects" for profiles containing types
|
||||||
|
// "space" and "objects".
|
||||||
|
noInuse := strings.TrimPrefix(sampleIndex, "inuse_")
|
||||||
|
for i, t := range p.SampleType {
|
||||||
|
if t.Type == sampleIndex || t.Type == noInuse {
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("sample_index %q must be one of: %v", sampleIndex, sampleTypes(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sampleTypes(p *Profile) []string {
|
||||||
|
types := make([]string, len(p.SampleType))
|
||||||
|
for i, t := range p.SampleType {
|
||||||
|
types[i] = t.Type
|
||||||
|
}
|
||||||
|
return types
|
||||||
|
}
|
315
vendor/github.com/google/pprof/profile/legacy_java_profile.go
generated
vendored
Normal file
315
vendor/github.com/google/pprof/profile/legacy_java_profile.go
generated
vendored
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// This file implements parsers to convert java legacy profiles into
|
||||||
|
// the profile.proto format.
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
attributeRx = regexp.MustCompile(`([\w ]+)=([\w ]+)`)
|
||||||
|
javaSampleRx = regexp.MustCompile(` *(\d+) +(\d+) +@ +([ x0-9a-f]*)`)
|
||||||
|
javaLocationRx = regexp.MustCompile(`^\s*0x([[:xdigit:]]+)\s+(.*)\s*$`)
|
||||||
|
javaLocationFileLineRx = regexp.MustCompile(`^(.*)\s+\((.+):(-?[[:digit:]]+)\)$`)
|
||||||
|
javaLocationPathRx = regexp.MustCompile(`^(.*)\s+\((.*)\)$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// javaCPUProfile returns a new Profile from profilez data.
|
||||||
|
// b is the profile bytes after the header, period is the profiling
|
||||||
|
// period, and parse is a function to parse 8-byte chunks from the
|
||||||
|
// profile in its native endianness.
|
||||||
|
func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) {
|
||||||
|
p := &Profile{
|
||||||
|
Period: period * 1000,
|
||||||
|
PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"},
|
||||||
|
SampleType: []*ValueType{{Type: "samples", Unit: "count"}, {Type: "cpu", Unit: "nanoseconds"}},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var locs map[uint64]*Location
|
||||||
|
if b, locs, err = parseCPUSamples(b, parse, false, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = parseJavaLocations(b, locs, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip out addresses for better merge.
|
||||||
|
if err = p.Aggregate(true, true, true, true, false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseJavaProfile returns a new profile from heapz or contentionz
|
||||||
|
// data. b is the profile bytes after the header.
|
||||||
|
func parseJavaProfile(b []byte) (*Profile, error) {
|
||||||
|
h := bytes.SplitAfterN(b, []byte("\n"), 2)
|
||||||
|
if len(h) < 2 {
|
||||||
|
return nil, errUnrecognized
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Profile{
|
||||||
|
PeriodType: &ValueType{},
|
||||||
|
}
|
||||||
|
header := string(bytes.TrimSpace(h[0]))
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var pType string
|
||||||
|
switch header {
|
||||||
|
case "--- heapz 1 ---":
|
||||||
|
pType = "heap"
|
||||||
|
case "--- contentionz 1 ---":
|
||||||
|
pType = "contention"
|
||||||
|
default:
|
||||||
|
return nil, errUnrecognized
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, err = parseJavaHeader(pType, h[1], p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var locs map[uint64]*Location
|
||||||
|
if b, locs, err = parseJavaSamples(pType, b, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = parseJavaLocations(b, locs, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip out addresses for better merge.
|
||||||
|
if err = p.Aggregate(true, true, true, true, false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseJavaHeader parses the attribute section on a java profile and
|
||||||
|
// populates a profile. Returns the remainder of the buffer after all
|
||||||
|
// attributes.
|
||||||
|
func parseJavaHeader(pType string, b []byte, p *Profile) ([]byte, error) {
|
||||||
|
nextNewLine := bytes.IndexByte(b, byte('\n'))
|
||||||
|
for nextNewLine != -1 {
|
||||||
|
line := string(bytes.TrimSpace(b[0:nextNewLine]))
|
||||||
|
if line != "" {
|
||||||
|
h := attributeRx.FindStringSubmatch(line)
|
||||||
|
if h == nil {
|
||||||
|
// Not a valid attribute, exit.
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
attribute, value := strings.TrimSpace(h[1]), strings.TrimSpace(h[2])
|
||||||
|
var err error
|
||||||
|
switch pType + "/" + attribute {
|
||||||
|
case "heap/format", "cpu/format", "contention/format":
|
||||||
|
if value != "java" {
|
||||||
|
return nil, errUnrecognized
|
||||||
|
}
|
||||||
|
case "heap/resolution":
|
||||||
|
p.SampleType = []*ValueType{
|
||||||
|
{Type: "inuse_objects", Unit: "count"},
|
||||||
|
{Type: "inuse_space", Unit: value},
|
||||||
|
}
|
||||||
|
case "contention/resolution":
|
||||||
|
p.SampleType = []*ValueType{
|
||||||
|
{Type: "contentions", Unit: "count"},
|
||||||
|
{Type: "delay", Unit: value},
|
||||||
|
}
|
||||||
|
case "contention/sampling period":
|
||||||
|
p.PeriodType = &ValueType{
|
||||||
|
Type: "contentions", Unit: "count",
|
||||||
|
}
|
||||||
|
if p.Period, err = strconv.ParseInt(value, 0, 64); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err)
|
||||||
|
}
|
||||||
|
case "contention/ms since reset":
|
||||||
|
millis, err := strconv.ParseInt(value, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err)
|
||||||
|
}
|
||||||
|
p.DurationNanos = millis * 1000 * 1000
|
||||||
|
default:
|
||||||
|
return nil, errUnrecognized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Grab next line.
|
||||||
|
b = b[nextNewLine+1:]
|
||||||
|
nextNewLine = bytes.IndexByte(b, byte('\n'))
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseJavaSamples parses the samples from a java profile and
|
||||||
|
// populates the Samples in a profile. Returns the remainder of the
|
||||||
|
// buffer after the samples.
|
||||||
|
func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*Location, error) {
|
||||||
|
nextNewLine := bytes.IndexByte(b, byte('\n'))
|
||||||
|
locs := make(map[uint64]*Location)
|
||||||
|
for nextNewLine != -1 {
|
||||||
|
line := string(bytes.TrimSpace(b[0:nextNewLine]))
|
||||||
|
if line != "" {
|
||||||
|
sample := javaSampleRx.FindStringSubmatch(line)
|
||||||
|
if sample == nil {
|
||||||
|
// Not a valid sample, exit.
|
||||||
|
return b, locs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java profiles have data/fields inverted compared to other
|
||||||
|
// profile types.
|
||||||
|
var err error
|
||||||
|
value1, value2, value3 := sample[2], sample[1], sample[3]
|
||||||
|
addrs, err := parseHexAddresses(value3)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sloc []*Location
|
||||||
|
for _, addr := range addrs {
|
||||||
|
loc := locs[addr]
|
||||||
|
if locs[addr] == nil {
|
||||||
|
loc = &Location{
|
||||||
|
Address: addr,
|
||||||
|
}
|
||||||
|
p.Location = append(p.Location, loc)
|
||||||
|
locs[addr] = loc
|
||||||
|
}
|
||||||
|
sloc = append(sloc, loc)
|
||||||
|
}
|
||||||
|
s := &Sample{
|
||||||
|
Value: make([]int64, 2),
|
||||||
|
Location: sloc,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Value[0], err = strconv.ParseInt(value1, 0, 64); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err)
|
||||||
|
}
|
||||||
|
if s.Value[1], err = strconv.ParseInt(value2, 0, 64); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pType {
|
||||||
|
case "heap":
|
||||||
|
const javaHeapzSamplingRate = 524288 // 512K
|
||||||
|
if s.Value[0] == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("parsing sample %s: second value must be non-zero", line)
|
||||||
|
}
|
||||||
|
s.NumLabel = map[string][]int64{"bytes": {s.Value[1] / s.Value[0]}}
|
||||||
|
s.Value[0], s.Value[1] = scaleHeapSample(s.Value[0], s.Value[1], javaHeapzSamplingRate)
|
||||||
|
case "contention":
|
||||||
|
if period := p.Period; period != 0 {
|
||||||
|
s.Value[0] = s.Value[0] * p.Period
|
||||||
|
s.Value[1] = s.Value[1] * p.Period
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Sample = append(p.Sample, s)
|
||||||
|
}
|
||||||
|
// Grab next line.
|
||||||
|
b = b[nextNewLine+1:]
|
||||||
|
nextNewLine = bytes.IndexByte(b, byte('\n'))
|
||||||
|
}
|
||||||
|
return b, locs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseJavaLocations parses the location information in a java
|
||||||
|
// profile and populates the Locations in a profile. It uses the
|
||||||
|
// location addresses from the profile as both the ID of each
|
||||||
|
// location.
|
||||||
|
func parseJavaLocations(b []byte, locs map[uint64]*Location, p *Profile) error {
|
||||||
|
r := bytes.NewBuffer(b)
|
||||||
|
fns := make(map[string]*Function)
|
||||||
|
for {
|
||||||
|
line, err := r.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if line == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if line = strings.TrimSpace(line); line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jloc := javaLocationRx.FindStringSubmatch(line)
|
||||||
|
if len(jloc) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr, err := strconv.ParseUint(jloc[1], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing sample %s: %v", line, err)
|
||||||
|
}
|
||||||
|
loc := locs[addr]
|
||||||
|
if loc == nil {
|
||||||
|
// Unused/unseen
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var lineFunc, lineFile string
|
||||||
|
var lineNo int64
|
||||||
|
|
||||||
|
if fileLine := javaLocationFileLineRx.FindStringSubmatch(jloc[2]); len(fileLine) == 4 {
|
||||||
|
// Found a line of the form: "function (file:line)"
|
||||||
|
lineFunc, lineFile = fileLine[1], fileLine[2]
|
||||||
|
if n, err := strconv.ParseInt(fileLine[3], 10, 64); err == nil && n > 0 {
|
||||||
|
lineNo = n
|
||||||
|
}
|
||||||
|
} else if filePath := javaLocationPathRx.FindStringSubmatch(jloc[2]); len(filePath) == 3 {
|
||||||
|
// If there's not a file:line, it's a shared library path.
|
||||||
|
// The path isn't interesting, so just give the .so.
|
||||||
|
lineFunc, lineFile = filePath[1], filepath.Base(filePath[2])
|
||||||
|
} else if strings.Contains(jloc[2], "generated stub/JIT") {
|
||||||
|
lineFunc = "STUB"
|
||||||
|
} else {
|
||||||
|
// Treat whole line as the function name. This is used by the
|
||||||
|
// java agent for internal states such as "GC" or "VM".
|
||||||
|
lineFunc = jloc[2]
|
||||||
|
}
|
||||||
|
fn := fns[lineFunc]
|
||||||
|
|
||||||
|
if fn == nil {
|
||||||
|
fn = &Function{
|
||||||
|
Name: lineFunc,
|
||||||
|
SystemName: lineFunc,
|
||||||
|
Filename: lineFile,
|
||||||
|
}
|
||||||
|
fns[lineFunc] = fn
|
||||||
|
p.Function = append(p.Function, fn)
|
||||||
|
}
|
||||||
|
loc.Line = []Line{
|
||||||
|
{
|
||||||
|
Function: fn,
|
||||||
|
Line: lineNo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loc.Address = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
p.remapLocationIDs()
|
||||||
|
p.remapFunctionIDs()
|
||||||
|
p.remapMappingIDs()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
1228
vendor/github.com/google/pprof/profile/legacy_profile.go
generated
vendored
Normal file
1228
vendor/github.com/google/pprof/profile/legacy_profile.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
667
vendor/github.com/google/pprof/profile/merge.go
generated
vendored
Normal file
667
vendor/github.com/google/pprof/profile/merge.go
generated
vendored
Normal file
|
@ -0,0 +1,667 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compact performs garbage collection on a profile to remove any
|
||||||
|
// unreferenced fields. This is useful to reduce the size of a profile
|
||||||
|
// after samples or locations have been removed.
|
||||||
|
func (p *Profile) Compact() *Profile {
|
||||||
|
p, _ = Merge([]*Profile{p})
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges all the profiles in profs into a single Profile.
|
||||||
|
// Returns a new profile independent of the input profiles. The merged
|
||||||
|
// profile is compacted to eliminate unused samples, locations,
|
||||||
|
// functions and mappings. Profiles must have identical profile sample
|
||||||
|
// and period types or the merge will fail. profile.Period of the
|
||||||
|
// resulting profile will be the maximum of all profiles, and
|
||||||
|
// profile.TimeNanos will be the earliest nonzero one. Merges are
|
||||||
|
// associative with the caveat of the first profile having some
|
||||||
|
// specialization in how headers are combined. There may be other
|
||||||
|
// subtleties now or in the future regarding associativity.
|
||||||
|
func Merge(srcs []*Profile) (*Profile, error) {
|
||||||
|
if len(srcs) == 0 {
|
||||||
|
return nil, fmt.Errorf("no profiles to merge")
|
||||||
|
}
|
||||||
|
p, err := combineHeaders(srcs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pm := &profileMerger{
|
||||||
|
p: p,
|
||||||
|
samples: make(map[sampleKey]*Sample, len(srcs[0].Sample)),
|
||||||
|
locations: make(map[locationKey]*Location, len(srcs[0].Location)),
|
||||||
|
functions: make(map[functionKey]*Function, len(srcs[0].Function)),
|
||||||
|
mappings: make(map[mappingKey]*Mapping, len(srcs[0].Mapping)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, src := range srcs {
|
||||||
|
// Clear the profile-specific hash tables
|
||||||
|
pm.locationsByID = makeLocationIDMap(len(src.Location))
|
||||||
|
pm.functionsByID = make(map[uint64]*Function, len(src.Function))
|
||||||
|
pm.mappingsByID = make(map[uint64]mapInfo, len(src.Mapping))
|
||||||
|
|
||||||
|
if len(pm.mappings) == 0 && len(src.Mapping) > 0 {
|
||||||
|
// The Mapping list has the property that the first mapping
|
||||||
|
// represents the main binary. Take the first Mapping we see,
|
||||||
|
// otherwise the operations below will add mappings in an
|
||||||
|
// arbitrary order.
|
||||||
|
pm.mapMapping(src.Mapping[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range src.Sample {
|
||||||
|
if !isZeroSample(s) {
|
||||||
|
pm.mapSample(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
if isZeroSample(s) {
|
||||||
|
// If there are any zero samples, re-merge the profile to GC
|
||||||
|
// them.
|
||||||
|
return Merge([]*Profile{p})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize normalizes the source profile by multiplying each value in profile by the
|
||||||
|
// ratio of the sum of the base profile's values of that sample type to the sum of the
|
||||||
|
// source profile's value of that sample type.
|
||||||
|
func (p *Profile) Normalize(pb *Profile) error {
|
||||||
|
|
||||||
|
if err := p.compatible(pb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
baseVals := make([]int64, len(p.SampleType))
|
||||||
|
for _, s := range pb.Sample {
|
||||||
|
for i, v := range s.Value {
|
||||||
|
baseVals[i] += v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcVals := make([]int64, len(p.SampleType))
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
for i, v := range s.Value {
|
||||||
|
srcVals[i] += v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normScale := make([]float64, len(baseVals))
|
||||||
|
for i := range baseVals {
|
||||||
|
if srcVals[i] == 0 {
|
||||||
|
normScale[i] = 0.0
|
||||||
|
} else {
|
||||||
|
normScale[i] = float64(baseVals[i]) / float64(srcVals[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.ScaleN(normScale)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZeroSample(s *Sample) bool {
|
||||||
|
for _, v := range s.Value {
|
||||||
|
if v != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type profileMerger struct {
|
||||||
|
p *Profile
|
||||||
|
|
||||||
|
// Memoization tables within a profile.
|
||||||
|
locationsByID locationIDMap
|
||||||
|
functionsByID map[uint64]*Function
|
||||||
|
mappingsByID map[uint64]mapInfo
|
||||||
|
|
||||||
|
// Memoization tables for profile entities.
|
||||||
|
samples map[sampleKey]*Sample
|
||||||
|
locations map[locationKey]*Location
|
||||||
|
functions map[functionKey]*Function
|
||||||
|
mappings map[mappingKey]*Mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapInfo struct {
|
||||||
|
m *Mapping
|
||||||
|
offset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *profileMerger) mapSample(src *Sample) *Sample {
|
||||||
|
// Check memoization table
|
||||||
|
k := pm.sampleKey(src)
|
||||||
|
if ss, ok := pm.samples[k]; ok {
|
||||||
|
for i, v := range src.Value {
|
||||||
|
ss.Value[i] += v
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make new sample.
|
||||||
|
s := &Sample{
|
||||||
|
Location: make([]*Location, len(src.Location)),
|
||||||
|
Value: make([]int64, len(src.Value)),
|
||||||
|
Label: make(map[string][]string, len(src.Label)),
|
||||||
|
NumLabel: make(map[string][]int64, len(src.NumLabel)),
|
||||||
|
NumUnit: make(map[string][]string, len(src.NumLabel)),
|
||||||
|
}
|
||||||
|
for i, l := range src.Location {
|
||||||
|
s.Location[i] = pm.mapLocation(l)
|
||||||
|
}
|
||||||
|
for k, v := range src.Label {
|
||||||
|
vv := make([]string, len(v))
|
||||||
|
copy(vv, v)
|
||||||
|
s.Label[k] = vv
|
||||||
|
}
|
||||||
|
for k, v := range src.NumLabel {
|
||||||
|
u := src.NumUnit[k]
|
||||||
|
vv := make([]int64, len(v))
|
||||||
|
uu := make([]string, len(u))
|
||||||
|
copy(vv, v)
|
||||||
|
copy(uu, u)
|
||||||
|
s.NumLabel[k] = vv
|
||||||
|
s.NumUnit[k] = uu
|
||||||
|
}
|
||||||
|
copy(s.Value, src.Value)
|
||||||
|
pm.samples[k] = s
|
||||||
|
pm.p.Sample = append(pm.p.Sample, s)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *profileMerger) sampleKey(sample *Sample) sampleKey {
|
||||||
|
// Accumulate contents into a string.
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.Grow(64) // Heuristic to avoid extra allocs
|
||||||
|
|
||||||
|
// encode a number
|
||||||
|
putNumber := func(v uint64) {
|
||||||
|
var num [binary.MaxVarintLen64]byte
|
||||||
|
n := binary.PutUvarint(num[:], v)
|
||||||
|
buf.Write(num[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode a string prefixed with its length.
|
||||||
|
putDelimitedString := func(s string) {
|
||||||
|
putNumber(uint64(len(s)))
|
||||||
|
buf.WriteString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range sample.Location {
|
||||||
|
// Get the location in the merged profile, which may have a different ID.
|
||||||
|
if loc := pm.mapLocation(l); loc != nil {
|
||||||
|
putNumber(loc.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putNumber(0) // Delimiter
|
||||||
|
|
||||||
|
for _, l := range sortedKeys1(sample.Label) {
|
||||||
|
putDelimitedString(l)
|
||||||
|
values := sample.Label[l]
|
||||||
|
putNumber(uint64(len(values)))
|
||||||
|
for _, v := range values {
|
||||||
|
putDelimitedString(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range sortedKeys2(sample.NumLabel) {
|
||||||
|
putDelimitedString(l)
|
||||||
|
values := sample.NumLabel[l]
|
||||||
|
putNumber(uint64(len(values)))
|
||||||
|
for _, v := range values {
|
||||||
|
putNumber(uint64(v))
|
||||||
|
}
|
||||||
|
units := sample.NumUnit[l]
|
||||||
|
putNumber(uint64(len(units)))
|
||||||
|
for _, v := range units {
|
||||||
|
putDelimitedString(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sampleKey(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
type sampleKey string
|
||||||
|
|
||||||
|
// sortedKeys1 returns the sorted keys found in a string->[]string map.
|
||||||
|
//
|
||||||
|
// Note: this is currently non-generic since github pprof runs golint,
|
||||||
|
// which does not support generics. When that issue is fixed, it can
|
||||||
|
// be merged with sortedKeys2 and made into a generic function.
|
||||||
|
func sortedKeys1(m map[string][]string) []string {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortedKeys2 returns the sorted keys found in a string->[]int64 map.
|
||||||
|
//
|
||||||
|
// Note: this is currently non-generic since github pprof runs golint,
|
||||||
|
// which does not support generics. When that issue is fixed, it can
|
||||||
|
// be merged with sortedKeys1 and made into a generic function.
|
||||||
|
func sortedKeys2(m map[string][]int64) []string {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *profileMerger) mapLocation(src *Location) *Location {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := pm.locationsByID.get(src.ID); l != nil {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
mi := pm.mapMapping(src.Mapping)
|
||||||
|
l := &Location{
|
||||||
|
ID: uint64(len(pm.p.Location) + 1),
|
||||||
|
Mapping: mi.m,
|
||||||
|
Address: uint64(int64(src.Address) + mi.offset),
|
||||||
|
Line: make([]Line, len(src.Line)),
|
||||||
|
IsFolded: src.IsFolded,
|
||||||
|
}
|
||||||
|
for i, ln := range src.Line {
|
||||||
|
l.Line[i] = pm.mapLine(ln)
|
||||||
|
}
|
||||||
|
// Check memoization table. Must be done on the remapped location to
|
||||||
|
// account for the remapped mapping ID.
|
||||||
|
k := l.key()
|
||||||
|
if ll, ok := pm.locations[k]; ok {
|
||||||
|
pm.locationsByID.set(src.ID, ll)
|
||||||
|
return ll
|
||||||
|
}
|
||||||
|
pm.locationsByID.set(src.ID, l)
|
||||||
|
pm.locations[k] = l
|
||||||
|
pm.p.Location = append(pm.p.Location, l)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// key generates locationKey to be used as a key for maps.
|
||||||
|
func (l *Location) key() locationKey {
|
||||||
|
key := locationKey{
|
||||||
|
addr: l.Address,
|
||||||
|
isFolded: l.IsFolded,
|
||||||
|
}
|
||||||
|
if l.Mapping != nil {
|
||||||
|
// Normalizes address to handle address space randomization.
|
||||||
|
key.addr -= l.Mapping.Start
|
||||||
|
key.mappingID = l.Mapping.ID
|
||||||
|
}
|
||||||
|
lines := make([]string, len(l.Line)*2)
|
||||||
|
for i, line := range l.Line {
|
||||||
|
if line.Function != nil {
|
||||||
|
lines[i*2] = strconv.FormatUint(line.Function.ID, 16)
|
||||||
|
}
|
||||||
|
lines[i*2+1] = strconv.FormatInt(line.Line, 16)
|
||||||
|
}
|
||||||
|
key.lines = strings.Join(lines, "|")
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
type locationKey struct {
|
||||||
|
addr, mappingID uint64
|
||||||
|
lines string
|
||||||
|
isFolded bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *profileMerger) mapMapping(src *Mapping) mapInfo {
|
||||||
|
if src == nil {
|
||||||
|
return mapInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mi, ok := pm.mappingsByID[src.ID]; ok {
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check memoization tables.
|
||||||
|
mk := src.key()
|
||||||
|
if m, ok := pm.mappings[mk]; ok {
|
||||||
|
mi := mapInfo{m, int64(m.Start) - int64(src.Start)}
|
||||||
|
pm.mappingsByID[src.ID] = mi
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
m := &Mapping{
|
||||||
|
ID: uint64(len(pm.p.Mapping) + 1),
|
||||||
|
Start: src.Start,
|
||||||
|
Limit: src.Limit,
|
||||||
|
Offset: src.Offset,
|
||||||
|
File: src.File,
|
||||||
|
KernelRelocationSymbol: src.KernelRelocationSymbol,
|
||||||
|
BuildID: src.BuildID,
|
||||||
|
HasFunctions: src.HasFunctions,
|
||||||
|
HasFilenames: src.HasFilenames,
|
||||||
|
HasLineNumbers: src.HasLineNumbers,
|
||||||
|
HasInlineFrames: src.HasInlineFrames,
|
||||||
|
}
|
||||||
|
pm.p.Mapping = append(pm.p.Mapping, m)
|
||||||
|
|
||||||
|
// Update memoization tables.
|
||||||
|
pm.mappings[mk] = m
|
||||||
|
mi := mapInfo{m, 0}
|
||||||
|
pm.mappingsByID[src.ID] = mi
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
|
||||||
|
// key generates encoded strings of Mapping to be used as a key for
|
||||||
|
// maps.
|
||||||
|
func (m *Mapping) key() mappingKey {
|
||||||
|
// Normalize addresses to handle address space randomization.
|
||||||
|
// Round up to next 4K boundary to avoid minor discrepancies.
|
||||||
|
const mapsizeRounding = 0x1000
|
||||||
|
|
||||||
|
size := m.Limit - m.Start
|
||||||
|
size = size + mapsizeRounding - 1
|
||||||
|
size = size - (size % mapsizeRounding)
|
||||||
|
key := mappingKey{
|
||||||
|
size: size,
|
||||||
|
offset: m.Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case m.BuildID != "":
|
||||||
|
key.buildIDOrFile = m.BuildID
|
||||||
|
case m.File != "":
|
||||||
|
key.buildIDOrFile = m.File
|
||||||
|
default:
|
||||||
|
// A mapping containing neither build ID nor file name is a fake mapping. A
|
||||||
|
// key with empty buildIDOrFile is used for fake mappings so that they are
|
||||||
|
// treated as the same mapping during merging.
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
type mappingKey struct {
|
||||||
|
size, offset uint64
|
||||||
|
buildIDOrFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *profileMerger) mapLine(src Line) Line {
|
||||||
|
ln := Line{
|
||||||
|
Function: pm.mapFunction(src.Function),
|
||||||
|
Line: src.Line,
|
||||||
|
}
|
||||||
|
return ln
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *profileMerger) mapFunction(src *Function) *Function {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if f, ok := pm.functionsByID[src.ID]; ok {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
k := src.key()
|
||||||
|
if f, ok := pm.functions[k]; ok {
|
||||||
|
pm.functionsByID[src.ID] = f
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
f := &Function{
|
||||||
|
ID: uint64(len(pm.p.Function) + 1),
|
||||||
|
Name: src.Name,
|
||||||
|
SystemName: src.SystemName,
|
||||||
|
Filename: src.Filename,
|
||||||
|
StartLine: src.StartLine,
|
||||||
|
}
|
||||||
|
pm.functions[k] = f
|
||||||
|
pm.functionsByID[src.ID] = f
|
||||||
|
pm.p.Function = append(pm.p.Function, f)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// key generates a struct to be used as a key for maps.
|
||||||
|
func (f *Function) key() functionKey {
|
||||||
|
return functionKey{
|
||||||
|
f.StartLine,
|
||||||
|
f.Name,
|
||||||
|
f.SystemName,
|
||||||
|
f.Filename,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type functionKey struct {
|
||||||
|
startLine int64
|
||||||
|
name, systemName, fileName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// combineHeaders checks that all profiles can be merged and returns
|
||||||
|
// their combined profile.
|
||||||
|
func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||||
|
for _, s := range srcs[1:] {
|
||||||
|
if err := srcs[0].compatible(s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeNanos, durationNanos, period int64
|
||||||
|
var comments []string
|
||||||
|
seenComments := map[string]bool{}
|
||||||
|
var defaultSampleType string
|
||||||
|
for _, s := range srcs {
|
||||||
|
if timeNanos == 0 || s.TimeNanos < timeNanos {
|
||||||
|
timeNanos = s.TimeNanos
|
||||||
|
}
|
||||||
|
durationNanos += s.DurationNanos
|
||||||
|
if period == 0 || period < s.Period {
|
||||||
|
period = s.Period
|
||||||
|
}
|
||||||
|
for _, c := range s.Comments {
|
||||||
|
if seen := seenComments[c]; !seen {
|
||||||
|
comments = append(comments, c)
|
||||||
|
seenComments[c] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if defaultSampleType == "" {
|
||||||
|
defaultSampleType = s.DefaultSampleType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Profile{
|
||||||
|
SampleType: make([]*ValueType, len(srcs[0].SampleType)),
|
||||||
|
|
||||||
|
DropFrames: srcs[0].DropFrames,
|
||||||
|
KeepFrames: srcs[0].KeepFrames,
|
||||||
|
|
||||||
|
TimeNanos: timeNanos,
|
||||||
|
DurationNanos: durationNanos,
|
||||||
|
PeriodType: srcs[0].PeriodType,
|
||||||
|
Period: period,
|
||||||
|
|
||||||
|
Comments: comments,
|
||||||
|
DefaultSampleType: defaultSampleType,
|
||||||
|
}
|
||||||
|
copy(p.SampleType, srcs[0].SampleType)
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compatible determines if two profiles can be compared/merged.
|
||||||
|
// returns nil if the profiles are compatible; otherwise an error with
|
||||||
|
// details on the incompatibility.
|
||||||
|
func (p *Profile) compatible(pb *Profile) error {
|
||||||
|
if !equalValueType(p.PeriodType, pb.PeriodType) {
|
||||||
|
return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.SampleType) != len(pb.SampleType) {
|
||||||
|
return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range p.SampleType {
|
||||||
|
if !equalValueType(p.SampleType[i], pb.SampleType[i]) {
|
||||||
|
return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// equalValueType returns true if the two value types are semantically
|
||||||
|
// equal. It ignores the internal fields used during encode/decode.
|
||||||
|
func equalValueType(st1, st2 *ValueType) bool {
|
||||||
|
return st1.Type == st2.Type && st1.Unit == st2.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// locationIDMap is like a map[uint64]*Location, but provides efficiency for
|
||||||
|
// ids that are densely numbered, which is often the case.
|
||||||
|
type locationIDMap struct {
|
||||||
|
dense []*Location // indexed by id for id < len(dense)
|
||||||
|
sparse map[uint64]*Location // indexed by id for id >= len(dense)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLocationIDMap(n int) locationIDMap {
|
||||||
|
return locationIDMap{
|
||||||
|
dense: make([]*Location, n),
|
||||||
|
sparse: map[uint64]*Location{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm locationIDMap) get(id uint64) *Location {
|
||||||
|
if id < uint64(len(lm.dense)) {
|
||||||
|
return lm.dense[int(id)]
|
||||||
|
}
|
||||||
|
return lm.sparse[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm locationIDMap) set(id uint64, loc *Location) {
|
||||||
|
if id < uint64(len(lm.dense)) {
|
||||||
|
lm.dense[id] = loc
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lm.sparse[id] = loc
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompatibilizeSampleTypes makes profiles compatible to be compared/merged. It
|
||||||
|
// keeps sample types that appear in all profiles only and drops/reorders the
|
||||||
|
// sample types as necessary.
|
||||||
|
//
|
||||||
|
// In the case of sample types order is not the same for given profiles the
|
||||||
|
// order is derived from the first profile.
|
||||||
|
//
|
||||||
|
// Profiles are modified in-place.
|
||||||
|
//
|
||||||
|
// It returns an error if the sample type's intersection is empty.
|
||||||
|
func CompatibilizeSampleTypes(ps []*Profile) error {
|
||||||
|
sTypes := commonSampleTypes(ps)
|
||||||
|
if len(sTypes) == 0 {
|
||||||
|
return fmt.Errorf("profiles have empty common sample type list")
|
||||||
|
}
|
||||||
|
for _, p := range ps {
|
||||||
|
if err := compatibilizeSampleTypes(p, sTypes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// commonSampleTypes returns sample types that appear in all profiles in the
|
||||||
|
// order how they ordered in the first profile.
|
||||||
|
func commonSampleTypes(ps []*Profile) []string {
|
||||||
|
if len(ps) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sTypes := map[string]int{}
|
||||||
|
for _, p := range ps {
|
||||||
|
for _, st := range p.SampleType {
|
||||||
|
sTypes[st.Type]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var res []string
|
||||||
|
for _, st := range ps[0].SampleType {
|
||||||
|
if sTypes[st.Type] == len(ps) {
|
||||||
|
res = append(res, st.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// compatibilizeSampleTypes drops sample types that are not present in sTypes
|
||||||
|
// list and reorder them if needed.
|
||||||
|
//
|
||||||
|
// It sets DefaultSampleType to sType[0] if it is not in sType list.
|
||||||
|
//
|
||||||
|
// It assumes that all sample types from the sTypes list are present in the
|
||||||
|
// given profile otherwise it returns an error.
|
||||||
|
func compatibilizeSampleTypes(p *Profile, sTypes []string) error {
|
||||||
|
if len(sTypes) == 0 {
|
||||||
|
return fmt.Errorf("sample type list is empty")
|
||||||
|
}
|
||||||
|
defaultSampleType := sTypes[0]
|
||||||
|
reMap, needToModify := make([]int, len(sTypes)), false
|
||||||
|
for i, st := range sTypes {
|
||||||
|
if st == p.DefaultSampleType {
|
||||||
|
defaultSampleType = p.DefaultSampleType
|
||||||
|
}
|
||||||
|
idx := searchValueType(p.SampleType, st)
|
||||||
|
if idx < 0 {
|
||||||
|
return fmt.Errorf("%q sample type is not found in profile", st)
|
||||||
|
}
|
||||||
|
reMap[i] = idx
|
||||||
|
if idx != i {
|
||||||
|
needToModify = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !needToModify && len(sTypes) == len(p.SampleType) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.DefaultSampleType = defaultSampleType
|
||||||
|
oldSampleTypes := p.SampleType
|
||||||
|
p.SampleType = make([]*ValueType, len(sTypes))
|
||||||
|
for i, idx := range reMap {
|
||||||
|
p.SampleType[i] = oldSampleTypes[idx]
|
||||||
|
}
|
||||||
|
values := make([]int64, len(sTypes))
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
for i, idx := range reMap {
|
||||||
|
values[i] = s.Value[idx]
|
||||||
|
}
|
||||||
|
s.Value = s.Value[:len(values)]
|
||||||
|
copy(s.Value, values)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchValueType(vts []*ValueType, s string) int {
|
||||||
|
for i, vt := range vts {
|
||||||
|
if vt.Type == s {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
813
vendor/github.com/google/pprof/profile/profile.go
generated
vendored
Normal file
813
vendor/github.com/google/pprof/profile/profile.go
generated
vendored
Normal file
|
@ -0,0 +1,813 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 profile provides a representation of profile.proto and
|
||||||
|
// methods to encode/decode profiles in this format.
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Profile is an in-memory representation of profile.proto.
|
||||||
|
type Profile struct {
|
||||||
|
SampleType []*ValueType
|
||||||
|
DefaultSampleType string
|
||||||
|
Sample []*Sample
|
||||||
|
Mapping []*Mapping
|
||||||
|
Location []*Location
|
||||||
|
Function []*Function
|
||||||
|
Comments []string
|
||||||
|
|
||||||
|
DropFrames string
|
||||||
|
KeepFrames string
|
||||||
|
|
||||||
|
TimeNanos int64
|
||||||
|
DurationNanos int64
|
||||||
|
PeriodType *ValueType
|
||||||
|
Period int64
|
||||||
|
|
||||||
|
// The following fields are modified during encoding and copying,
|
||||||
|
// so are protected by a Mutex.
|
||||||
|
encodeMu sync.Mutex
|
||||||
|
|
||||||
|
commentX []int64
|
||||||
|
dropFramesX int64
|
||||||
|
keepFramesX int64
|
||||||
|
stringTable []string
|
||||||
|
defaultSampleTypeX int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueType corresponds to Profile.ValueType
|
||||||
|
type ValueType struct {
|
||||||
|
Type string // cpu, wall, inuse_space, etc
|
||||||
|
Unit string // seconds, nanoseconds, bytes, etc
|
||||||
|
|
||||||
|
typeX int64
|
||||||
|
unitX int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample corresponds to Profile.Sample
|
||||||
|
type Sample struct {
|
||||||
|
Location []*Location
|
||||||
|
Value []int64
|
||||||
|
Label map[string][]string
|
||||||
|
NumLabel map[string][]int64
|
||||||
|
NumUnit map[string][]string
|
||||||
|
|
||||||
|
locationIDX []uint64
|
||||||
|
labelX []label
|
||||||
|
}
|
||||||
|
|
||||||
|
// label corresponds to Profile.Label
|
||||||
|
type label struct {
|
||||||
|
keyX int64
|
||||||
|
// Exactly one of the two following values must be set
|
||||||
|
strX int64
|
||||||
|
numX int64 // Integer value for this label
|
||||||
|
// can be set if numX has value
|
||||||
|
unitX int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapping corresponds to Profile.Mapping
|
||||||
|
type Mapping struct {
|
||||||
|
ID uint64
|
||||||
|
Start uint64
|
||||||
|
Limit uint64
|
||||||
|
Offset uint64
|
||||||
|
File string
|
||||||
|
BuildID string
|
||||||
|
HasFunctions bool
|
||||||
|
HasFilenames bool
|
||||||
|
HasLineNumbers bool
|
||||||
|
HasInlineFrames bool
|
||||||
|
|
||||||
|
fileX int64
|
||||||
|
buildIDX int64
|
||||||
|
|
||||||
|
// Name of the kernel relocation symbol ("_text" or "_stext"), extracted from File.
|
||||||
|
// For linux kernel mappings generated by some tools, correct symbolization depends
|
||||||
|
// on knowing which of the two possible relocation symbols was used for `Start`.
|
||||||
|
// This is given to us as a suffix in `File` (e.g. "[kernel.kallsyms]_stext").
|
||||||
|
//
|
||||||
|
// Note, this public field is not persisted in the proto. For the purposes of
|
||||||
|
// copying / merging / hashing profiles, it is considered subsumed by `File`.
|
||||||
|
KernelRelocationSymbol string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Location corresponds to Profile.Location
|
||||||
|
type Location struct {
|
||||||
|
ID uint64
|
||||||
|
Mapping *Mapping
|
||||||
|
Address uint64
|
||||||
|
Line []Line
|
||||||
|
IsFolded bool
|
||||||
|
|
||||||
|
mappingIDX uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line corresponds to Profile.Line
|
||||||
|
type Line struct {
|
||||||
|
Function *Function
|
||||||
|
Line int64
|
||||||
|
|
||||||
|
functionIDX uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function corresponds to Profile.Function
|
||||||
|
type Function struct {
|
||||||
|
ID uint64
|
||||||
|
Name string
|
||||||
|
SystemName string
|
||||||
|
Filename string
|
||||||
|
StartLine int64
|
||||||
|
|
||||||
|
nameX int64
|
||||||
|
systemNameX int64
|
||||||
|
filenameX int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses a profile and checks for its validity. The input
|
||||||
|
// may be a gzip-compressed encoded protobuf or one of many legacy
|
||||||
|
// profile formats which may be unsupported in the future.
|
||||||
|
func Parse(r io.Reader) (*Profile, error) {
|
||||||
|
data, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ParseData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseData parses a profile from a buffer and checks for its
|
||||||
|
// validity.
|
||||||
|
func ParseData(data []byte) (*Profile, error) {
|
||||||
|
var p *Profile
|
||||||
|
var err error
|
||||||
|
if len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
|
if err == nil {
|
||||||
|
data, err = io.ReadAll(gz)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decompressing profile: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p, err = ParseUncompressed(data); err != nil && err != errNoData && err != errConcatProfile {
|
||||||
|
p, err = parseLegacy(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing profile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.CheckValid(); err != nil {
|
||||||
|
return nil, fmt.Errorf("malformed profile: %v", err)
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errUnrecognized = fmt.Errorf("unrecognized profile format")
|
||||||
|
var errMalformed = fmt.Errorf("malformed profile format")
|
||||||
|
var errNoData = fmt.Errorf("empty input file")
|
||||||
|
var errConcatProfile = fmt.Errorf("concatenated profiles detected")
|
||||||
|
|
||||||
|
func parseLegacy(data []byte) (*Profile, error) {
|
||||||
|
parsers := []func([]byte) (*Profile, error){
|
||||||
|
parseCPU,
|
||||||
|
parseHeap,
|
||||||
|
parseGoCount, // goroutine, threadcreate
|
||||||
|
parseThread,
|
||||||
|
parseContention,
|
||||||
|
parseJavaProfile,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, parser := range parsers {
|
||||||
|
p, err := parser(data)
|
||||||
|
if err == nil {
|
||||||
|
p.addLegacyFrameInfo()
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
if err != errUnrecognized {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errUnrecognized
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUncompressed parses an uncompressed protobuf into a profile.
|
||||||
|
func ParseUncompressed(data []byte) (*Profile, error) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil, errNoData
|
||||||
|
}
|
||||||
|
p := &Profile{}
|
||||||
|
if err := unmarshal(data, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.postDecode(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`)
|
||||||
|
|
||||||
|
// massageMappings applies heuristic-based changes to the profile
|
||||||
|
// mappings to account for quirks of some environments.
|
||||||
|
func (p *Profile) massageMappings() {
|
||||||
|
// Merge adjacent regions with matching names, checking that the offsets match
|
||||||
|
if len(p.Mapping) > 1 {
|
||||||
|
mappings := []*Mapping{p.Mapping[0]}
|
||||||
|
for _, m := range p.Mapping[1:] {
|
||||||
|
lm := mappings[len(mappings)-1]
|
||||||
|
if adjacent(lm, m) {
|
||||||
|
lm.Limit = m.Limit
|
||||||
|
if m.File != "" {
|
||||||
|
lm.File = m.File
|
||||||
|
}
|
||||||
|
if m.BuildID != "" {
|
||||||
|
lm.BuildID = m.BuildID
|
||||||
|
}
|
||||||
|
p.updateLocationMapping(m, lm)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mappings = append(mappings, m)
|
||||||
|
}
|
||||||
|
p.Mapping = mappings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use heuristics to identify main binary and move it to the top of the list of mappings
|
||||||
|
for i, m := range p.Mapping {
|
||||||
|
file := strings.TrimSpace(strings.Replace(m.File, "(deleted)", "", -1))
|
||||||
|
if len(file) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(libRx.FindStringSubmatch(file)) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file[0] == '[' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Swap what we guess is main to position 0.
|
||||||
|
p.Mapping[0], p.Mapping[i] = p.Mapping[i], p.Mapping[0]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the mapping IDs neatly sorted
|
||||||
|
for i, m := range p.Mapping {
|
||||||
|
m.ID = uint64(i + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjacent returns whether two mapping entries represent the same
|
||||||
|
// mapping that has been split into two. Check that their addresses are adjacent,
|
||||||
|
// and if the offsets match, if they are available.
|
||||||
|
func adjacent(m1, m2 *Mapping) bool {
|
||||||
|
if m1.File != "" && m2.File != "" {
|
||||||
|
if m1.File != m2.File {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m1.BuildID != "" && m2.BuildID != "" {
|
||||||
|
if m1.BuildID != m2.BuildID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m1.Limit != m2.Start {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if m1.Offset != 0 && m2.Offset != 0 {
|
||||||
|
offset := m1.Offset + (m1.Limit - m1.Start)
|
||||||
|
if offset != m2.Offset {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Profile) updateLocationMapping(from, to *Mapping) {
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if l.Mapping == from {
|
||||||
|
l.Mapping = to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serialize(p *Profile) []byte {
|
||||||
|
p.encodeMu.Lock()
|
||||||
|
p.preEncode()
|
||||||
|
b := marshal(p)
|
||||||
|
p.encodeMu.Unlock()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the profile as a gzip-compressed marshaled protobuf.
|
||||||
|
func (p *Profile) Write(w io.Writer) error {
|
||||||
|
zw := gzip.NewWriter(w)
|
||||||
|
defer zw.Close()
|
||||||
|
_, err := zw.Write(serialize(p))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteUncompressed writes the profile as a marshaled protobuf.
|
||||||
|
func (p *Profile) WriteUncompressed(w io.Writer) error {
|
||||||
|
_, err := w.Write(serialize(p))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckValid tests whether the profile is valid. Checks include, but are
|
||||||
|
// not limited to:
|
||||||
|
// - len(Profile.Sample[n].value) == len(Profile.value_unit)
|
||||||
|
// - Sample.id has a corresponding Profile.Location
|
||||||
|
func (p *Profile) CheckValid() error {
|
||||||
|
// Check that sample values are consistent
|
||||||
|
sampleLen := len(p.SampleType)
|
||||||
|
if sampleLen == 0 && len(p.Sample) != 0 {
|
||||||
|
return fmt.Errorf("missing sample type information")
|
||||||
|
}
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
if s == nil {
|
||||||
|
return fmt.Errorf("profile has nil sample")
|
||||||
|
}
|
||||||
|
if len(s.Value) != sampleLen {
|
||||||
|
return fmt.Errorf("mismatch: sample has %d values vs. %d types", len(s.Value), len(p.SampleType))
|
||||||
|
}
|
||||||
|
for _, l := range s.Location {
|
||||||
|
if l == nil {
|
||||||
|
return fmt.Errorf("sample has nil location")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all mappings/locations/functions are in the tables
|
||||||
|
// Check that there are no duplicate ids
|
||||||
|
mappings := make(map[uint64]*Mapping, len(p.Mapping))
|
||||||
|
for _, m := range p.Mapping {
|
||||||
|
if m == nil {
|
||||||
|
return fmt.Errorf("profile has nil mapping")
|
||||||
|
}
|
||||||
|
if m.ID == 0 {
|
||||||
|
return fmt.Errorf("found mapping with reserved ID=0")
|
||||||
|
}
|
||||||
|
if mappings[m.ID] != nil {
|
||||||
|
return fmt.Errorf("multiple mappings with same id: %d", m.ID)
|
||||||
|
}
|
||||||
|
mappings[m.ID] = m
|
||||||
|
}
|
||||||
|
functions := make(map[uint64]*Function, len(p.Function))
|
||||||
|
for _, f := range p.Function {
|
||||||
|
if f == nil {
|
||||||
|
return fmt.Errorf("profile has nil function")
|
||||||
|
}
|
||||||
|
if f.ID == 0 {
|
||||||
|
return fmt.Errorf("found function with reserved ID=0")
|
||||||
|
}
|
||||||
|
if functions[f.ID] != nil {
|
||||||
|
return fmt.Errorf("multiple functions with same id: %d", f.ID)
|
||||||
|
}
|
||||||
|
functions[f.ID] = f
|
||||||
|
}
|
||||||
|
locations := make(map[uint64]*Location, len(p.Location))
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if l == nil {
|
||||||
|
return fmt.Errorf("profile has nil location")
|
||||||
|
}
|
||||||
|
if l.ID == 0 {
|
||||||
|
return fmt.Errorf("found location with reserved id=0")
|
||||||
|
}
|
||||||
|
if locations[l.ID] != nil {
|
||||||
|
return fmt.Errorf("multiple locations with same id: %d", l.ID)
|
||||||
|
}
|
||||||
|
locations[l.ID] = l
|
||||||
|
if m := l.Mapping; m != nil {
|
||||||
|
if m.ID == 0 || mappings[m.ID] != m {
|
||||||
|
return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ln := range l.Line {
|
||||||
|
f := ln.Function
|
||||||
|
if f == nil {
|
||||||
|
return fmt.Errorf("location id: %d has a line with nil function", l.ID)
|
||||||
|
}
|
||||||
|
if f.ID == 0 || functions[f.ID] != f {
|
||||||
|
return fmt.Errorf("inconsistent function %p: %d", f, f.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate merges the locations in the profile into equivalence
|
||||||
|
// classes preserving the request attributes. It also updates the
|
||||||
|
// samples to point to the merged locations.
|
||||||
|
func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error {
|
||||||
|
for _, m := range p.Mapping {
|
||||||
|
m.HasInlineFrames = m.HasInlineFrames && inlineFrame
|
||||||
|
m.HasFunctions = m.HasFunctions && function
|
||||||
|
m.HasFilenames = m.HasFilenames && filename
|
||||||
|
m.HasLineNumbers = m.HasLineNumbers && linenumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate functions
|
||||||
|
if !function || !filename {
|
||||||
|
for _, f := range p.Function {
|
||||||
|
if !function {
|
||||||
|
f.Name = ""
|
||||||
|
f.SystemName = ""
|
||||||
|
}
|
||||||
|
if !filename {
|
||||||
|
f.Filename = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate locations
|
||||||
|
if !inlineFrame || !address || !linenumber {
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if !inlineFrame && len(l.Line) > 1 {
|
||||||
|
l.Line = l.Line[len(l.Line)-1:]
|
||||||
|
}
|
||||||
|
if !linenumber {
|
||||||
|
for i := range l.Line {
|
||||||
|
l.Line[i].Line = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !address {
|
||||||
|
l.Address = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.CheckValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumLabelUnits returns a map of numeric label keys to the units
|
||||||
|
// associated with those keys and a map of those keys to any units
|
||||||
|
// that were encountered but not used.
|
||||||
|
// Unit for a given key is the first encountered unit for that key. If multiple
|
||||||
|
// units are encountered for values paired with a particular key, then the first
|
||||||
|
// unit encountered is used and all other units are returned in sorted order
|
||||||
|
// in map of ignored units.
|
||||||
|
// If no units are encountered for a particular key, the unit is then inferred
|
||||||
|
// based on the key.
|
||||||
|
func (p *Profile) NumLabelUnits() (map[string]string, map[string][]string) {
|
||||||
|
numLabelUnits := map[string]string{}
|
||||||
|
ignoredUnits := map[string]map[string]bool{}
|
||||||
|
encounteredKeys := map[string]bool{}
|
||||||
|
|
||||||
|
// Determine units based on numeric tags for each sample.
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
for k := range s.NumLabel {
|
||||||
|
encounteredKeys[k] = true
|
||||||
|
for _, unit := range s.NumUnit[k] {
|
||||||
|
if unit == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if wantUnit, ok := numLabelUnits[k]; !ok {
|
||||||
|
numLabelUnits[k] = unit
|
||||||
|
} else if wantUnit != unit {
|
||||||
|
if v, ok := ignoredUnits[k]; ok {
|
||||||
|
v[unit] = true
|
||||||
|
} else {
|
||||||
|
ignoredUnits[k] = map[string]bool{unit: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Infer units for keys without any units associated with
|
||||||
|
// numeric tag values.
|
||||||
|
for key := range encounteredKeys {
|
||||||
|
unit := numLabelUnits[key]
|
||||||
|
if unit == "" {
|
||||||
|
switch key {
|
||||||
|
case "alignment", "request":
|
||||||
|
numLabelUnits[key] = "bytes"
|
||||||
|
default:
|
||||||
|
numLabelUnits[key] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy ignored units into more readable format
|
||||||
|
unitsIgnored := make(map[string][]string, len(ignoredUnits))
|
||||||
|
for key, values := range ignoredUnits {
|
||||||
|
units := make([]string, len(values))
|
||||||
|
i := 0
|
||||||
|
for unit := range values {
|
||||||
|
units[i] = unit
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Strings(units)
|
||||||
|
unitsIgnored[key] = units
|
||||||
|
}
|
||||||
|
|
||||||
|
return numLabelUnits, unitsIgnored
|
||||||
|
}
|
||||||
|
|
||||||
|
// String dumps a text representation of a profile. Intended mainly
|
||||||
|
// for debugging purposes.
|
||||||
|
func (p *Profile) String() string {
|
||||||
|
ss := make([]string, 0, len(p.Comments)+len(p.Sample)+len(p.Mapping)+len(p.Location))
|
||||||
|
for _, c := range p.Comments {
|
||||||
|
ss = append(ss, "Comment: "+c)
|
||||||
|
}
|
||||||
|
if pt := p.PeriodType; pt != nil {
|
||||||
|
ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
|
||||||
|
}
|
||||||
|
ss = append(ss, fmt.Sprintf("Period: %d", p.Period))
|
||||||
|
if p.TimeNanos != 0 {
|
||||||
|
ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos)))
|
||||||
|
}
|
||||||
|
if p.DurationNanos != 0 {
|
||||||
|
ss = append(ss, fmt.Sprintf("Duration: %.4v", time.Duration(p.DurationNanos)))
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = append(ss, "Samples:")
|
||||||
|
var sh1 string
|
||||||
|
for _, s := range p.SampleType {
|
||||||
|
dflt := ""
|
||||||
|
if s.Type == p.DefaultSampleType {
|
||||||
|
dflt = "[dflt]"
|
||||||
|
}
|
||||||
|
sh1 = sh1 + fmt.Sprintf("%s/%s%s ", s.Type, s.Unit, dflt)
|
||||||
|
}
|
||||||
|
ss = append(ss, strings.TrimSpace(sh1))
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
ss = append(ss, s.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = append(ss, "Locations")
|
||||||
|
for _, l := range p.Location {
|
||||||
|
ss = append(ss, l.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = append(ss, "Mappings")
|
||||||
|
for _, m := range p.Mapping {
|
||||||
|
ss = append(ss, m.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(ss, "\n") + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// string dumps a text representation of a mapping. Intended mainly
|
||||||
|
// for debugging purposes.
|
||||||
|
func (m *Mapping) string() string {
|
||||||
|
bits := ""
|
||||||
|
if m.HasFunctions {
|
||||||
|
bits = bits + "[FN]"
|
||||||
|
}
|
||||||
|
if m.HasFilenames {
|
||||||
|
bits = bits + "[FL]"
|
||||||
|
}
|
||||||
|
if m.HasLineNumbers {
|
||||||
|
bits = bits + "[LN]"
|
||||||
|
}
|
||||||
|
if m.HasInlineFrames {
|
||||||
|
bits = bits + "[IN]"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s",
|
||||||
|
m.ID,
|
||||||
|
m.Start, m.Limit, m.Offset,
|
||||||
|
m.File,
|
||||||
|
m.BuildID,
|
||||||
|
bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// string dumps a text representation of a location. Intended mainly
|
||||||
|
// for debugging purposes.
|
||||||
|
func (l *Location) string() string {
|
||||||
|
ss := []string{}
|
||||||
|
locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address)
|
||||||
|
if m := l.Mapping; m != nil {
|
||||||
|
locStr = locStr + fmt.Sprintf("M=%d ", m.ID)
|
||||||
|
}
|
||||||
|
if l.IsFolded {
|
||||||
|
locStr = locStr + "[F] "
|
||||||
|
}
|
||||||
|
if len(l.Line) == 0 {
|
||||||
|
ss = append(ss, locStr)
|
||||||
|
}
|
||||||
|
for li := range l.Line {
|
||||||
|
lnStr := "??"
|
||||||
|
if fn := l.Line[li].Function; fn != nil {
|
||||||
|
lnStr = fmt.Sprintf("%s %s:%d s=%d",
|
||||||
|
fn.Name,
|
||||||
|
fn.Filename,
|
||||||
|
l.Line[li].Line,
|
||||||
|
fn.StartLine)
|
||||||
|
if fn.Name != fn.SystemName {
|
||||||
|
lnStr = lnStr + "(" + fn.SystemName + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss = append(ss, locStr+lnStr)
|
||||||
|
// Do not print location details past the first line
|
||||||
|
locStr = " "
|
||||||
|
}
|
||||||
|
return strings.Join(ss, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// string dumps a text representation of a sample. Intended mainly
|
||||||
|
// for debugging purposes.
|
||||||
|
func (s *Sample) string() string {
|
||||||
|
ss := []string{}
|
||||||
|
var sv string
|
||||||
|
for _, v := range s.Value {
|
||||||
|
sv = fmt.Sprintf("%s %10d", sv, v)
|
||||||
|
}
|
||||||
|
sv = sv + ": "
|
||||||
|
for _, l := range s.Location {
|
||||||
|
sv = sv + fmt.Sprintf("%d ", l.ID)
|
||||||
|
}
|
||||||
|
ss = append(ss, sv)
|
||||||
|
const labelHeader = " "
|
||||||
|
if len(s.Label) > 0 {
|
||||||
|
ss = append(ss, labelHeader+labelsToString(s.Label))
|
||||||
|
}
|
||||||
|
if len(s.NumLabel) > 0 {
|
||||||
|
ss = append(ss, labelHeader+numLabelsToString(s.NumLabel, s.NumUnit))
|
||||||
|
}
|
||||||
|
return strings.Join(ss, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelsToString returns a string representation of a
|
||||||
|
// map representing labels.
|
||||||
|
func labelsToString(labels map[string][]string) string {
|
||||||
|
ls := []string{}
|
||||||
|
for k, v := range labels {
|
||||||
|
ls = append(ls, fmt.Sprintf("%s:%v", k, v))
|
||||||
|
}
|
||||||
|
sort.Strings(ls)
|
||||||
|
return strings.Join(ls, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// numLabelsToString returns a string representation of a map
|
||||||
|
// representing numeric labels.
|
||||||
|
func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]string) string {
|
||||||
|
ls := []string{}
|
||||||
|
for k, v := range numLabels {
|
||||||
|
units := numUnits[k]
|
||||||
|
var labelString string
|
||||||
|
if len(units) == len(v) {
|
||||||
|
values := make([]string, len(v))
|
||||||
|
for i, vv := range v {
|
||||||
|
values[i] = fmt.Sprintf("%d %s", vv, units[i])
|
||||||
|
}
|
||||||
|
labelString = fmt.Sprintf("%s:%v", k, values)
|
||||||
|
} else {
|
||||||
|
labelString = fmt.Sprintf("%s:%v", k, v)
|
||||||
|
}
|
||||||
|
ls = append(ls, labelString)
|
||||||
|
}
|
||||||
|
sort.Strings(ls)
|
||||||
|
return strings.Join(ls, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabel sets the specified key to the specified value for all samples in the
|
||||||
|
// profile.
|
||||||
|
func (p *Profile) SetLabel(key string, value []string) {
|
||||||
|
for _, sample := range p.Sample {
|
||||||
|
if sample.Label == nil {
|
||||||
|
sample.Label = map[string][]string{key: value}
|
||||||
|
} else {
|
||||||
|
sample.Label[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLabel removes all labels associated with the specified key for all
|
||||||
|
// samples in the profile.
|
||||||
|
func (p *Profile) RemoveLabel(key string) {
|
||||||
|
for _, sample := range p.Sample {
|
||||||
|
delete(sample.Label, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasLabel returns true if a sample has a label with indicated key and value.
|
||||||
|
func (s *Sample) HasLabel(key, value string) bool {
|
||||||
|
for _, v := range s.Label[key] {
|
||||||
|
if v == value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffBaseSample returns true if a sample belongs to the diff base and false
|
||||||
|
// otherwise.
|
||||||
|
func (s *Sample) DiffBaseSample() bool {
|
||||||
|
return s.HasLabel("pprof::base", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale multiplies all sample values in a profile by a constant and keeps
|
||||||
|
// only samples that have at least one non-zero value.
|
||||||
|
func (p *Profile) Scale(ratio float64) {
|
||||||
|
if ratio == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ratios := make([]float64, len(p.SampleType))
|
||||||
|
for i := range p.SampleType {
|
||||||
|
ratios[i] = ratio
|
||||||
|
}
|
||||||
|
p.ScaleN(ratios)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScaleN multiplies each sample values in a sample by a different amount
|
||||||
|
// and keeps only samples that have at least one non-zero value.
|
||||||
|
func (p *Profile) ScaleN(ratios []float64) error {
|
||||||
|
if len(p.SampleType) != len(ratios) {
|
||||||
|
return fmt.Errorf("mismatched scale ratios, got %d, want %d", len(ratios), len(p.SampleType))
|
||||||
|
}
|
||||||
|
allOnes := true
|
||||||
|
for _, r := range ratios {
|
||||||
|
if r != 1 {
|
||||||
|
allOnes = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allOnes {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fillIdx := 0
|
||||||
|
for _, s := range p.Sample {
|
||||||
|
keepSample := false
|
||||||
|
for i, v := range s.Value {
|
||||||
|
if ratios[i] != 1 {
|
||||||
|
val := int64(math.Round(float64(v) * ratios[i]))
|
||||||
|
s.Value[i] = val
|
||||||
|
keepSample = keepSample || val != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keepSample {
|
||||||
|
p.Sample[fillIdx] = s
|
||||||
|
fillIdx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Sample = p.Sample[:fillIdx]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFunctions determines if all locations in this profile have
|
||||||
|
// symbolized function information.
|
||||||
|
func (p *Profile) HasFunctions() bool {
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if l.Mapping != nil && !l.Mapping.HasFunctions {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFileLines determines if all locations in this profile have
|
||||||
|
// symbolized file and line number information.
|
||||||
|
func (p *Profile) HasFileLines() bool {
|
||||||
|
for _, l := range p.Location {
|
||||||
|
if l.Mapping != nil && (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsymbolizable returns true if a mapping points to a binary for which
|
||||||
|
// locations can't be symbolized in principle, at least now. Examples are
|
||||||
|
// "[vdso]", [vsyscall]" and some others, see the code.
|
||||||
|
func (m *Mapping) Unsymbolizable() bool {
|
||||||
|
name := filepath.Base(m.File)
|
||||||
|
return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a fully independent copy of a profile.
|
||||||
|
func (p *Profile) Copy() *Profile {
|
||||||
|
pp := &Profile{}
|
||||||
|
if err := unmarshal(serialize(p), pp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := pp.postDecode(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp
|
||||||
|
}
|
367
vendor/github.com/google/pprof/profile/proto.go
generated
vendored
Normal file
367
vendor/github.com/google/pprof/profile/proto.go
generated
vendored
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// This file is a simple protocol buffer encoder and decoder.
|
||||||
|
// The format is described at
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/encoding
|
||||||
|
//
|
||||||
|
// A protocol message must implement the message interface:
|
||||||
|
// decoder() []decoder
|
||||||
|
// encode(*buffer)
|
||||||
|
//
|
||||||
|
// The decode method returns a slice indexed by field number that gives the
|
||||||
|
// function to decode that field.
|
||||||
|
// The encode method encodes its receiver into the given buffer.
|
||||||
|
//
|
||||||
|
// The two methods are simple enough to be implemented by hand rather than
|
||||||
|
// by using a protocol compiler.
|
||||||
|
//
|
||||||
|
// See profile.go for examples of messages implementing this interface.
|
||||||
|
//
|
||||||
|
// There is no support for groups, message sets, or "has" bits.
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type buffer struct {
|
||||||
|
field int // field tag
|
||||||
|
typ int // proto wire type code for field
|
||||||
|
u64 uint64
|
||||||
|
data []byte
|
||||||
|
tmp [16]byte
|
||||||
|
tmpLines []Line // temporary storage used while decoding "repeated Line".
|
||||||
|
}
|
||||||
|
|
||||||
|
type decoder func(*buffer, message) error
|
||||||
|
|
||||||
|
type message interface {
|
||||||
|
decoder() []decoder
|
||||||
|
encode(*buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(m message) []byte {
|
||||||
|
var b buffer
|
||||||
|
m.encode(&b)
|
||||||
|
return b.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarint(b *buffer, x uint64) {
|
||||||
|
for x >= 128 {
|
||||||
|
b.data = append(b.data, byte(x)|0x80)
|
||||||
|
x >>= 7
|
||||||
|
}
|
||||||
|
b.data = append(b.data, byte(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeLength(b *buffer, tag int, len int) {
|
||||||
|
encodeVarint(b, uint64(tag)<<3|2)
|
||||||
|
encodeVarint(b, uint64(len))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeUint64(b *buffer, tag int, x uint64) {
|
||||||
|
// append varint to b.data
|
||||||
|
encodeVarint(b, uint64(tag)<<3)
|
||||||
|
encodeVarint(b, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeUint64s(b *buffer, tag int, x []uint64) {
|
||||||
|
if len(x) > 2 {
|
||||||
|
// Use packed encoding
|
||||||
|
n1 := len(b.data)
|
||||||
|
for _, u := range x {
|
||||||
|
encodeVarint(b, u)
|
||||||
|
}
|
||||||
|
n2 := len(b.data)
|
||||||
|
encodeLength(b, tag, n2-n1)
|
||||||
|
n3 := len(b.data)
|
||||||
|
copy(b.tmp[:], b.data[n2:n3])
|
||||||
|
copy(b.data[n1+(n3-n2):], b.data[n1:n2])
|
||||||
|
copy(b.data[n1:], b.tmp[:n3-n2])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range x {
|
||||||
|
encodeUint64(b, tag, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeUint64Opt(b *buffer, tag int, x uint64) {
|
||||||
|
if x == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encodeUint64(b, tag, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInt64(b *buffer, tag int, x int64) {
|
||||||
|
u := uint64(x)
|
||||||
|
encodeUint64(b, tag, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInt64s(b *buffer, tag int, x []int64) {
|
||||||
|
if len(x) > 2 {
|
||||||
|
// Use packed encoding
|
||||||
|
n1 := len(b.data)
|
||||||
|
for _, u := range x {
|
||||||
|
encodeVarint(b, uint64(u))
|
||||||
|
}
|
||||||
|
n2 := len(b.data)
|
||||||
|
encodeLength(b, tag, n2-n1)
|
||||||
|
n3 := len(b.data)
|
||||||
|
copy(b.tmp[:], b.data[n2:n3])
|
||||||
|
copy(b.data[n1+(n3-n2):], b.data[n1:n2])
|
||||||
|
copy(b.data[n1:], b.tmp[:n3-n2])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range x {
|
||||||
|
encodeInt64(b, tag, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInt64Opt(b *buffer, tag int, x int64) {
|
||||||
|
if x == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encodeInt64(b, tag, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeString(b *buffer, tag int, x string) {
|
||||||
|
encodeLength(b, tag, len(x))
|
||||||
|
b.data = append(b.data, x...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeStrings(b *buffer, tag int, x []string) {
|
||||||
|
for _, s := range x {
|
||||||
|
encodeString(b, tag, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBool(b *buffer, tag int, x bool) {
|
||||||
|
if x {
|
||||||
|
encodeUint64(b, tag, 1)
|
||||||
|
} else {
|
||||||
|
encodeUint64(b, tag, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBoolOpt(b *buffer, tag int, x bool) {
|
||||||
|
if x {
|
||||||
|
encodeBool(b, tag, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMessage(b *buffer, tag int, m message) {
|
||||||
|
n1 := len(b.data)
|
||||||
|
m.encode(b)
|
||||||
|
n2 := len(b.data)
|
||||||
|
encodeLength(b, tag, n2-n1)
|
||||||
|
n3 := len(b.data)
|
||||||
|
copy(b.tmp[:], b.data[n2:n3])
|
||||||
|
copy(b.data[n1+(n3-n2):], b.data[n1:n2])
|
||||||
|
copy(b.data[n1:], b.tmp[:n3-n2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(data []byte, m message) (err error) {
|
||||||
|
b := buffer{data: data, typ: 2}
|
||||||
|
return decodeMessage(&b, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func le64(p []byte) uint64 {
|
||||||
|
return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56
|
||||||
|
}
|
||||||
|
|
||||||
|
func le32(p []byte) uint32 {
|
||||||
|
return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeVarint(data []byte) (uint64, []byte, error) {
|
||||||
|
var u uint64
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if i >= 10 || i >= len(data) {
|
||||||
|
return 0, nil, errors.New("bad varint")
|
||||||
|
}
|
||||||
|
u |= uint64(data[i]&0x7F) << uint(7*i)
|
||||||
|
if data[i]&0x80 == 0 {
|
||||||
|
return u, data[i+1:], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeField(b *buffer, data []byte) ([]byte, error) {
|
||||||
|
x, data, err := decodeVarint(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.field = int(x >> 3)
|
||||||
|
b.typ = int(x & 7)
|
||||||
|
b.data = nil
|
||||||
|
b.u64 = 0
|
||||||
|
switch b.typ {
|
||||||
|
case 0:
|
||||||
|
b.u64, data, err = decodeVarint(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if len(data) < 8 {
|
||||||
|
return nil, errors.New("not enough data")
|
||||||
|
}
|
||||||
|
b.u64 = le64(data[:8])
|
||||||
|
data = data[8:]
|
||||||
|
case 2:
|
||||||
|
var n uint64
|
||||||
|
n, data, err = decodeVarint(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n > uint64(len(data)) {
|
||||||
|
return nil, errors.New("too much data")
|
||||||
|
}
|
||||||
|
b.data = data[:n]
|
||||||
|
data = data[n:]
|
||||||
|
case 5:
|
||||||
|
if len(data) < 4 {
|
||||||
|
return nil, errors.New("not enough data")
|
||||||
|
}
|
||||||
|
b.u64 = uint64(le32(data[:4]))
|
||||||
|
data = data[4:]
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown wire type: %d", b.typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkType(b *buffer, typ int) error {
|
||||||
|
if b.typ != typ {
|
||||||
|
return errors.New("type mismatch")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMessage(b *buffer, m message) error {
|
||||||
|
if err := checkType(b, 2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dec := m.decoder()
|
||||||
|
data := b.data
|
||||||
|
for len(data) > 0 {
|
||||||
|
// pull varint field# + type
|
||||||
|
var err error
|
||||||
|
data, err = decodeField(b, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if b.field >= len(dec) || dec[b.field] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := dec[b.field](b, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInt64(b *buffer, x *int64) error {
|
||||||
|
if err := checkType(b, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = int64(b.u64)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInt64s(b *buffer, x *[]int64) error {
|
||||||
|
if b.typ == 2 {
|
||||||
|
// Packed encoding
|
||||||
|
data := b.data
|
||||||
|
for len(data) > 0 {
|
||||||
|
var u uint64
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if u, data, err = decodeVarint(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = append(*x, int64(u))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var i int64
|
||||||
|
if err := decodeInt64(b, &i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = append(*x, i)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeUint64(b *buffer, x *uint64) error {
|
||||||
|
if err := checkType(b, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = b.u64
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeUint64s(b *buffer, x *[]uint64) error {
|
||||||
|
if b.typ == 2 {
|
||||||
|
data := b.data
|
||||||
|
// Packed encoding
|
||||||
|
for len(data) > 0 {
|
||||||
|
var u uint64
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if u, data, err = decodeVarint(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = append(*x, u)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var u uint64
|
||||||
|
if err := decodeUint64(b, &u); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = append(*x, u)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeString(b *buffer, x *string) error {
|
||||||
|
if err := checkType(b, 2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = string(b.data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeStrings(b *buffer, x *[]string) error {
|
||||||
|
var s string
|
||||||
|
if err := decodeString(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = append(*x, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBool(b *buffer, x *bool) error {
|
||||||
|
if err := checkType(b, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if int64(b.u64) == 0 {
|
||||||
|
*x = false
|
||||||
|
} else {
|
||||||
|
*x = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
194
vendor/github.com/google/pprof/profile/prune.go
generated
vendored
Normal file
194
vendor/github.com/google/pprof/profile/prune.go
generated
vendored
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Implements methods to remove frames from profiles.
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
reservedNames = []string{"(anonymous namespace)", "operator()"}
|
||||||
|
bracketRx = func() *regexp.Regexp {
|
||||||
|
var quotedNames []string
|
||||||
|
for _, name := range append(reservedNames, "(") {
|
||||||
|
quotedNames = append(quotedNames, regexp.QuoteMeta(name))
|
||||||
|
}
|
||||||
|
return regexp.MustCompile(strings.Join(quotedNames, "|"))
|
||||||
|
}()
|
||||||
|
)
|
||||||
|
|
||||||
|
// simplifyFunc does some primitive simplification of function names.
|
||||||
|
func simplifyFunc(f string) string {
|
||||||
|
// Account for leading '.' on the PPC ELF v1 ABI.
|
||||||
|
funcName := strings.TrimPrefix(f, ".")
|
||||||
|
// Account for unsimplified names -- try to remove the argument list by trimming
|
||||||
|
// starting from the first '(', but skipping reserved names that have '('.
|
||||||
|
for _, ind := range bracketRx.FindAllStringSubmatchIndex(funcName, -1) {
|
||||||
|
foundReserved := false
|
||||||
|
for _, res := range reservedNames {
|
||||||
|
if funcName[ind[0]:ind[1]] == res {
|
||||||
|
foundReserved = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundReserved {
|
||||||
|
funcName = funcName[:ind[0]]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return funcName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune removes all nodes beneath a node matching dropRx, and not
|
||||||
|
// matching keepRx. If the root node of a Sample matches, the sample
|
||||||
|
// will have an empty stack.
|
||||||
|
func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) {
|
||||||
|
prune := make(map[uint64]bool)
|
||||||
|
pruneBeneath := make(map[uint64]bool)
|
||||||
|
|
||||||
|
// simplifyFunc can be expensive, so cache results.
|
||||||
|
// Note that the same function name can be encountered many times due
|
||||||
|
// different lines and addresses in the same function.
|
||||||
|
pruneCache := map[string]bool{} // Map from function to whether or not to prune
|
||||||
|
pruneFromHere := func(s string) bool {
|
||||||
|
if r, ok := pruneCache[s]; ok {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
funcName := simplifyFunc(s)
|
||||||
|
if dropRx.MatchString(funcName) {
|
||||||
|
if keepRx == nil || !keepRx.MatchString(funcName) {
|
||||||
|
pruneCache[s] = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pruneCache[s] = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, loc := range p.Location {
|
||||||
|
var i int
|
||||||
|
for i = len(loc.Line) - 1; i >= 0; i-- {
|
||||||
|
if fn := loc.Line[i].Function; fn != nil && fn.Name != "" {
|
||||||
|
if pruneFromHere(fn.Name) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= 0 {
|
||||||
|
// Found matching entry to prune.
|
||||||
|
pruneBeneath[loc.ID] = true
|
||||||
|
|
||||||
|
// Remove the matching location.
|
||||||
|
if i == len(loc.Line)-1 {
|
||||||
|
// Matched the top entry: prune the whole location.
|
||||||
|
prune[loc.ID] = true
|
||||||
|
} else {
|
||||||
|
loc.Line = loc.Line[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune locs from each Sample
|
||||||
|
for _, sample := range p.Sample {
|
||||||
|
// Scan from the root to the leaves to find the prune location.
|
||||||
|
// Do not prune frames before the first user frame, to avoid
|
||||||
|
// pruning everything.
|
||||||
|
foundUser := false
|
||||||
|
for i := len(sample.Location) - 1; i >= 0; i-- {
|
||||||
|
id := sample.Location[i].ID
|
||||||
|
if !prune[id] && !pruneBeneath[id] {
|
||||||
|
foundUser = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !foundUser {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if prune[id] {
|
||||||
|
sample.Location = sample.Location[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if pruneBeneath[id] {
|
||||||
|
sample.Location = sample.Location[i:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUninteresting prunes and elides profiles using built-in
|
||||||
|
// tables of uninteresting function names.
|
||||||
|
func (p *Profile) RemoveUninteresting() error {
|
||||||
|
var keep, drop *regexp.Regexp
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if p.DropFrames != "" {
|
||||||
|
if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil {
|
||||||
|
return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err)
|
||||||
|
}
|
||||||
|
if p.KeepFrames != "" {
|
||||||
|
if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil {
|
||||||
|
return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Prune(drop, keep)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PruneFrom removes all nodes beneath the lowest node matching dropRx, not including itself.
|
||||||
|
//
|
||||||
|
// Please see the example below to understand this method as well as
|
||||||
|
// the difference from Prune method.
|
||||||
|
//
|
||||||
|
// A sample contains Location of [A,B,C,B,D] where D is the top frame and there's no inline.
|
||||||
|
//
|
||||||
|
// PruneFrom(A) returns [A,B,C,B,D] because there's no node beneath A.
|
||||||
|
// Prune(A, nil) returns [B,C,B,D] by removing A itself.
|
||||||
|
//
|
||||||
|
// PruneFrom(B) returns [B,C,B,D] by removing all nodes beneath the first B when scanning from the bottom.
|
||||||
|
// Prune(B, nil) returns [D] because a matching node is found by scanning from the root.
|
||||||
|
func (p *Profile) PruneFrom(dropRx *regexp.Regexp) {
|
||||||
|
pruneBeneath := make(map[uint64]bool)
|
||||||
|
|
||||||
|
for _, loc := range p.Location {
|
||||||
|
for i := 0; i < len(loc.Line); i++ {
|
||||||
|
if fn := loc.Line[i].Function; fn != nil && fn.Name != "" {
|
||||||
|
funcName := simplifyFunc(fn.Name)
|
||||||
|
if dropRx.MatchString(funcName) {
|
||||||
|
// Found matching entry to prune.
|
||||||
|
pruneBeneath[loc.ID] = true
|
||||||
|
loc.Line = loc.Line[i:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune locs from each Sample
|
||||||
|
for _, sample := range p.Sample {
|
||||||
|
// Scan from the bottom leaf to the root to find the prune location.
|
||||||
|
for i, loc := range sample.Location {
|
||||||
|
if pruneBeneath[loc.ID] {
|
||||||
|
sample.Location = sample.Location[i:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
vendor/github.com/hashicorp/golang-lru/v2/.gitignore
generated
vendored
Normal file
23
vendor/github.com/hashicorp/golang-lru/v2/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
30
vendor/github.com/hashicorp/golang-lru/v2/.golangci.yml
generated
vendored
Normal file
30
vendor/github.com/hashicorp/golang-lru/v2/.golangci.yml
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- megacheck
|
||||||
|
- revive
|
||||||
|
- govet
|
||||||
|
- unconvert
|
||||||
|
- megacheck
|
||||||
|
- gas
|
||||||
|
- gocyclo
|
||||||
|
- dupl
|
||||||
|
- misspell
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
- typecheck
|
||||||
|
- ineffassign
|
||||||
|
- stylecheck
|
||||||
|
- exportloopref
|
||||||
|
- gocritic
|
||||||
|
- nakedret
|
||||||
|
- gosimple
|
||||||
|
- prealloc
|
||||||
|
fast: false
|
||||||
|
disable-all: true
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
|
exclude-use-default: false
|
223
vendor/github.com/hashicorp/golang-lru/v2/2q.go
generated
vendored
Normal file
223
vendor/github.com/hashicorp/golang-lru/v2/2q.go
generated
vendored
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default2QRecentRatio is the ratio of the 2Q cache dedicated
|
||||||
|
// to recently added entries that have only been accessed once.
|
||||||
|
Default2QRecentRatio = 0.25
|
||||||
|
|
||||||
|
// Default2QGhostEntries is the default ratio of ghost
|
||||||
|
// entries kept to track entries recently evicted
|
||||||
|
Default2QGhostEntries = 0.50
|
||||||
|
)
|
||||||
|
|
||||||
|
// TwoQueueCache is a thread-safe fixed size 2Q cache.
|
||||||
|
// 2Q is an enhancement over the standard LRU cache
|
||||||
|
// in that it tracks both frequently and recently used
|
||||||
|
// entries separately. This avoids a burst in access to new
|
||||||
|
// entries from evicting frequently used entries. It adds some
|
||||||
|
// additional tracking overhead to the standard LRU cache, and is
|
||||||
|
// computationally about 2x the cost, and adds some metadata over
|
||||||
|
// head. The ARCCache is similar, but does not require setting any
|
||||||
|
// parameters.
|
||||||
|
type TwoQueueCache[K comparable, V any] struct {
|
||||||
|
size int
|
||||||
|
recentSize int
|
||||||
|
|
||||||
|
recent simplelru.LRUCache[K, V]
|
||||||
|
frequent simplelru.LRUCache[K, V]
|
||||||
|
recentEvict simplelru.LRUCache[K, V]
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New2Q creates a new TwoQueueCache using the default
|
||||||
|
// values for the parameters.
|
||||||
|
func New2Q[K comparable, V any](size int) (*TwoQueueCache[K, V], error) {
|
||||||
|
return New2QParams[K, V](size, Default2QRecentRatio, Default2QGhostEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New2QParams creates a new TwoQueueCache using the provided
|
||||||
|
// parameter values.
|
||||||
|
func New2QParams[K comparable, V any](size int, recentRatio, ghostRatio float64) (*TwoQueueCache[K, V], error) {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil, fmt.Errorf("invalid size")
|
||||||
|
}
|
||||||
|
if recentRatio < 0.0 || recentRatio > 1.0 {
|
||||||
|
return nil, fmt.Errorf("invalid recent ratio")
|
||||||
|
}
|
||||||
|
if ghostRatio < 0.0 || ghostRatio > 1.0 {
|
||||||
|
return nil, fmt.Errorf("invalid ghost ratio")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the sub-sizes
|
||||||
|
recentSize := int(float64(size) * recentRatio)
|
||||||
|
evictSize := int(float64(size) * ghostRatio)
|
||||||
|
|
||||||
|
// Allocate the LRUs
|
||||||
|
recent, err := simplelru.NewLRU[K, V](size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frequent, err := simplelru.NewLRU[K, V](size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
recentEvict, err := simplelru.NewLRU[K, V](evictSize, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the cache
|
||||||
|
c := &TwoQueueCache[K, V]{
|
||||||
|
size: size,
|
||||||
|
recentSize: recentSize,
|
||||||
|
recent: recent,
|
||||||
|
frequent: frequent,
|
||||||
|
recentEvict: recentEvict,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *TwoQueueCache[K, V]) Get(key K) (value V, ok bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Check if this is a frequent value
|
||||||
|
if val, ok := c.frequent.Get(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is contained in recent, then we
|
||||||
|
// promote it to frequent
|
||||||
|
if val, ok := c.recent.Peek(key); ok {
|
||||||
|
c.recent.Remove(key)
|
||||||
|
c.frequent.Add(key, val)
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// No hit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache.
|
||||||
|
func (c *TwoQueueCache[K, V]) Add(key K, value V) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Check if the value is frequently used already,
|
||||||
|
// and just update the value
|
||||||
|
if c.frequent.Contains(key) {
|
||||||
|
c.frequent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is recently used, and promote
|
||||||
|
// the value into the frequent list
|
||||||
|
if c.recent.Contains(key) {
|
||||||
|
c.recent.Remove(key)
|
||||||
|
c.frequent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value was recently evicted, add it to the
|
||||||
|
// frequently used list
|
||||||
|
if c.recentEvict.Contains(key) {
|
||||||
|
c.ensureSpace(true)
|
||||||
|
c.recentEvict.Remove(key)
|
||||||
|
c.frequent.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the recently seen list
|
||||||
|
c.ensureSpace(false)
|
||||||
|
c.recent.Add(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureSpace is used to ensure we have space in the cache
|
||||||
|
func (c *TwoQueueCache[K, V]) ensureSpace(recentEvict bool) {
|
||||||
|
// If we have space, nothing to do
|
||||||
|
recentLen := c.recent.Len()
|
||||||
|
freqLen := c.frequent.Len()
|
||||||
|
if recentLen+freqLen < c.size {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the recent buffer is larger than
|
||||||
|
// the target, evict from there
|
||||||
|
if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
|
||||||
|
k, _, _ := c.recent.RemoveOldest()
|
||||||
|
var empty V
|
||||||
|
c.recentEvict.Add(k, empty)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from the frequent list otherwise
|
||||||
|
c.frequent.RemoveOldest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
|
func (c *TwoQueueCache[K, V]) Len() int {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.recent.Len() + c.frequent.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of the keys in the cache.
|
||||||
|
// The frequently used keys are first in the returned slice.
|
||||||
|
func (c *TwoQueueCache[K, V]) Keys() []K {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
k1 := c.frequent.Keys()
|
||||||
|
k2 := c.recent.Keys()
|
||||||
|
return append(k1, k2...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided key from the cache.
|
||||||
|
func (c *TwoQueueCache[K, V]) Remove(key K) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
if c.frequent.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.recent.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.recentEvict.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to completely clear the cache.
|
||||||
|
func (c *TwoQueueCache[K, V]) Purge() {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
c.recent.Purge()
|
||||||
|
c.frequent.Purge()
|
||||||
|
c.recentEvict.Purge()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains is used to check if the cache contains a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *TwoQueueCache[K, V]) Contains(key K) bool {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.frequent.Contains(key) || c.recent.Contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek is used to inspect the cache value of a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *TwoQueueCache[K, V]) Peek(key K) (value V, ok bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
if val, ok := c.frequent.Peek(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
return c.recent.Peek(key)
|
||||||
|
}
|
364
vendor/github.com/hashicorp/golang-lru/v2/LICENSE
generated
vendored
Normal file
364
vendor/github.com/hashicorp/golang-lru/v2/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
Copyright (c) 2014 HashiCorp, Inc.
|
||||||
|
|
||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described in
|
||||||
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
|
a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
|
||||||
|
means a work that combines Covered Software with other material, in a
|
||||||
|
separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
|
||||||
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
|
controlled by, or is under common control with You. For purposes of this
|
||||||
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
|
the direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
|
sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
|
of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
|
limitations of liability) contained within the Source Code Form of the
|
||||||
|
Covered Software, except that You may alter any license notices to the
|
||||||
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the
|
||||||
|
limitations and the code they affect. Such description must be placed in a
|
||||||
|
text file included with all distributions of the Covered Software under
|
||||||
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
|
skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
|
then the rights granted under this License from a particular Contributor
|
||||||
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
|
some reasonable means prior to 60 days after You have come back into
|
||||||
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
|
received notice of non-compliance with this License from such
|
||||||
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
|
of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
|
license agreements (excluding distributors and resellers) which have been
|
||||||
|
validly granted by You or Your distributors under this License prior to
|
||||||
|
termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
|
including, without limitation, warranties that the Covered Software is free
|
||||||
|
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||||
|
The entire risk as to the quality and performance of the Covered Software
|
||||||
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
|
part of this License. No use of any Covered Software is authorized under
|
||||||
|
this License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character including, without limitation, damages for lost profits, loss of
|
||||||
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party's negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts
|
||||||
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
|
business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
|
counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
|
the language of a contract shall be construed against the drafter shall not
|
||||||
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this License must be
|
||||||
|
attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
|
notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible
|
||||||
|
With Secondary Licenses", as defined by
|
||||||
|
the Mozilla Public License, v. 2.0.
|
25
vendor/github.com/hashicorp/golang-lru/v2/README.md
generated
vendored
Normal file
25
vendor/github.com/hashicorp/golang-lru/v2/README.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
golang-lru
|
||||||
|
==========
|
||||||
|
|
||||||
|
This provides the `lru` package which implements a fixed-size
|
||||||
|
thread safe LRU cache. It is based on the cache in Groupcache.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
Full docs are available on [Go Packages](https://pkg.go.dev/github.com/hashicorp/golang-lru/v2)
|
||||||
|
|
||||||
|
Example
|
||||||
|
=======
|
||||||
|
|
||||||
|
Using the LRU is very simple:
|
||||||
|
|
||||||
|
```go
|
||||||
|
l, _ := New(128)
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
l.Add(i, nil)
|
||||||
|
}
|
||||||
|
if l.Len() != 128 {
|
||||||
|
panic(fmt.Sprintf("bad len: %v", l.Len()))
|
||||||
|
}
|
||||||
|
```
|
258
vendor/github.com/hashicorp/golang-lru/v2/arc.go
generated
vendored
Normal file
258
vendor/github.com/hashicorp/golang-lru/v2/arc.go
generated
vendored
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC).
|
||||||
|
// ARC is an enhancement over the standard LRU cache in that tracks both
|
||||||
|
// frequency and recency of use. This avoids a burst in access to new
|
||||||
|
// entries from evicting the frequently used older entries. It adds some
|
||||||
|
// additional tracking overhead to a standard LRU cache, computationally
|
||||||
|
// it is roughly 2x the cost, and the extra memory overhead is linear
|
||||||
|
// with the size of the cache. ARC has been patented by IBM, but is
|
||||||
|
// similar to the TwoQueueCache (2Q) which requires setting parameters.
|
||||||
|
type ARCCache[K comparable, V any] struct {
|
||||||
|
size int // Size is the total capacity of the cache
|
||||||
|
p int // P is the dynamic preference towards T1 or T2
|
||||||
|
|
||||||
|
t1 simplelru.LRUCache[K, V] // T1 is the LRU for recently accessed items
|
||||||
|
b1 simplelru.LRUCache[K, V] // B1 is the LRU for evictions from t1
|
||||||
|
|
||||||
|
t2 simplelru.LRUCache[K, V] // T2 is the LRU for frequently accessed items
|
||||||
|
b2 simplelru.LRUCache[K, V] // B2 is the LRU for evictions from t2
|
||||||
|
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewARC creates an ARC of the given size
|
||||||
|
func NewARC[K comparable, V any](size int) (*ARCCache[K, V], error) {
|
||||||
|
// Create the sub LRUs
|
||||||
|
b1, err := simplelru.NewLRU[K, V](size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b2, err := simplelru.NewLRU[K, V](size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t1, err := simplelru.NewLRU[K, V](size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t2, err := simplelru.NewLRU[K, V](size, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the ARC
|
||||||
|
c := &ARCCache[K, V]{
|
||||||
|
size: size,
|
||||||
|
p: 0,
|
||||||
|
t1: t1,
|
||||||
|
b1: b1,
|
||||||
|
t2: t2,
|
||||||
|
b2: b2,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *ARCCache[K, V]) Get(key K) (value V, ok bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// If the value is contained in T1 (recent), then
|
||||||
|
// promote it to T2 (frequent)
|
||||||
|
if val, ok := c.t1.Peek(key); ok {
|
||||||
|
c.t1.Remove(key)
|
||||||
|
c.t2.Add(key, val)
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is contained in T2 (frequent)
|
||||||
|
if val, ok := c.t2.Get(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// No hit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache.
|
||||||
|
func (c *ARCCache[K, V]) Add(key K, value V) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Check if the value is contained in T1 (recent), and potentially
|
||||||
|
// promote it to frequent T2
|
||||||
|
if c.t1.Contains(key) {
|
||||||
|
c.t1.Remove(key)
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is already in T2 (frequent) and update it
|
||||||
|
if c.t2.Contains(key) {
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this value was recently evicted as part of the
|
||||||
|
// recently used list
|
||||||
|
if c.b1.Contains(key) {
|
||||||
|
// T1 set is too small, increase P appropriately
|
||||||
|
delta := 1
|
||||||
|
b1Len := c.b1.Len()
|
||||||
|
b2Len := c.b2.Len()
|
||||||
|
if b2Len > b1Len {
|
||||||
|
delta = b2Len / b1Len
|
||||||
|
}
|
||||||
|
if c.p+delta >= c.size {
|
||||||
|
c.p = c.size
|
||||||
|
} else {
|
||||||
|
c.p += delta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially need to make room in the cache
|
||||||
|
if c.t1.Len()+c.t2.Len() >= c.size {
|
||||||
|
c.replace(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from B1
|
||||||
|
c.b1.Remove(key)
|
||||||
|
|
||||||
|
// Add the key to the frequently used list
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this value was recently evicted as part of the
|
||||||
|
// frequently used list
|
||||||
|
if c.b2.Contains(key) {
|
||||||
|
// T2 set is too small, decrease P appropriately
|
||||||
|
delta := 1
|
||||||
|
b1Len := c.b1.Len()
|
||||||
|
b2Len := c.b2.Len()
|
||||||
|
if b1Len > b2Len {
|
||||||
|
delta = b1Len / b2Len
|
||||||
|
}
|
||||||
|
if delta >= c.p {
|
||||||
|
c.p = 0
|
||||||
|
} else {
|
||||||
|
c.p -= delta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially need to make room in the cache
|
||||||
|
if c.t1.Len()+c.t2.Len() >= c.size {
|
||||||
|
c.replace(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from B2
|
||||||
|
c.b2.Remove(key)
|
||||||
|
|
||||||
|
// Add the key to the frequently used list
|
||||||
|
c.t2.Add(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially need to make room in the cache
|
||||||
|
if c.t1.Len()+c.t2.Len() >= c.size {
|
||||||
|
c.replace(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the size of the ghost buffers trim
|
||||||
|
if c.b1.Len() > c.size-c.p {
|
||||||
|
c.b1.RemoveOldest()
|
||||||
|
}
|
||||||
|
if c.b2.Len() > c.p {
|
||||||
|
c.b2.RemoveOldest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the recently seen list
|
||||||
|
c.t1.Add(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace is used to adaptively evict from either T1 or T2
|
||||||
|
// based on the current learned value of P
|
||||||
|
func (c *ARCCache[K, V]) replace(b2ContainsKey bool) {
|
||||||
|
t1Len := c.t1.Len()
|
||||||
|
if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) {
|
||||||
|
k, _, ok := c.t1.RemoveOldest()
|
||||||
|
if ok {
|
||||||
|
var empty V
|
||||||
|
c.b1.Add(k, empty)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
k, _, ok := c.t2.RemoveOldest()
|
||||||
|
if ok {
|
||||||
|
var empty V
|
||||||
|
c.b2.Add(k, empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of cached entries
|
||||||
|
func (c *ARCCache[K, V]) Len() int {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.t1.Len() + c.t2.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns all the cached keys
|
||||||
|
func (c *ARCCache[K, V]) Keys() []K {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
k1 := c.t1.Keys()
|
||||||
|
k2 := c.t2.Keys()
|
||||||
|
return append(k1, k2...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove is used to purge a key from the cache
|
||||||
|
func (c *ARCCache[K, V]) Remove(key K) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
if c.t1.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.t2.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.b1.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.b2.Remove(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to clear the cache
|
||||||
|
func (c *ARCCache[K, V]) Purge() {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
c.t1.Purge()
|
||||||
|
c.t2.Purge()
|
||||||
|
c.b1.Purge()
|
||||||
|
c.b2.Purge()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains is used to check if the cache contains a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *ARCCache[K, V]) Contains(key K) bool {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
return c.t1.Contains(key) || c.t2.Contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek is used to inspect the cache value of a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *ARCCache[K, V]) Peek(key K) (value V, ok bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
if val, ok := c.t1.Peek(key); ok {
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
return c.t2.Peek(key)
|
||||||
|
}
|
21
vendor/github.com/hashicorp/golang-lru/v2/doc.go
generated
vendored
Normal file
21
vendor/github.com/hashicorp/golang-lru/v2/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Package lru provides three different LRU caches of varying sophistication.
|
||||||
|
//
|
||||||
|
// Cache is a simple LRU cache. It is based on the
|
||||||
|
// LRU implementation in groupcache:
|
||||||
|
// https://github.com/golang/groupcache/tree/master/lru
|
||||||
|
//
|
||||||
|
// TwoQueueCache tracks frequently used and recently used entries separately.
|
||||||
|
// This avoids a burst of accesses from taking out frequently used entries,
|
||||||
|
// at the cost of about 2x computational overhead and some extra bookkeeping.
|
||||||
|
//
|
||||||
|
// ARCCache is an adaptive replacement cache. It tracks recent evictions as
|
||||||
|
// well as recent usage in both the frequent and recent caches. Its
|
||||||
|
// computational overhead is comparable to TwoQueueCache, but the memory
|
||||||
|
// overhead is linear with the size of the cache.
|
||||||
|
//
|
||||||
|
// ARC has been patented by IBM, so do not use it if that is problematic for
|
||||||
|
// your program.
|
||||||
|
//
|
||||||
|
// All caches in this package take locks while operating, and are therefore
|
||||||
|
// thread-safe for consumers.
|
||||||
|
package lru
|
239
vendor/github.com/hashicorp/golang-lru/v2/lru.go
generated
vendored
Normal file
239
vendor/github.com/hashicorp/golang-lru/v2/lru.go
generated
vendored
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultEvictedBufferSize defines the default buffer size to store evicted key/val
|
||||||
|
DefaultEvictedBufferSize = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cache is a thread-safe fixed size LRU cache.
|
||||||
|
type Cache[K comparable, V any] struct {
|
||||||
|
lru *simplelru.LRU[K, V]
|
||||||
|
evictedKeys []K
|
||||||
|
evictedVals []V
|
||||||
|
onEvictedCB func(k K, v V)
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates an LRU of the given size.
|
||||||
|
func New[K comparable, V any](size int) (*Cache[K, V], error) {
|
||||||
|
return NewWithEvict[K, V](size, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithEvict constructs a fixed size cache with the given eviction
|
||||||
|
// callback.
|
||||||
|
func NewWithEvict[K comparable, V any](size int, onEvicted func(key K, value V)) (c *Cache[K, V], err error) {
|
||||||
|
// create a cache with default settings
|
||||||
|
c = &Cache[K, V]{
|
||||||
|
onEvictedCB: onEvicted,
|
||||||
|
}
|
||||||
|
if onEvicted != nil {
|
||||||
|
c.initEvictBuffers()
|
||||||
|
onEvicted = c.onEvicted
|
||||||
|
}
|
||||||
|
c.lru, err = simplelru.NewLRU(size, onEvicted)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache[K, V]) initEvictBuffers() {
|
||||||
|
c.evictedKeys = make([]K, 0, DefaultEvictedBufferSize)
|
||||||
|
c.evictedVals = make([]V, 0, DefaultEvictedBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onEvicted save evicted key/val and sent in externally registered callback
|
||||||
|
// outside of critical section
|
||||||
|
func (c *Cache[K, V]) onEvicted(k K, v V) {
|
||||||
|
c.evictedKeys = append(c.evictedKeys, k)
|
||||||
|
c.evictedVals = append(c.evictedVals, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to completely clear the cache.
|
||||||
|
func (c *Cache[K, V]) Purge() {
|
||||||
|
var ks []K
|
||||||
|
var vs []V
|
||||||
|
c.lock.Lock()
|
||||||
|
c.lru.Purge()
|
||||||
|
if c.onEvictedCB != nil && len(c.evictedKeys) > 0 {
|
||||||
|
ks, vs = c.evictedKeys, c.evictedVals
|
||||||
|
c.initEvictBuffers()
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
// invoke callback outside of critical section
|
||||||
|
if c.onEvictedCB != nil {
|
||||||
|
for i := 0; i < len(ks); i++ {
|
||||||
|
c.onEvictedCB(ks[i], vs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||||
|
func (c *Cache[K, V]) Add(key K, value V) (evicted bool) {
|
||||||
|
var k K
|
||||||
|
var v V
|
||||||
|
c.lock.Lock()
|
||||||
|
evicted = c.lru.Add(key, value)
|
||||||
|
if c.onEvictedCB != nil && evicted {
|
||||||
|
k, v = c.evictedKeys[0], c.evictedVals[0]
|
||||||
|
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
if c.onEvictedCB != nil && evicted {
|
||||||
|
c.onEvictedCB(k, v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
value, ok = c.lru.Get(key)
|
||||||
|
c.lock.Unlock()
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains checks if a key is in the cache, without updating the
|
||||||
|
// recent-ness or deleting it for being stale.
|
||||||
|
func (c *Cache[K, V]) Contains(key K) bool {
|
||||||
|
c.lock.RLock()
|
||||||
|
containKey := c.lru.Contains(key)
|
||||||
|
c.lock.RUnlock()
|
||||||
|
return containKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the key value (or undefined if not found) without updating
|
||||||
|
// the "recently used"-ness of the key.
|
||||||
|
func (c *Cache[K, V]) Peek(key K) (value V, ok bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
value, ok = c.lru.Peek(key)
|
||||||
|
c.lock.RUnlock()
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsOrAdd checks if a key is in the cache without updating the
|
||||||
|
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||||
|
// Returns whether found and whether an eviction occurred.
|
||||||
|
func (c *Cache[K, V]) ContainsOrAdd(key K, value V) (ok, evicted bool) {
|
||||||
|
var k K
|
||||||
|
var v V
|
||||||
|
c.lock.Lock()
|
||||||
|
if c.lru.Contains(key) {
|
||||||
|
c.lock.Unlock()
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
evicted = c.lru.Add(key, value)
|
||||||
|
if c.onEvictedCB != nil && evicted {
|
||||||
|
k, v = c.evictedKeys[0], c.evictedVals[0]
|
||||||
|
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
if c.onEvictedCB != nil && evicted {
|
||||||
|
c.onEvictedCB(k, v)
|
||||||
|
}
|
||||||
|
return false, evicted
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekOrAdd checks if a key is in the cache without updating the
|
||||||
|
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||||
|
// Returns whether found and whether an eviction occurred.
|
||||||
|
func (c *Cache[K, V]) PeekOrAdd(key K, value V) (previous V, ok, evicted bool) {
|
||||||
|
var k K
|
||||||
|
var v V
|
||||||
|
c.lock.Lock()
|
||||||
|
previous, ok = c.lru.Peek(key)
|
||||||
|
if ok {
|
||||||
|
c.lock.Unlock()
|
||||||
|
return previous, true, false
|
||||||
|
}
|
||||||
|
evicted = c.lru.Add(key, value)
|
||||||
|
if c.onEvictedCB != nil && evicted {
|
||||||
|
k, v = c.evictedKeys[0], c.evictedVals[0]
|
||||||
|
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
if c.onEvictedCB != nil && evicted {
|
||||||
|
c.onEvictedCB(k, v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided key from the cache.
|
||||||
|
func (c *Cache[K, V]) Remove(key K) (present bool) {
|
||||||
|
var k K
|
||||||
|
var v V
|
||||||
|
c.lock.Lock()
|
||||||
|
present = c.lru.Remove(key)
|
||||||
|
if c.onEvictedCB != nil && present {
|
||||||
|
k, v = c.evictedKeys[0], c.evictedVals[0]
|
||||||
|
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
if c.onEvictedCB != nil && present {
|
||||||
|
c.onEvictedCB(k, v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize changes the cache size.
|
||||||
|
func (c *Cache[K, V]) Resize(size int) (evicted int) {
|
||||||
|
var ks []K
|
||||||
|
var vs []V
|
||||||
|
c.lock.Lock()
|
||||||
|
evicted = c.lru.Resize(size)
|
||||||
|
if c.onEvictedCB != nil && evicted > 0 {
|
||||||
|
ks, vs = c.evictedKeys, c.evictedVals
|
||||||
|
c.initEvictBuffers()
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
if c.onEvictedCB != nil && evicted > 0 {
|
||||||
|
for i := 0; i < len(ks); i++ {
|
||||||
|
c.onEvictedCB(ks[i], vs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return evicted
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldest removes the oldest item from the cache.
|
||||||
|
func (c *Cache[K, V]) RemoveOldest() (key K, value V, ok bool) {
|
||||||
|
var k K
|
||||||
|
var v V
|
||||||
|
c.lock.Lock()
|
||||||
|
key, value, ok = c.lru.RemoveOldest()
|
||||||
|
if c.onEvictedCB != nil && ok {
|
||||||
|
k, v = c.evictedKeys[0], c.evictedVals[0]
|
||||||
|
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
||||||
|
}
|
||||||
|
c.lock.Unlock()
|
||||||
|
if c.onEvictedCB != nil && ok {
|
||||||
|
c.onEvictedCB(k, v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOldest returns the oldest entry
|
||||||
|
func (c *Cache[K, V]) GetOldest() (key K, value V, ok bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
key, value, ok = c.lru.GetOldest()
|
||||||
|
c.lock.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||||
|
func (c *Cache[K, V]) Keys() []K {
|
||||||
|
c.lock.RLock()
|
||||||
|
keys := c.lru.Keys()
|
||||||
|
c.lock.RUnlock()
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
|
func (c *Cache[K, V]) Len() int {
|
||||||
|
c.lock.RLock()
|
||||||
|
length := c.lru.Len()
|
||||||
|
c.lock.RUnlock()
|
||||||
|
return length
|
||||||
|
}
|
29
vendor/github.com/hashicorp/golang-lru/v2/simplelru/LICENSE_list
generated
vendored
Normal file
29
vendor/github.com/hashicorp/golang-lru/v2/simplelru/LICENSE_list
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
This license applies to simplelru/list.go
|
||||||
|
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
128
vendor/github.com/hashicorp/golang-lru/v2/simplelru/list.go
generated
vendored
Normal file
128
vendor/github.com/hashicorp/golang-lru/v2/simplelru/list.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE_list file.
|
||||||
|
|
||||||
|
package simplelru
|
||||||
|
|
||||||
|
// entry is an LRU entry
|
||||||
|
type entry[K comparable, V any] struct {
|
||||||
|
// Next and previous pointers in the doubly-linked list of elements.
|
||||||
|
// To simplify the implementation, internally a list l is implemented
|
||||||
|
// as a ring, such that &l.root is both the next element of the last
|
||||||
|
// list element (l.Back()) and the previous element of the first list
|
||||||
|
// element (l.Front()).
|
||||||
|
next, prev *entry[K, V]
|
||||||
|
|
||||||
|
// The list to which this element belongs.
|
||||||
|
list *lruList[K, V]
|
||||||
|
|
||||||
|
// The LRU key of this element.
|
||||||
|
key K
|
||||||
|
|
||||||
|
// The value stored with this element.
|
||||||
|
value V
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevEntry returns the previous list element or nil.
|
||||||
|
func (e *entry[K, V]) prevEntry() *entry[K, V] {
|
||||||
|
if p := e.prev; e.list != nil && p != &e.list.root {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// lruList represents a doubly linked list.
|
||||||
|
// The zero value for lruList is an empty list ready to use.
|
||||||
|
type lruList[K comparable, V any] struct {
|
||||||
|
root entry[K, V] // sentinel list element, only &root, root.prev, and root.next are used
|
||||||
|
len int // current list length excluding (this) sentinel element
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes or clears list l.
|
||||||
|
func (l *lruList[K, V]) init() *lruList[K, V] {
|
||||||
|
l.root.next = &l.root
|
||||||
|
l.root.prev = &l.root
|
||||||
|
l.len = 0
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// newList returns an initialized list.
|
||||||
|
func newList[K comparable, V any]() *lruList[K, V] { return new(lruList[K, V]).init() }
|
||||||
|
|
||||||
|
// length returns the number of elements of list l.
|
||||||
|
// The complexity is O(1).
|
||||||
|
func (l *lruList[K, V]) length() int { return l.len }
|
||||||
|
|
||||||
|
// back returns the last element of list l or nil if the list is empty.
|
||||||
|
func (l *lruList[K, V]) back() *entry[K, V] {
|
||||||
|
if l.len == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.root.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazyInit lazily initializes a zero List value.
|
||||||
|
func (l *lruList[K, V]) lazyInit() {
|
||||||
|
if l.root.next == nil {
|
||||||
|
l.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert inserts e after at, increments l.len, and returns e.
|
||||||
|
func (l *lruList[K, V]) insert(e, at *entry[K, V]) *entry[K, V] {
|
||||||
|
e.prev = at
|
||||||
|
e.next = at.next
|
||||||
|
e.prev.next = e
|
||||||
|
e.next.prev = e
|
||||||
|
e.list = l
|
||||||
|
l.len++
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
|
||||||
|
func (l *lruList[K, V]) insertValue(k K, v V, at *entry[K, V]) *entry[K, V] {
|
||||||
|
return l.insert(&entry[K, V]{value: v, key: k}, at)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove removes e from its list, decrements l.len
|
||||||
|
func (l *lruList[K, V]) remove(e *entry[K, V]) V {
|
||||||
|
e.prev.next = e.next
|
||||||
|
e.next.prev = e.prev
|
||||||
|
e.next = nil // avoid memory leaks
|
||||||
|
e.prev = nil // avoid memory leaks
|
||||||
|
e.list = nil
|
||||||
|
l.len--
|
||||||
|
|
||||||
|
return e.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// move moves e to next to at.
|
||||||
|
func (l *lruList[K, V]) move(e, at *entry[K, V]) {
|
||||||
|
if e == at {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.prev.next = e.next
|
||||||
|
e.next.prev = e.prev
|
||||||
|
|
||||||
|
e.prev = at
|
||||||
|
e.next = at.next
|
||||||
|
e.prev.next = e
|
||||||
|
e.next.prev = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// pushFront inserts a new element e with value v at the front of list l and returns e.
|
||||||
|
func (l *lruList[K, V]) pushFront(k K, v V) *entry[K, V] {
|
||||||
|
l.lazyInit()
|
||||||
|
return l.insertValue(k, v, &l.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// moveToFront moves element e to the front of list l.
|
||||||
|
// If e is not an element of l, the list is not modified.
|
||||||
|
// The element must not be nil.
|
||||||
|
func (l *lruList[K, V]) moveToFront(e *entry[K, V]) {
|
||||||
|
if e.list != l || l.root.next == e {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// see comment in List.Remove about initialization of l
|
||||||
|
l.move(e, &l.root)
|
||||||
|
}
|
164
vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru.go
generated
vendored
Normal file
164
vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package simplelru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvictCallback is used to get a callback when a cache entry is evicted
|
||||||
|
type EvictCallback[K comparable, V any] func(key K, value V)
|
||||||
|
|
||||||
|
// LRU implements a non-thread safe fixed size LRU cache
|
||||||
|
type LRU[K comparable, V any] struct {
|
||||||
|
size int
|
||||||
|
evictList *lruList[K, V]
|
||||||
|
items map[K]*entry[K, V]
|
||||||
|
onEvict EvictCallback[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLRU constructs an LRU of the given size
|
||||||
|
func NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V]) (*LRU[K, V], error) {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil, errors.New("must provide a positive size")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &LRU[K, V]{
|
||||||
|
size: size,
|
||||||
|
evictList: newList[K, V](),
|
||||||
|
items: make(map[K]*entry[K, V]),
|
||||||
|
onEvict: onEvict,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge is used to completely clear the cache.
|
||||||
|
func (c *LRU[K, V]) Purge() {
|
||||||
|
for k, v := range c.items {
|
||||||
|
if c.onEvict != nil {
|
||||||
|
c.onEvict(k, v.value)
|
||||||
|
}
|
||||||
|
delete(c.items, k)
|
||||||
|
}
|
||||||
|
c.evictList.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||||
|
func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
|
||||||
|
// Check for existing item
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
c.evictList.moveToFront(ent)
|
||||||
|
ent.value = value
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new item
|
||||||
|
ent := c.evictList.pushFront(key, value)
|
||||||
|
c.items[key] = ent
|
||||||
|
|
||||||
|
evict := c.evictList.length() > c.size
|
||||||
|
// Verify size not exceeded
|
||||||
|
if evict {
|
||||||
|
c.removeOldest()
|
||||||
|
}
|
||||||
|
return evict
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
c.evictList.moveToFront(ent)
|
||||||
|
return ent.value, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||||
|
// or deleting it for being stale.
|
||||||
|
func (c *LRU[K, V]) Contains(key K) (ok bool) {
|
||||||
|
_, ok = c.items[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the key value (or undefined if not found) without updating
|
||||||
|
// the "recently used"-ness of the key.
|
||||||
|
func (c *LRU[K, V]) Peek(key K) (value V, ok bool) {
|
||||||
|
var ent *entry[K, V]
|
||||||
|
if ent, ok = c.items[key]; ok {
|
||||||
|
return ent.value, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided key from the cache, returning if the
|
||||||
|
// key was contained.
|
||||||
|
func (c *LRU[K, V]) Remove(key K) (present bool) {
|
||||||
|
if ent, ok := c.items[key]; ok {
|
||||||
|
c.removeElement(ent)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldest removes the oldest item from the cache.
|
||||||
|
func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
|
||||||
|
ent := c.evictList.back()
|
||||||
|
if ent != nil {
|
||||||
|
c.removeElement(ent)
|
||||||
|
return ent.key, ent.value, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOldest returns the oldest entry
|
||||||
|
func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {
|
||||||
|
ent := c.evictList.back()
|
||||||
|
if ent != nil {
|
||||||
|
return ent.key, ent.value, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||||
|
func (c *LRU[K, V]) Keys() []K {
|
||||||
|
keys := make([]K, c.evictList.length())
|
||||||
|
i := 0
|
||||||
|
for ent := c.evictList.back(); ent != nil; ent = ent.prevEntry() {
|
||||||
|
keys[i] = ent.key
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
|
func (c *LRU[K, V]) Len() int {
|
||||||
|
return c.evictList.length()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize changes the cache size.
|
||||||
|
func (c *LRU[K, V]) Resize(size int) (evicted int) {
|
||||||
|
diff := c.Len() - size
|
||||||
|
if diff < 0 {
|
||||||
|
diff = 0
|
||||||
|
}
|
||||||
|
for i := 0; i < diff; i++ {
|
||||||
|
c.removeOldest()
|
||||||
|
}
|
||||||
|
c.size = size
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeOldest removes the oldest item from the cache.
|
||||||
|
func (c *LRU[K, V]) removeOldest() {
|
||||||
|
ent := c.evictList.back()
|
||||||
|
if ent != nil {
|
||||||
|
c.removeElement(ent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeElement is used to remove a given list element from the cache
|
||||||
|
func (c *LRU[K, V]) removeElement(e *entry[K, V]) {
|
||||||
|
c.evictList.remove(e)
|
||||||
|
delete(c.items, e.key)
|
||||||
|
if c.onEvict != nil {
|
||||||
|
c.onEvict(e.key, e.value)
|
||||||
|
}
|
||||||
|
}
|
40
vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru_interface.go
generated
vendored
Normal file
40
vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru_interface.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Package simplelru provides simple LRU implementation based on build-in container/list.
|
||||||
|
package simplelru
|
||||||
|
|
||||||
|
// LRUCache is the interface for simple LRU cache.
|
||||||
|
type LRUCache[K comparable, V any] interface {
|
||||||
|
// Adds a value to the cache, returns true if an eviction occurred and
|
||||||
|
// updates the "recently used"-ness of the key.
|
||||||
|
Add(key K, value V) bool
|
||||||
|
|
||||||
|
// Returns key's value from the cache and
|
||||||
|
// updates the "recently used"-ness of the key. #value, isFound
|
||||||
|
Get(key K) (value V, ok bool)
|
||||||
|
|
||||||
|
// Checks if a key exists in cache without updating the recent-ness.
|
||||||
|
Contains(key K) (ok bool)
|
||||||
|
|
||||||
|
// Returns key's value without updating the "recently used"-ness of the key.
|
||||||
|
Peek(key K) (value V, ok bool)
|
||||||
|
|
||||||
|
// Removes a key from the cache.
|
||||||
|
Remove(key K) bool
|
||||||
|
|
||||||
|
// Removes the oldest entry from cache.
|
||||||
|
RemoveOldest() (K, V, bool)
|
||||||
|
|
||||||
|
// Returns the oldest entry from the cache. #key, value, isFound
|
||||||
|
GetOldest() (K, V, bool)
|
||||||
|
|
||||||
|
// Returns a slice of the keys in the cache, from oldest to newest.
|
||||||
|
Keys() []K
|
||||||
|
|
||||||
|
// Returns the number of items in the cache.
|
||||||
|
Len() int
|
||||||
|
|
||||||
|
// Clears all cache entries.
|
||||||
|
Purge()
|
||||||
|
|
||||||
|
// Resizes cache, returning number evicted
|
||||||
|
Resize(int) int
|
||||||
|
}
|
16
vendor/github.com/hashicorp/golang-lru/v2/testing.go
generated
vendored
Normal file
16
vendor/github.com/hashicorp/golang-lru/v2/testing.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package lru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getRand(tb testing.TB) int64 {
|
||||||
|
out, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
|
||||||
|
if err != nil {
|
||||||
|
tb.Fatal(err)
|
||||||
|
}
|
||||||
|
return out.Int64()
|
||||||
|
}
|
25
vendor/github.com/klauspost/compress/README.md
generated
vendored
25
vendor/github.com/klauspost/compress/README.md
generated
vendored
|
@ -17,6 +17,24 @@ This package provides various compression algorithms.
|
||||||
|
|
||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
* Sept 26, 2022 (v1.15.11)
|
||||||
|
|
||||||
|
* flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678
|
||||||
|
* zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677
|
||||||
|
* zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668
|
||||||
|
* zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667
|
||||||
|
|
||||||
|
* Sept 16, 2022 (v1.15.10)
|
||||||
|
|
||||||
|
* zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649
|
||||||
|
* Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651
|
||||||
|
* flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656
|
||||||
|
* zstd: Improve "better" compresssion https://github.com/klauspost/compress/pull/657
|
||||||
|
* s2: Improve "best" compression https://github.com/klauspost/compress/pull/658
|
||||||
|
* s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635
|
||||||
|
* s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646
|
||||||
|
* Use arrays for constant size copies https://github.com/klauspost/compress/pull/659
|
||||||
|
|
||||||
* July 21, 2022 (v1.15.9)
|
* July 21, 2022 (v1.15.9)
|
||||||
|
|
||||||
* zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645
|
* zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645
|
||||||
|
@ -97,15 +115,15 @@ This package provides various compression algorithms.
|
||||||
* gzhttp: Add zstd to transport by @klauspost in [#400](https://github.com/klauspost/compress/pull/400)
|
* gzhttp: Add zstd to transport by @klauspost in [#400](https://github.com/klauspost/compress/pull/400)
|
||||||
* gzhttp: Make content-type optional by @klauspost in [#510](https://github.com/klauspost/compress/pull/510)
|
* gzhttp: Make content-type optional by @klauspost in [#510](https://github.com/klauspost/compress/pull/510)
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>See Details</summary>
|
|
||||||
Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines.
|
Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines.
|
||||||
|
|
||||||
Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected.
|
Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected.
|
||||||
|
|
||||||
While the release has been extensively tested, it is recommended to testing when upgrading.
|
While the release has been extensively tested, it is recommended to testing when upgrading.
|
||||||
</details>
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>See changes to v1.14.x</summary>
|
||||||
|
|
||||||
* Feb 22, 2022 (v1.14.4)
|
* Feb 22, 2022 (v1.14.4)
|
||||||
* flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503)
|
* flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503)
|
||||||
* zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502)
|
* zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502)
|
||||||
|
@ -131,6 +149,7 @@ While the release has been extensively tested, it is recommended to testing when
|
||||||
* zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468)
|
* zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468)
|
||||||
* zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464)
|
* zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464)
|
||||||
* Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445)
|
* Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445)
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>See changes to v1.13.x</summary>
|
<summary>See changes to v1.13.x</summary>
|
||||||
|
|
910
vendor/github.com/klauspost/compress/flate/deflate.go
generated
vendored
Normal file
910
vendor/github.com/klauspost/compress/flate/deflate.go
generated
vendored
Normal file
|
@ -0,0 +1,910 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright (c) 2015 Klaus Post
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoCompression = 0
|
||||||
|
BestSpeed = 1
|
||||||
|
BestCompression = 9
|
||||||
|
DefaultCompression = -1
|
||||||
|
|
||||||
|
// HuffmanOnly disables Lempel-Ziv match searching and only performs Huffman
|
||||||
|
// entropy encoding. This mode is useful in compressing data that has
|
||||||
|
// already been compressed with an LZ style algorithm (e.g. Snappy or LZ4)
|
||||||
|
// that lacks an entropy encoder. Compression gains are achieved when
|
||||||
|
// certain bytes in the input stream occur more frequently than others.
|
||||||
|
//
|
||||||
|
// Note that HuffmanOnly produces a compressed output that is
|
||||||
|
// RFC 1951 compliant. That is, any valid DEFLATE decompressor will
|
||||||
|
// continue to be able to decompress this output.
|
||||||
|
HuffmanOnly = -2
|
||||||
|
ConstantCompression = HuffmanOnly // compatibility alias.
|
||||||
|
|
||||||
|
logWindowSize = 15
|
||||||
|
windowSize = 1 << logWindowSize
|
||||||
|
windowMask = windowSize - 1
|
||||||
|
logMaxOffsetSize = 15 // Standard DEFLATE
|
||||||
|
minMatchLength = 4 // The smallest match that the compressor looks for
|
||||||
|
maxMatchLength = 258 // The longest match for the compressor
|
||||||
|
minOffsetSize = 1 // The shortest offset that makes any sense
|
||||||
|
|
||||||
|
// The maximum number of tokens we will encode at the time.
|
||||||
|
// Smaller sizes usually creates less optimal blocks.
|
||||||
|
// Bigger can make context switching slow.
|
||||||
|
// We use this for levels 7-9, so we make it big.
|
||||||
|
maxFlateBlockTokens = 1 << 15
|
||||||
|
maxStoreBlockSize = 65535
|
||||||
|
hashBits = 17 // After 17 performance degrades
|
||||||
|
hashSize = 1 << hashBits
|
||||||
|
hashMask = (1 << hashBits) - 1
|
||||||
|
hashShift = (hashBits + minMatchLength - 1) / minMatchLength
|
||||||
|
maxHashOffset = 1 << 28
|
||||||
|
|
||||||
|
skipNever = math.MaxInt32
|
||||||
|
|
||||||
|
debugDeflate = false
|
||||||
|
)
|
||||||
|
|
||||||
|
type compressionLevel struct {
|
||||||
|
good, lazy, nice, chain, fastSkipHashing, level int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compression levels have been rebalanced from zlib deflate defaults
|
||||||
|
// to give a bigger spread in speed and compression.
|
||||||
|
// See https://blog.klauspost.com/rebalancing-deflate-compression-levels/
|
||||||
|
var levels = []compressionLevel{
|
||||||
|
{}, // 0
|
||||||
|
// Level 1-6 uses specialized algorithm - values not used
|
||||||
|
{0, 0, 0, 0, 0, 1},
|
||||||
|
{0, 0, 0, 0, 0, 2},
|
||||||
|
{0, 0, 0, 0, 0, 3},
|
||||||
|
{0, 0, 0, 0, 0, 4},
|
||||||
|
{0, 0, 0, 0, 0, 5},
|
||||||
|
{0, 0, 0, 0, 0, 6},
|
||||||
|
// Levels 7-9 use increasingly more lazy matching
|
||||||
|
// and increasingly stringent conditions for "good enough".
|
||||||
|
{8, 12, 16, 24, skipNever, 7},
|
||||||
|
{16, 30, 40, 64, skipNever, 8},
|
||||||
|
{32, 258, 258, 1024, skipNever, 9},
|
||||||
|
}
|
||||||
|
|
||||||
|
// advancedState contains state for the advanced levels, with bigger hash tables, etc.
|
||||||
|
type advancedState struct {
|
||||||
|
// deflate state
|
||||||
|
length int
|
||||||
|
offset int
|
||||||
|
maxInsertIndex int
|
||||||
|
chainHead int
|
||||||
|
hashOffset int
|
||||||
|
|
||||||
|
ii uint16 // position of last match, intended to overflow to reset.
|
||||||
|
|
||||||
|
// input window: unprocessed data is window[index:windowEnd]
|
||||||
|
index int
|
||||||
|
estBitsPerByte int
|
||||||
|
hashMatch [maxMatchLength + minMatchLength]uint32
|
||||||
|
|
||||||
|
// Input hash chains
|
||||||
|
// hashHead[hashValue] contains the largest inputIndex with the specified hash value
|
||||||
|
// If hashHead[hashValue] is within the current window, then
|
||||||
|
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
|
||||||
|
// with the same hash value.
|
||||||
|
hashHead [hashSize]uint32
|
||||||
|
hashPrev [windowSize]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type compressor struct {
|
||||||
|
compressionLevel
|
||||||
|
|
||||||
|
h *huffmanEncoder
|
||||||
|
w *huffmanBitWriter
|
||||||
|
|
||||||
|
// compression algorithm
|
||||||
|
fill func(*compressor, []byte) int // copy data to window
|
||||||
|
step func(*compressor) // process window
|
||||||
|
|
||||||
|
window []byte
|
||||||
|
windowEnd int
|
||||||
|
blockStart int // window index where current tokens start
|
||||||
|
err error
|
||||||
|
|
||||||
|
// queued output tokens
|
||||||
|
tokens tokens
|
||||||
|
fast fastEnc
|
||||||
|
state *advancedState
|
||||||
|
|
||||||
|
sync bool // requesting flush
|
||||||
|
byteAvailable bool // if true, still need to process window[index-1].
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) fillDeflate(b []byte) int {
|
||||||
|
s := d.state
|
||||||
|
if s.index >= 2*windowSize-(minMatchLength+maxMatchLength) {
|
||||||
|
// shift the window by windowSize
|
||||||
|
//copy(d.window[:], d.window[windowSize:2*windowSize])
|
||||||
|
*(*[windowSize]byte)(d.window) = *(*[windowSize]byte)(d.window[windowSize:])
|
||||||
|
s.index -= windowSize
|
||||||
|
d.windowEnd -= windowSize
|
||||||
|
if d.blockStart >= windowSize {
|
||||||
|
d.blockStart -= windowSize
|
||||||
|
} else {
|
||||||
|
d.blockStart = math.MaxInt32
|
||||||
|
}
|
||||||
|
s.hashOffset += windowSize
|
||||||
|
if s.hashOffset > maxHashOffset {
|
||||||
|
delta := s.hashOffset - 1
|
||||||
|
s.hashOffset -= delta
|
||||||
|
s.chainHead -= delta
|
||||||
|
// Iterate over slices instead of arrays to avoid copying
|
||||||
|
// the entire table onto the stack (Issue #18625).
|
||||||
|
for i, v := range s.hashPrev[:] {
|
||||||
|
if int(v) > delta {
|
||||||
|
s.hashPrev[i] = uint32(int(v) - delta)
|
||||||
|
} else {
|
||||||
|
s.hashPrev[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, v := range s.hashHead[:] {
|
||||||
|
if int(v) > delta {
|
||||||
|
s.hashHead[i] = uint32(int(v) - delta)
|
||||||
|
} else {
|
||||||
|
s.hashHead[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := copy(d.window[d.windowEnd:], b)
|
||||||
|
d.windowEnd += n
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) writeBlock(tok *tokens, index int, eof bool) error {
|
||||||
|
if index > 0 || eof {
|
||||||
|
var window []byte
|
||||||
|
if d.blockStart <= index {
|
||||||
|
window = d.window[d.blockStart:index]
|
||||||
|
}
|
||||||
|
d.blockStart = index
|
||||||
|
//d.w.writeBlock(tok, eof, window)
|
||||||
|
d.w.writeBlockDynamic(tok, eof, window, d.sync)
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBlockSkip writes the current block and uses the number of tokens
|
||||||
|
// to determine if the block should be stored on no matches, or
|
||||||
|
// only huffman encoded.
|
||||||
|
func (d *compressor) writeBlockSkip(tok *tokens, index int, eof bool) error {
|
||||||
|
if index > 0 || eof {
|
||||||
|
if d.blockStart <= index {
|
||||||
|
window := d.window[d.blockStart:index]
|
||||||
|
// If we removed less than a 64th of all literals
|
||||||
|
// we huffman compress the block.
|
||||||
|
if int(tok.n) > len(window)-int(tok.n>>6) {
|
||||||
|
d.w.writeBlockHuff(eof, window, d.sync)
|
||||||
|
} else {
|
||||||
|
// Write a dynamic huffman block.
|
||||||
|
d.w.writeBlockDynamic(tok, eof, window, d.sync)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.w.writeBlock(tok, eof, nil)
|
||||||
|
}
|
||||||
|
d.blockStart = index
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillWindow will fill the current window with the supplied
|
||||||
|
// dictionary and calculate all hashes.
|
||||||
|
// This is much faster than doing a full encode.
|
||||||
|
// Should only be used after a start/reset.
|
||||||
|
func (d *compressor) fillWindow(b []byte) {
|
||||||
|
// Do not fill window if we are in store-only or huffman mode.
|
||||||
|
if d.level <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.fast != nil {
|
||||||
|
// encode the last data, but discard the result
|
||||||
|
if len(b) > maxMatchOffset {
|
||||||
|
b = b[len(b)-maxMatchOffset:]
|
||||||
|
}
|
||||||
|
d.fast.Encode(&d.tokens, b)
|
||||||
|
d.tokens.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := d.state
|
||||||
|
// If we are given too much, cut it.
|
||||||
|
if len(b) > windowSize {
|
||||||
|
b = b[len(b)-windowSize:]
|
||||||
|
}
|
||||||
|
// Add all to window.
|
||||||
|
n := copy(d.window[d.windowEnd:], b)
|
||||||
|
|
||||||
|
// Calculate 256 hashes at the time (more L1 cache hits)
|
||||||
|
loops := (n + 256 - minMatchLength) / 256
|
||||||
|
for j := 0; j < loops; j++ {
|
||||||
|
startindex := j * 256
|
||||||
|
end := startindex + 256 + minMatchLength - 1
|
||||||
|
if end > n {
|
||||||
|
end = n
|
||||||
|
}
|
||||||
|
tocheck := d.window[startindex:end]
|
||||||
|
dstSize := len(tocheck) - minMatchLength + 1
|
||||||
|
|
||||||
|
if dstSize <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := s.hashMatch[:dstSize]
|
||||||
|
bulkHash4(tocheck, dst)
|
||||||
|
var newH uint32
|
||||||
|
for i, val := range dst {
|
||||||
|
di := i + startindex
|
||||||
|
newH = val & hashMask
|
||||||
|
// Get previous value with the same hash.
|
||||||
|
// Our chain should point to the previous value.
|
||||||
|
s.hashPrev[di&windowMask] = s.hashHead[newH]
|
||||||
|
// Set the head of the hash chain to us.
|
||||||
|
s.hashHead[newH] = uint32(di + s.hashOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update window information.
|
||||||
|
d.windowEnd += n
|
||||||
|
s.index = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find a match starting at index whose length is greater than prevSize.
|
||||||
|
// We only look at chainCount possibilities before giving up.
|
||||||
|
// pos = s.index, prevHead = s.chainHead-s.hashOffset, prevLength=minMatchLength-1, lookahead
|
||||||
|
func (d *compressor) findMatch(pos int, prevHead int, lookahead int) (length, offset int, ok bool) {
|
||||||
|
minMatchLook := maxMatchLength
|
||||||
|
if lookahead < minMatchLook {
|
||||||
|
minMatchLook = lookahead
|
||||||
|
}
|
||||||
|
|
||||||
|
win := d.window[0 : pos+minMatchLook]
|
||||||
|
|
||||||
|
// We quit when we get a match that's at least nice long
|
||||||
|
nice := len(win) - pos
|
||||||
|
if d.nice < nice {
|
||||||
|
nice = d.nice
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've got a match that's good enough, only look in 1/4 the chain.
|
||||||
|
tries := d.chain
|
||||||
|
length = minMatchLength - 1
|
||||||
|
|
||||||
|
wEnd := win[pos+length]
|
||||||
|
wPos := win[pos:]
|
||||||
|
minIndex := pos - windowSize
|
||||||
|
if minIndex < 0 {
|
||||||
|
minIndex = 0
|
||||||
|
}
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
cGain := 0
|
||||||
|
if d.chain < 100 {
|
||||||
|
for i := prevHead; tries > 0; tries-- {
|
||||||
|
if wEnd == win[i+length] {
|
||||||
|
n := matchLen(win[i:i+minMatchLook], wPos)
|
||||||
|
if n > length {
|
||||||
|
length = n
|
||||||
|
offset = pos - i
|
||||||
|
ok = true
|
||||||
|
if n >= nice {
|
||||||
|
// The match is good enough that we don't try to find a better one.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
wEnd = win[pos+n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i <= minIndex {
|
||||||
|
// hashPrev[i & windowMask] has already been overwritten, so stop now.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset
|
||||||
|
if i < minIndex {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some like it higher (CSV), some like it lower (JSON)
|
||||||
|
const baseCost = 6
|
||||||
|
// Base is 4 bytes at with an additional cost.
|
||||||
|
// Matches must be better than this.
|
||||||
|
for i := prevHead; tries > 0; tries-- {
|
||||||
|
if wEnd == win[i+length] {
|
||||||
|
n := matchLen(win[i:i+minMatchLook], wPos)
|
||||||
|
if n > length {
|
||||||
|
// Calculate gain. Estimate
|
||||||
|
newGain := d.h.bitLengthRaw(wPos[:n]) - int(offsetExtraBits[offsetCode(uint32(pos-i))]) - baseCost - int(lengthExtraBits[lengthCodes[(n-3)&255]])
|
||||||
|
|
||||||
|
//fmt.Println(n, "gain:", newGain, "prev:", cGain, "raw:", d.h.bitLengthRaw(wPos[:n]))
|
||||||
|
if newGain > cGain {
|
||||||
|
length = n
|
||||||
|
offset = pos - i
|
||||||
|
cGain = newGain
|
||||||
|
ok = true
|
||||||
|
if n >= nice {
|
||||||
|
// The match is good enough that we don't try to find a better one.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
wEnd = win[pos+n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i <= minIndex {
|
||||||
|
// hashPrev[i & windowMask] has already been overwritten, so stop now.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset
|
||||||
|
if i < minIndex {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) writeStoredBlock(buf []byte) error {
|
||||||
|
if d.w.writeStoredHeader(len(buf), false); d.w.err != nil {
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
d.w.writeBytes(buf)
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash4 returns a hash representation of the first 4 bytes
|
||||||
|
// of the supplied slice.
|
||||||
|
// The caller must ensure that len(b) >= 4.
|
||||||
|
func hash4(b []byte) uint32 {
|
||||||
|
return hash4u(binary.LittleEndian.Uint32(b), hashBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash4 returns the hash of u to fit in a hash table with h bits.
|
||||||
|
// Preferably h should be a constant and should always be <32.
|
||||||
|
func hash4u(u uint32, h uint8) uint32 {
|
||||||
|
return (u * prime4bytes) >> (32 - h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulkHash4 will compute hashes using the same
|
||||||
|
// algorithm as hash4
|
||||||
|
func bulkHash4(b []byte, dst []uint32) {
|
||||||
|
if len(b) < 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hb := binary.LittleEndian.Uint32(b)
|
||||||
|
|
||||||
|
dst[0] = hash4u(hb, hashBits)
|
||||||
|
end := len(b) - 4 + 1
|
||||||
|
for i := 1; i < end; i++ {
|
||||||
|
hb = (hb >> 8) | uint32(b[i+3])<<24
|
||||||
|
dst[i] = hash4u(hb, hashBits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) initDeflate() {
|
||||||
|
d.window = make([]byte, 2*windowSize)
|
||||||
|
d.byteAvailable = false
|
||||||
|
d.err = nil
|
||||||
|
if d.state == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := d.state
|
||||||
|
s.index = 0
|
||||||
|
s.hashOffset = 1
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
s.offset = 0
|
||||||
|
s.chainHead = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// deflateLazy is the same as deflate, but with d.fastSkipHashing == skipNever,
|
||||||
|
// meaning it always has lazy matching on.
|
||||||
|
func (d *compressor) deflateLazy() {
|
||||||
|
s := d.state
|
||||||
|
// Sanity enables additional runtime tests.
|
||||||
|
// It's intended to be used during development
|
||||||
|
// to supplement the currently ad-hoc unit tests.
|
||||||
|
const sanity = debugDeflate
|
||||||
|
|
||||||
|
if d.windowEnd-s.index < minMatchLength+maxMatchLength && !d.sync {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.windowEnd != s.index && d.chain > 100 {
|
||||||
|
// Get literal huffman coder.
|
||||||
|
if d.h == nil {
|
||||||
|
d.h = newHuffmanEncoder(maxFlateBlockTokens)
|
||||||
|
}
|
||||||
|
var tmp [256]uint16
|
||||||
|
for _, v := range d.window[s.index:d.windowEnd] {
|
||||||
|
tmp[v]++
|
||||||
|
}
|
||||||
|
d.h.generate(tmp[:], 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.maxInsertIndex = d.windowEnd - (minMatchLength - 1)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if sanity && s.index > d.windowEnd {
|
||||||
|
panic("index > windowEnd")
|
||||||
|
}
|
||||||
|
lookahead := d.windowEnd - s.index
|
||||||
|
if lookahead < minMatchLength+maxMatchLength {
|
||||||
|
if !d.sync {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sanity && s.index > d.windowEnd {
|
||||||
|
panic("index > windowEnd")
|
||||||
|
}
|
||||||
|
if lookahead == 0 {
|
||||||
|
// Flush current output block if any.
|
||||||
|
if d.byteAvailable {
|
||||||
|
// There is still one pending token that needs to be flushed
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
d.byteAvailable = false
|
||||||
|
}
|
||||||
|
if d.tokens.n > 0 {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.index < s.maxInsertIndex {
|
||||||
|
// Update the hash
|
||||||
|
hash := hash4(d.window[s.index:])
|
||||||
|
ch := s.hashHead[hash]
|
||||||
|
s.chainHead = int(ch)
|
||||||
|
s.hashPrev[s.index&windowMask] = ch
|
||||||
|
s.hashHead[hash] = uint32(s.index + s.hashOffset)
|
||||||
|
}
|
||||||
|
prevLength := s.length
|
||||||
|
prevOffset := s.offset
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
s.offset = 0
|
||||||
|
minIndex := s.index - windowSize
|
||||||
|
if minIndex < 0 {
|
||||||
|
minIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.chainHead-s.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy {
|
||||||
|
if newLength, newOffset, ok := d.findMatch(s.index, s.chainHead-s.hashOffset, lookahead); ok {
|
||||||
|
s.length = newLength
|
||||||
|
s.offset = newOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevLength >= minMatchLength && s.length <= prevLength {
|
||||||
|
// Check for better match at end...
|
||||||
|
//
|
||||||
|
// checkOff must be >=2 since we otherwise risk checking s.index
|
||||||
|
// Offset of 2 seems to yield best results.
|
||||||
|
const checkOff = 2
|
||||||
|
prevIndex := s.index - 1
|
||||||
|
if prevIndex+prevLength+checkOff < s.maxInsertIndex {
|
||||||
|
end := lookahead
|
||||||
|
if lookahead > maxMatchLength {
|
||||||
|
end = maxMatchLength
|
||||||
|
}
|
||||||
|
end += prevIndex
|
||||||
|
idx := prevIndex + prevLength - (4 - checkOff)
|
||||||
|
h := hash4(d.window[idx:])
|
||||||
|
ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength + (4 - checkOff)
|
||||||
|
if ch2 > minIndex {
|
||||||
|
length := matchLen(d.window[prevIndex:end], d.window[ch2:])
|
||||||
|
// It seems like a pure length metric is best.
|
||||||
|
if length > prevLength {
|
||||||
|
prevLength = length
|
||||||
|
prevOffset = prevIndex - ch2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There was a match at the previous step, and the current match is
|
||||||
|
// not better. Output the previous match.
|
||||||
|
d.tokens.AddMatch(uint32(prevLength-3), uint32(prevOffset-minOffsetSize))
|
||||||
|
|
||||||
|
// Insert in the hash table all strings up to the end of the match.
|
||||||
|
// index and index-1 are already inserted. If there is not enough
|
||||||
|
// lookahead, the last two strings are not inserted into the hash
|
||||||
|
// table.
|
||||||
|
newIndex := s.index + prevLength - 1
|
||||||
|
// Calculate missing hashes
|
||||||
|
end := newIndex
|
||||||
|
if end > s.maxInsertIndex {
|
||||||
|
end = s.maxInsertIndex
|
||||||
|
}
|
||||||
|
end += minMatchLength - 1
|
||||||
|
startindex := s.index + 1
|
||||||
|
if startindex > s.maxInsertIndex {
|
||||||
|
startindex = s.maxInsertIndex
|
||||||
|
}
|
||||||
|
tocheck := d.window[startindex:end]
|
||||||
|
dstSize := len(tocheck) - minMatchLength + 1
|
||||||
|
if dstSize > 0 {
|
||||||
|
dst := s.hashMatch[:dstSize]
|
||||||
|
bulkHash4(tocheck, dst)
|
||||||
|
var newH uint32
|
||||||
|
for i, val := range dst {
|
||||||
|
di := i + startindex
|
||||||
|
newH = val & hashMask
|
||||||
|
// Get previous value with the same hash.
|
||||||
|
// Our chain should point to the previous value.
|
||||||
|
s.hashPrev[di&windowMask] = s.hashHead[newH]
|
||||||
|
// Set the head of the hash chain to us.
|
||||||
|
s.hashHead[newH] = uint32(di + s.hashOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.index = newIndex
|
||||||
|
d.byteAvailable = false
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
// The block includes the current character
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
s.ii = 0
|
||||||
|
} else {
|
||||||
|
// Reset, if we got a match this run.
|
||||||
|
if s.length >= minMatchLength {
|
||||||
|
s.ii = 0
|
||||||
|
}
|
||||||
|
// We have a byte waiting. Emit it.
|
||||||
|
if d.byteAvailable {
|
||||||
|
s.ii++
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
s.index++
|
||||||
|
|
||||||
|
// If we have a long run of no matches, skip additional bytes
|
||||||
|
// Resets when s.ii overflows after 64KB.
|
||||||
|
if n := int(s.ii) - d.chain; n > 0 {
|
||||||
|
n = 1 + int(n>>6)
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if s.index >= d.windowEnd-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
// Index...
|
||||||
|
if s.index < s.maxInsertIndex {
|
||||||
|
h := hash4(d.window[s.index:])
|
||||||
|
ch := s.hashHead[h]
|
||||||
|
s.chainHead = int(ch)
|
||||||
|
s.hashPrev[s.index&windowMask] = ch
|
||||||
|
s.hashHead[h] = uint32(s.index + s.hashOffset)
|
||||||
|
}
|
||||||
|
s.index++
|
||||||
|
}
|
||||||
|
// Flush last byte
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
d.byteAvailable = false
|
||||||
|
// s.length = minMatchLength - 1 // not needed, since s.ii is reset above, so it should never be > minMatchLength
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.index++
|
||||||
|
d.byteAvailable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) store() {
|
||||||
|
if d.windowEnd > 0 && (d.windowEnd == maxStoreBlockSize || d.sync) {
|
||||||
|
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||||
|
d.windowEnd = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillWindow will fill the buffer with data for huffman-only compression.
|
||||||
|
// The number of bytes copied is returned.
|
||||||
|
func (d *compressor) fillBlock(b []byte) int {
|
||||||
|
n := copy(d.window[d.windowEnd:], b)
|
||||||
|
d.windowEnd += n
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeHuff will compress and store the currently added data,
|
||||||
|
// if enough has been accumulated or we at the end of the stream.
|
||||||
|
// Any error that occurred will be in d.err
|
||||||
|
func (d *compressor) storeHuff() {
|
||||||
|
if d.windowEnd < len(d.window) && !d.sync || d.windowEnd == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync)
|
||||||
|
d.err = d.w.err
|
||||||
|
d.windowEnd = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeFast will compress and store the currently added data,
|
||||||
|
// if enough has been accumulated or we at the end of the stream.
|
||||||
|
// Any error that occurred will be in d.err
|
||||||
|
func (d *compressor) storeFast() {
|
||||||
|
// We only compress if we have maxStoreBlockSize.
|
||||||
|
if d.windowEnd < len(d.window) {
|
||||||
|
if !d.sync {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Handle extremely small sizes.
|
||||||
|
if d.windowEnd < 128 {
|
||||||
|
if d.windowEnd == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.windowEnd <= 32 {
|
||||||
|
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||||
|
} else {
|
||||||
|
d.w.writeBlockHuff(false, d.window[:d.windowEnd], true)
|
||||||
|
d.err = d.w.err
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
d.windowEnd = 0
|
||||||
|
d.fast.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.fast.Encode(&d.tokens, d.window[:d.windowEnd])
|
||||||
|
// If we made zero matches, store the block as is.
|
||||||
|
if d.tokens.n == 0 {
|
||||||
|
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||||
|
// If we removed less than 1/16th, huffman compress the block.
|
||||||
|
} else if int(d.tokens.n) > d.windowEnd-(d.windowEnd>>4) {
|
||||||
|
d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync)
|
||||||
|
d.err = d.w.err
|
||||||
|
} else {
|
||||||
|
d.w.writeBlockDynamic(&d.tokens, false, d.window[:d.windowEnd], d.sync)
|
||||||
|
d.err = d.w.err
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
d.windowEnd = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// write will add input byte to the stream.
|
||||||
|
// Unless an error occurs all bytes will be consumed.
|
||||||
|
func (d *compressor) write(b []byte) (n int, err error) {
|
||||||
|
if d.err != nil {
|
||||||
|
return 0, d.err
|
||||||
|
}
|
||||||
|
n = len(b)
|
||||||
|
for len(b) > 0 {
|
||||||
|
if d.windowEnd == len(d.window) || d.sync {
|
||||||
|
d.step(d)
|
||||||
|
}
|
||||||
|
b = b[d.fill(d, b):]
|
||||||
|
if d.err != nil {
|
||||||
|
return 0, d.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) syncFlush() error {
|
||||||
|
d.sync = true
|
||||||
|
if d.err != nil {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
d.step(d)
|
||||||
|
if d.err == nil {
|
||||||
|
d.w.writeStoredHeader(0, false)
|
||||||
|
d.w.flush()
|
||||||
|
d.err = d.w.err
|
||||||
|
}
|
||||||
|
d.sync = false
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) init(w io.Writer, level int) (err error) {
|
||||||
|
d.w = newHuffmanBitWriter(w)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case level == NoCompression:
|
||||||
|
d.window = make([]byte, maxStoreBlockSize)
|
||||||
|
d.fill = (*compressor).fillBlock
|
||||||
|
d.step = (*compressor).store
|
||||||
|
case level == ConstantCompression:
|
||||||
|
d.w.logNewTablePenalty = 10
|
||||||
|
d.window = make([]byte, 32<<10)
|
||||||
|
d.fill = (*compressor).fillBlock
|
||||||
|
d.step = (*compressor).storeHuff
|
||||||
|
case level == DefaultCompression:
|
||||||
|
level = 5
|
||||||
|
fallthrough
|
||||||
|
case level >= 1 && level <= 6:
|
||||||
|
d.w.logNewTablePenalty = 7
|
||||||
|
d.fast = newFastEnc(level)
|
||||||
|
d.window = make([]byte, maxStoreBlockSize)
|
||||||
|
d.fill = (*compressor).fillBlock
|
||||||
|
d.step = (*compressor).storeFast
|
||||||
|
case 7 <= level && level <= 9:
|
||||||
|
d.w.logNewTablePenalty = 8
|
||||||
|
d.state = &advancedState{}
|
||||||
|
d.compressionLevel = levels[level]
|
||||||
|
d.initDeflate()
|
||||||
|
d.fill = (*compressor).fillDeflate
|
||||||
|
d.step = (*compressor).deflateLazy
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level)
|
||||||
|
}
|
||||||
|
d.level = level
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the state of the compressor.
|
||||||
|
func (d *compressor) reset(w io.Writer) {
|
||||||
|
d.w.reset(w)
|
||||||
|
d.sync = false
|
||||||
|
d.err = nil
|
||||||
|
// We only need to reset a few things for Snappy.
|
||||||
|
if d.fast != nil {
|
||||||
|
d.fast.Reset()
|
||||||
|
d.windowEnd = 0
|
||||||
|
d.tokens.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch d.compressionLevel.chain {
|
||||||
|
case 0:
|
||||||
|
// level was NoCompression or ConstantCompresssion.
|
||||||
|
d.windowEnd = 0
|
||||||
|
default:
|
||||||
|
s := d.state
|
||||||
|
s.chainHead = -1
|
||||||
|
for i := range s.hashHead {
|
||||||
|
s.hashHead[i] = 0
|
||||||
|
}
|
||||||
|
for i := range s.hashPrev {
|
||||||
|
s.hashPrev[i] = 0
|
||||||
|
}
|
||||||
|
s.hashOffset = 1
|
||||||
|
s.index, d.windowEnd = 0, 0
|
||||||
|
d.blockStart, d.byteAvailable = 0, false
|
||||||
|
d.tokens.Reset()
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
s.offset = 0
|
||||||
|
s.ii = 0
|
||||||
|
s.maxInsertIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) close() error {
|
||||||
|
if d.err != nil {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
d.sync = true
|
||||||
|
d.step(d)
|
||||||
|
if d.err != nil {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
if d.w.writeStoredHeader(0, true); d.w.err != nil {
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
d.w.flush()
|
||||||
|
d.w.reset(nil)
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter returns a new Writer compressing data at the given level.
|
||||||
|
// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression);
|
||||||
|
// higher levels typically run slower but compress more.
|
||||||
|
// Level 0 (NoCompression) does not attempt any compression; it only adds the
|
||||||
|
// necessary DEFLATE framing.
|
||||||
|
// Level -1 (DefaultCompression) uses the default compression level.
|
||||||
|
// Level -2 (ConstantCompression) will use Huffman compression only, giving
|
||||||
|
// a very fast compression for all types of input, but sacrificing considerable
|
||||||
|
// compression efficiency.
|
||||||
|
//
|
||||||
|
// If level is in the range [-2, 9] then the error returned will be nil.
|
||||||
|
// Otherwise the error returned will be non-nil.
|
||||||
|
func NewWriter(w io.Writer, level int) (*Writer, error) {
|
||||||
|
var dw Writer
|
||||||
|
if err := dw.d.init(w, level); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriterDict is like NewWriter but initializes the new
|
||||||
|
// Writer with a preset dictionary. The returned Writer behaves
|
||||||
|
// as if the dictionary had been written to it without producing
|
||||||
|
// any compressed output. The compressed data written to w
|
||||||
|
// can only be decompressed by a Reader initialized with the
|
||||||
|
// same dictionary.
|
||||||
|
func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
|
||||||
|
zw, err := NewWriter(w, level)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
zw.d.fillWindow(dict)
|
||||||
|
zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method.
|
||||||
|
return zw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Writer takes data written to it and writes the compressed
|
||||||
|
// form of that data to an underlying writer (see NewWriter).
|
||||||
|
type Writer struct {
|
||||||
|
d compressor
|
||||||
|
dict []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes data to w, which will eventually write the
|
||||||
|
// compressed form of data to its underlying writer.
|
||||||
|
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
|
return w.d.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush flushes any pending data to the underlying writer.
|
||||||
|
// It is useful mainly in compressed network protocols, to ensure that
|
||||||
|
// a remote reader has enough data to reconstruct a packet.
|
||||||
|
// Flush does not return until the data has been written.
|
||||||
|
// Calling Flush when there is no pending data still causes the Writer
|
||||||
|
// to emit a sync marker of at least 4 bytes.
|
||||||
|
// If the underlying writer returns an error, Flush returns that error.
|
||||||
|
//
|
||||||
|
// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
|
||||||
|
func (w *Writer) Flush() error {
|
||||||
|
// For more about flushing:
|
||||||
|
// http://www.bolet.org/~pornin/deflate-flush.html
|
||||||
|
return w.d.syncFlush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close flushes and closes the writer.
|
||||||
|
func (w *Writer) Close() error {
|
||||||
|
return w.d.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset discards the writer's state and makes it equivalent to
|
||||||
|
// the result of NewWriter or NewWriterDict called with dst
|
||||||
|
// and w's level and dictionary.
|
||||||
|
func (w *Writer) Reset(dst io.Writer) {
|
||||||
|
if len(w.dict) > 0 {
|
||||||
|
// w was created with NewWriterDict
|
||||||
|
w.d.reset(dst)
|
||||||
|
if dst != nil {
|
||||||
|
w.d.fillWindow(w.dict)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// w was created with NewWriter
|
||||||
|
w.d.reset(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetDict discards the writer's state and makes it equivalent to
|
||||||
|
// the result of NewWriter or NewWriterDict called with dst
|
||||||
|
// and w's level, but sets a specific dictionary.
|
||||||
|
func (w *Writer) ResetDict(dst io.Writer, dict []byte) {
|
||||||
|
w.dict = dict
|
||||||
|
w.d.reset(dst)
|
||||||
|
w.d.fillWindow(w.dict)
|
||||||
|
}
|
184
vendor/github.com/klauspost/compress/flate/dict_decoder.go
generated
vendored
Normal file
184
vendor/github.com/klauspost/compress/flate/dict_decoder.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
// dictDecoder implements the LZ77 sliding dictionary as used in decompression.
|
||||||
|
// LZ77 decompresses data through sequences of two forms of commands:
|
||||||
|
//
|
||||||
|
// - Literal insertions: Runs of one or more symbols are inserted into the data
|
||||||
|
// stream as is. This is accomplished through the writeByte method for a
|
||||||
|
// single symbol, or combinations of writeSlice/writeMark for multiple symbols.
|
||||||
|
// Any valid stream must start with a literal insertion if no preset dictionary
|
||||||
|
// is used.
|
||||||
|
//
|
||||||
|
// - Backward copies: Runs of one or more symbols are copied from previously
|
||||||
|
// emitted data. Backward copies come as the tuple (dist, length) where dist
|
||||||
|
// determines how far back in the stream to copy from and length determines how
|
||||||
|
// many bytes to copy. Note that it is valid for the length to be greater than
|
||||||
|
// the distance. Since LZ77 uses forward copies, that situation is used to
|
||||||
|
// perform a form of run-length encoding on repeated runs of symbols.
|
||||||
|
// The writeCopy and tryWriteCopy are used to implement this command.
|
||||||
|
//
|
||||||
|
// For performance reasons, this implementation performs little to no sanity
|
||||||
|
// checks about the arguments. As such, the invariants documented for each
|
||||||
|
// method call must be respected.
|
||||||
|
type dictDecoder struct {
|
||||||
|
hist []byte // Sliding window history
|
||||||
|
|
||||||
|
// Invariant: 0 <= rdPos <= wrPos <= len(hist)
|
||||||
|
wrPos int // Current output position in buffer
|
||||||
|
rdPos int // Have emitted hist[:rdPos] already
|
||||||
|
full bool // Has a full window length been written yet?
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes dictDecoder to have a sliding window dictionary of the given
|
||||||
|
// size. If a preset dict is provided, it will initialize the dictionary with
|
||||||
|
// the contents of dict.
|
||||||
|
func (dd *dictDecoder) init(size int, dict []byte) {
|
||||||
|
*dd = dictDecoder{hist: dd.hist}
|
||||||
|
|
||||||
|
if cap(dd.hist) < size {
|
||||||
|
dd.hist = make([]byte, size)
|
||||||
|
}
|
||||||
|
dd.hist = dd.hist[:size]
|
||||||
|
|
||||||
|
if len(dict) > len(dd.hist) {
|
||||||
|
dict = dict[len(dict)-len(dd.hist):]
|
||||||
|
}
|
||||||
|
dd.wrPos = copy(dd.hist, dict)
|
||||||
|
if dd.wrPos == len(dd.hist) {
|
||||||
|
dd.wrPos = 0
|
||||||
|
dd.full = true
|
||||||
|
}
|
||||||
|
dd.rdPos = dd.wrPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// histSize reports the total amount of historical data in the dictionary.
|
||||||
|
func (dd *dictDecoder) histSize() int {
|
||||||
|
if dd.full {
|
||||||
|
return len(dd.hist)
|
||||||
|
}
|
||||||
|
return dd.wrPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// availRead reports the number of bytes that can be flushed by readFlush.
|
||||||
|
func (dd *dictDecoder) availRead() int {
|
||||||
|
return dd.wrPos - dd.rdPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// availWrite reports the available amount of output buffer space.
|
||||||
|
func (dd *dictDecoder) availWrite() int {
|
||||||
|
return len(dd.hist) - dd.wrPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSlice returns a slice of the available buffer to write data to.
|
||||||
|
//
|
||||||
|
// This invariant will be kept: len(s) <= availWrite()
|
||||||
|
func (dd *dictDecoder) writeSlice() []byte {
|
||||||
|
return dd.hist[dd.wrPos:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeMark advances the writer pointer by cnt.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 <= cnt <= availWrite()
|
||||||
|
func (dd *dictDecoder) writeMark(cnt int) {
|
||||||
|
dd.wrPos += cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeByte writes a single byte to the dictionary.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 < availWrite()
|
||||||
|
func (dd *dictDecoder) writeByte(c byte) {
|
||||||
|
dd.hist[dd.wrPos] = c
|
||||||
|
dd.wrPos++
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCopy copies a string at a given (dist, length) to the output.
|
||||||
|
// This returns the number of bytes copied and may be less than the requested
|
||||||
|
// length if the available space in the output buffer is too small.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 < dist <= histSize()
|
||||||
|
func (dd *dictDecoder) writeCopy(dist, length int) int {
|
||||||
|
dstBase := dd.wrPos
|
||||||
|
dstPos := dstBase
|
||||||
|
srcPos := dstPos - dist
|
||||||
|
endPos := dstPos + length
|
||||||
|
if endPos > len(dd.hist) {
|
||||||
|
endPos = len(dd.hist)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy non-overlapping section after destination position.
|
||||||
|
//
|
||||||
|
// This section is non-overlapping in that the copy length for this section
|
||||||
|
// is always less than or equal to the backwards distance. This can occur
|
||||||
|
// if a distance refers to data that wraps-around in the buffer.
|
||||||
|
// Thus, a backwards copy is performed here; that is, the exact bytes in
|
||||||
|
// the source prior to the copy is placed in the destination.
|
||||||
|
if srcPos < 0 {
|
||||||
|
srcPos += len(dd.hist)
|
||||||
|
dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:])
|
||||||
|
srcPos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy possibly overlapping section before destination position.
|
||||||
|
//
|
||||||
|
// This section can overlap if the copy length for this section is larger
|
||||||
|
// than the backwards distance. This is allowed by LZ77 so that repeated
|
||||||
|
// strings can be succinctly represented using (dist, length) pairs.
|
||||||
|
// Thus, a forwards copy is performed here; that is, the bytes copied is
|
||||||
|
// possibly dependent on the resulting bytes in the destination as the copy
|
||||||
|
// progresses along. This is functionally equivalent to the following:
|
||||||
|
//
|
||||||
|
// for i := 0; i < endPos-dstPos; i++ {
|
||||||
|
// dd.hist[dstPos+i] = dd.hist[srcPos+i]
|
||||||
|
// }
|
||||||
|
// dstPos = endPos
|
||||||
|
//
|
||||||
|
for dstPos < endPos {
|
||||||
|
dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos])
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.wrPos = dstPos
|
||||||
|
return dstPos - dstBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryWriteCopy tries to copy a string at a given (distance, length) to the
|
||||||
|
// output. This specialized version is optimized for short distances.
|
||||||
|
//
|
||||||
|
// This method is designed to be inlined for performance reasons.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 < dist <= histSize()
|
||||||
|
func (dd *dictDecoder) tryWriteCopy(dist, length int) int {
|
||||||
|
dstPos := dd.wrPos
|
||||||
|
endPos := dstPos + length
|
||||||
|
if dstPos < dist || endPos > len(dd.hist) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
dstBase := dstPos
|
||||||
|
srcPos := dstPos - dist
|
||||||
|
|
||||||
|
// Copy possibly overlapping section before destination position.
|
||||||
|
loop:
|
||||||
|
dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos])
|
||||||
|
if dstPos < endPos {
|
||||||
|
goto loop // Avoid for-loop so that this function can be inlined
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.wrPos = dstPos
|
||||||
|
return dstPos - dstBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFlush returns a slice of the historical buffer that is ready to be
|
||||||
|
// emitted to the user. The data returned by readFlush must be fully consumed
|
||||||
|
// before calling any other dictDecoder methods.
|
||||||
|
func (dd *dictDecoder) readFlush() []byte {
|
||||||
|
toRead := dd.hist[dd.rdPos:dd.wrPos]
|
||||||
|
dd.rdPos = dd.wrPos
|
||||||
|
if dd.wrPos == len(dd.hist) {
|
||||||
|
dd.wrPos, dd.rdPos = 0, 0
|
||||||
|
dd.full = true
|
||||||
|
}
|
||||||
|
return toRead
|
||||||
|
}
|
216
vendor/github.com/klauspost/compress/flate/fast_encoder.go
generated
vendored
Normal file
216
vendor/github.com/klauspost/compress/flate/fast_encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||||
|
// Modified for deflate by Klaus Post (c) 2015.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fastEnc interface {
|
||||||
|
Encode(dst *tokens, src []byte)
|
||||||
|
Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFastEnc(level int) fastEnc {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return &fastEncL1{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 2:
|
||||||
|
return &fastEncL2{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 3:
|
||||||
|
return &fastEncL3{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 4:
|
||||||
|
return &fastEncL4{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 5:
|
||||||
|
return &fastEncL5{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 6:
|
||||||
|
return &fastEncL6{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
default:
|
||||||
|
panic("invalid level specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tableBits = 15 // Bits used in the table
|
||||||
|
tableSize = 1 << tableBits // Size of the table
|
||||||
|
tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32.
|
||||||
|
baseMatchOffset = 1 // The smallest match offset
|
||||||
|
baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5
|
||||||
|
maxMatchOffset = 1 << 15 // The largest match offset
|
||||||
|
|
||||||
|
bTableBits = 17 // Bits used in the big tables
|
||||||
|
bTableSize = 1 << bTableBits // Size of the table
|
||||||
|
allocHistory = maxStoreBlockSize * 5 // Size to preallocate for history.
|
||||||
|
bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize - 1 // Reset the buffer offset when reaching this.
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prime3bytes = 506832829
|
||||||
|
prime4bytes = 2654435761
|
||||||
|
prime5bytes = 889523592379
|
||||||
|
prime6bytes = 227718039650203
|
||||||
|
prime7bytes = 58295818150454627
|
||||||
|
prime8bytes = 0xcf1bbcdcb7a56463
|
||||||
|
)
|
||||||
|
|
||||||
|
func load3232(b []byte, i int32) uint32 {
|
||||||
|
return binary.LittleEndian.Uint32(b[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func load6432(b []byte, i int32) uint64 {
|
||||||
|
return binary.LittleEndian.Uint64(b[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
type tableEntry struct {
|
||||||
|
offset int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// fastGen maintains the table for matches,
|
||||||
|
// and the previous byte block for level 2.
|
||||||
|
// This is the generic implementation.
|
||||||
|
type fastGen struct {
|
||||||
|
hist []byte
|
||||||
|
cur int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fastGen) addBlock(src []byte) int32 {
|
||||||
|
// check if we have space already
|
||||||
|
if len(e.hist)+len(src) > cap(e.hist) {
|
||||||
|
if cap(e.hist) == 0 {
|
||||||
|
e.hist = make([]byte, 0, allocHistory)
|
||||||
|
} else {
|
||||||
|
if cap(e.hist) < maxMatchOffset*2 {
|
||||||
|
panic("unexpected buffer size")
|
||||||
|
}
|
||||||
|
// Move down
|
||||||
|
offset := int32(len(e.hist)) - maxMatchOffset
|
||||||
|
// copy(e.hist[0:maxMatchOffset], e.hist[offset:])
|
||||||
|
*(*[maxMatchOffset]byte)(e.hist) = *(*[maxMatchOffset]byte)(e.hist[offset:])
|
||||||
|
e.cur += offset
|
||||||
|
e.hist = e.hist[:maxMatchOffset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s := int32(len(e.hist))
|
||||||
|
e.hist = append(e.hist, src...)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type tableEntryPrev struct {
|
||||||
|
Cur tableEntry
|
||||||
|
Prev tableEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits.
|
||||||
|
// Preferably h should be a constant and should always be <64.
|
||||||
|
func hash7(u uint64, h uint8) uint32 {
|
||||||
|
return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & reg8SizeMask64))
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashLen returns a hash of the lowest mls bytes of with length output bits.
|
||||||
|
// mls must be >=3 and <=8. Any other value will return hash for 4 bytes.
|
||||||
|
// length should always be < 32.
|
||||||
|
// Preferably length and mls should be a constant for inlining.
|
||||||
|
func hashLen(u uint64, length, mls uint8) uint32 {
|
||||||
|
switch mls {
|
||||||
|
case 3:
|
||||||
|
return (uint32(u<<8) * prime3bytes) >> (32 - length)
|
||||||
|
case 5:
|
||||||
|
return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length))
|
||||||
|
case 6:
|
||||||
|
return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length))
|
||||||
|
case 7:
|
||||||
|
return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length))
|
||||||
|
case 8:
|
||||||
|
return uint32((u * prime8bytes) >> (64 - length))
|
||||||
|
default:
|
||||||
|
return (uint32(u) * prime4bytes) >> (32 - length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchlen will return the match length between offsets and t in src.
|
||||||
|
// The maximum length returned is maxMatchLength - 4.
|
||||||
|
// It is assumed that s > t, that t >=0 and s < len(src).
|
||||||
|
func (e *fastGen) matchlen(s, t int32, src []byte) int32 {
|
||||||
|
if debugDecode {
|
||||||
|
if t >= s {
|
||||||
|
panic(fmt.Sprint("t >=s:", t, s))
|
||||||
|
}
|
||||||
|
if int(s) >= len(src) {
|
||||||
|
panic(fmt.Sprint("s >= len(src):", s, len(src)))
|
||||||
|
}
|
||||||
|
if t < 0 {
|
||||||
|
panic(fmt.Sprint("t < 0:", t))
|
||||||
|
}
|
||||||
|
if s-t > maxMatchOffset {
|
||||||
|
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s1 := int(s) + maxMatchLength - 4
|
||||||
|
if s1 > len(src) {
|
||||||
|
s1 = len(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the match to be as long as possible.
|
||||||
|
return int32(matchLen(src[s:s1], src[t:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchlenLong will return the match length between offsets and t in src.
|
||||||
|
// It is assumed that s > t, that t >=0 and s < len(src).
|
||||||
|
func (e *fastGen) matchlenLong(s, t int32, src []byte) int32 {
|
||||||
|
if debugDeflate {
|
||||||
|
if t >= s {
|
||||||
|
panic(fmt.Sprint("t >=s:", t, s))
|
||||||
|
}
|
||||||
|
if int(s) >= len(src) {
|
||||||
|
panic(fmt.Sprint("s >= len(src):", s, len(src)))
|
||||||
|
}
|
||||||
|
if t < 0 {
|
||||||
|
panic(fmt.Sprint("t < 0:", t))
|
||||||
|
}
|
||||||
|
if s-t > maxMatchOffset {
|
||||||
|
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Extend the match to be as long as possible.
|
||||||
|
return int32(matchLen(src[s:], src[t:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the encoding table.
|
||||||
|
func (e *fastGen) Reset() {
|
||||||
|
if cap(e.hist) < allocHistory {
|
||||||
|
e.hist = make([]byte, 0, allocHistory)
|
||||||
|
}
|
||||||
|
// We offset current position so everything will be out of reach.
|
||||||
|
// If we are above the buffer reset it will be cleared anyway since len(hist) == 0.
|
||||||
|
if e.cur <= bufferReset {
|
||||||
|
e.cur += maxMatchOffset + int32(len(e.hist))
|
||||||
|
}
|
||||||
|
e.hist = e.hist[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchLen returns the maximum length.
|
||||||
|
// 'a' must be the shortest of the two.
|
||||||
|
func matchLen(a, b []byte) int {
|
||||||
|
var checked int
|
||||||
|
|
||||||
|
for len(a) >= 8 {
|
||||||
|
if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 {
|
||||||
|
return checked + (bits.TrailingZeros64(diff) >> 3)
|
||||||
|
}
|
||||||
|
checked += 8
|
||||||
|
a = a[8:]
|
||||||
|
b = b[8:]
|
||||||
|
}
|
||||||
|
b = b[:len(a)]
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return i + checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(a) + checked
|
||||||
|
}
|
1187
vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go
generated
vendored
Normal file
1187
vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
417
vendor/github.com/klauspost/compress/flate/huffman_code.go
generated
vendored
Normal file
417
vendor/github.com/klauspost/compress/flate/huffman_code.go
generated
vendored
Normal file
|
@ -0,0 +1,417 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxBitsLimit = 16
|
||||||
|
// number of valid literals
|
||||||
|
literalCount = 286
|
||||||
|
)
|
||||||
|
|
||||||
|
// hcode is a huffman code with a bit code and bit length.
|
||||||
|
type hcode uint32
|
||||||
|
|
||||||
|
func (h hcode) len() uint8 {
|
||||||
|
return uint8(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hcode) code64() uint64 {
|
||||||
|
return uint64(h >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hcode) zero() bool {
|
||||||
|
return h == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type huffmanEncoder struct {
|
||||||
|
codes []hcode
|
||||||
|
bitCount [17]int32
|
||||||
|
|
||||||
|
// Allocate a reusable buffer with the longest possible frequency table.
|
||||||
|
// Possible lengths are codegenCodeCount, offsetCodeCount and literalCount.
|
||||||
|
// The largest of these is literalCount, so we allocate for that case.
|
||||||
|
freqcache [literalCount + 1]literalNode
|
||||||
|
}
|
||||||
|
|
||||||
|
type literalNode struct {
|
||||||
|
literal uint16
|
||||||
|
freq uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// A levelInfo describes the state of the constructed tree for a given depth.
|
||||||
|
type levelInfo struct {
|
||||||
|
// Our level. for better printing
|
||||||
|
level int32
|
||||||
|
|
||||||
|
// The frequency of the last node at this level
|
||||||
|
lastFreq int32
|
||||||
|
|
||||||
|
// The frequency of the next character to add to this level
|
||||||
|
nextCharFreq int32
|
||||||
|
|
||||||
|
// The frequency of the next pair (from level below) to add to this level.
|
||||||
|
// Only valid if the "needed" value of the next lower level is 0.
|
||||||
|
nextPairFreq int32
|
||||||
|
|
||||||
|
// The number of chains remaining to generate for this level before moving
|
||||||
|
// up to the next level
|
||||||
|
needed int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sets the code and length of an hcode.
|
||||||
|
func (h *hcode) set(code uint16, length uint8) {
|
||||||
|
*h = hcode(length) | (hcode(code) << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newhcode(code uint16, length uint8) hcode {
|
||||||
|
return hcode(length) | (hcode(code) << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverseBits(number uint16, bitLength byte) uint16 {
|
||||||
|
return bits.Reverse16(number << ((16 - bitLength) & 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxUint16} }
|
||||||
|
|
||||||
|
func newHuffmanEncoder(size int) *huffmanEncoder {
|
||||||
|
// Make capacity to next power of two.
|
||||||
|
c := uint(bits.Len32(uint32(size - 1)))
|
||||||
|
return &huffmanEncoder{codes: make([]hcode, size, 1<<c)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a HuffmanCode corresponding to the fixed literal table
|
||||||
|
func generateFixedLiteralEncoding() *huffmanEncoder {
|
||||||
|
h := newHuffmanEncoder(literalCount)
|
||||||
|
codes := h.codes
|
||||||
|
var ch uint16
|
||||||
|
for ch = 0; ch < literalCount; ch++ {
|
||||||
|
var bits uint16
|
||||||
|
var size uint8
|
||||||
|
switch {
|
||||||
|
case ch < 144:
|
||||||
|
// size 8, 000110000 .. 10111111
|
||||||
|
bits = ch + 48
|
||||||
|
size = 8
|
||||||
|
case ch < 256:
|
||||||
|
// size 9, 110010000 .. 111111111
|
||||||
|
bits = ch + 400 - 144
|
||||||
|
size = 9
|
||||||
|
case ch < 280:
|
||||||
|
// size 7, 0000000 .. 0010111
|
||||||
|
bits = ch - 256
|
||||||
|
size = 7
|
||||||
|
default:
|
||||||
|
// size 8, 11000000 .. 11000111
|
||||||
|
bits = ch + 192 - 280
|
||||||
|
size = 8
|
||||||
|
}
|
||||||
|
codes[ch] = newhcode(reverseBits(bits, size), size)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFixedOffsetEncoding() *huffmanEncoder {
|
||||||
|
h := newHuffmanEncoder(30)
|
||||||
|
codes := h.codes
|
||||||
|
for ch := range codes {
|
||||||
|
codes[ch] = newhcode(reverseBits(uint16(ch), 5), 5)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixedLiteralEncoding = generateFixedLiteralEncoding()
|
||||||
|
var fixedOffsetEncoding = generateFixedOffsetEncoding()
|
||||||
|
|
||||||
|
func (h *huffmanEncoder) bitLength(freq []uint16) int {
|
||||||
|
var total int
|
||||||
|
for i, f := range freq {
|
||||||
|
if f != 0 {
|
||||||
|
total += int(f) * int(h.codes[i].len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *huffmanEncoder) bitLengthRaw(b []byte) int {
|
||||||
|
var total int
|
||||||
|
for _, f := range b {
|
||||||
|
total += int(h.codes[f].len())
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// canReuseBits returns the number of bits or math.MaxInt32 if the encoder cannot be reused.
|
||||||
|
func (h *huffmanEncoder) canReuseBits(freq []uint16) int {
|
||||||
|
var total int
|
||||||
|
for i, f := range freq {
|
||||||
|
if f != 0 {
|
||||||
|
code := h.codes[i]
|
||||||
|
if code.zero() {
|
||||||
|
return math.MaxInt32
|
||||||
|
}
|
||||||
|
total += int(f) * int(code.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of literals assigned to each bit size in the Huffman encoding
|
||||||
|
//
|
||||||
|
// This method is only called when list.length >= 3
|
||||||
|
// The cases of 0, 1, and 2 literals are handled by special case code.
|
||||||
|
//
|
||||||
|
// list An array of the literals with non-zero frequencies
|
||||||
|
//
|
||||||
|
// and their associated frequencies. The array is in order of increasing
|
||||||
|
// frequency, and has as its last element a special element with frequency
|
||||||
|
// MaxInt32
|
||||||
|
//
|
||||||
|
// maxBits The maximum number of bits that should be used to encode any literal.
|
||||||
|
//
|
||||||
|
// Must be less than 16.
|
||||||
|
//
|
||||||
|
// return An integer array in which array[i] indicates the number of literals
|
||||||
|
//
|
||||||
|
// that should be encoded in i bits.
|
||||||
|
func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
|
||||||
|
if maxBits >= maxBitsLimit {
|
||||||
|
panic("flate: maxBits too large")
|
||||||
|
}
|
||||||
|
n := int32(len(list))
|
||||||
|
list = list[0 : n+1]
|
||||||
|
list[n] = maxNode()
|
||||||
|
|
||||||
|
// The tree can't have greater depth than n - 1, no matter what. This
|
||||||
|
// saves a little bit of work in some small cases
|
||||||
|
if maxBits > n-1 {
|
||||||
|
maxBits = n - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create information about each of the levels.
|
||||||
|
// A bogus "Level 0" whose sole purpose is so that
|
||||||
|
// level1.prev.needed==0. This makes level1.nextPairFreq
|
||||||
|
// be a legitimate value that never gets chosen.
|
||||||
|
var levels [maxBitsLimit]levelInfo
|
||||||
|
// leafCounts[i] counts the number of literals at the left
|
||||||
|
// of ancestors of the rightmost node at level i.
|
||||||
|
// leafCounts[i][j] is the number of literals at the left
|
||||||
|
// of the level j ancestor.
|
||||||
|
var leafCounts [maxBitsLimit][maxBitsLimit]int32
|
||||||
|
|
||||||
|
// Descending to only have 1 bounds check.
|
||||||
|
l2f := int32(list[2].freq)
|
||||||
|
l1f := int32(list[1].freq)
|
||||||
|
l0f := int32(list[0].freq) + int32(list[1].freq)
|
||||||
|
|
||||||
|
for level := int32(1); level <= maxBits; level++ {
|
||||||
|
// For every level, the first two items are the first two characters.
|
||||||
|
// We initialize the levels as if we had already figured this out.
|
||||||
|
levels[level] = levelInfo{
|
||||||
|
level: level,
|
||||||
|
lastFreq: l1f,
|
||||||
|
nextCharFreq: l2f,
|
||||||
|
nextPairFreq: l0f,
|
||||||
|
}
|
||||||
|
leafCounts[level][level] = 2
|
||||||
|
if level == 1 {
|
||||||
|
levels[level].nextPairFreq = math.MaxInt32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a total of 2*n - 2 items at top level and have already generated 2.
|
||||||
|
levels[maxBits].needed = 2*n - 4
|
||||||
|
|
||||||
|
level := uint32(maxBits)
|
||||||
|
for level < 16 {
|
||||||
|
l := &levels[level]
|
||||||
|
if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 {
|
||||||
|
// We've run out of both leafs and pairs.
|
||||||
|
// End all calculations for this level.
|
||||||
|
// To make sure we never come back to this level or any lower level,
|
||||||
|
// set nextPairFreq impossibly large.
|
||||||
|
l.needed = 0
|
||||||
|
levels[level+1].nextPairFreq = math.MaxInt32
|
||||||
|
level++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
prevFreq := l.lastFreq
|
||||||
|
if l.nextCharFreq < l.nextPairFreq {
|
||||||
|
// The next item on this row is a leaf node.
|
||||||
|
n := leafCounts[level][level] + 1
|
||||||
|
l.lastFreq = l.nextCharFreq
|
||||||
|
// Lower leafCounts are the same of the previous node.
|
||||||
|
leafCounts[level][level] = n
|
||||||
|
e := list[n]
|
||||||
|
if e.literal < math.MaxUint16 {
|
||||||
|
l.nextCharFreq = int32(e.freq)
|
||||||
|
} else {
|
||||||
|
l.nextCharFreq = math.MaxInt32
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The next item on this row is a pair from the previous row.
|
||||||
|
// nextPairFreq isn't valid until we generate two
|
||||||
|
// more values in the level below
|
||||||
|
l.lastFreq = l.nextPairFreq
|
||||||
|
// Take leaf counts from the lower level, except counts[level] remains the same.
|
||||||
|
if true {
|
||||||
|
save := leafCounts[level][level]
|
||||||
|
leafCounts[level] = leafCounts[level-1]
|
||||||
|
leafCounts[level][level] = save
|
||||||
|
} else {
|
||||||
|
copy(leafCounts[level][:level], leafCounts[level-1][:level])
|
||||||
|
}
|
||||||
|
levels[l.level-1].needed = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.needed--; l.needed == 0 {
|
||||||
|
// We've done everything we need to do for this level.
|
||||||
|
// Continue calculating one level up. Fill in nextPairFreq
|
||||||
|
// of that level with the sum of the two nodes we've just calculated on
|
||||||
|
// this level.
|
||||||
|
if l.level == maxBits {
|
||||||
|
// All done!
|
||||||
|
break
|
||||||
|
}
|
||||||
|
levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq
|
||||||
|
level++
|
||||||
|
} else {
|
||||||
|
// If we stole from below, move down temporarily to replenish it.
|
||||||
|
for levels[level-1].needed > 0 {
|
||||||
|
level--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Somethings is wrong if at the end, the top level is null or hasn't used
|
||||||
|
// all of the leaves.
|
||||||
|
if leafCounts[maxBits][maxBits] != n {
|
||||||
|
panic("leafCounts[maxBits][maxBits] != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
bitCount := h.bitCount[:maxBits+1]
|
||||||
|
bits := 1
|
||||||
|
counts := &leafCounts[maxBits]
|
||||||
|
for level := maxBits; level > 0; level-- {
|
||||||
|
// chain.leafCount gives the number of literals requiring at least "bits"
|
||||||
|
// bits to encode.
|
||||||
|
bitCount[bits] = counts[level] - counts[level-1]
|
||||||
|
bits++
|
||||||
|
}
|
||||||
|
return bitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look at the leaves and assign them a bit count and an encoding as specified
|
||||||
|
// in RFC 1951 3.2.2
|
||||||
|
func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalNode) {
|
||||||
|
code := uint16(0)
|
||||||
|
for n, bits := range bitCount {
|
||||||
|
code <<= 1
|
||||||
|
if n == 0 || bits == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The literals list[len(list)-bits] .. list[len(list)-bits]
|
||||||
|
// are encoded using "bits" bits, and get the values
|
||||||
|
// code, code + 1, .... The code values are
|
||||||
|
// assigned in literal order (not frequency order).
|
||||||
|
chunk := list[len(list)-int(bits):]
|
||||||
|
|
||||||
|
sortByLiteral(chunk)
|
||||||
|
for _, node := range chunk {
|
||||||
|
h.codes[node.literal] = newhcode(reverseBits(code, uint8(n)), uint8(n))
|
||||||
|
code++
|
||||||
|
}
|
||||||
|
list = list[0 : len(list)-int(bits)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update this Huffman Code object to be the minimum code for the specified frequency count.
|
||||||
|
//
|
||||||
|
// freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
|
||||||
|
// maxBits The maximum number of bits to use for any literal.
|
||||||
|
func (h *huffmanEncoder) generate(freq []uint16, maxBits int32) {
|
||||||
|
list := h.freqcache[:len(freq)+1]
|
||||||
|
codes := h.codes[:len(freq)]
|
||||||
|
// Number of non-zero literals
|
||||||
|
count := 0
|
||||||
|
// Set list to be the set of all non-zero literals and their frequencies
|
||||||
|
for i, f := range freq {
|
||||||
|
if f != 0 {
|
||||||
|
list[count] = literalNode{uint16(i), f}
|
||||||
|
count++
|
||||||
|
} else {
|
||||||
|
codes[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list[count] = literalNode{}
|
||||||
|
|
||||||
|
list = list[:count]
|
||||||
|
if count <= 2 {
|
||||||
|
// Handle the small cases here, because they are awkward for the general case code. With
|
||||||
|
// two or fewer literals, everything has bit length 1.
|
||||||
|
for i, node := range list {
|
||||||
|
// "list" is in order of increasing literal value.
|
||||||
|
h.codes[node.literal].set(uint16(i), 1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sortByFreq(list)
|
||||||
|
|
||||||
|
// Get the number of literals for each bit count
|
||||||
|
bitCount := h.bitCounts(list, maxBits)
|
||||||
|
// And do the assignment
|
||||||
|
h.assignEncodingAndSize(bitCount, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// atLeastOne clamps the result between 1 and 15.
|
||||||
|
func atLeastOne(v float32) float32 {
|
||||||
|
if v < 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if v > 15 {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func histogram(b []byte, h []uint16) {
|
||||||
|
if true && len(b) >= 8<<10 {
|
||||||
|
// Split for bigger inputs
|
||||||
|
histogramSplit(b, h)
|
||||||
|
} else {
|
||||||
|
h = h[:256]
|
||||||
|
for _, t := range b {
|
||||||
|
h[t]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func histogramSplit(b []byte, h []uint16) {
|
||||||
|
// Tested, and slightly faster than 2-way.
|
||||||
|
// Writing to separate arrays and combining is also slightly slower.
|
||||||
|
h = h[:256]
|
||||||
|
for len(b)&3 != 0 {
|
||||||
|
h[b[0]]++
|
||||||
|
b = b[1:]
|
||||||
|
}
|
||||||
|
n := len(b) / 4
|
||||||
|
x, y, z, w := b[:n], b[n:], b[n+n:], b[n+n+n:]
|
||||||
|
y, z, w = y[:len(x)], z[:len(x)], w[:len(x)]
|
||||||
|
for i, t := range x {
|
||||||
|
v0 := &h[t]
|
||||||
|
v1 := &h[y[i]]
|
||||||
|
v3 := &h[w[i]]
|
||||||
|
v2 := &h[z[i]]
|
||||||
|
*v0++
|
||||||
|
*v1++
|
||||||
|
*v2++
|
||||||
|
*v3++
|
||||||
|
}
|
||||||
|
}
|
178
vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go
generated
vendored
Normal file
178
vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
// Sort sorts data.
|
||||||
|
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
|
||||||
|
// data.Less and data.Swap. The sort is not guaranteed to be stable.
|
||||||
|
func sortByFreq(data []literalNode) {
|
||||||
|
n := len(data)
|
||||||
|
quickSortByFreq(data, 0, n, maxDepth(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func quickSortByFreq(data []literalNode, a, b, maxDepth int) {
|
||||||
|
for b-a > 12 { // Use ShellSort for slices <= 12 elements
|
||||||
|
if maxDepth == 0 {
|
||||||
|
heapSort(data, a, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxDepth--
|
||||||
|
mlo, mhi := doPivotByFreq(data, a, b)
|
||||||
|
// Avoiding recursion on the larger subproblem guarantees
|
||||||
|
// a stack depth of at most lg(b-a).
|
||||||
|
if mlo-a < b-mhi {
|
||||||
|
quickSortByFreq(data, a, mlo, maxDepth)
|
||||||
|
a = mhi // i.e., quickSortByFreq(data, mhi, b)
|
||||||
|
} else {
|
||||||
|
quickSortByFreq(data, mhi, b, maxDepth)
|
||||||
|
b = mlo // i.e., quickSortByFreq(data, a, mlo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b-a > 1 {
|
||||||
|
// Do ShellSort pass with gap 6
|
||||||
|
// It could be written in this simplified form cause b-a <= 12
|
||||||
|
for i := a + 6; i < b; i++ {
|
||||||
|
if data[i].freq == data[i-6].freq && data[i].literal < data[i-6].literal || data[i].freq < data[i-6].freq {
|
||||||
|
data[i], data[i-6] = data[i-6], data[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertionSortByFreq(data, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// siftDownByFreq implements the heap property on data[lo, hi).
|
||||||
|
// first is an offset into the array where the root of the heap lies.
|
||||||
|
func siftDownByFreq(data []literalNode, lo, hi, first int) {
|
||||||
|
root := lo
|
||||||
|
for {
|
||||||
|
child := 2*root + 1
|
||||||
|
if child >= hi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if child+1 < hi && (data[first+child].freq == data[first+child+1].freq && data[first+child].literal < data[first+child+1].literal || data[first+child].freq < data[first+child+1].freq) {
|
||||||
|
child++
|
||||||
|
}
|
||||||
|
if data[first+root].freq == data[first+child].freq && data[first+root].literal > data[first+child].literal || data[first+root].freq > data[first+child].freq {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||||
|
root = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func doPivotByFreq(data []literalNode, lo, hi int) (midlo, midhi int) {
|
||||||
|
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
|
||||||
|
if hi-lo > 40 {
|
||||||
|
// Tukey's ``Ninther,'' median of three medians of three.
|
||||||
|
s := (hi - lo) / 8
|
||||||
|
medianOfThreeSortByFreq(data, lo, lo+s, lo+2*s)
|
||||||
|
medianOfThreeSortByFreq(data, m, m-s, m+s)
|
||||||
|
medianOfThreeSortByFreq(data, hi-1, hi-1-s, hi-1-2*s)
|
||||||
|
}
|
||||||
|
medianOfThreeSortByFreq(data, lo, m, hi-1)
|
||||||
|
|
||||||
|
// Invariants are:
|
||||||
|
// data[lo] = pivot (set up by ChoosePivot)
|
||||||
|
// data[lo < i < a] < pivot
|
||||||
|
// data[a <= i < b] <= pivot
|
||||||
|
// data[b <= i < c] unexamined
|
||||||
|
// data[c <= i < hi-1] > pivot
|
||||||
|
// data[hi-1] >= pivot
|
||||||
|
pivot := lo
|
||||||
|
a, c := lo+1, hi-1
|
||||||
|
|
||||||
|
for ; a < c && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ {
|
||||||
|
}
|
||||||
|
b := a
|
||||||
|
for {
|
||||||
|
for ; b < c && (data[pivot].freq == data[b].freq && data[pivot].literal > data[b].literal || data[pivot].freq > data[b].freq); b++ { // data[b] <= pivot
|
||||||
|
}
|
||||||
|
for ; b < c && (data[pivot].freq == data[c-1].freq && data[pivot].literal < data[c-1].literal || data[pivot].freq < data[c-1].freq); c-- { // data[c-1] > pivot
|
||||||
|
}
|
||||||
|
if b >= c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[b] > pivot; data[c-1] <= pivot
|
||||||
|
data[b], data[c-1] = data[c-1], data[b]
|
||||||
|
b++
|
||||||
|
c--
|
||||||
|
}
|
||||||
|
// If hi-c<3 then there are duplicates (by property of median of nine).
|
||||||
|
// Let's be a bit more conservative, and set border to 5.
|
||||||
|
protect := hi-c < 5
|
||||||
|
if !protect && hi-c < (hi-lo)/4 {
|
||||||
|
// Lets test some points for equality to pivot
|
||||||
|
dups := 0
|
||||||
|
if data[pivot].freq == data[hi-1].freq && data[pivot].literal > data[hi-1].literal || data[pivot].freq > data[hi-1].freq { // data[hi-1] = pivot
|
||||||
|
data[c], data[hi-1] = data[hi-1], data[c]
|
||||||
|
c++
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
if data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq { // data[b-1] = pivot
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// m-lo = (hi-lo)/2 > 6
|
||||||
|
// b-lo > (hi-lo)*3/4-1 > 8
|
||||||
|
// ==> m < b ==> data[m] <= pivot
|
||||||
|
if data[m].freq == data[pivot].freq && data[m].literal > data[pivot].literal || data[m].freq > data[pivot].freq { // data[m] = pivot
|
||||||
|
data[m], data[b-1] = data[b-1], data[m]
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// if at least 2 points are equal to pivot, assume skewed distribution
|
||||||
|
protect = dups > 1
|
||||||
|
}
|
||||||
|
if protect {
|
||||||
|
// Protect against a lot of duplicates
|
||||||
|
// Add invariant:
|
||||||
|
// data[a <= i < b] unexamined
|
||||||
|
// data[b <= i < c] = pivot
|
||||||
|
for {
|
||||||
|
for ; a < b && (data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq); b-- { // data[b] == pivot
|
||||||
|
}
|
||||||
|
for ; a < b && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { // data[a] < pivot
|
||||||
|
}
|
||||||
|
if a >= b {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[a] == pivot; data[b-1] < pivot
|
||||||
|
data[a], data[b-1] = data[b-1], data[a]
|
||||||
|
a++
|
||||||
|
b--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Swap pivot into middle
|
||||||
|
data[pivot], data[b-1] = data[b-1], data[pivot]
|
||||||
|
return b - 1, c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insertion sort
|
||||||
|
func insertionSortByFreq(data []literalNode, a, b int) {
|
||||||
|
for i := a + 1; i < b; i++ {
|
||||||
|
for j := i; j > a && (data[j].freq == data[j-1].freq && data[j].literal < data[j-1].literal || data[j].freq < data[j-1].freq); j-- {
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// quickSortByFreq, loosely following Bentley and McIlroy,
|
||||||
|
// ``Engineering a Sort Function,'' SP&E November 1993.
|
||||||
|
|
||||||
|
// medianOfThreeSortByFreq moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
|
||||||
|
func medianOfThreeSortByFreq(data []literalNode, m1, m0, m2 int) {
|
||||||
|
// sort 3 elements
|
||||||
|
if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
// data[m0] <= data[m1]
|
||||||
|
if data[m2].freq == data[m1].freq && data[m2].literal < data[m1].literal || data[m2].freq < data[m1].freq {
|
||||||
|
data[m2], data[m1] = data[m1], data[m2]
|
||||||
|
// data[m0] <= data[m2] && data[m1] < data[m2]
|
||||||
|
if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now data[m0] <= data[m1] <= data[m2]
|
||||||
|
}
|
201
vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go
generated
vendored
Normal file
201
vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
// Sort sorts data.
|
||||||
|
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
|
||||||
|
// data.Less and data.Swap. The sort is not guaranteed to be stable.
|
||||||
|
func sortByLiteral(data []literalNode) {
|
||||||
|
n := len(data)
|
||||||
|
quickSort(data, 0, n, maxDepth(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func quickSort(data []literalNode, a, b, maxDepth int) {
|
||||||
|
for b-a > 12 { // Use ShellSort for slices <= 12 elements
|
||||||
|
if maxDepth == 0 {
|
||||||
|
heapSort(data, a, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxDepth--
|
||||||
|
mlo, mhi := doPivot(data, a, b)
|
||||||
|
// Avoiding recursion on the larger subproblem guarantees
|
||||||
|
// a stack depth of at most lg(b-a).
|
||||||
|
if mlo-a < b-mhi {
|
||||||
|
quickSort(data, a, mlo, maxDepth)
|
||||||
|
a = mhi // i.e., quickSort(data, mhi, b)
|
||||||
|
} else {
|
||||||
|
quickSort(data, mhi, b, maxDepth)
|
||||||
|
b = mlo // i.e., quickSort(data, a, mlo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b-a > 1 {
|
||||||
|
// Do ShellSort pass with gap 6
|
||||||
|
// It could be written in this simplified form cause b-a <= 12
|
||||||
|
for i := a + 6; i < b; i++ {
|
||||||
|
if data[i].literal < data[i-6].literal {
|
||||||
|
data[i], data[i-6] = data[i-6], data[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertionSort(data, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func heapSort(data []literalNode, a, b int) {
|
||||||
|
first := a
|
||||||
|
lo := 0
|
||||||
|
hi := b - a
|
||||||
|
|
||||||
|
// Build heap with greatest element at top.
|
||||||
|
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||||
|
siftDown(data, i, hi, first)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop elements, largest first, into end of data.
|
||||||
|
for i := hi - 1; i >= 0; i-- {
|
||||||
|
data[first], data[first+i] = data[first+i], data[first]
|
||||||
|
siftDown(data, lo, i, first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// siftDown implements the heap property on data[lo, hi).
|
||||||
|
// first is an offset into the array where the root of the heap lies.
|
||||||
|
func siftDown(data []literalNode, lo, hi, first int) {
|
||||||
|
root := lo
|
||||||
|
for {
|
||||||
|
child := 2*root + 1
|
||||||
|
if child >= hi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if child+1 < hi && data[first+child].literal < data[first+child+1].literal {
|
||||||
|
child++
|
||||||
|
}
|
||||||
|
if data[first+root].literal > data[first+child].literal {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||||
|
root = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func doPivot(data []literalNode, lo, hi int) (midlo, midhi int) {
|
||||||
|
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
|
||||||
|
if hi-lo > 40 {
|
||||||
|
// Tukey's ``Ninther,'' median of three medians of three.
|
||||||
|
s := (hi - lo) / 8
|
||||||
|
medianOfThree(data, lo, lo+s, lo+2*s)
|
||||||
|
medianOfThree(data, m, m-s, m+s)
|
||||||
|
medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
|
||||||
|
}
|
||||||
|
medianOfThree(data, lo, m, hi-1)
|
||||||
|
|
||||||
|
// Invariants are:
|
||||||
|
// data[lo] = pivot (set up by ChoosePivot)
|
||||||
|
// data[lo < i < a] < pivot
|
||||||
|
// data[a <= i < b] <= pivot
|
||||||
|
// data[b <= i < c] unexamined
|
||||||
|
// data[c <= i < hi-1] > pivot
|
||||||
|
// data[hi-1] >= pivot
|
||||||
|
pivot := lo
|
||||||
|
a, c := lo+1, hi-1
|
||||||
|
|
||||||
|
for ; a < c && data[a].literal < data[pivot].literal; a++ {
|
||||||
|
}
|
||||||
|
b := a
|
||||||
|
for {
|
||||||
|
for ; b < c && data[pivot].literal > data[b].literal; b++ { // data[b] <= pivot
|
||||||
|
}
|
||||||
|
for ; b < c && data[pivot].literal < data[c-1].literal; c-- { // data[c-1] > pivot
|
||||||
|
}
|
||||||
|
if b >= c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[b] > pivot; data[c-1] <= pivot
|
||||||
|
data[b], data[c-1] = data[c-1], data[b]
|
||||||
|
b++
|
||||||
|
c--
|
||||||
|
}
|
||||||
|
// If hi-c<3 then there are duplicates (by property of median of nine).
|
||||||
|
// Let's be a bit more conservative, and set border to 5.
|
||||||
|
protect := hi-c < 5
|
||||||
|
if !protect && hi-c < (hi-lo)/4 {
|
||||||
|
// Lets test some points for equality to pivot
|
||||||
|
dups := 0
|
||||||
|
if data[pivot].literal > data[hi-1].literal { // data[hi-1] = pivot
|
||||||
|
data[c], data[hi-1] = data[hi-1], data[c]
|
||||||
|
c++
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
if data[b-1].literal > data[pivot].literal { // data[b-1] = pivot
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// m-lo = (hi-lo)/2 > 6
|
||||||
|
// b-lo > (hi-lo)*3/4-1 > 8
|
||||||
|
// ==> m < b ==> data[m] <= pivot
|
||||||
|
if data[m].literal > data[pivot].literal { // data[m] = pivot
|
||||||
|
data[m], data[b-1] = data[b-1], data[m]
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// if at least 2 points are equal to pivot, assume skewed distribution
|
||||||
|
protect = dups > 1
|
||||||
|
}
|
||||||
|
if protect {
|
||||||
|
// Protect against a lot of duplicates
|
||||||
|
// Add invariant:
|
||||||
|
// data[a <= i < b] unexamined
|
||||||
|
// data[b <= i < c] = pivot
|
||||||
|
for {
|
||||||
|
for ; a < b && data[b-1].literal > data[pivot].literal; b-- { // data[b] == pivot
|
||||||
|
}
|
||||||
|
for ; a < b && data[a].literal < data[pivot].literal; a++ { // data[a] < pivot
|
||||||
|
}
|
||||||
|
if a >= b {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[a] == pivot; data[b-1] < pivot
|
||||||
|
data[a], data[b-1] = data[b-1], data[a]
|
||||||
|
a++
|
||||||
|
b--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Swap pivot into middle
|
||||||
|
data[pivot], data[b-1] = data[b-1], data[pivot]
|
||||||
|
return b - 1, c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insertion sort
|
||||||
|
func insertionSort(data []literalNode, a, b int) {
|
||||||
|
for i := a + 1; i < b; i++ {
|
||||||
|
for j := i; j > a && data[j].literal < data[j-1].literal; j-- {
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxDepth returns a threshold at which quicksort should switch
|
||||||
|
// to heapsort. It returns 2*ceil(lg(n+1)).
|
||||||
|
func maxDepth(n int) int {
|
||||||
|
var depth int
|
||||||
|
for i := n; i > 0; i >>= 1 {
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
return depth * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
|
||||||
|
func medianOfThree(data []literalNode, m1, m0, m2 int) {
|
||||||
|
// sort 3 elements
|
||||||
|
if data[m1].literal < data[m0].literal {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
// data[m0] <= data[m1]
|
||||||
|
if data[m2].literal < data[m1].literal {
|
||||||
|
data[m2], data[m1] = data[m1], data[m2]
|
||||||
|
// data[m0] <= data[m2] && data[m1] < data[m2]
|
||||||
|
if data[m1].literal < data[m0].literal {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now data[m0] <= data[m1] <= data[m2]
|
||||||
|
}
|
793
vendor/github.com/klauspost/compress/flate/inflate.go
generated
vendored
Normal file
793
vendor/github.com/klauspost/compress/flate/inflate.go
generated
vendored
Normal file
|
@ -0,0 +1,793 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package flate implements the DEFLATE compressed data format, described in
|
||||||
|
// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file
|
||||||
|
// formats.
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"compress/flate"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/bits"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxCodeLen = 16 // max length of Huffman code
|
||||||
|
maxCodeLenMask = 15 // mask for max length of Huffman code
|
||||||
|
// The next three numbers come from the RFC section 3.2.7, with the
|
||||||
|
// additional proviso in section 3.2.5 which implies that distance codes
|
||||||
|
// 30 and 31 should never occur in compressed data.
|
||||||
|
maxNumLit = 286
|
||||||
|
maxNumDist = 30
|
||||||
|
numCodes = 19 // number of codes in Huffman meta-code
|
||||||
|
|
||||||
|
debugDecode = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value of length - 3 and extra bits.
|
||||||
|
type lengthExtra struct {
|
||||||
|
length, extra uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var decCodeToLen = [32]lengthExtra{{length: 0x0, extra: 0x0}, {length: 0x1, extra: 0x0}, {length: 0x2, extra: 0x0}, {length: 0x3, extra: 0x0}, {length: 0x4, extra: 0x0}, {length: 0x5, extra: 0x0}, {length: 0x6, extra: 0x0}, {length: 0x7, extra: 0x0}, {length: 0x8, extra: 0x1}, {length: 0xa, extra: 0x1}, {length: 0xc, extra: 0x1}, {length: 0xe, extra: 0x1}, {length: 0x10, extra: 0x2}, {length: 0x14, extra: 0x2}, {length: 0x18, extra: 0x2}, {length: 0x1c, extra: 0x2}, {length: 0x20, extra: 0x3}, {length: 0x28, extra: 0x3}, {length: 0x30, extra: 0x3}, {length: 0x38, extra: 0x3}, {length: 0x40, extra: 0x4}, {length: 0x50, extra: 0x4}, {length: 0x60, extra: 0x4}, {length: 0x70, extra: 0x4}, {length: 0x80, extra: 0x5}, {length: 0xa0, extra: 0x5}, {length: 0xc0, extra: 0x5}, {length: 0xe0, extra: 0x5}, {length: 0xff, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}}
|
||||||
|
|
||||||
|
var bitMask32 = [32]uint32{
|
||||||
|
0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF,
|
||||||
|
0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
|
||||||
|
0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF,
|
||||||
|
0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF,
|
||||||
|
} // up to 32 bits
|
||||||
|
|
||||||
|
// Initialize the fixedHuffmanDecoder only once upon first use.
|
||||||
|
var fixedOnce sync.Once
|
||||||
|
var fixedHuffmanDecoder huffmanDecoder
|
||||||
|
|
||||||
|
// A CorruptInputError reports the presence of corrupt input at a given offset.
|
||||||
|
type CorruptInputError = flate.CorruptInputError
|
||||||
|
|
||||||
|
// An InternalError reports an error in the flate code itself.
|
||||||
|
type InternalError string
|
||||||
|
|
||||||
|
func (e InternalError) Error() string { return "flate: internal error: " + string(e) }
|
||||||
|
|
||||||
|
// A ReadError reports an error encountered while reading input.
|
||||||
|
//
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
type ReadError = flate.ReadError
|
||||||
|
|
||||||
|
// A WriteError reports an error encountered while writing output.
|
||||||
|
//
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
type WriteError = flate.WriteError
|
||||||
|
|
||||||
|
// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to
|
||||||
|
// to switch to a new underlying Reader. This permits reusing a ReadCloser
|
||||||
|
// instead of allocating a new one.
|
||||||
|
type Resetter interface {
|
||||||
|
// Reset discards any buffered data and resets the Resetter as if it was
|
||||||
|
// newly initialized with the given reader.
|
||||||
|
Reset(r io.Reader, dict []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data structure for decoding Huffman tables is based on that of
|
||||||
|
// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits),
|
||||||
|
// For codes smaller than the table width, there are multiple entries
|
||||||
|
// (each combination of trailing bits has the same value). For codes
|
||||||
|
// larger than the table width, the table contains a link to an overflow
|
||||||
|
// table. The width of each entry in the link table is the maximum code
|
||||||
|
// size minus the chunk width.
|
||||||
|
//
|
||||||
|
// Note that you can do a lookup in the table even without all bits
|
||||||
|
// filled. Since the extra bits are zero, and the DEFLATE Huffman codes
|
||||||
|
// have the property that shorter codes come before longer ones, the
|
||||||
|
// bit length estimate in the result is a lower bound on the actual
|
||||||
|
// number of bits.
|
||||||
|
//
|
||||||
|
// See the following:
|
||||||
|
// http://www.gzip.org/algorithm.txt
|
||||||
|
|
||||||
|
// chunk & 15 is number of bits
|
||||||
|
// chunk >> 4 is value, including table link
|
||||||
|
|
||||||
|
const (
|
||||||
|
huffmanChunkBits = 9
|
||||||
|
huffmanNumChunks = 1 << huffmanChunkBits
|
||||||
|
huffmanCountMask = 15
|
||||||
|
huffmanValueShift = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type huffmanDecoder struct {
|
||||||
|
maxRead int // the maximum number of bits we can read and not overread
|
||||||
|
chunks *[huffmanNumChunks]uint16 // chunks as described above
|
||||||
|
links [][]uint16 // overflow links
|
||||||
|
linkMask uint32 // mask the width of the link table
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Huffman decoding tables from array of code lengths.
|
||||||
|
// Following this function, h is guaranteed to be initialized into a complete
|
||||||
|
// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a
|
||||||
|
// degenerate case where the tree has only a single symbol with length 1. Empty
|
||||||
|
// trees are permitted.
|
||||||
|
func (h *huffmanDecoder) init(lengths []int) bool {
|
||||||
|
// Sanity enables additional runtime tests during Huffman
|
||||||
|
// table construction. It's intended to be used during
|
||||||
|
// development to supplement the currently ad-hoc unit tests.
|
||||||
|
const sanity = false
|
||||||
|
|
||||||
|
if h.chunks == nil {
|
||||||
|
h.chunks = &[huffmanNumChunks]uint16{}
|
||||||
|
}
|
||||||
|
if h.maxRead != 0 {
|
||||||
|
*h = huffmanDecoder{chunks: h.chunks, links: h.links}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count number of codes of each length,
|
||||||
|
// compute maxRead and max length.
|
||||||
|
var count [maxCodeLen]int
|
||||||
|
var min, max int
|
||||||
|
for _, n := range lengths {
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if min == 0 || n < min {
|
||||||
|
min = n
|
||||||
|
}
|
||||||
|
if n > max {
|
||||||
|
max = n
|
||||||
|
}
|
||||||
|
count[n&maxCodeLenMask]++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty tree. The decompressor.huffSym function will fail later if the tree
|
||||||
|
// is used. Technically, an empty tree is only valid for the HDIST tree and
|
||||||
|
// not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree
|
||||||
|
// is guaranteed to fail since it will attempt to use the tree to decode the
|
||||||
|
// codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is
|
||||||
|
// guaranteed to fail later since the compressed data section must be
|
||||||
|
// composed of at least one symbol (the end-of-block marker).
|
||||||
|
if max == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
code := 0
|
||||||
|
var nextcode [maxCodeLen]int
|
||||||
|
for i := min; i <= max; i++ {
|
||||||
|
code <<= 1
|
||||||
|
nextcode[i&maxCodeLenMask] = code
|
||||||
|
code += count[i&maxCodeLenMask]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the coding is complete (i.e., that we've
|
||||||
|
// assigned all 2-to-the-max possible bit sequences).
|
||||||
|
// Exception: To be compatible with zlib, we also need to
|
||||||
|
// accept degenerate single-code codings. See also
|
||||||
|
// TestDegenerateHuffmanCoding.
|
||||||
|
if code != 1<<uint(max) && !(code == 1 && max == 1) {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("coding failed, code, max:", code, max, code == 1<<uint(max), code == 1 && max == 1, "(one should be true)")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
h.maxRead = min
|
||||||
|
chunks := h.chunks[:]
|
||||||
|
for i := range chunks {
|
||||||
|
chunks[i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if max > huffmanChunkBits {
|
||||||
|
numLinks := 1 << (uint(max) - huffmanChunkBits)
|
||||||
|
h.linkMask = uint32(numLinks - 1)
|
||||||
|
|
||||||
|
// create link tables
|
||||||
|
link := nextcode[huffmanChunkBits+1] >> 1
|
||||||
|
if cap(h.links) < huffmanNumChunks-link {
|
||||||
|
h.links = make([][]uint16, huffmanNumChunks-link)
|
||||||
|
} else {
|
||||||
|
h.links = h.links[:huffmanNumChunks-link]
|
||||||
|
}
|
||||||
|
for j := uint(link); j < huffmanNumChunks; j++ {
|
||||||
|
reverse := int(bits.Reverse16(uint16(j)))
|
||||||
|
reverse >>= uint(16 - huffmanChunkBits)
|
||||||
|
off := j - uint(link)
|
||||||
|
if sanity && h.chunks[reverse] != 0 {
|
||||||
|
panic("impossible: overwriting existing chunk")
|
||||||
|
}
|
||||||
|
h.chunks[reverse] = uint16(off<<huffmanValueShift | (huffmanChunkBits + 1))
|
||||||
|
if cap(h.links[off]) < numLinks {
|
||||||
|
h.links[off] = make([]uint16, numLinks)
|
||||||
|
} else {
|
||||||
|
links := h.links[off][:0]
|
||||||
|
h.links[off] = links[:numLinks]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.links = h.links[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := range lengths {
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
code := nextcode[n]
|
||||||
|
nextcode[n]++
|
||||||
|
chunk := uint16(i<<huffmanValueShift | n)
|
||||||
|
reverse := int(bits.Reverse16(uint16(code)))
|
||||||
|
reverse >>= uint(16 - n)
|
||||||
|
if n <= huffmanChunkBits {
|
||||||
|
for off := reverse; off < len(h.chunks); off += 1 << uint(n) {
|
||||||
|
// We should never need to overwrite
|
||||||
|
// an existing chunk. Also, 0 is
|
||||||
|
// never a valid chunk, because the
|
||||||
|
// lower 4 "count" bits should be
|
||||||
|
// between 1 and 15.
|
||||||
|
if sanity && h.chunks[off] != 0 {
|
||||||
|
panic("impossible: overwriting existing chunk")
|
||||||
|
}
|
||||||
|
h.chunks[off] = chunk
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
j := reverse & (huffmanNumChunks - 1)
|
||||||
|
if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 {
|
||||||
|
// Longer codes should have been
|
||||||
|
// associated with a link table above.
|
||||||
|
panic("impossible: not an indirect chunk")
|
||||||
|
}
|
||||||
|
value := h.chunks[j] >> huffmanValueShift
|
||||||
|
linktab := h.links[value]
|
||||||
|
reverse >>= huffmanChunkBits
|
||||||
|
for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) {
|
||||||
|
if sanity && linktab[off] != 0 {
|
||||||
|
panic("impossible: overwriting existing chunk")
|
||||||
|
}
|
||||||
|
linktab[off] = chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sanity {
|
||||||
|
// Above we've sanity checked that we never overwrote
|
||||||
|
// an existing entry. Here we additionally check that
|
||||||
|
// we filled the tables completely.
|
||||||
|
for i, chunk := range h.chunks {
|
||||||
|
if chunk == 0 {
|
||||||
|
// As an exception, in the degenerate
|
||||||
|
// single-code case, we allow odd
|
||||||
|
// chunks to be missing.
|
||||||
|
if code == 1 && i%2 == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
panic("impossible: missing chunk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, linktab := range h.links {
|
||||||
|
for _, chunk := range linktab {
|
||||||
|
if chunk == 0 {
|
||||||
|
panic("impossible: missing chunk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual read interface needed by NewReader.
|
||||||
|
// If the passed in io.Reader does not also have ReadByte,
|
||||||
|
// the NewReader will introduce its own buffering.
|
||||||
|
type Reader interface {
|
||||||
|
io.Reader
|
||||||
|
io.ByteReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress state.
|
||||||
|
type decompressor struct {
|
||||||
|
// Input source.
|
||||||
|
r Reader
|
||||||
|
roffset int64
|
||||||
|
|
||||||
|
// Huffman decoders for literal/length, distance.
|
||||||
|
h1, h2 huffmanDecoder
|
||||||
|
|
||||||
|
// Length arrays used to define Huffman codes.
|
||||||
|
bits *[maxNumLit + maxNumDist]int
|
||||||
|
codebits *[numCodes]int
|
||||||
|
|
||||||
|
// Output history, buffer.
|
||||||
|
dict dictDecoder
|
||||||
|
|
||||||
|
// Next step in the decompression,
|
||||||
|
// and decompression state.
|
||||||
|
step func(*decompressor)
|
||||||
|
stepState int
|
||||||
|
err error
|
||||||
|
toRead []byte
|
||||||
|
hl, hd *huffmanDecoder
|
||||||
|
copyLen int
|
||||||
|
copyDist int
|
||||||
|
|
||||||
|
// Temporary buffer (avoids repeated allocation).
|
||||||
|
buf [4]byte
|
||||||
|
|
||||||
|
// Input bits, in top of b.
|
||||||
|
b uint32
|
||||||
|
|
||||||
|
nb uint
|
||||||
|
final bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) nextBlock() {
|
||||||
|
for f.nb < 1+2 {
|
||||||
|
if f.err = f.moreBits(); f.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.final = f.b&1 == 1
|
||||||
|
f.b >>= 1
|
||||||
|
typ := f.b & 3
|
||||||
|
f.b >>= 2
|
||||||
|
f.nb -= 1 + 2
|
||||||
|
switch typ {
|
||||||
|
case 0:
|
||||||
|
f.dataBlock()
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("stored block")
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
// compressed, fixed Huffman tables
|
||||||
|
f.hl = &fixedHuffmanDecoder
|
||||||
|
f.hd = nil
|
||||||
|
f.huffmanBlockDecoder()()
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("predefinied huffman block")
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
// compressed, dynamic Huffman tables
|
||||||
|
if f.err = f.readHuffman(); f.err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f.hl = &f.h1
|
||||||
|
f.hd = &f.h2
|
||||||
|
f.huffmanBlockDecoder()()
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("dynamic huffman block")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 3 is reserved.
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("reserved data block encountered")
|
||||||
|
}
|
||||||
|
f.err = CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) Read(b []byte) (int, error) {
|
||||||
|
for {
|
||||||
|
if len(f.toRead) > 0 {
|
||||||
|
n := copy(b, f.toRead)
|
||||||
|
f.toRead = f.toRead[n:]
|
||||||
|
if len(f.toRead) == 0 {
|
||||||
|
return n, f.err
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if f.err != nil {
|
||||||
|
return 0, f.err
|
||||||
|
}
|
||||||
|
f.step(f)
|
||||||
|
if f.err != nil && len(f.toRead) == 0 {
|
||||||
|
f.toRead = f.dict.readFlush() // Flush what's left in case of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support the io.WriteTo interface for io.Copy and friends.
|
||||||
|
func (f *decompressor) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
total := int64(0)
|
||||||
|
flushed := false
|
||||||
|
for {
|
||||||
|
if len(f.toRead) > 0 {
|
||||||
|
n, err := w.Write(f.toRead)
|
||||||
|
total += int64(n)
|
||||||
|
if err != nil {
|
||||||
|
f.err = err
|
||||||
|
return total, err
|
||||||
|
}
|
||||||
|
if n != len(f.toRead) {
|
||||||
|
return total, io.ErrShortWrite
|
||||||
|
}
|
||||||
|
f.toRead = f.toRead[:0]
|
||||||
|
}
|
||||||
|
if f.err != nil && flushed {
|
||||||
|
if f.err == io.EOF {
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
return total, f.err
|
||||||
|
}
|
||||||
|
if f.err == nil {
|
||||||
|
f.step(f)
|
||||||
|
}
|
||||||
|
if len(f.toRead) == 0 && f.err != nil && !flushed {
|
||||||
|
f.toRead = f.dict.readFlush() // Flush what's left in case of error
|
||||||
|
flushed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) Close() error {
|
||||||
|
if f.err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 1951 section 3.2.7.
|
||||||
|
// Compression with dynamic Huffman codes
|
||||||
|
|
||||||
|
var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||||
|
|
||||||
|
func (f *decompressor) readHuffman() error {
|
||||||
|
// HLIT[5], HDIST[5], HCLEN[4].
|
||||||
|
for f.nb < 5+5+4 {
|
||||||
|
if err := f.moreBits(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nlit := int(f.b&0x1F) + 257
|
||||||
|
if nlit > maxNumLit {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("nlit > maxNumLit", nlit)
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
f.b >>= 5
|
||||||
|
ndist := int(f.b&0x1F) + 1
|
||||||
|
if ndist > maxNumDist {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("ndist > maxNumDist", ndist)
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
f.b >>= 5
|
||||||
|
nclen := int(f.b&0xF) + 4
|
||||||
|
// numCodes is 19, so nclen is always valid.
|
||||||
|
f.b >>= 4
|
||||||
|
f.nb -= 5 + 5 + 4
|
||||||
|
|
||||||
|
// (HCLEN+4)*3 bits: code lengths in the magic codeOrder order.
|
||||||
|
for i := 0; i < nclen; i++ {
|
||||||
|
for f.nb < 3 {
|
||||||
|
if err := f.moreBits(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.codebits[codeOrder[i]] = int(f.b & 0x7)
|
||||||
|
f.b >>= 3
|
||||||
|
f.nb -= 3
|
||||||
|
}
|
||||||
|
for i := nclen; i < len(codeOrder); i++ {
|
||||||
|
f.codebits[codeOrder[i]] = 0
|
||||||
|
}
|
||||||
|
if !f.h1.init(f.codebits[0:]) {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("init codebits failed")
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HLIT + 257 code lengths, HDIST + 1 code lengths,
|
||||||
|
// using the code length Huffman code.
|
||||||
|
for i, n := 0, nlit+ndist; i < n; {
|
||||||
|
x, err := f.huffSym(&f.h1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if x < 16 {
|
||||||
|
// Actual length.
|
||||||
|
f.bits[i] = x
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Repeat previous length or zero.
|
||||||
|
var rep int
|
||||||
|
var nb uint
|
||||||
|
var b int
|
||||||
|
switch x {
|
||||||
|
default:
|
||||||
|
return InternalError("unexpected length code")
|
||||||
|
case 16:
|
||||||
|
rep = 3
|
||||||
|
nb = 2
|
||||||
|
if i == 0 {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("i==0")
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
b = f.bits[i-1]
|
||||||
|
case 17:
|
||||||
|
rep = 3
|
||||||
|
nb = 3
|
||||||
|
b = 0
|
||||||
|
case 18:
|
||||||
|
rep = 11
|
||||||
|
nb = 7
|
||||||
|
b = 0
|
||||||
|
}
|
||||||
|
for f.nb < nb {
|
||||||
|
if err := f.moreBits(); err != nil {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("morebits:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rep += int(f.b & uint32(1<<(nb®SizeMaskUint32)-1))
|
||||||
|
f.b >>= nb & regSizeMaskUint32
|
||||||
|
f.nb -= nb
|
||||||
|
if i+rep > n {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("i+rep > n", i, rep, n)
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
for j := 0; j < rep; j++ {
|
||||||
|
f.bits[i] = b
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("init2 failed")
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// As an optimization, we can initialize the maxRead bits to read at a time
|
||||||
|
// for the HLIT tree to the length of the EOB marker since we know that
|
||||||
|
// every block must terminate with one. This preserves the property that
|
||||||
|
// we never read any extra bytes after the end of the DEFLATE stream.
|
||||||
|
if f.h1.maxRead < f.bits[endBlockMarker] {
|
||||||
|
f.h1.maxRead = f.bits[endBlockMarker]
|
||||||
|
}
|
||||||
|
if !f.final {
|
||||||
|
// If not the final block, the smallest block possible is
|
||||||
|
// a predefined table, BTYPE=01, with a single EOB marker.
|
||||||
|
// This will take up 3 + 7 bits.
|
||||||
|
f.h1.maxRead += 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a single uncompressed data block from input to output.
|
||||||
|
func (f *decompressor) dataBlock() {
|
||||||
|
// Uncompressed.
|
||||||
|
// Discard current half-byte.
|
||||||
|
left := (f.nb) & 7
|
||||||
|
f.nb -= left
|
||||||
|
f.b >>= left
|
||||||
|
|
||||||
|
offBytes := f.nb >> 3
|
||||||
|
// Unfilled values will be overwritten.
|
||||||
|
f.buf[0] = uint8(f.b)
|
||||||
|
f.buf[1] = uint8(f.b >> 8)
|
||||||
|
f.buf[2] = uint8(f.b >> 16)
|
||||||
|
f.buf[3] = uint8(f.b >> 24)
|
||||||
|
|
||||||
|
f.roffset += int64(offBytes)
|
||||||
|
f.nb, f.b = 0, 0
|
||||||
|
|
||||||
|
// Length then ones-complement of length.
|
||||||
|
nr, err := io.ReadFull(f.r, f.buf[offBytes:4])
|
||||||
|
f.roffset += int64(nr)
|
||||||
|
if err != nil {
|
||||||
|
f.err = noEOF(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n := uint16(f.buf[0]) | uint16(f.buf[1])<<8
|
||||||
|
nn := uint16(f.buf[2]) | uint16(f.buf[3])<<8
|
||||||
|
if nn != ^n {
|
||||||
|
if debugDecode {
|
||||||
|
ncomp := ^n
|
||||||
|
fmt.Println("uint16(nn) != uint16(^n)", nn, ncomp)
|
||||||
|
}
|
||||||
|
f.err = CorruptInputError(f.roffset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
f.toRead = f.dict.readFlush()
|
||||||
|
f.finishBlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.copyLen = int(n)
|
||||||
|
f.copyData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyData copies f.copyLen bytes from the underlying reader into f.hist.
|
||||||
|
// It pauses for reads when f.hist is full.
|
||||||
|
func (f *decompressor) copyData() {
|
||||||
|
buf := f.dict.writeSlice()
|
||||||
|
if len(buf) > f.copyLen {
|
||||||
|
buf = buf[:f.copyLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt, err := io.ReadFull(f.r, buf)
|
||||||
|
f.roffset += int64(cnt)
|
||||||
|
f.copyLen -= cnt
|
||||||
|
f.dict.writeMark(cnt)
|
||||||
|
if err != nil {
|
||||||
|
f.err = noEOF(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.dict.availWrite() == 0 || f.copyLen > 0 {
|
||||||
|
f.toRead = f.dict.readFlush()
|
||||||
|
f.step = (*decompressor).copyData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.finishBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) finishBlock() {
|
||||||
|
if f.final {
|
||||||
|
if f.dict.availRead() > 0 {
|
||||||
|
f.toRead = f.dict.readFlush()
|
||||||
|
}
|
||||||
|
f.err = io.EOF
|
||||||
|
}
|
||||||
|
f.step = (*decompressor).nextBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// noEOF returns err, unless err == io.EOF, in which case it returns io.ErrUnexpectedEOF.
|
||||||
|
func noEOF(e error) error {
|
||||||
|
if e == io.EOF {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) moreBits() error {
|
||||||
|
c, err := f.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return noEOF(err)
|
||||||
|
}
|
||||||
|
f.roffset++
|
||||||
|
f.b |= uint32(c) << (f.nb & regSizeMaskUint32)
|
||||||
|
f.nb += 8
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the next Huffman-encoded symbol from f according to h.
|
||||||
|
func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
|
||||||
|
// Since a huffmanDecoder can be empty or be composed of a degenerate tree
|
||||||
|
// with single element, huffSym must error on these two edge cases. In both
|
||||||
|
// cases, the chunks slice will be 0 for the invalid sequence, leading it
|
||||||
|
// satisfy the n == 0 check below.
|
||||||
|
n := uint(h.maxRead)
|
||||||
|
// Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers,
|
||||||
|
// but is smart enough to keep local variables in registers, so use nb and b,
|
||||||
|
// inline call to moreBits and reassign b,nb back to f on return.
|
||||||
|
nb, b := f.nb, f.b
|
||||||
|
for {
|
||||||
|
for nb < n {
|
||||||
|
c, err := f.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
f.b = b
|
||||||
|
f.nb = nb
|
||||||
|
return 0, noEOF(err)
|
||||||
|
}
|
||||||
|
f.roffset++
|
||||||
|
b |= uint32(c) << (nb & regSizeMaskUint32)
|
||||||
|
nb += 8
|
||||||
|
}
|
||||||
|
chunk := h.chunks[b&(huffmanNumChunks-1)]
|
||||||
|
n = uint(chunk & huffmanCountMask)
|
||||||
|
if n > huffmanChunkBits {
|
||||||
|
chunk = h.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&h.linkMask]
|
||||||
|
n = uint(chunk & huffmanCountMask)
|
||||||
|
}
|
||||||
|
if n <= nb {
|
||||||
|
if n == 0 {
|
||||||
|
f.b = b
|
||||||
|
f.nb = nb
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("huffsym: n==0")
|
||||||
|
}
|
||||||
|
f.err = CorruptInputError(f.roffset)
|
||||||
|
return 0, f.err
|
||||||
|
}
|
||||||
|
f.b = b >> (n & regSizeMaskUint32)
|
||||||
|
f.nb = nb - n
|
||||||
|
return int(chunk >> huffmanValueShift), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeReader(r io.Reader) Reader {
|
||||||
|
if rr, ok := r.(Reader); ok {
|
||||||
|
return rr
|
||||||
|
}
|
||||||
|
return bufio.NewReader(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixedHuffmanDecoderInit() {
|
||||||
|
fixedOnce.Do(func() {
|
||||||
|
// These come from the RFC section 3.2.6.
|
||||||
|
var bits [288]int
|
||||||
|
for i := 0; i < 144; i++ {
|
||||||
|
bits[i] = 8
|
||||||
|
}
|
||||||
|
for i := 144; i < 256; i++ {
|
||||||
|
bits[i] = 9
|
||||||
|
}
|
||||||
|
for i := 256; i < 280; i++ {
|
||||||
|
bits[i] = 7
|
||||||
|
}
|
||||||
|
for i := 280; i < 288; i++ {
|
||||||
|
bits[i] = 8
|
||||||
|
}
|
||||||
|
fixedHuffmanDecoder.init(bits[:])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) Reset(r io.Reader, dict []byte) error {
|
||||||
|
*f = decompressor{
|
||||||
|
r: makeReader(r),
|
||||||
|
bits: f.bits,
|
||||||
|
codebits: f.codebits,
|
||||||
|
h1: f.h1,
|
||||||
|
h2: f.h2,
|
||||||
|
dict: f.dict,
|
||||||
|
step: (*decompressor).nextBlock,
|
||||||
|
}
|
||||||
|
f.dict.init(maxMatchOffset, dict)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader returns a new ReadCloser that can be used
|
||||||
|
// to read the uncompressed version of r.
|
||||||
|
// If r does not also implement io.ByteReader,
|
||||||
|
// the decompressor may read more data than necessary from r.
|
||||||
|
// It is the caller's responsibility to call Close on the ReadCloser
|
||||||
|
// when finished reading.
|
||||||
|
//
|
||||||
|
// The ReadCloser returned by NewReader also implements Resetter.
|
||||||
|
func NewReader(r io.Reader) io.ReadCloser {
|
||||||
|
fixedHuffmanDecoderInit()
|
||||||
|
|
||||||
|
var f decompressor
|
||||||
|
f.r = makeReader(r)
|
||||||
|
f.bits = new([maxNumLit + maxNumDist]int)
|
||||||
|
f.codebits = new([numCodes]int)
|
||||||
|
f.step = (*decompressor).nextBlock
|
||||||
|
f.dict.init(maxMatchOffset, nil)
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReaderDict is like NewReader but initializes the reader
|
||||||
|
// with a preset dictionary. The returned Reader behaves as if
|
||||||
|
// the uncompressed data stream started with the given dictionary,
|
||||||
|
// which has already been read. NewReaderDict is typically used
|
||||||
|
// to read data compressed by NewWriterDict.
|
||||||
|
//
|
||||||
|
// The ReadCloser returned by NewReader also implements Resetter.
|
||||||
|
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
|
||||||
|
fixedHuffmanDecoderInit()
|
||||||
|
|
||||||
|
var f decompressor
|
||||||
|
f.r = makeReader(r)
|
||||||
|
f.bits = new([maxNumLit + maxNumDist]int)
|
||||||
|
f.codebits = new([numCodes]int)
|
||||||
|
f.step = (*decompressor).nextBlock
|
||||||
|
f.dict.init(maxMatchOffset, dict)
|
||||||
|
return &f
|
||||||
|
}
|
1283
vendor/github.com/klauspost/compress/flate/inflate_gen.go
generated
vendored
Normal file
1283
vendor/github.com/klauspost/compress/flate/inflate_gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
241
vendor/github.com/klauspost/compress/flate/level1.go
generated
vendored
Normal file
241
vendor/github.com/klauspost/compress/flate/level1.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fastGen maintains the table for matches,
|
||||||
|
// and the previous byte block for level 2.
|
||||||
|
// This is the generic implementation.
|
||||||
|
type fastEncL1 struct {
|
||||||
|
fastGen
|
||||||
|
table [tableSize]tableEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeL1 uses a similar algorithm to level 1
|
||||||
|
func (e *fastEncL1) Encode(dst *tokens, src []byte) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
hashBytes = 5
|
||||||
|
)
|
||||||
|
if debugDeflate && e.cur < 0 {
|
||||||
|
panic(fmt.Sprint("e.cur < 0: ", e.cur))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect against e.cur wraparound.
|
||||||
|
for e.cur >= bufferReset {
|
||||||
|
if len(e.hist) == 0 {
|
||||||
|
for i := range e.table[:] {
|
||||||
|
e.table[i] = tableEntry{}
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Shift down everything in the table that isn't already too far away.
|
||||||
|
minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
|
||||||
|
for i := range e.table[:] {
|
||||||
|
v := e.table[i].offset
|
||||||
|
if v <= minOff {
|
||||||
|
v = 0
|
||||||
|
} else {
|
||||||
|
v = v - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.table[i].offset = v
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
s := e.addBlock(src)
|
||||||
|
|
||||||
|
// This check isn't in the Snappy implementation, but there, the caller
|
||||||
|
// instead of the callee handles this case.
|
||||||
|
if len(src) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = uint16(len(src))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override src
|
||||||
|
src = e.hist
|
||||||
|
nextEmit := s
|
||||||
|
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int32(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load6432(src, s)
|
||||||
|
|
||||||
|
for {
|
||||||
|
const skipLog = 5
|
||||||
|
const doEvery = 2
|
||||||
|
|
||||||
|
nextS := s
|
||||||
|
var candidate tableEntry
|
||||||
|
for {
|
||||||
|
nextHash := hashLen(cv, tableBits, hashBytes)
|
||||||
|
candidate = e.table[nextHash]
|
||||||
|
nextS = s + doEvery + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
now := load6432(src, nextS)
|
||||||
|
e.table[nextHash] = tableEntry{offset: s + e.cur}
|
||||||
|
nextHash = hashLen(now, tableBits, hashBytes)
|
||||||
|
|
||||||
|
offset := s - (candidate.offset - e.cur)
|
||||||
|
if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
e.table[nextHash] = tableEntry{offset: nextS + e.cur}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do one right away...
|
||||||
|
cv = now
|
||||||
|
s = nextS
|
||||||
|
nextS++
|
||||||
|
candidate = e.table[nextHash]
|
||||||
|
now >>= 8
|
||||||
|
e.table[nextHash] = tableEntry{offset: s + e.cur}
|
||||||
|
|
||||||
|
offset = s - (candidate.offset - e.cur)
|
||||||
|
if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
e.table[nextHash] = tableEntry{offset: nextS + e.cur}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cv = now
|
||||||
|
s = nextS
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||||
|
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||||
|
// them as literal bytes.
|
||||||
|
for {
|
||||||
|
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||||
|
// literal bytes prior to s.
|
||||||
|
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
t := candidate.offset - e.cur
|
||||||
|
var l = int32(4)
|
||||||
|
if false {
|
||||||
|
l = e.matchlenLong(s+4, t+4, src) + 4
|
||||||
|
} else {
|
||||||
|
// inlined:
|
||||||
|
a := src[s+4:]
|
||||||
|
b := src[t+4:]
|
||||||
|
for len(a) >= 8 {
|
||||||
|
if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 {
|
||||||
|
l += int32(bits.TrailingZeros64(diff) >> 3)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l += 8
|
||||||
|
a = a[8:]
|
||||||
|
b = b[8:]
|
||||||
|
}
|
||||||
|
if len(a) < 8 {
|
||||||
|
b = b[:len(a)]
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the match found
|
||||||
|
if false {
|
||||||
|
dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
|
||||||
|
} else {
|
||||||
|
// Inlined...
|
||||||
|
xoffset := uint32(s - t - baseMatchOffset)
|
||||||
|
xlength := l
|
||||||
|
oc := offsetCode(xoffset)
|
||||||
|
xoffset |= oc << 16
|
||||||
|
for xlength > 0 {
|
||||||
|
xl := xlength
|
||||||
|
if xl > 258 {
|
||||||
|
if xl > 258+baseMatchLength {
|
||||||
|
xl = 258
|
||||||
|
} else {
|
||||||
|
xl = 258 - baseMatchLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlength -= xl
|
||||||
|
xl -= baseMatchLength
|
||||||
|
dst.extraHist[lengthCodes1[uint8(xl)]]++
|
||||||
|
dst.offHist[oc]++
|
||||||
|
dst.tokens[dst.n] = token(matchType | uint32(xl)<<lengthShift | xoffset)
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
if s >= sLimit {
|
||||||
|
// Index first pair after match end.
|
||||||
|
if int(s+l+8) < len(src) {
|
||||||
|
cv := load6432(src, s)
|
||||||
|
e.table[hashLen(cv, tableBits, hashBytes)] = tableEntry{offset: s + e.cur}
|
||||||
|
}
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-2 and at s. If
|
||||||
|
// another emitCopy is not our next move, also calculate nextHash
|
||||||
|
// at s+1. At least on GOARCH=amd64, these three hash calculations
|
||||||
|
// are faster as one load64 call (with some shifts) instead of
|
||||||
|
// three load32 calls.
|
||||||
|
x := load6432(src, s-2)
|
||||||
|
o := e.cur + s - 2
|
||||||
|
prevHash := hashLen(x, tableBits, hashBytes)
|
||||||
|
e.table[prevHash] = tableEntry{offset: o}
|
||||||
|
x >>= 16
|
||||||
|
currHash := hashLen(x, tableBits, hashBytes)
|
||||||
|
candidate = e.table[currHash]
|
||||||
|
e.table[currHash] = tableEntry{offset: o + 2}
|
||||||
|
|
||||||
|
offset := s - (candidate.offset - e.cur)
|
||||||
|
if offset > maxMatchOffset || uint32(x) != load3232(src, candidate.offset-e.cur) {
|
||||||
|
cv = x >> 8
|
||||||
|
s++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
214
vendor/github.com/klauspost/compress/flate/level2.go
generated
vendored
Normal file
214
vendor/github.com/klauspost/compress/flate/level2.go
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// fastGen maintains the table for matches,
|
||||||
|
// and the previous byte block for level 2.
|
||||||
|
// This is the generic implementation.
|
||||||
|
type fastEncL2 struct {
|
||||||
|
fastGen
|
||||||
|
table [bTableSize]tableEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeL2 uses a similar algorithm to level 1, but is capable
|
||||||
|
// of matching across blocks giving better compression at a small slowdown.
|
||||||
|
func (e *fastEncL2) Encode(dst *tokens, src []byte) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
hashBytes = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
if debugDeflate && e.cur < 0 {
|
||||||
|
panic(fmt.Sprint("e.cur < 0: ", e.cur))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect against e.cur wraparound.
|
||||||
|
for e.cur >= bufferReset {
|
||||||
|
if len(e.hist) == 0 {
|
||||||
|
for i := range e.table[:] {
|
||||||
|
e.table[i] = tableEntry{}
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Shift down everything in the table that isn't already too far away.
|
||||||
|
minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
|
||||||
|
for i := range e.table[:] {
|
||||||
|
v := e.table[i].offset
|
||||||
|
if v <= minOff {
|
||||||
|
v = 0
|
||||||
|
} else {
|
||||||
|
v = v - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.table[i].offset = v
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
s := e.addBlock(src)
|
||||||
|
|
||||||
|
// This check isn't in the Snappy implementation, but there, the caller
|
||||||
|
// instead of the callee handles this case.
|
||||||
|
if len(src) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = uint16(len(src))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override src
|
||||||
|
src = e.hist
|
||||||
|
nextEmit := s
|
||||||
|
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int32(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load6432(src, s)
|
||||||
|
for {
|
||||||
|
// When should we start skipping if we haven't found matches in a long while.
|
||||||
|
const skipLog = 5
|
||||||
|
const doEvery = 2
|
||||||
|
|
||||||
|
nextS := s
|
||||||
|
var candidate tableEntry
|
||||||
|
for {
|
||||||
|
nextHash := hashLen(cv, bTableBits, hashBytes)
|
||||||
|
s = nextS
|
||||||
|
nextS = s + doEvery + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
candidate = e.table[nextHash]
|
||||||
|
now := load6432(src, nextS)
|
||||||
|
e.table[nextHash] = tableEntry{offset: s + e.cur}
|
||||||
|
nextHash = hashLen(now, bTableBits, hashBytes)
|
||||||
|
|
||||||
|
offset := s - (candidate.offset - e.cur)
|
||||||
|
if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
e.table[nextHash] = tableEntry{offset: nextS + e.cur}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do one right away...
|
||||||
|
cv = now
|
||||||
|
s = nextS
|
||||||
|
nextS++
|
||||||
|
candidate = e.table[nextHash]
|
||||||
|
now >>= 8
|
||||||
|
e.table[nextHash] = tableEntry{offset: s + e.cur}
|
||||||
|
|
||||||
|
offset = s - (candidate.offset - e.cur)
|
||||||
|
if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cv = now
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||||
|
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||||
|
// them as literal bytes.
|
||||||
|
|
||||||
|
// Call emitCopy, and then see if another emitCopy could be our next
|
||||||
|
// move. Repeat until we find no match for the input immediately after
|
||||||
|
// what was consumed by the last emitCopy call.
|
||||||
|
//
|
||||||
|
// If we exit this loop normally then we need to call emitLiteral next,
|
||||||
|
// though we don't yet know how big the literal will be. We handle that
|
||||||
|
// by proceeding to the next iteration of the main loop. We also can
|
||||||
|
// exit this loop via goto if we get close to exhausting the input.
|
||||||
|
for {
|
||||||
|
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||||
|
// literal bytes prior to s.
|
||||||
|
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
t := candidate.offset - e.cur
|
||||||
|
l := e.matchlenLong(s+4, t+4, src) + 4
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s >= sLimit {
|
||||||
|
// Index first pair after match end.
|
||||||
|
if int(s+l+8) < len(src) {
|
||||||
|
cv := load6432(src, s)
|
||||||
|
e.table[hashLen(cv, bTableBits, hashBytes)] = tableEntry{offset: s + e.cur}
|
||||||
|
}
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store every second hash in-between, but offset by 1.
|
||||||
|
for i := s - l + 2; i < s-5; i += 7 {
|
||||||
|
x := load6432(src, i)
|
||||||
|
nextHash := hashLen(x, bTableBits, hashBytes)
|
||||||
|
e.table[nextHash] = tableEntry{offset: e.cur + i}
|
||||||
|
// Skip one
|
||||||
|
x >>= 16
|
||||||
|
nextHash = hashLen(x, bTableBits, hashBytes)
|
||||||
|
e.table[nextHash] = tableEntry{offset: e.cur + i + 2}
|
||||||
|
// Skip one
|
||||||
|
x >>= 16
|
||||||
|
nextHash = hashLen(x, bTableBits, hashBytes)
|
||||||
|
e.table[nextHash] = tableEntry{offset: e.cur + i + 4}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-2 to s. If
|
||||||
|
// another emitCopy is not our next move, also calculate nextHash
|
||||||
|
// at s+1. At least on GOARCH=amd64, these three hash calculations
|
||||||
|
// are faster as one load64 call (with some shifts) instead of
|
||||||
|
// three load32 calls.
|
||||||
|
x := load6432(src, s-2)
|
||||||
|
o := e.cur + s - 2
|
||||||
|
prevHash := hashLen(x, bTableBits, hashBytes)
|
||||||
|
prevHash2 := hashLen(x>>8, bTableBits, hashBytes)
|
||||||
|
e.table[prevHash] = tableEntry{offset: o}
|
||||||
|
e.table[prevHash2] = tableEntry{offset: o + 1}
|
||||||
|
currHash := hashLen(x>>16, bTableBits, hashBytes)
|
||||||
|
candidate = e.table[currHash]
|
||||||
|
e.table[currHash] = tableEntry{offset: o + 2}
|
||||||
|
|
||||||
|
offset := s - (candidate.offset - e.cur)
|
||||||
|
if offset > maxMatchOffset || uint32(x>>16) != load3232(src, candidate.offset-e.cur) {
|
||||||
|
cv = x >> 24
|
||||||
|
s++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
241
vendor/github.com/klauspost/compress/flate/level3.go
generated
vendored
Normal file
241
vendor/github.com/klauspost/compress/flate/level3.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// fastEncL3
|
||||||
|
type fastEncL3 struct {
|
||||||
|
fastGen
|
||||||
|
table [1 << 16]tableEntryPrev
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode uses a similar algorithm to level 2, will check up to two candidates.
|
||||||
|
func (e *fastEncL3) Encode(dst *tokens, src []byte) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
tableBits = 16
|
||||||
|
tableSize = 1 << tableBits
|
||||||
|
hashBytes = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
if debugDeflate && e.cur < 0 {
|
||||||
|
panic(fmt.Sprint("e.cur < 0: ", e.cur))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect against e.cur wraparound.
|
||||||
|
for e.cur >= bufferReset {
|
||||||
|
if len(e.hist) == 0 {
|
||||||
|
for i := range e.table[:] {
|
||||||
|
e.table[i] = tableEntryPrev{}
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Shift down everything in the table that isn't already too far away.
|
||||||
|
minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
|
||||||
|
for i := range e.table[:] {
|
||||||
|
v := e.table[i]
|
||||||
|
if v.Cur.offset <= minOff {
|
||||||
|
v.Cur.offset = 0
|
||||||
|
} else {
|
||||||
|
v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
if v.Prev.offset <= minOff {
|
||||||
|
v.Prev.offset = 0
|
||||||
|
} else {
|
||||||
|
v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.table[i] = v
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
s := e.addBlock(src)
|
||||||
|
|
||||||
|
// Skip if too small.
|
||||||
|
if len(src) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = uint16(len(src))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override src
|
||||||
|
src = e.hist
|
||||||
|
nextEmit := s
|
||||||
|
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int32(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load6432(src, s)
|
||||||
|
for {
|
||||||
|
const skipLog = 7
|
||||||
|
nextS := s
|
||||||
|
var candidate tableEntry
|
||||||
|
for {
|
||||||
|
nextHash := hashLen(cv, tableBits, hashBytes)
|
||||||
|
s = nextS
|
||||||
|
nextS = s + 1 + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
candidates := e.table[nextHash]
|
||||||
|
now := load6432(src, nextS)
|
||||||
|
|
||||||
|
// Safe offset distance until s + 4...
|
||||||
|
minOffset := e.cur + s - (maxMatchOffset - 4)
|
||||||
|
e.table[nextHash] = tableEntryPrev{Prev: candidates.Cur, Cur: tableEntry{offset: s + e.cur}}
|
||||||
|
|
||||||
|
// Check both candidates
|
||||||
|
candidate = candidates.Cur
|
||||||
|
if candidate.offset < minOffset {
|
||||||
|
cv = now
|
||||||
|
// Previous will also be invalid, we have nothing.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
if candidates.Prev.offset < minOffset || uint32(cv) != load3232(src, candidates.Prev.offset-e.cur) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Both match and are valid, pick longest.
|
||||||
|
offset := s - (candidate.offset - e.cur)
|
||||||
|
o2 := s - (candidates.Prev.offset - e.cur)
|
||||||
|
l1, l2 := matchLen(src[s+4:], src[s-offset+4:]), matchLen(src[s+4:], src[s-o2+4:])
|
||||||
|
if l2 > l1 {
|
||||||
|
candidate = candidates.Prev
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
// We only check if value mismatches.
|
||||||
|
// Offset will always be invalid in other cases.
|
||||||
|
candidate = candidates.Prev
|
||||||
|
if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cv = now
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call emitCopy, and then see if another emitCopy could be our next
|
||||||
|
// move. Repeat until we find no match for the input immediately after
|
||||||
|
// what was consumed by the last emitCopy call.
|
||||||
|
//
|
||||||
|
// If we exit this loop normally then we need to call emitLiteral next,
|
||||||
|
// though we don't yet know how big the literal will be. We handle that
|
||||||
|
// by proceeding to the next iteration of the main loop. We also can
|
||||||
|
// exit this loop via goto if we get close to exhausting the input.
|
||||||
|
for {
|
||||||
|
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||||
|
// literal bytes prior to s.
|
||||||
|
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
//
|
||||||
|
t := candidate.offset - e.cur
|
||||||
|
l := e.matchlenLong(s+4, t+4, src) + 4
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s >= sLimit {
|
||||||
|
t += l
|
||||||
|
// Index first pair after match end.
|
||||||
|
if int(t+8) < len(src) && t > 0 {
|
||||||
|
cv = load6432(src, t)
|
||||||
|
nextHash := hashLen(cv, tableBits, hashBytes)
|
||||||
|
e.table[nextHash] = tableEntryPrev{
|
||||||
|
Prev: e.table[nextHash].Cur,
|
||||||
|
Cur: tableEntry{offset: e.cur + t},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store every 5th hash in-between.
|
||||||
|
for i := s - l + 2; i < s-5; i += 6 {
|
||||||
|
nextHash := hashLen(load6432(src, i), tableBits, hashBytes)
|
||||||
|
e.table[nextHash] = tableEntryPrev{
|
||||||
|
Prev: e.table[nextHash].Cur,
|
||||||
|
Cur: tableEntry{offset: e.cur + i}}
|
||||||
|
}
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-2 to s.
|
||||||
|
x := load6432(src, s-2)
|
||||||
|
prevHash := hashLen(x, tableBits, hashBytes)
|
||||||
|
|
||||||
|
e.table[prevHash] = tableEntryPrev{
|
||||||
|
Prev: e.table[prevHash].Cur,
|
||||||
|
Cur: tableEntry{offset: e.cur + s - 2},
|
||||||
|
}
|
||||||
|
x >>= 8
|
||||||
|
prevHash = hashLen(x, tableBits, hashBytes)
|
||||||
|
|
||||||
|
e.table[prevHash] = tableEntryPrev{
|
||||||
|
Prev: e.table[prevHash].Cur,
|
||||||
|
Cur: tableEntry{offset: e.cur + s - 1},
|
||||||
|
}
|
||||||
|
x >>= 8
|
||||||
|
currHash := hashLen(x, tableBits, hashBytes)
|
||||||
|
candidates := e.table[currHash]
|
||||||
|
cv = x
|
||||||
|
e.table[currHash] = tableEntryPrev{
|
||||||
|
Prev: candidates.Cur,
|
||||||
|
Cur: tableEntry{offset: s + e.cur},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check both candidates
|
||||||
|
candidate = candidates.Cur
|
||||||
|
minOffset := e.cur + s - (maxMatchOffset - 4)
|
||||||
|
|
||||||
|
if candidate.offset > minOffset {
|
||||||
|
if uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
// Found a match...
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
candidate = candidates.Prev
|
||||||
|
if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
|
||||||
|
// Match at prev...
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cv = x >> 8
|
||||||
|
s++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
221
vendor/github.com/klauspost/compress/flate/level4.go
generated
vendored
Normal file
221
vendor/github.com/klauspost/compress/flate/level4.go
generated
vendored
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type fastEncL4 struct {
|
||||||
|
fastGen
|
||||||
|
table [tableSize]tableEntry
|
||||||
|
bTable [tableSize]tableEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fastEncL4) Encode(dst *tokens, src []byte) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
hashShortBytes = 4
|
||||||
|
)
|
||||||
|
if debugDeflate && e.cur < 0 {
|
||||||
|
panic(fmt.Sprint("e.cur < 0: ", e.cur))
|
||||||
|
}
|
||||||
|
// Protect against e.cur wraparound.
|
||||||
|
for e.cur >= bufferReset {
|
||||||
|
if len(e.hist) == 0 {
|
||||||
|
for i := range e.table[:] {
|
||||||
|
e.table[i] = tableEntry{}
|
||||||
|
}
|
||||||
|
for i := range e.bTable[:] {
|
||||||
|
e.bTable[i] = tableEntry{}
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Shift down everything in the table that isn't already too far away.
|
||||||
|
minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
|
||||||
|
for i := range e.table[:] {
|
||||||
|
v := e.table[i].offset
|
||||||
|
if v <= minOff {
|
||||||
|
v = 0
|
||||||
|
} else {
|
||||||
|
v = v - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.table[i].offset = v
|
||||||
|
}
|
||||||
|
for i := range e.bTable[:] {
|
||||||
|
v := e.bTable[i].offset
|
||||||
|
if v <= minOff {
|
||||||
|
v = 0
|
||||||
|
} else {
|
||||||
|
v = v - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.bTable[i].offset = v
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
s := e.addBlock(src)
|
||||||
|
|
||||||
|
// This check isn't in the Snappy implementation, but there, the caller
|
||||||
|
// instead of the callee handles this case.
|
||||||
|
if len(src) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = uint16(len(src))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override src
|
||||||
|
src = e.hist
|
||||||
|
nextEmit := s
|
||||||
|
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int32(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load6432(src, s)
|
||||||
|
for {
|
||||||
|
const skipLog = 6
|
||||||
|
const doEvery = 1
|
||||||
|
|
||||||
|
nextS := s
|
||||||
|
var t int32
|
||||||
|
for {
|
||||||
|
nextHashS := hashLen(cv, tableBits, hashShortBytes)
|
||||||
|
nextHashL := hash7(cv, tableBits)
|
||||||
|
|
||||||
|
s = nextS
|
||||||
|
nextS = s + doEvery + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
// Fetch a short+long candidate
|
||||||
|
sCandidate := e.table[nextHashS]
|
||||||
|
lCandidate := e.bTable[nextHashL]
|
||||||
|
next := load6432(src, nextS)
|
||||||
|
entry := tableEntry{offset: s + e.cur}
|
||||||
|
e.table[nextHashS] = entry
|
||||||
|
e.bTable[nextHashL] = entry
|
||||||
|
|
||||||
|
t = lCandidate.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.offset-e.cur) {
|
||||||
|
// We got a long match. Use that.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t = sCandidate.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) {
|
||||||
|
// Found a 4 match...
|
||||||
|
lCandidate = e.bTable[hash7(next, tableBits)]
|
||||||
|
|
||||||
|
// If the next long is a candidate, check if we should use that instead...
|
||||||
|
lOff := nextS - (lCandidate.offset - e.cur)
|
||||||
|
if lOff < maxMatchOffset && load3232(src, lCandidate.offset-e.cur) == uint32(next) {
|
||||||
|
l1, l2 := matchLen(src[s+4:], src[t+4:]), matchLen(src[nextS+4:], src[nextS-lOff+4:])
|
||||||
|
if l2 > l1 {
|
||||||
|
s = nextS
|
||||||
|
t = lCandidate.offset - e.cur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cv = next
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||||
|
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||||
|
// them as literal bytes.
|
||||||
|
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
l := e.matchlenLong(s+4, t+4, src) + 4
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if debugDeflate {
|
||||||
|
if t >= s {
|
||||||
|
panic("s-t")
|
||||||
|
}
|
||||||
|
if (s - t) > maxMatchOffset {
|
||||||
|
panic(fmt.Sprintln("mmo", t))
|
||||||
|
}
|
||||||
|
if l < baseMatchLength {
|
||||||
|
panic("bml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s >= sLimit {
|
||||||
|
// Index first pair after match end.
|
||||||
|
if int(s+8) < len(src) {
|
||||||
|
cv := load6432(src, s)
|
||||||
|
e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: s + e.cur}
|
||||||
|
e.bTable[hash7(cv, tableBits)] = tableEntry{offset: s + e.cur}
|
||||||
|
}
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store every 3rd hash in-between
|
||||||
|
if true {
|
||||||
|
i := nextS
|
||||||
|
if i < s-1 {
|
||||||
|
cv := load6432(src, i)
|
||||||
|
t := tableEntry{offset: i + e.cur}
|
||||||
|
t2 := tableEntry{offset: t.offset + 1}
|
||||||
|
e.bTable[hash7(cv, tableBits)] = t
|
||||||
|
e.bTable[hash7(cv>>8, tableBits)] = t2
|
||||||
|
e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2
|
||||||
|
|
||||||
|
i += 3
|
||||||
|
for ; i < s-1; i += 3 {
|
||||||
|
cv := load6432(src, i)
|
||||||
|
t := tableEntry{offset: i + e.cur}
|
||||||
|
t2 := tableEntry{offset: t.offset + 1}
|
||||||
|
e.bTable[hash7(cv, tableBits)] = t
|
||||||
|
e.bTable[hash7(cv>>8, tableBits)] = t2
|
||||||
|
e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-1 and at s.
|
||||||
|
x := load6432(src, s-1)
|
||||||
|
o := e.cur + s - 1
|
||||||
|
prevHashS := hashLen(x, tableBits, hashShortBytes)
|
||||||
|
prevHashL := hash7(x, tableBits)
|
||||||
|
e.table[prevHashS] = tableEntry{offset: o}
|
||||||
|
e.bTable[prevHashL] = tableEntry{offset: o}
|
||||||
|
cv = x >> 8
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
310
vendor/github.com/klauspost/compress/flate/level5.go
generated
vendored
Normal file
310
vendor/github.com/klauspost/compress/flate/level5.go
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type fastEncL5 struct {
|
||||||
|
fastGen
|
||||||
|
table [tableSize]tableEntry
|
||||||
|
bTable [tableSize]tableEntryPrev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fastEncL5) Encode(dst *tokens, src []byte) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
hashShortBytes = 4
|
||||||
|
)
|
||||||
|
if debugDeflate && e.cur < 0 {
|
||||||
|
panic(fmt.Sprint("e.cur < 0: ", e.cur))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect against e.cur wraparound.
|
||||||
|
for e.cur >= bufferReset {
|
||||||
|
if len(e.hist) == 0 {
|
||||||
|
for i := range e.table[:] {
|
||||||
|
e.table[i] = tableEntry{}
|
||||||
|
}
|
||||||
|
for i := range e.bTable[:] {
|
||||||
|
e.bTable[i] = tableEntryPrev{}
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Shift down everything in the table that isn't already too far away.
|
||||||
|
minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
|
||||||
|
for i := range e.table[:] {
|
||||||
|
v := e.table[i].offset
|
||||||
|
if v <= minOff {
|
||||||
|
v = 0
|
||||||
|
} else {
|
||||||
|
v = v - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.table[i].offset = v
|
||||||
|
}
|
||||||
|
for i := range e.bTable[:] {
|
||||||
|
v := e.bTable[i]
|
||||||
|
if v.Cur.offset <= minOff {
|
||||||
|
v.Cur.offset = 0
|
||||||
|
v.Prev.offset = 0
|
||||||
|
} else {
|
||||||
|
v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset
|
||||||
|
if v.Prev.offset <= minOff {
|
||||||
|
v.Prev.offset = 0
|
||||||
|
} else {
|
||||||
|
v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.bTable[i] = v
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
s := e.addBlock(src)
|
||||||
|
|
||||||
|
// This check isn't in the Snappy implementation, but there, the caller
|
||||||
|
// instead of the callee handles this case.
|
||||||
|
if len(src) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = uint16(len(src))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override src
|
||||||
|
src = e.hist
|
||||||
|
nextEmit := s
|
||||||
|
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int32(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load6432(src, s)
|
||||||
|
for {
|
||||||
|
const skipLog = 6
|
||||||
|
const doEvery = 1
|
||||||
|
|
||||||
|
nextS := s
|
||||||
|
var l int32
|
||||||
|
var t int32
|
||||||
|
for {
|
||||||
|
nextHashS := hashLen(cv, tableBits, hashShortBytes)
|
||||||
|
nextHashL := hash7(cv, tableBits)
|
||||||
|
|
||||||
|
s = nextS
|
||||||
|
nextS = s + doEvery + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
// Fetch a short+long candidate
|
||||||
|
sCandidate := e.table[nextHashS]
|
||||||
|
lCandidate := e.bTable[nextHashL]
|
||||||
|
next := load6432(src, nextS)
|
||||||
|
entry := tableEntry{offset: s + e.cur}
|
||||||
|
e.table[nextHashS] = entry
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = entry, eLong.Cur
|
||||||
|
|
||||||
|
nextHashS = hashLen(next, tableBits, hashShortBytes)
|
||||||
|
nextHashL = hash7(next, tableBits)
|
||||||
|
|
||||||
|
t = lCandidate.Cur.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset {
|
||||||
|
if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) {
|
||||||
|
// Store the next match
|
||||||
|
e.table[nextHashS] = tableEntry{offset: nextS + e.cur}
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur
|
||||||
|
|
||||||
|
t2 := lCandidate.Prev.offset - e.cur
|
||||||
|
if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) {
|
||||||
|
l = e.matchlen(s+4, t+4, src) + 4
|
||||||
|
ml1 := e.matchlen(s+4, t2+4, src) + 4
|
||||||
|
if ml1 > l {
|
||||||
|
t = t2
|
||||||
|
l = ml1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t = lCandidate.Prev.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) {
|
||||||
|
// Store the next match
|
||||||
|
e.table[nextHashS] = tableEntry{offset: nextS + e.cur}
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t = sCandidate.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) {
|
||||||
|
// Found a 4 match...
|
||||||
|
l = e.matchlen(s+4, t+4, src) + 4
|
||||||
|
lCandidate = e.bTable[nextHashL]
|
||||||
|
// Store the next match
|
||||||
|
|
||||||
|
e.table[nextHashS] = tableEntry{offset: nextS + e.cur}
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur
|
||||||
|
|
||||||
|
// If the next long is a candidate, use that...
|
||||||
|
t2 := lCandidate.Cur.offset - e.cur
|
||||||
|
if nextS-t2 < maxMatchOffset {
|
||||||
|
if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) {
|
||||||
|
ml := e.matchlen(nextS+4, t2+4, src) + 4
|
||||||
|
if ml > l {
|
||||||
|
t = t2
|
||||||
|
s = nextS
|
||||||
|
l = ml
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the previous long is a candidate, use that...
|
||||||
|
t2 = lCandidate.Prev.offset - e.cur
|
||||||
|
if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) {
|
||||||
|
ml := e.matchlen(nextS+4, t2+4, src) + 4
|
||||||
|
if ml > l {
|
||||||
|
t = t2
|
||||||
|
s = nextS
|
||||||
|
l = ml
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cv = next
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||||
|
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||||
|
// them as literal bytes.
|
||||||
|
|
||||||
|
if l == 0 {
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
l = e.matchlenLong(s+4, t+4, src) + 4
|
||||||
|
} else if l == maxMatchLength {
|
||||||
|
l += e.matchlenLong(s+l, t+l, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to locate a better match by checking the end of best match...
|
||||||
|
if sAt := s + l; l < 30 && sAt < sLimit {
|
||||||
|
// Allow some bytes at the beginning to mismatch.
|
||||||
|
// Sweet spot is 2/3 bytes depending on input.
|
||||||
|
// 3 is only a little better when it is but sometimes a lot worse.
|
||||||
|
// The skipped bytes are tested in Extend backwards,
|
||||||
|
// and still picked up as part of the match if they do.
|
||||||
|
const skipBeginning = 2
|
||||||
|
eLong := e.bTable[hash7(load6432(src, sAt), tableBits)].Cur.offset
|
||||||
|
t2 := eLong - e.cur - l + skipBeginning
|
||||||
|
s2 := s + skipBeginning
|
||||||
|
off := s2 - t2
|
||||||
|
if t2 >= 0 && off < maxMatchOffset && off > 0 {
|
||||||
|
if l2 := e.matchlenLong(s2, t2, src); l2 > l {
|
||||||
|
t = t2
|
||||||
|
l = l2
|
||||||
|
s = s2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if debugDeflate {
|
||||||
|
if t >= s {
|
||||||
|
panic(fmt.Sprintln("s-t", s, t))
|
||||||
|
}
|
||||||
|
if (s - t) > maxMatchOffset {
|
||||||
|
panic(fmt.Sprintln("mmo", s-t))
|
||||||
|
}
|
||||||
|
if l < baseMatchLength {
|
||||||
|
panic("bml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s >= sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store every 3rd hash in-between.
|
||||||
|
if true {
|
||||||
|
const hashEvery = 3
|
||||||
|
i := s - l + 1
|
||||||
|
if i < s-1 {
|
||||||
|
cv := load6432(src, i)
|
||||||
|
t := tableEntry{offset: i + e.cur}
|
||||||
|
e.table[hashLen(cv, tableBits, hashShortBytes)] = t
|
||||||
|
eLong := &e.bTable[hash7(cv, tableBits)]
|
||||||
|
eLong.Cur, eLong.Prev = t, eLong.Cur
|
||||||
|
|
||||||
|
// Do an long at i+1
|
||||||
|
cv >>= 8
|
||||||
|
t = tableEntry{offset: t.offset + 1}
|
||||||
|
eLong = &e.bTable[hash7(cv, tableBits)]
|
||||||
|
eLong.Cur, eLong.Prev = t, eLong.Cur
|
||||||
|
|
||||||
|
// We only have enough bits for a short entry at i+2
|
||||||
|
cv >>= 8
|
||||||
|
t = tableEntry{offset: t.offset + 1}
|
||||||
|
e.table[hashLen(cv, tableBits, hashShortBytes)] = t
|
||||||
|
|
||||||
|
// Skip one - otherwise we risk hitting 's'
|
||||||
|
i += 4
|
||||||
|
for ; i < s-1; i += hashEvery {
|
||||||
|
cv := load6432(src, i)
|
||||||
|
t := tableEntry{offset: i + e.cur}
|
||||||
|
t2 := tableEntry{offset: t.offset + 1}
|
||||||
|
eLong := &e.bTable[hash7(cv, tableBits)]
|
||||||
|
eLong.Cur, eLong.Prev = t, eLong.Cur
|
||||||
|
e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-1 and at s.
|
||||||
|
x := load6432(src, s-1)
|
||||||
|
o := e.cur + s - 1
|
||||||
|
prevHashS := hashLen(x, tableBits, hashShortBytes)
|
||||||
|
prevHashL := hash7(x, tableBits)
|
||||||
|
e.table[prevHashS] = tableEntry{offset: o}
|
||||||
|
eLong := &e.bTable[prevHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: o}, eLong.Cur
|
||||||
|
cv = x >> 8
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
325
vendor/github.com/klauspost/compress/flate/level6.go
generated
vendored
Normal file
325
vendor/github.com/klauspost/compress/flate/level6.go
generated
vendored
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type fastEncL6 struct {
|
||||||
|
fastGen
|
||||||
|
table [tableSize]tableEntry
|
||||||
|
bTable [tableSize]tableEntryPrev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fastEncL6) Encode(dst *tokens, src []byte) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
hashShortBytes = 4
|
||||||
|
)
|
||||||
|
if debugDeflate && e.cur < 0 {
|
||||||
|
panic(fmt.Sprint("e.cur < 0: ", e.cur))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect against e.cur wraparound.
|
||||||
|
for e.cur >= bufferReset {
|
||||||
|
if len(e.hist) == 0 {
|
||||||
|
for i := range e.table[:] {
|
||||||
|
e.table[i] = tableEntry{}
|
||||||
|
}
|
||||||
|
for i := range e.bTable[:] {
|
||||||
|
e.bTable[i] = tableEntryPrev{}
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Shift down everything in the table that isn't already too far away.
|
||||||
|
minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
|
||||||
|
for i := range e.table[:] {
|
||||||
|
v := e.table[i].offset
|
||||||
|
if v <= minOff {
|
||||||
|
v = 0
|
||||||
|
} else {
|
||||||
|
v = v - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
e.table[i].offset = v
|
||||||
|
}
|
||||||
|
for i := range e.bTable[:] {
|
||||||
|
v := e.bTable[i]
|
||||||
|
if v.Cur.offset <= minOff {
|
||||||
|
v.Cur.offset = 0
|
||||||
|
v.Prev.offset = 0
|
||||||
|
} else {
|
||||||
|
v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset
|
||||||
|
if v.Prev.offset <= minOff {
|
||||||
|
v.Prev.offset = 0
|
||||||
|
} else {
|
||||||
|
v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.bTable[i] = v
|
||||||
|
}
|
||||||
|
e.cur = maxMatchOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
s := e.addBlock(src)
|
||||||
|
|
||||||
|
// This check isn't in the Snappy implementation, but there, the caller
|
||||||
|
// instead of the callee handles this case.
|
||||||
|
if len(src) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = uint16(len(src))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override src
|
||||||
|
src = e.hist
|
||||||
|
nextEmit := s
|
||||||
|
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int32(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load6432(src, s)
|
||||||
|
// Repeat MUST be > 1 and within range
|
||||||
|
repeat := int32(1)
|
||||||
|
for {
|
||||||
|
const skipLog = 7
|
||||||
|
const doEvery = 1
|
||||||
|
|
||||||
|
nextS := s
|
||||||
|
var l int32
|
||||||
|
var t int32
|
||||||
|
for {
|
||||||
|
nextHashS := hashLen(cv, tableBits, hashShortBytes)
|
||||||
|
nextHashL := hash7(cv, tableBits)
|
||||||
|
s = nextS
|
||||||
|
nextS = s + doEvery + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
// Fetch a short+long candidate
|
||||||
|
sCandidate := e.table[nextHashS]
|
||||||
|
lCandidate := e.bTable[nextHashL]
|
||||||
|
next := load6432(src, nextS)
|
||||||
|
entry := tableEntry{offset: s + e.cur}
|
||||||
|
e.table[nextHashS] = entry
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = entry, eLong.Cur
|
||||||
|
|
||||||
|
// Calculate hashes of 'next'
|
||||||
|
nextHashS = hashLen(next, tableBits, hashShortBytes)
|
||||||
|
nextHashL = hash7(next, tableBits)
|
||||||
|
|
||||||
|
t = lCandidate.Cur.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset {
|
||||||
|
if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) {
|
||||||
|
// Long candidate matches at least 4 bytes.
|
||||||
|
|
||||||
|
// Store the next match
|
||||||
|
e.table[nextHashS] = tableEntry{offset: nextS + e.cur}
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur
|
||||||
|
|
||||||
|
// Check the previous long candidate as well.
|
||||||
|
t2 := lCandidate.Prev.offset - e.cur
|
||||||
|
if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) {
|
||||||
|
l = e.matchlen(s+4, t+4, src) + 4
|
||||||
|
ml1 := e.matchlen(s+4, t2+4, src) + 4
|
||||||
|
if ml1 > l {
|
||||||
|
t = t2
|
||||||
|
l = ml1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Current value did not match, but check if previous long value does.
|
||||||
|
t = lCandidate.Prev.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) {
|
||||||
|
// Store the next match
|
||||||
|
e.table[nextHashS] = tableEntry{offset: nextS + e.cur}
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t = sCandidate.offset - e.cur
|
||||||
|
if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) {
|
||||||
|
// Found a 4 match...
|
||||||
|
l = e.matchlen(s+4, t+4, src) + 4
|
||||||
|
|
||||||
|
// Look up next long candidate (at nextS)
|
||||||
|
lCandidate = e.bTable[nextHashL]
|
||||||
|
|
||||||
|
// Store the next match
|
||||||
|
e.table[nextHashS] = tableEntry{offset: nextS + e.cur}
|
||||||
|
eLong := &e.bTable[nextHashL]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur
|
||||||
|
|
||||||
|
// Check repeat at s + repOff
|
||||||
|
const repOff = 1
|
||||||
|
t2 := s - repeat + repOff
|
||||||
|
if load3232(src, t2) == uint32(cv>>(8*repOff)) {
|
||||||
|
ml := e.matchlen(s+4+repOff, t2+4, src) + 4
|
||||||
|
if ml > l {
|
||||||
|
t = t2
|
||||||
|
l = ml
|
||||||
|
s += repOff
|
||||||
|
// Not worth checking more.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the next long is a candidate, use that...
|
||||||
|
t2 = lCandidate.Cur.offset - e.cur
|
||||||
|
if nextS-t2 < maxMatchOffset {
|
||||||
|
if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) {
|
||||||
|
ml := e.matchlen(nextS+4, t2+4, src) + 4
|
||||||
|
if ml > l {
|
||||||
|
t = t2
|
||||||
|
s = nextS
|
||||||
|
l = ml
|
||||||
|
// This is ok, but check previous as well.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the previous long is a candidate, use that...
|
||||||
|
t2 = lCandidate.Prev.offset - e.cur
|
||||||
|
if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) {
|
||||||
|
ml := e.matchlen(nextS+4, t2+4, src) + 4
|
||||||
|
if ml > l {
|
||||||
|
t = t2
|
||||||
|
s = nextS
|
||||||
|
l = ml
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cv = next
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||||
|
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||||
|
// them as literal bytes.
|
||||||
|
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
if l == 0 {
|
||||||
|
l = e.matchlenLong(s+4, t+4, src) + 4
|
||||||
|
} else if l == maxMatchLength {
|
||||||
|
l += e.matchlenLong(s+l, t+l, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to locate a better match by checking the end-of-match...
|
||||||
|
if sAt := s + l; sAt < sLimit {
|
||||||
|
// Allow some bytes at the beginning to mismatch.
|
||||||
|
// Sweet spot is 2/3 bytes depending on input.
|
||||||
|
// 3 is only a little better when it is but sometimes a lot worse.
|
||||||
|
// The skipped bytes are tested in Extend backwards,
|
||||||
|
// and still picked up as part of the match if they do.
|
||||||
|
const skipBeginning = 2
|
||||||
|
eLong := &e.bTable[hash7(load6432(src, sAt), tableBits)]
|
||||||
|
// Test current
|
||||||
|
t2 := eLong.Cur.offset - e.cur - l + skipBeginning
|
||||||
|
s2 := s + skipBeginning
|
||||||
|
off := s2 - t2
|
||||||
|
if off < maxMatchOffset {
|
||||||
|
if off > 0 && t2 >= 0 {
|
||||||
|
if l2 := e.matchlenLong(s2, t2, src); l2 > l {
|
||||||
|
t = t2
|
||||||
|
l = l2
|
||||||
|
s = s2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Test next:
|
||||||
|
t2 = eLong.Prev.offset - e.cur - l + skipBeginning
|
||||||
|
off := s2 - t2
|
||||||
|
if off > 0 && off < maxMatchOffset && t2 >= 0 {
|
||||||
|
if l2 := e.matchlenLong(s2, t2, src); l2 > l {
|
||||||
|
t = t2
|
||||||
|
l = l2
|
||||||
|
s = s2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if false {
|
||||||
|
if t >= s {
|
||||||
|
panic(fmt.Sprintln("s-t", s, t))
|
||||||
|
}
|
||||||
|
if (s - t) > maxMatchOffset {
|
||||||
|
panic(fmt.Sprintln("mmo", s-t))
|
||||||
|
}
|
||||||
|
if l < baseMatchLength {
|
||||||
|
panic("bml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
|
||||||
|
repeat = s - t
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s >= sLimit {
|
||||||
|
// Index after match end.
|
||||||
|
for i := nextS + 1; i < int32(len(src))-8; i += 2 {
|
||||||
|
cv := load6432(src, i)
|
||||||
|
e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: i + e.cur}
|
||||||
|
eLong := &e.bTable[hash7(cv, tableBits)]
|
||||||
|
eLong.Cur, eLong.Prev = tableEntry{offset: i + e.cur}, eLong.Cur
|
||||||
|
}
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store every long hash in-between and every second short.
|
||||||
|
if true {
|
||||||
|
for i := nextS + 1; i < s-1; i += 2 {
|
||||||
|
cv := load6432(src, i)
|
||||||
|
t := tableEntry{offset: i + e.cur}
|
||||||
|
t2 := tableEntry{offset: t.offset + 1}
|
||||||
|
eLong := &e.bTable[hash7(cv, tableBits)]
|
||||||
|
eLong2 := &e.bTable[hash7(cv>>8, tableBits)]
|
||||||
|
e.table[hashLen(cv, tableBits, hashShortBytes)] = t
|
||||||
|
eLong.Cur, eLong.Prev = t, eLong.Cur
|
||||||
|
eLong2.Cur, eLong2.Prev = t2, eLong2.Cur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-1 and at s.
|
||||||
|
cv = load6432(src, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
37
vendor/github.com/klauspost/compress/flate/regmask_amd64.go
generated
vendored
Normal file
37
vendor/github.com/klauspost/compress/flate/regmask_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Masks for shifts with register sizes of the shift value.
|
||||||
|
// This can be used to work around the x86 design of shifting by mod register size.
|
||||||
|
// It can be used when a variable shift is always smaller than the register size.
|
||||||
|
|
||||||
|
// reg8SizeMaskX - shift value is 8 bits, shifted is X
|
||||||
|
reg8SizeMask8 = 7
|
||||||
|
reg8SizeMask16 = 15
|
||||||
|
reg8SizeMask32 = 31
|
||||||
|
reg8SizeMask64 = 63
|
||||||
|
|
||||||
|
// reg16SizeMaskX - shift value is 16 bits, shifted is X
|
||||||
|
reg16SizeMask8 = reg8SizeMask8
|
||||||
|
reg16SizeMask16 = reg8SizeMask16
|
||||||
|
reg16SizeMask32 = reg8SizeMask32
|
||||||
|
reg16SizeMask64 = reg8SizeMask64
|
||||||
|
|
||||||
|
// reg32SizeMaskX - shift value is 32 bits, shifted is X
|
||||||
|
reg32SizeMask8 = reg8SizeMask8
|
||||||
|
reg32SizeMask16 = reg8SizeMask16
|
||||||
|
reg32SizeMask32 = reg8SizeMask32
|
||||||
|
reg32SizeMask64 = reg8SizeMask64
|
||||||
|
|
||||||
|
// reg64SizeMaskX - shift value is 64 bits, shifted is X
|
||||||
|
reg64SizeMask8 = reg8SizeMask8
|
||||||
|
reg64SizeMask16 = reg8SizeMask16
|
||||||
|
reg64SizeMask32 = reg8SizeMask32
|
||||||
|
reg64SizeMask64 = reg8SizeMask64
|
||||||
|
|
||||||
|
// regSizeMaskUintX - shift value is uint, shifted is X
|
||||||
|
regSizeMaskUint8 = reg8SizeMask8
|
||||||
|
regSizeMaskUint16 = reg8SizeMask16
|
||||||
|
regSizeMaskUint32 = reg8SizeMask32
|
||||||
|
regSizeMaskUint64 = reg8SizeMask64
|
||||||
|
)
|
40
vendor/github.com/klauspost/compress/flate/regmask_other.go
generated
vendored
Normal file
40
vendor/github.com/klauspost/compress/flate/regmask_other.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
//go:build !amd64
|
||||||
|
// +build !amd64
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Masks for shifts with register sizes of the shift value.
|
||||||
|
// This can be used to work around the x86 design of shifting by mod register size.
|
||||||
|
// It can be used when a variable shift is always smaller than the register size.
|
||||||
|
|
||||||
|
// reg8SizeMaskX - shift value is 8 bits, shifted is X
|
||||||
|
reg8SizeMask8 = 0xff
|
||||||
|
reg8SizeMask16 = 0xff
|
||||||
|
reg8SizeMask32 = 0xff
|
||||||
|
reg8SizeMask64 = 0xff
|
||||||
|
|
||||||
|
// reg16SizeMaskX - shift value is 16 bits, shifted is X
|
||||||
|
reg16SizeMask8 = 0xffff
|
||||||
|
reg16SizeMask16 = 0xffff
|
||||||
|
reg16SizeMask32 = 0xffff
|
||||||
|
reg16SizeMask64 = 0xffff
|
||||||
|
|
||||||
|
// reg32SizeMaskX - shift value is 32 bits, shifted is X
|
||||||
|
reg32SizeMask8 = 0xffffffff
|
||||||
|
reg32SizeMask16 = 0xffffffff
|
||||||
|
reg32SizeMask32 = 0xffffffff
|
||||||
|
reg32SizeMask64 = 0xffffffff
|
||||||
|
|
||||||
|
// reg64SizeMaskX - shift value is 64 bits, shifted is X
|
||||||
|
reg64SizeMask8 = 0xffffffffffffffff
|
||||||
|
reg64SizeMask16 = 0xffffffffffffffff
|
||||||
|
reg64SizeMask32 = 0xffffffffffffffff
|
||||||
|
reg64SizeMask64 = 0xffffffffffffffff
|
||||||
|
|
||||||
|
// regSizeMaskUintX - shift value is uint, shifted is X
|
||||||
|
regSizeMaskUint8 = ^uint(0)
|
||||||
|
regSizeMaskUint16 = ^uint(0)
|
||||||
|
regSizeMaskUint32 = ^uint(0)
|
||||||
|
regSizeMaskUint64 = ^uint(0)
|
||||||
|
)
|
305
vendor/github.com/klauspost/compress/flate/stateless.go
generated
vendored
Normal file
305
vendor/github.com/klauspost/compress/flate/stateless.go
generated
vendored
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxStatelessBlock = math.MaxInt16
|
||||||
|
// dictionary will be taken from maxStatelessBlock, so limit it.
|
||||||
|
maxStatelessDict = 8 << 10
|
||||||
|
|
||||||
|
slTableBits = 13
|
||||||
|
slTableSize = 1 << slTableBits
|
||||||
|
slTableShift = 32 - slTableBits
|
||||||
|
)
|
||||||
|
|
||||||
|
type statelessWriter struct {
|
||||||
|
dst io.Writer
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statelessWriter) Close() error {
|
||||||
|
if s.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.closed = true
|
||||||
|
// Emit EOF block
|
||||||
|
return StatelessDeflate(s.dst, nil, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statelessWriter) Write(p []byte) (n int, err error) {
|
||||||
|
err = StatelessDeflate(s.dst, p, false, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statelessWriter) Reset(w io.Writer) {
|
||||||
|
s.dst = w
|
||||||
|
s.closed = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatelessWriter will do compression but without maintaining any state
|
||||||
|
// between Write calls.
|
||||||
|
// There will be no memory kept between Write calls,
|
||||||
|
// but compression and speed will be suboptimal.
|
||||||
|
// Because of this, the size of actual Write calls will affect output size.
|
||||||
|
func NewStatelessWriter(dst io.Writer) io.WriteCloser {
|
||||||
|
return &statelessWriter{dst: dst}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitWriterPool contains bit writers that can be reused.
|
||||||
|
var bitWriterPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return newHuffmanBitWriter(nil)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatelessDeflate allows compressing directly to a Writer without retaining state.
|
||||||
|
// When returning everything will be flushed.
|
||||||
|
// Up to 8KB of an optional dictionary can be given which is presumed to precede the block.
|
||||||
|
// Longer dictionaries will be truncated and will still produce valid output.
|
||||||
|
// Sending nil dictionary is perfectly fine.
|
||||||
|
func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error {
|
||||||
|
var dst tokens
|
||||||
|
bw := bitWriterPool.Get().(*huffmanBitWriter)
|
||||||
|
bw.reset(out)
|
||||||
|
defer func() {
|
||||||
|
// don't keep a reference to our output
|
||||||
|
bw.reset(nil)
|
||||||
|
bitWriterPool.Put(bw)
|
||||||
|
}()
|
||||||
|
if eof && len(in) == 0 {
|
||||||
|
// Just write an EOF block.
|
||||||
|
// Could be faster...
|
||||||
|
bw.writeStoredHeader(0, true)
|
||||||
|
bw.flush()
|
||||||
|
return bw.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate dict
|
||||||
|
if len(dict) > maxStatelessDict {
|
||||||
|
dict = dict[len(dict)-maxStatelessDict:]
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(in) > 0 {
|
||||||
|
todo := in
|
||||||
|
if len(todo) > maxStatelessBlock-len(dict) {
|
||||||
|
todo = todo[:maxStatelessBlock-len(dict)]
|
||||||
|
}
|
||||||
|
in = in[len(todo):]
|
||||||
|
uncompressed := todo
|
||||||
|
if len(dict) > 0 {
|
||||||
|
// combine dict and source
|
||||||
|
bufLen := len(todo) + len(dict)
|
||||||
|
combined := make([]byte, bufLen)
|
||||||
|
copy(combined, dict)
|
||||||
|
copy(combined[len(dict):], todo)
|
||||||
|
todo = combined
|
||||||
|
}
|
||||||
|
// Compress
|
||||||
|
statelessEnc(&dst, todo, int16(len(dict)))
|
||||||
|
isEof := eof && len(in) == 0
|
||||||
|
|
||||||
|
if dst.n == 0 {
|
||||||
|
bw.writeStoredHeader(len(uncompressed), isEof)
|
||||||
|
if bw.err != nil {
|
||||||
|
return bw.err
|
||||||
|
}
|
||||||
|
bw.writeBytes(uncompressed)
|
||||||
|
} else if int(dst.n) > len(uncompressed)-len(uncompressed)>>4 {
|
||||||
|
// If we removed less than 1/16th, huffman compress the block.
|
||||||
|
bw.writeBlockHuff(isEof, uncompressed, len(in) == 0)
|
||||||
|
} else {
|
||||||
|
bw.writeBlockDynamic(&dst, isEof, uncompressed, len(in) == 0)
|
||||||
|
}
|
||||||
|
if len(in) > 0 {
|
||||||
|
// Retain a dict if we have more
|
||||||
|
dict = todo[len(todo)-maxStatelessDict:]
|
||||||
|
dst.Reset()
|
||||||
|
}
|
||||||
|
if bw.err != nil {
|
||||||
|
return bw.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !eof {
|
||||||
|
// Align, only a stored block can do that.
|
||||||
|
bw.writeStoredHeader(0, false)
|
||||||
|
}
|
||||||
|
bw.flush()
|
||||||
|
return bw.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashSL(u uint32) uint32 {
|
||||||
|
return (u * 0x1e35a7bd) >> slTableShift
|
||||||
|
}
|
||||||
|
|
||||||
|
func load3216(b []byte, i int16) uint32 {
|
||||||
|
// Help the compiler eliminate bounds checks on the read so it can be done in a single read.
|
||||||
|
b = b[i:]
|
||||||
|
b = b[:4]
|
||||||
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
func load6416(b []byte, i int16) uint64 {
|
||||||
|
// Help the compiler eliminate bounds checks on the read so it can be done in a single read.
|
||||||
|
b = b[i:]
|
||||||
|
b = b[:8]
|
||||||
|
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||||
|
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||||
|
}
|
||||||
|
|
||||||
|
func statelessEnc(dst *tokens, src []byte, startAt int16) {
|
||||||
|
const (
|
||||||
|
inputMargin = 12 - 1
|
||||||
|
minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||||
|
)
|
||||||
|
|
||||||
|
type tableEntry struct {
|
||||||
|
offset int16
|
||||||
|
}
|
||||||
|
|
||||||
|
var table [slTableSize]tableEntry
|
||||||
|
|
||||||
|
// This check isn't in the Snappy implementation, but there, the caller
|
||||||
|
// instead of the callee handles this case.
|
||||||
|
if len(src)-int(startAt) < minNonLiteralBlockSize {
|
||||||
|
// We do not fill the token table.
|
||||||
|
// This will be picked up by caller.
|
||||||
|
dst.n = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Index until startAt
|
||||||
|
if startAt > 0 {
|
||||||
|
cv := load3232(src, 0)
|
||||||
|
for i := int16(0); i < startAt; i++ {
|
||||||
|
table[hashSL(cv)] = tableEntry{offset: i}
|
||||||
|
cv = (cv >> 8) | (uint32(src[i+4]) << 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := startAt + 1
|
||||||
|
nextEmit := startAt
|
||||||
|
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||||
|
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||||
|
// looking for copies.
|
||||||
|
sLimit := int16(len(src) - inputMargin)
|
||||||
|
|
||||||
|
// nextEmit is where in src the next emitLiteral should start from.
|
||||||
|
cv := load3216(src, s)
|
||||||
|
|
||||||
|
for {
|
||||||
|
const skipLog = 5
|
||||||
|
const doEvery = 2
|
||||||
|
|
||||||
|
nextS := s
|
||||||
|
var candidate tableEntry
|
||||||
|
for {
|
||||||
|
nextHash := hashSL(cv)
|
||||||
|
candidate = table[nextHash]
|
||||||
|
nextS = s + doEvery + (s-nextEmit)>>skipLog
|
||||||
|
if nextS > sLimit || nextS <= 0 {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
now := load6416(src, nextS)
|
||||||
|
table[nextHash] = tableEntry{offset: s}
|
||||||
|
nextHash = hashSL(uint32(now))
|
||||||
|
|
||||||
|
if cv == load3216(src, candidate.offset) {
|
||||||
|
table[nextHash] = tableEntry{offset: nextS}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do one right away...
|
||||||
|
cv = uint32(now)
|
||||||
|
s = nextS
|
||||||
|
nextS++
|
||||||
|
candidate = table[nextHash]
|
||||||
|
now >>= 8
|
||||||
|
table[nextHash] = tableEntry{offset: s}
|
||||||
|
|
||||||
|
if cv == load3216(src, candidate.offset) {
|
||||||
|
table[nextHash] = tableEntry{offset: nextS}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cv = uint32(now)
|
||||||
|
s = nextS
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||||
|
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||||
|
// them as literal bytes.
|
||||||
|
for {
|
||||||
|
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||||
|
// literal bytes prior to s.
|
||||||
|
|
||||||
|
// Extend the 4-byte match as long as possible.
|
||||||
|
t := candidate.offset
|
||||||
|
l := int16(matchLen(src[s+4:], src[t+4:]) + 4)
|
||||||
|
|
||||||
|
// Extend backwards
|
||||||
|
for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
|
||||||
|
s--
|
||||||
|
t--
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if nextEmit < s {
|
||||||
|
if false {
|
||||||
|
emitLiteral(dst, src[nextEmit:s])
|
||||||
|
} else {
|
||||||
|
for _, v := range src[nextEmit:s] {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the match found
|
||||||
|
dst.AddMatchLong(int32(l), uint32(s-t-baseMatchOffset))
|
||||||
|
s += l
|
||||||
|
nextEmit = s
|
||||||
|
if nextS >= s {
|
||||||
|
s = nextS + 1
|
||||||
|
}
|
||||||
|
if s >= sLimit {
|
||||||
|
goto emitRemainder
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could immediately start working at s now, but to improve
|
||||||
|
// compression we first update the hash table at s-2 and at s. If
|
||||||
|
// another emitCopy is not our next move, also calculate nextHash
|
||||||
|
// at s+1. At least on GOARCH=amd64, these three hash calculations
|
||||||
|
// are faster as one load64 call (with some shifts) instead of
|
||||||
|
// three load32 calls.
|
||||||
|
x := load6416(src, s-2)
|
||||||
|
o := s - 2
|
||||||
|
prevHash := hashSL(uint32(x))
|
||||||
|
table[prevHash] = tableEntry{offset: o}
|
||||||
|
x >>= 16
|
||||||
|
currHash := hashSL(uint32(x))
|
||||||
|
candidate = table[currHash]
|
||||||
|
table[currHash] = tableEntry{offset: o + 2}
|
||||||
|
|
||||||
|
if uint32(x) != load3216(src, candidate.offset) {
|
||||||
|
cv = uint32(x >> 8)
|
||||||
|
s++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
if int(nextEmit) < len(src) {
|
||||||
|
// If nothing was added, don't encode literals.
|
||||||
|
if dst.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emitLiteral(dst, src[nextEmit:])
|
||||||
|
}
|
||||||
|
}
|
379
vendor/github.com/klauspost/compress/flate/token.go
generated
vendored
Normal file
379
vendor/github.com/klauspost/compress/flate/token.go
generated
vendored
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// bits 0-16 xoffset = offset - MIN_OFFSET_SIZE, or literal - 16 bits
|
||||||
|
// bits 16-22 offsetcode - 5 bits
|
||||||
|
// bits 22-30 xlength = length - MIN_MATCH_LENGTH - 8 bits
|
||||||
|
// bits 30-32 type 0 = literal 1=EOF 2=Match 3=Unused - 2 bits
|
||||||
|
lengthShift = 22
|
||||||
|
offsetMask = 1<<lengthShift - 1
|
||||||
|
typeMask = 3 << 30
|
||||||
|
literalType = 0 << 30
|
||||||
|
matchType = 1 << 30
|
||||||
|
matchOffsetOnlyMask = 0xffff
|
||||||
|
)
|
||||||
|
|
||||||
|
// The length code for length X (MIN_MATCH_LENGTH <= X <= MAX_MATCH_LENGTH)
|
||||||
|
// is lengthCodes[length - MIN_MATCH_LENGTH]
|
||||||
|
var lengthCodes = [256]uint8{
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 8,
|
||||||
|
9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
|
||||||
|
13, 13, 13, 13, 14, 14, 14, 14, 15, 15,
|
||||||
|
15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||||
|
17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
|
||||||
|
18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
|
||||||
|
19, 19, 19, 19, 20, 20, 20, 20, 20, 20,
|
||||||
|
20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||||
|
22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 26, 26, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 27, 28,
|
||||||
|
}
|
||||||
|
|
||||||
|
// lengthCodes1 is length codes, but starting at 1.
|
||||||
|
var lengthCodes1 = [256]uint8{
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 9,
|
||||||
|
10, 10, 11, 11, 12, 12, 13, 13, 13, 13,
|
||||||
|
14, 14, 14, 14, 15, 15, 15, 15, 16, 16,
|
||||||
|
16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||||
|
18, 18, 18, 18, 18, 18, 18, 18, 19, 19,
|
||||||
|
19, 19, 19, 19, 19, 19, 20, 20, 20, 20,
|
||||||
|
20, 20, 20, 20, 21, 21, 21, 21, 21, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 23, 23, 23, 23,
|
||||||
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
23, 23, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 25, 25,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 28, 28, 28, 28, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 29,
|
||||||
|
}
|
||||||
|
|
||||||
|
var offsetCodes = [256]uint32{
|
||||||
|
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||||
|
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||||
|
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||||
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||||
|
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||||
|
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||||
|
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||||
|
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||||
|
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||||
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
// offsetCodes14 are offsetCodes, but with 14 added.
|
||||||
|
var offsetCodes14 = [256]uint32{
|
||||||
|
14, 15, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
|
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
|
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||||
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||||
|
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||||
|
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||||
|
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||||
|
}
|
||||||
|
|
||||||
|
type token uint32
|
||||||
|
|
||||||
|
type tokens struct {
|
||||||
|
extraHist [32]uint16 // codes 256->maxnumlit
|
||||||
|
offHist [32]uint16 // offset codes
|
||||||
|
litHist [256]uint16 // codes 0->255
|
||||||
|
nFilled int
|
||||||
|
n uint16 // Must be able to contain maxStoreBlockSize
|
||||||
|
tokens [maxStoreBlockSize + 1]token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tokens) Reset() {
|
||||||
|
if t.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.n = 0
|
||||||
|
t.nFilled = 0
|
||||||
|
for i := range t.litHist[:] {
|
||||||
|
t.litHist[i] = 0
|
||||||
|
}
|
||||||
|
for i := range t.extraHist[:] {
|
||||||
|
t.extraHist[i] = 0
|
||||||
|
}
|
||||||
|
for i := range t.offHist[:] {
|
||||||
|
t.offHist[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tokens) Fill() {
|
||||||
|
if t.n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, v := range t.litHist[:] {
|
||||||
|
if v == 0 {
|
||||||
|
t.litHist[i] = 1
|
||||||
|
t.nFilled++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, v := range t.extraHist[:literalCount-256] {
|
||||||
|
if v == 0 {
|
||||||
|
t.nFilled++
|
||||||
|
t.extraHist[i] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, v := range t.offHist[:offsetCodeCount] {
|
||||||
|
if v == 0 {
|
||||||
|
t.offHist[i] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexTokens(in []token) tokens {
|
||||||
|
var t tokens
|
||||||
|
t.indexTokens(in)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tokens) indexTokens(in []token) {
|
||||||
|
t.Reset()
|
||||||
|
for _, tok := range in {
|
||||||
|
if tok < matchType {
|
||||||
|
t.AddLiteral(tok.literal())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.AddMatch(uint32(tok.length()), tok.offset()&matchOffsetOnlyMask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||||
|
func emitLiteral(dst *tokens, lit []byte) {
|
||||||
|
for _, v := range lit {
|
||||||
|
dst.tokens[dst.n] = token(v)
|
||||||
|
dst.litHist[v]++
|
||||||
|
dst.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tokens) AddLiteral(lit byte) {
|
||||||
|
t.tokens[t.n] = token(lit)
|
||||||
|
t.litHist[lit]++
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
// from https://stackoverflow.com/a/28730362
|
||||||
|
func mFastLog2(val float32) float32 {
|
||||||
|
ux := int32(math.Float32bits(val))
|
||||||
|
log2 := (float32)(((ux >> 23) & 255) - 128)
|
||||||
|
ux &= -0x7f800001
|
||||||
|
ux += 127 << 23
|
||||||
|
uval := math.Float32frombits(uint32(ux))
|
||||||
|
log2 += ((-0.34484843)*uval+2.02466578)*uval - 0.67487759
|
||||||
|
return log2
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimatedBits will return an minimum size estimated by an *optimal*
|
||||||
|
// compression of the block.
|
||||||
|
// The size of the block
|
||||||
|
func (t *tokens) EstimatedBits() int {
|
||||||
|
shannon := float32(0)
|
||||||
|
bits := int(0)
|
||||||
|
nMatches := 0
|
||||||
|
total := int(t.n) + t.nFilled
|
||||||
|
if total > 0 {
|
||||||
|
invTotal := 1.0 / float32(total)
|
||||||
|
for _, v := range t.litHist[:] {
|
||||||
|
if v > 0 {
|
||||||
|
n := float32(v)
|
||||||
|
shannon += atLeastOne(-mFastLog2(n*invTotal)) * n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Just add 15 for EOB
|
||||||
|
shannon += 15
|
||||||
|
for i, v := range t.extraHist[1 : literalCount-256] {
|
||||||
|
if v > 0 {
|
||||||
|
n := float32(v)
|
||||||
|
shannon += atLeastOne(-mFastLog2(n*invTotal)) * n
|
||||||
|
bits += int(lengthExtraBits[i&31]) * int(v)
|
||||||
|
nMatches += int(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nMatches > 0 {
|
||||||
|
invTotal := 1.0 / float32(nMatches)
|
||||||
|
for i, v := range t.offHist[:offsetCodeCount] {
|
||||||
|
if v > 0 {
|
||||||
|
n := float32(v)
|
||||||
|
shannon += atLeastOne(-mFastLog2(n*invTotal)) * n
|
||||||
|
bits += int(offsetExtraBits[i&31]) * int(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return int(shannon) + bits
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatch adds a match to the tokens.
|
||||||
|
// This function is very sensitive to inlining and right on the border.
|
||||||
|
func (t *tokens) AddMatch(xlength uint32, xoffset uint32) {
|
||||||
|
if debugDeflate {
|
||||||
|
if xlength >= maxMatchLength+baseMatchLength {
|
||||||
|
panic(fmt.Errorf("invalid length: %v", xlength))
|
||||||
|
}
|
||||||
|
if xoffset >= maxMatchOffset+baseMatchOffset {
|
||||||
|
panic(fmt.Errorf("invalid offset: %v", xoffset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oCode := offsetCode(xoffset)
|
||||||
|
xoffset |= oCode << 16
|
||||||
|
|
||||||
|
t.extraHist[lengthCodes1[uint8(xlength)]]++
|
||||||
|
t.offHist[oCode&31]++
|
||||||
|
t.tokens[t.n] = token(matchType | xlength<<lengthShift | xoffset)
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatchLong adds a match to the tokens, potentially longer than max match length.
|
||||||
|
// Length should NOT have the base subtracted, only offset should.
|
||||||
|
func (t *tokens) AddMatchLong(xlength int32, xoffset uint32) {
|
||||||
|
if debugDeflate {
|
||||||
|
if xoffset >= maxMatchOffset+baseMatchOffset {
|
||||||
|
panic(fmt.Errorf("invalid offset: %v", xoffset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oc := offsetCode(xoffset)
|
||||||
|
xoffset |= oc << 16
|
||||||
|
for xlength > 0 {
|
||||||
|
xl := xlength
|
||||||
|
if xl > 258 {
|
||||||
|
// We need to have at least baseMatchLength left over for next loop.
|
||||||
|
if xl > 258+baseMatchLength {
|
||||||
|
xl = 258
|
||||||
|
} else {
|
||||||
|
xl = 258 - baseMatchLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlength -= xl
|
||||||
|
xl -= baseMatchLength
|
||||||
|
t.extraHist[lengthCodes1[uint8(xl)]]++
|
||||||
|
t.offHist[oc&31]++
|
||||||
|
t.tokens[t.n] = token(matchType | uint32(xl)<<lengthShift | xoffset)
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tokens) AddEOB() {
|
||||||
|
t.tokens[t.n] = token(endBlockMarker)
|
||||||
|
t.extraHist[0]++
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tokens) Slice() []token {
|
||||||
|
return t.tokens[:t.n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarInt returns the tokens as varint encoded bytes.
|
||||||
|
func (t *tokens) VarInt() []byte {
|
||||||
|
var b = make([]byte, binary.MaxVarintLen32*int(t.n))
|
||||||
|
var off int
|
||||||
|
for _, v := range t.tokens[:t.n] {
|
||||||
|
off += binary.PutUvarint(b[off:], uint64(v))
|
||||||
|
}
|
||||||
|
return b[:off]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromVarInt restores t to the varint encoded tokens provided.
|
||||||
|
// Any data in t is removed.
|
||||||
|
func (t *tokens) FromVarInt(b []byte) error {
|
||||||
|
var buf = bytes.NewReader(b)
|
||||||
|
var toks []token
|
||||||
|
for {
|
||||||
|
r, err := binary.ReadUvarint(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toks = append(toks, token(r))
|
||||||
|
}
|
||||||
|
t.indexTokens(toks)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the type of a token
|
||||||
|
func (t token) typ() uint32 { return uint32(t) & typeMask }
|
||||||
|
|
||||||
|
// Returns the literal of a literal token
|
||||||
|
func (t token) literal() uint8 { return uint8(t) }
|
||||||
|
|
||||||
|
// Returns the extra offset of a match token
|
||||||
|
func (t token) offset() uint32 { return uint32(t) & offsetMask }
|
||||||
|
|
||||||
|
func (t token) length() uint8 { return uint8(t >> lengthShift) }
|
||||||
|
|
||||||
|
// Convert length to code.
|
||||||
|
func lengthCode(len uint8) uint8 { return lengthCodes[len] }
|
||||||
|
|
||||||
|
// Returns the offset code corresponding to a specific offset
|
||||||
|
func offsetCode(off uint32) uint32 {
|
||||||
|
if false {
|
||||||
|
if off < uint32(len(offsetCodes)) {
|
||||||
|
return offsetCodes[off&255]
|
||||||
|
} else if off>>7 < uint32(len(offsetCodes)) {
|
||||||
|
return offsetCodes[(off>>7)&255] + 14
|
||||||
|
} else {
|
||||||
|
return offsetCodes[(off>>14)&255] + 28
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if off < uint32(len(offsetCodes)) {
|
||||||
|
return offsetCodes[uint8(off)]
|
||||||
|
}
|
||||||
|
return offsetCodes14[uint8(off>>7)]
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue