bump deps
This commit is contained in:
@@ -977,5 +977,5 @@ func (c *authRefreshCache) Get(key interface{}) (interface{}, bool) {
|
||||
|
||||
// Set implements cache setter with key converted to string
|
||||
func (c *authRefreshCache) Set(key, value interface{}) {
|
||||
_, _ = c.LoadingCache.Get(key.(string), func() (cache.Value, error) { return value, nil })
|
||||
_, _ = c.LoadingCache.Get(key.(string), func() (interface{}, error) { return value, nil })
|
||||
}
|
||||
|
||||
@@ -507,7 +507,7 @@ func (s *DataStore) HasReplies(comment store.Comment) bool {
|
||||
// When this code is reached, key "comment.ID" is not in cache.
|
||||
// Calling cache.Get on it will put it in cache with 5 minutes TTL.
|
||||
// We call it with empty struct as value as we care about keys and not values.
|
||||
_, _ = s.repliesCache.Get(comment.ID, func() (lcw.Value, error) { return struct{}{}, nil })
|
||||
_, _ = s.repliesCache.Get(comment.ID, func() (interface{}, error) { return struct{}{}, nil })
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func NewTitleExtractor(client http.Client) *TitleExtractor {
|
||||
// Get page for url and return title
|
||||
func (t *TitleExtractor) Get(url string) (string, error) {
|
||||
client := http.Client{Timeout: t.client.Timeout, Transport: t.client.Transport}
|
||||
b, err := t.cache.Get(url, func() (lcw.Value, error) {
|
||||
b, err := t.cache.Get(url, func() (interface{}, error) {
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to load page %s", url)
|
||||
@@ -63,7 +63,7 @@ func (t *TitleExtractor) Get(url string) (string, error) {
|
||||
|
||||
// on error save result (empty string) to cache too and return "" title
|
||||
if err != nil {
|
||||
_, _ = t.cache.Get(url, func() (lcw.Value, error) { return "", nil })
|
||||
_, _ = t.cache.Get(url, func() (interface{}, error) { return "", nil })
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -14,12 +14,12 @@ require (
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/go-pkgz/auth v1.13.1
|
||||
github.com/go-pkgz/jrpc v0.2.0
|
||||
github.com/go-pkgz/lcw v0.7.1
|
||||
github.com/go-pkgz/lgr v0.7.0
|
||||
github.com/go-pkgz/lcw v0.8.1
|
||||
github.com/go-pkgz/lgr v0.10.4
|
||||
github.com/go-pkgz/repeater v1.1.3
|
||||
github.com/go-pkgz/rest v1.5.0
|
||||
github.com/go-pkgz/syncs v1.1.1
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gorilla/feeds v1.1.1
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
github.com/kyokomi/emoji v2.2.1+incompatible
|
||||
@@ -27,10 +27,10 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/rs/xid v1.2.1
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
github.com/russross/blackfriday/v2 v2.1.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/umputun/go-flags v1.5.1
|
||||
go.etcd.io/bbolt v1.3.4
|
||||
go.etcd.io/bbolt v1.3.5
|
||||
go.uber.org/goleak v1.0.0
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1
|
||||
|
||||
+12
-8
@@ -64,18 +64,18 @@ github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6d
|
||||
github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ=
|
||||
github.com/go-pkgz/jrpc v0.2.0 h1:CLy/eZyekjraVrxZV18N2R1mYLMJ/nWrgdfyIOGPY/E=
|
||||
github.com/go-pkgz/jrpc v0.2.0/go.mod h1:wd8vtQ4CgtCnuqua6x2b1SKIgv0VSOh5Dn0uUITbiUE=
|
||||
github.com/go-pkgz/lcw v0.7.1 h1:2Q2k1am6xb2KgxZG84kL072q66rlWFUR+MvgPcDcJXA=
|
||||
github.com/go-pkgz/lcw v0.7.1/go.mod h1:3P6g9QrJsDePXEMe42ywO+tW08L17tBJGwIDdI7lZ6g=
|
||||
github.com/go-pkgz/lgr v0.7.0 h1:S/AAPwt/RE9a5mNJskA7dGVp+Dq6SMIW6LYjG3ITxY8=
|
||||
github.com/go-pkgz/lgr v0.7.0/go.mod h1:yMgxU+GobMRJgIEbSzDKy/67W18S7qmGx/7BVL5AB8Q=
|
||||
github.com/go-pkgz/lcw v0.8.1 h1:Bpt2yYTE1J8hIhz8tjdm1WPOgH13eo5iTNsXyop7cMQ=
|
||||
github.com/go-pkgz/lcw v0.8.1/go.mod h1:Xw0/ZfApATgbjVPYRZO4XHdWyxAjErDWDWJ7TLlw1Vc=
|
||||
github.com/go-pkgz/lgr v0.10.4 h1:l7qyFjqEZgwRgaQQSEp6tve4A3OU80VrfzpvtEX8ngw=
|
||||
github.com/go-pkgz/lgr v0.10.4/go.mod h1:CD0s1z6EFpIUplV067gitF77tn25JItzwHNKAPqeCF0=
|
||||
github.com/go-pkgz/repeater v1.1.3 h1:q6+JQF14ESSy28Dd7F+wRelY4F+41HJ0LEy/szNnMiE=
|
||||
github.com/go-pkgz/repeater v1.1.3/go.mod h1:hVTavuO5x3Gxnu8zW7d6sQBfAneKV8X2FjU48kGfpKw=
|
||||
github.com/go-pkgz/rest v1.5.0 h1:C8SxXcXza4GiUUAn/95iCkvoIrGbS30qpwK19iqlrWQ=
|
||||
github.com/go-pkgz/rest v1.5.0/go.mod h1:nQaM3RhSTUAmbBZWY4hfe4buyeC9VckvhoCktiQXJxI=
|
||||
github.com/go-pkgz/syncs v1.1.1 h1:jWN+y6FS/Xe+8z4l3QMbSnODGyaxDHGojIS+wyKIjxg=
|
||||
github.com/go-pkgz/syncs v1.1.1/go.mod h1:bt9lxWRRJ9vOCMGc8Big8ttjYHLKP88ofj1y38UlaHE=
|
||||
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
|
||||
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
|
||||
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
@@ -115,8 +115,8 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
|
||||
@@ -197,6 +197,8 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
@@ -266,6 +268,8 @@ github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.mongodb.org/mongo-driver v1.4.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs=
|
||||
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
|
||||
|
||||
+16
-16
@@ -12,9 +12,6 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Value type wraps interface{}
|
||||
type Value interface{}
|
||||
|
||||
// Sizer allows to perform size-based restrictions, optional.
|
||||
// If not defined both maxValueSize and maxCacheSize checks will be ignored
|
||||
type Sizer interface {
|
||||
@@ -23,14 +20,14 @@ type Sizer interface {
|
||||
|
||||
// LoadingCache defines guava-like cache with Get method returning cached value ao retrieving it if not in cache
|
||||
type LoadingCache interface {
|
||||
Get(key string, fn func() (Value, error)) (val Value, err error) // load or get from cache
|
||||
Peek(key string) (Value, bool) // get from cache by key
|
||||
Invalidate(fn func(key string) bool) // invalidate items for func(key) == true
|
||||
Delete(key string) // delete by key
|
||||
Purge() // clear cache
|
||||
Stat() CacheStat // cache stats
|
||||
Keys() []string // list of all keys
|
||||
Close() error // close open connections
|
||||
Get(key string, fn func() (interface{}, error)) (val interface{}, err error) // load or get from cache
|
||||
Peek(key string) (interface{}, bool) // get from cache by key
|
||||
Invalidate(fn func(key string) bool) // invalidate items for func(key) == true
|
||||
Delete(key string) // delete by key
|
||||
Purge() // clear cache
|
||||
Stat() CacheStat // cache stats
|
||||
Keys() []string // list of all keys
|
||||
Close() error // close open connections
|
||||
}
|
||||
|
||||
// CacheStat represent stats values
|
||||
@@ -44,8 +41,12 @@ type CacheStat struct {
|
||||
|
||||
// String formats cache stats
|
||||
func (s CacheStat) String() string {
|
||||
return fmt.Sprintf("{hits:%d, misses:%d, ratio:%.1f%%, keys:%d, size:%d, errors:%d}",
|
||||
s.Hits, s.Misses, 100*(float64(s.Hits)/float64(s.Hits+s.Misses)), s.Keys, s.Size, s.Errors)
|
||||
ratio := 0.0
|
||||
if s.Hits+s.Misses > 0 {
|
||||
ratio = float64(s.Hits) / float64(s.Hits+s.Misses)
|
||||
}
|
||||
return fmt.Sprintf("{hits:%d, misses:%d, ratio:%.2f, keys:%d, size:%d, errors:%d}",
|
||||
s.Hits, s.Misses, ratio, s.Keys, s.Size, s.Errors)
|
||||
}
|
||||
|
||||
// Nop is do-nothing implementation of LoadingCache
|
||||
@@ -57,10 +58,10 @@ func NewNopCache() *Nop {
|
||||
}
|
||||
|
||||
// Get calls fn without any caching
|
||||
func (n *Nop) Get(key string, fn func() (Value, error)) (Value, error) { return fn() }
|
||||
func (n *Nop) Get(key string, fn func() (interface{}, error)) (interface{}, error) { return fn() }
|
||||
|
||||
// Peek does nothing and always returns false
|
||||
func (n *Nop) Peek(key string) (Value, bool) { return nil, false }
|
||||
func (n *Nop) Peek(key string) (interface{}, bool) { return nil, false }
|
||||
|
||||
// Invalidate does nothing for nop cache
|
||||
func (n *Nop) Invalidate(fn func(key string) bool) {}
|
||||
@@ -83,4 +84,3 @@ func (n *Nop) Stat() CacheStat {
|
||||
func (n *Nop) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -69,7 +69,7 @@ func NewExpirableCache(opts ...Option) (*ExpirableCache, error) {
|
||||
}
|
||||
|
||||
// Get gets value by key or load with fn if not found in cache
|
||||
func (c *ExpirableCache) Get(key string, fn func() (Value, error)) (data Value, err error) {
|
||||
func (c *ExpirableCache) Get(key string, fn func() (interface{}, error)) (data interface{}, err error) {
|
||||
if v, ok := c.backend.Get(key); ok {
|
||||
atomic.AddInt64(&c.Hits, 1)
|
||||
return v, nil
|
||||
@@ -104,7 +104,7 @@ func (c *ExpirableCache) Invalidate(fn func(key string) bool) {
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
|
||||
func (c *ExpirableCache) Peek(key string) (Value, bool) {
|
||||
func (c *ExpirableCache) Peek(key string) (interface{}, bool) {
|
||||
return c.backend.Peek(key)
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ func (c *ExpirableCache) keys() int {
|
||||
return c.backend.ItemCount()
|
||||
}
|
||||
|
||||
func (c *ExpirableCache) allowed(key string, data Value) bool {
|
||||
func (c *ExpirableCache) allowed(key string, data interface{}) bool {
|
||||
if c.backend.ItemCount() >= c.maxKeys {
|
||||
return false
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
module github.com/go-pkgz/lcw
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/alicebob/miniredis/v2 v2.11.4
|
||||
github.com/go-redis/redis/v7 v7.2.0
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
||||
+9
-7
@@ -9,15 +9,15 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
|
||||
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
|
||||
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||
@@ -40,9 +40,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -67,6 +68,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
+3
-3
@@ -65,7 +65,7 @@ func (c *LruCache) init() error {
|
||||
}
|
||||
|
||||
// Get gets value by key or load with fn if not found in cache
|
||||
func (c *LruCache) Get(key string, fn func() (Value, error)) (data Value, err error) {
|
||||
func (c *LruCache) Get(key string, fn func() (interface{}, error)) (data interface{}, err error) {
|
||||
if v, ok := c.backend.Get(key); ok {
|
||||
atomic.AddInt64(&c.Hits, 1)
|
||||
return v, nil
|
||||
@@ -97,7 +97,7 @@ func (c *LruCache) Get(key string, fn func() (Value, error)) (data Value, err er
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
|
||||
func (c *LruCache) Peek(key string) (Value, bool) {
|
||||
func (c *LruCache) Peek(key string) (interface{}, bool) {
|
||||
return c.backend.Peek(key)
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ func (c *LruCache) keys() int {
|
||||
return c.backend.Len()
|
||||
}
|
||||
|
||||
func (c *LruCache) allowed(key string, data Value) bool {
|
||||
func (c *LruCache) allowed(key string, data interface{}) bool {
|
||||
if c.maxKeySize > 0 && len(key) > c.maxKeySize {
|
||||
return false
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ type options struct {
|
||||
maxKeySize int
|
||||
maxCacheSize int64
|
||||
ttl time.Duration
|
||||
onEvicted func(key string, value Value)
|
||||
onEvicted func(key string, value interface{})
|
||||
eventBus eventbus.PubSub
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func TTL(ttl time.Duration) Option {
|
||||
}
|
||||
|
||||
// OnEvicted sets callback on invalidation event
|
||||
func OnEvicted(fn func(key string, value Value)) Option {
|
||||
func OnEvicted(fn func(key string, value interface{})) Option {
|
||||
return func(o *options) error {
|
||||
o.onEvicted = fn
|
||||
return nil
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ func NewRedisCache(backend *redis.Client, opts ...Option) (*RedisCache, error) {
|
||||
}
|
||||
|
||||
// Get gets value by key or load with fn if not found in cache
|
||||
func (c *RedisCache) Get(key string, fn func() (Value, error)) (data Value, err error) {
|
||||
func (c *RedisCache) Get(key string, fn func() (interface{}, error)) (data interface{}, err error) {
|
||||
v, getErr := c.backend.Get(key).Result()
|
||||
switch getErr {
|
||||
// RedisClient returns nil when find a key in DB
|
||||
@@ -84,7 +84,7 @@ func (c *RedisCache) Invalidate(fn func(key string) bool) {
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
|
||||
func (c *RedisCache) Peek(key string) (Value, bool) {
|
||||
func (c *RedisCache) Peek(key string) (interface{}, bool) {
|
||||
ret, err := c.backend.Get(key).Result()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
@@ -132,7 +132,7 @@ func (c *RedisCache) keys() int {
|
||||
return int(c.backend.DBSize().Val())
|
||||
}
|
||||
|
||||
func (c *RedisCache) allowed(key string, data Value) bool {
|
||||
func (c *RedisCache) allowed(key string, data interface{}) bool {
|
||||
if c.maxKeys > 0 && c.backend.DBSize().Val() >= int64(c.maxKeys) {
|
||||
return false
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ func NewScache(lc LoadingCache) *Scache {
|
||||
// Get retrieves a key from underlying backend
|
||||
func (m *Scache) Get(key Key, fn func() ([]byte, error)) (data []byte, err error) {
|
||||
keyStr := key.String()
|
||||
val, err := m.lc.Get(keyStr, func() (value Value, e error) {
|
||||
val, err := m.lc.Get(keyStr, func() (value interface{}, e error) {
|
||||
return fn()
|
||||
})
|
||||
return val.([]byte), err
|
||||
|
||||
+18
-8
@@ -23,11 +23,12 @@ linters-settings:
|
||||
- experimental
|
||||
disabled-checks:
|
||||
- wrapperFunc
|
||||
- hugeParam
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- megacheck
|
||||
- golint
|
||||
- govet
|
||||
- unconvert
|
||||
- megacheck
|
||||
@@ -42,19 +43,28 @@ linters:
|
||||
- typecheck
|
||||
- ineffassign
|
||||
- varcheck
|
||||
- stylecheck
|
||||
- gochecknoinits
|
||||
- scopelint
|
||||
- gocritic
|
||||
- nakedret
|
||||
- gosimple
|
||||
- prealloc
|
||||
fast: false
|
||||
|
||||
disable-all: true
|
||||
|
||||
run:
|
||||
# modules-download-mode: vendor
|
||||
output:
|
||||
format: tab
|
||||
skip-dirs:
|
||||
- vendor
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- text: "weak cryptographic primitive"
|
||||
- text: "should have a package comment, unless it's in another file for this package"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.16.x
|
||||
- golint
|
||||
- text: "at least one file in a package should have a package comment"
|
||||
linters:
|
||||
- stylecheck
|
||||
exclude-use-default: false
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Umputun
|
||||
Copyright (c) 2020 Umputun
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+38
-9
@@ -1,4 +1,5 @@
|
||||
# lgr - simple logger with some extras [](https://github.com/go-pkgz/lgr/actions) [](https://coveralls.io/github/go-pkgz/lgr?branch=master) [](https://godoc.org/github.com/go-pkgz/lgr)
|
||||
# lgr - simple logger with some extras
|
||||
[](https://github.com/go-pkgz/lgr/actions) [](https://coveralls.io/github/go-pkgz/lgr?branch=master) [](https://godoc.org/github.com/go-pkgz/lgr)
|
||||
|
||||
## install
|
||||
|
||||
@@ -24,7 +25,7 @@ _Without `lgr.Caller*` it will drop `{caller}` part_
|
||||
|
||||
### interfaces and default loggers
|
||||
|
||||
- `lgr` package provides a single interface `lgr.L` with a single method `Logf(format string, args ...interface{})`. Function wrapper `lgr.Func` allows to make `lgr.L` from a function directly.
|
||||
- `lgr` package provides a single interface `lgr.L` with a single method `Logf(format string, args ...interface{})`. Function wrapper `lgr.Func` allows making `lgr.L` from a function directly.
|
||||
- Default logger functionality can be used without `lgr.New` (see "global logger")
|
||||
- Two predefined loggers available: `lgr.NoOp` (do-nothing logger) and `lgr.Std` (passing directly to stdlib log)
|
||||
|
||||
@@ -41,8 +42,10 @@ _Without `lgr.Caller*` it will drop `{caller}` part_
|
||||
- `lgr.CallerPkg` - adds the caller package
|
||||
- `lgr.LevelBraces` - wraps levels with "[" and "]"
|
||||
- `lgr.Msec` - adds milliseconds to timestamp
|
||||
- `lgr.Format` - sets custom template, overwrite all other formatting modifiers.
|
||||
- `lgr.Format` - sets a custom template, overwrite all other formatting modifiers.
|
||||
- `lgr.Secret(secret ...)` - sets list of the secrets to hide from the logging outputs.
|
||||
- `lgr.Map(mapper)` - sets mapper functions to change elements of the logging output based on levels.
|
||||
- `lgr.StackTraceOnError` - turns on stack trace for ERROR level.
|
||||
|
||||
example: `l := lgr.New(lgr.Debug, lgr.Msec)`
|
||||
|
||||
@@ -61,16 +64,16 @@ Several predefined templates provided and can be passed directly to `lgr.Format`
|
||||
|
||||
User can make a custom template and pass it directly to `lgr.Format`. For example:
|
||||
|
||||
```go
|
||||
```go
|
||||
lgr.Format(`{{.Level}} - {{.DT.Format "2006-01-02T15:04:05Z07:00"}} - {{.CallerPkg}} - {{.Message}}`)
|
||||
```
|
||||
|
||||
_Note: formatter (predefined or custom) adds measurable overhead - the cost will depend on the version of Go, but is between 30
|
||||
and 50% in recent tests with 1.12. You can validate this in your environment via benchmarks: `go test -bench=. -run=Bench`_
|
||||
|
||||
|
||||
### levels
|
||||
|
||||
`lgr.Logf` recognizes prefixes like "INFO" or "[INFO]" as levels. The full list of supported levels - "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "PANIC" and "FATAL"
|
||||
`lgr.Logf` recognize prefixes like `INFO` or `[INFO]` as levels. The full list of supported levels - `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `PANIC` and `FATAL`.
|
||||
|
||||
- `TRACE` will be filtered unless `lgr.Trace` option defined
|
||||
- `DEBUG` will be filtered unless `lgr.Debug` or `lgr.Trace` options defined
|
||||
@@ -78,7 +81,29 @@ _Note: formatter (predefined or custom) adds measurable overhead - the cost will
|
||||
- `ERROR` sends messages to both out and err writers
|
||||
- `FATAL` and send messages to both out and err writers and exit(1)
|
||||
- `PANIC` does the same as `FATAL` but in addition sends dump of callers and runtime info to err.
|
||||
|
||||
|
||||
### mapper
|
||||
|
||||
Elements of the output can be altered with a set of user defined function passed as `lgr.Map` options. Such a mapper changes
|
||||
the value of an element (i.e. timestamp, level, message, caller) and has separate functions for each level. Note: both level
|
||||
and messages elements handled by the same function for a given level.
|
||||
|
||||
_A typical use-case is to produce colorful output with a user-define colorization library._
|
||||
|
||||
example with [fatih/color](https://github.com/fatih/color):
|
||||
|
||||
```go
|
||||
colorizer := lgr.Mapper{
|
||||
ErrorFunc: func(s string) string { return color.New(color.FgHiRed).Sprint(s) },
|
||||
WarnFunc: func(s string) string { return color.New(color.FgHiYellow).Sprint(s) },
|
||||
InfoFunc: func(s string) string { return color.New(color.FgHiWhite).Sprint(s) },
|
||||
DebugFunc: func(s string) string { return color.New(color.FgWhite).Sprint(s) },
|
||||
CallerFunc: func(s string) string { return color.New(color.FgBlue).Sprint(s) },
|
||||
TimeFunc: func(s string) string { return color.New(color.FgCyan).Sprint(s) },
|
||||
}
|
||||
|
||||
logOpts := []lgr.Option{lgr.Msec, lgr.LevelBraces, lgr.Map(colorizer)}
|
||||
```
|
||||
### adaptors
|
||||
|
||||
`lgr` logger can be converted to `io.Writer` or `*log.Logger`
|
||||
@@ -86,10 +111,14 @@ _Note: formatter (predefined or custom) adds measurable overhead - the cost will
|
||||
- `lgr.ToWriter(l lgr.L, level string) io.Writer` - makes io.Writer forwarding write ops to underlying `lgr.L`
|
||||
- `lgr.ToStdLogger(l lgr.L, level string) *log.Logger` - makes standard logger on top of `lgr.L`
|
||||
|
||||
_`level` parameter is optional, if defined (non-empty) will enforce the level._
|
||||
|
||||
_`level` parameter is optional, if defined (non-empty) will enforce the level._
|
||||
|
||||
- `lgr.SetupStdLogger(opts ...Option)` initializes std global logger (`log.std`) with lgr logger and given options.
|
||||
All standard methods like `log.Print`, `log.Println`, `log.Fatal` and so on will be forwarder to lgr.
|
||||
|
||||
### global logger
|
||||
|
||||
Users **should avoid** global logger and pass the concrete logger as a dependency. However, in some cases a global logger may be needed, for example migration from stdlib `log` to `lgr`. For such cases `log "github.com/go-pkgz/lgr"` can be imported instead of `log` package.
|
||||
|
||||
Global logger provides `lgr.Printf`, `lgr.Print` and `lgr.Fatalf` functions. User can customize the logger by calling `lgr.Setup(options ...)`. The instance of this logger can be retrieved with `lgr.Default()`
|
||||
|
||||
|
||||
+11
@@ -25,6 +25,17 @@ func ToWriter(l L, level string) *Writer {
|
||||
return &Writer{l, level}
|
||||
}
|
||||
|
||||
// ToStdLogger makes standard logger
|
||||
func ToStdLogger(l L, level string) *log.Logger {
|
||||
return log.New(ToWriter(l, level), "", 0)
|
||||
}
|
||||
|
||||
// SetupStdLogger makes the default std logger with lgr.L
|
||||
func SetupStdLogger(opts ...Option) {
|
||||
logOpts := append([]Option{CallerDepth(3)}, opts...) // skip 3 more frames to compensate stdlog calls
|
||||
l := New(logOpts...)
|
||||
l.reTrace = reTraceStd // std logger split on log/ path
|
||||
log.SetOutput(ToWriter(l, ""))
|
||||
log.SetPrefix("")
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
module github.com/go-pkgz/lgr
|
||||
|
||||
require github.com/stretchr/testify v1.3.0
|
||||
require github.com/stretchr/testify v1.6.1
|
||||
|
||||
go 1.13
|
||||
go 1.15
|
||||
|
||||
+7
-2
@@ -2,6 +2,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
+95
-20
@@ -6,7 +6,6 @@
|
||||
// Debug and trace levels can be filtered based on lgr.Trace and lgr.Debug options.
|
||||
// ERROR, FATAL and PANIC levels send to err as well. FATAL terminate caller application with os.Exit(1)
|
||||
// and PANIC also prints stack trace.
|
||||
|
||||
package lgr
|
||||
|
||||
import (
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -26,16 +26,27 @@ import (
|
||||
var levels = []string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "PANIC", "FATAL"}
|
||||
|
||||
const (
|
||||
Short = `{{.DT.Format "2006/01/02 15:04:05"}} {{.Level}} {{.Message}}`
|
||||
WithMsec = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} {{.Message}}`
|
||||
WithPkg = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerPkg}}) {{.Message}}`
|
||||
// Short logging format
|
||||
Short = `{{.DT.Format "2006/01/02 15:04:05"}} {{.Level}} {{.Message}}`
|
||||
// WithMsec is a logging format with milliseconds
|
||||
WithMsec = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} {{.Message}}`
|
||||
// WithPkg is WithMsec logging format with caller package
|
||||
WithPkg = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerPkg}}) {{.Message}}`
|
||||
// ShortDebug is WithMsec logging format with caller file and line
|
||||
ShortDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFile}}:{{.CallerLine}}) {{.Message}}`
|
||||
FuncDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFunc}}) {{.Message}}`
|
||||
FullDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFile}}:{{.CallerLine}} {{.CallerFunc}}) {{.Message}}`
|
||||
// FuncDebug is WithMsec logging format with caller function
|
||||
FuncDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFunc}}) {{.Message}}`
|
||||
// FullDebug is WithMsec logging format with caller file, line and function
|
||||
FullDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFile}}:{{.CallerLine}} {{.CallerFunc}}) {{.Message}}`
|
||||
)
|
||||
|
||||
var secretReplacement = []byte("******")
|
||||
|
||||
var (
|
||||
reTraceDefault = regexp.MustCompile(`.*/lgr/logger\.go.*\n`)
|
||||
reTraceStd = regexp.MustCompile(`.*/log/log\.go.*\n`)
|
||||
)
|
||||
|
||||
// Logger provided simple logger with basic support of levels. Thread safe
|
||||
type Logger struct {
|
||||
// set with Option calls
|
||||
@@ -48,7 +59,8 @@ type Logger struct {
|
||||
levelBraces bool // encloses level with [], i.e. [INFO]
|
||||
callerDepth int // how many stack frames to skip, relative to the real (reported) frame
|
||||
format string // layout template
|
||||
secrets []string // sub-strings to secrets by matching
|
||||
secrets [][]byte // sub-strings to secrets by matching
|
||||
mapper Mapper // map (alter) output based on levels
|
||||
|
||||
// internal use
|
||||
now nowFn
|
||||
@@ -57,7 +69,9 @@ type Logger struct {
|
||||
lock sync.Mutex
|
||||
callerOn bool
|
||||
levelBracesOn bool
|
||||
errorDump bool
|
||||
templ *template.Template
|
||||
reTrace *regexp.Regexp
|
||||
}
|
||||
|
||||
// can be redefined internally for testing
|
||||
@@ -85,6 +99,8 @@ func New(options ...Option) *Logger {
|
||||
stdout: os.Stdout,
|
||||
stderr: os.Stderr,
|
||||
callerDepth: 0,
|
||||
mapper: nopMapper,
|
||||
reTrace: reTraceDefault,
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(&res)
|
||||
@@ -124,9 +140,16 @@ func (l *Logger) Logf(format string, args ...interface{}) {
|
||||
l.logf(format, args...)
|
||||
}
|
||||
|
||||
//nolint gocyclo
|
||||
func (l *Logger) logf(format string, args ...interface{}) {
|
||||
|
||||
lv, msg := l.extractLevel(fmt.Sprintf(format, args...))
|
||||
var lv, msg string
|
||||
if len(args) == 0 {
|
||||
lv, msg = l.extractLevel(format)
|
||||
} else {
|
||||
lv, msg = l.extractLevel(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
if lv == "DEBUG" && !l.dbg {
|
||||
return
|
||||
}
|
||||
@@ -177,6 +200,15 @@ func (l *Logger) logf(format string, args ...interface{}) {
|
||||
if l.stderr != l.stdout {
|
||||
_, _ = l.stderr.Write(data)
|
||||
}
|
||||
if l.errorDump {
|
||||
stackInfo := make([]byte, 1024*1024)
|
||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
||||
traceLines := l.reTrace.Split(string(stackInfo[:stackSize]), -1)
|
||||
if len(traceLines) > 0 {
|
||||
_, _ = l.stdout.Write([]byte(">>> stack trace:\n" + traceLines[len(traceLines)-1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
case "FATAL":
|
||||
if l.stderr != l.stdout {
|
||||
_, _ = l.stderr.Write(data)
|
||||
@@ -195,7 +227,7 @@ func (l *Logger) logf(format string, args ...interface{}) {
|
||||
|
||||
func (l *Logger) hideSecrets(data []byte) []byte {
|
||||
for _, h := range l.secrets {
|
||||
data = bytes.Replace(data, []byte(h), secretReplacement, -1)
|
||||
data = bytes.Replace(data, h, secretReplacement, -1)
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -268,15 +300,17 @@ func (l *Logger) formatWithOptions(elems layout) (res string) {
|
||||
|
||||
parts := make([]string, 0, 4)
|
||||
|
||||
parts = append(parts, orElse(l.msec,
|
||||
func() string { return elems.DT.Format("2006/01/02 15:04:05.000") },
|
||||
func() string { return elems.DT.Format("2006/01/02 15:04:05") },
|
||||
))
|
||||
|
||||
parts = append(parts, orElse(l.levelBraces,
|
||||
func() string { return `[` + elems.Level + `]` },
|
||||
func() string { return elems.Level },
|
||||
))
|
||||
parts = append(
|
||||
parts,
|
||||
l.mapper.TimeFunc(orElse(l.msec,
|
||||
func() string { return elems.DT.Format("2006/01/02 15:04:05.000") },
|
||||
func() string { return elems.DT.Format("2006/01/02 15:04:05") },
|
||||
)),
|
||||
l.levelMapper(elems.Level)(orElse(l.levelBraces,
|
||||
func() string { return `[` + elems.Level + `]` },
|
||||
func() string { return elems.Level },
|
||||
)),
|
||||
)
|
||||
|
||||
if l.callerFile || l.callerFunc || l.callerPkg {
|
||||
var callerParts []string
|
||||
@@ -290,10 +324,20 @@ func (l *Logger) formatWithOptions(elems layout) (res string) {
|
||||
if v := orElse(l.callerPkg, func() string { return elems.CallerPkg }, nothing); v != "" {
|
||||
callerParts = append(callerParts, v)
|
||||
}
|
||||
parts = append(parts, "{"+strings.Join(callerParts, " ")+"}")
|
||||
|
||||
caller := "{" + strings.Join(callerParts, " ") + "}"
|
||||
if l.mapper.CallerFunc != nil {
|
||||
caller = l.mapper.CallerFunc(caller)
|
||||
}
|
||||
parts = append(parts, caller)
|
||||
}
|
||||
|
||||
parts = append(parts, elems.Message)
|
||||
msg := elems.Message
|
||||
if l.mapper.MessageFunc != nil {
|
||||
msg = l.mapper.MessageFunc(elems.Message)
|
||||
}
|
||||
|
||||
parts = append(parts, l.levelMapper(elems.Level)(msg))
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
@@ -320,6 +364,37 @@ func (l *Logger) extractLevel(line string) (level, msg string) {
|
||||
return "INFO", line
|
||||
}
|
||||
|
||||
func (l *Logger) levelMapper(level string) mapFunc {
|
||||
|
||||
nop := func(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
switch level {
|
||||
case "TRACE", "DEBUG":
|
||||
if l.mapper.DebugFunc == nil {
|
||||
return nop
|
||||
}
|
||||
return l.mapper.DebugFunc
|
||||
case "INFO ":
|
||||
if l.mapper.InfoFunc == nil {
|
||||
return nop
|
||||
}
|
||||
return l.mapper.InfoFunc
|
||||
case "WARN ":
|
||||
if l.mapper.WarnFunc == nil {
|
||||
return nop
|
||||
}
|
||||
return l.mapper.WarnFunc
|
||||
case "ERROR", "PANIC", "FATAL":
|
||||
if l.mapper.ErrorFunc == nil {
|
||||
return nop
|
||||
}
|
||||
return l.mapper.ErrorFunc
|
||||
}
|
||||
return func(s string) string { return s }
|
||||
}
|
||||
|
||||
// getDump reads runtime stack and returns as a string
|
||||
func getDump() []byte {
|
||||
maxSize := 5 * 1024 * 1024
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package lgr
|
||||
|
||||
// Mapper defines optional functions to change elements of the logged message for each part, based on levels.
|
||||
// Only some mapFunc can be defined, by default does nothing. Can be used to alter the output, for example making some
|
||||
// part of the output colorful.
|
||||
type Mapper struct {
|
||||
MessageFunc mapFunc // message mapper on all levels
|
||||
ErrorFunc mapFunc // message mapper on ERROR level
|
||||
WarnFunc mapFunc // message mapper on WARN level
|
||||
InfoFunc mapFunc // message mapper on INFO level
|
||||
DebugFunc mapFunc // message mapper on DEBUG level
|
||||
|
||||
CallerFunc mapFunc // caller mapper, all levels
|
||||
TimeFunc mapFunc // time mapper, all levels
|
||||
}
|
||||
|
||||
type mapFunc func(string) string
|
||||
|
||||
// nopMapper is a default, doing nothing
|
||||
var nopMapper = Mapper{
|
||||
MessageFunc: func(s string) string { return s },
|
||||
ErrorFunc: func(s string) string { return s },
|
||||
WarnFunc: func(s string) string { return s },
|
||||
InfoFunc: func(s string) string { return s },
|
||||
DebugFunc: func(s string) string { return s },
|
||||
CallerFunc: func(s string) string { return s },
|
||||
TimeFunc: func(s string) string { return s },
|
||||
}
|
||||
+15
-1
@@ -73,6 +73,20 @@ func Msec(l *Logger) {
|
||||
// Useful to prevent passwords or other sensitive tokens to be logged.
|
||||
func Secret(vals ...string) Option {
|
||||
return func(l *Logger) {
|
||||
l.secrets = vals
|
||||
for _, v := range vals {
|
||||
l.secrets = append(l.secrets, []byte(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map sets mapper functions to change elements of the logged message based on levels.
|
||||
func Map(m Mapper) Option {
|
||||
return func(l *Logger) {
|
||||
l.mapper = m
|
||||
}
|
||||
}
|
||||
|
||||
// StackTraceOnError turns on stack trace for ERROR level.
|
||||
func StackTraceOnError(l *Logger) {
|
||||
l.errorDump = true
|
||||
}
|
||||
|
||||
-2
@@ -1,12 +1,10 @@
|
||||
dist: xenial
|
||||
sudo: false
|
||||
language: go
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
+4
@@ -4,6 +4,10 @@
|
||||
|
||||
- Existing `HMSet` is renamed to `HSet` and old deprecated `HMSet` is restored for Redis 3 users.
|
||||
|
||||
## v7.1
|
||||
|
||||
- Existing `Cmd.String` is renamed to `Cmd.Text`. New `Cmd.String` implements `fmt.Stringer` interface.
|
||||
|
||||
## v7
|
||||
|
||||
- *Important*. Tx.Pipeline now returns a non-transactional pipeline. Use Tx.TxPipeline for a transactional pipeline.
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ bench: testdeps
|
||||
|
||||
testdata/redis:
|
||||
mkdir -p $@
|
||||
wget -qO- http://download.redis.io/releases/redis-5.0.7.tar.gz | tar xvz --strip-components=1 -C $@
|
||||
wget -qO- http://download.redis.io/redis-stable.tar.gz | tar xvz --strip-components=1 -C $@
|
||||
|
||||
testdata/redis/src/redis-server: testdata/redis
|
||||
cd $< && make all
|
||||
|
||||
+5
-2
@@ -101,7 +101,7 @@ set, err := client.SetNX("key", "value", 10*time.Second).Result()
|
||||
vals, err := client.Sort("list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()
|
||||
|
||||
// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
|
||||
vals, err := client.ZRangeByScoreWithScores("zset", redis.ZRangeBy{
|
||||
vals, err := client.ZRangeByScoreWithScores("zset", &redis.ZRangeBy{
|
||||
Min: "-inf",
|
||||
Max: "+inf",
|
||||
Offset: 0,
|
||||
@@ -109,7 +109,10 @@ vals, err := client.ZRangeByScoreWithScores("zset", redis.ZRangeBy{
|
||||
}).Result()
|
||||
|
||||
// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
|
||||
vals, err := client.ZInterStore("out", redis.ZStore{Weights: []int64{2, 3}}, "zset1", "zset2").Result()
|
||||
vals, err := client.ZInterStore("out", &redis.ZStore{
|
||||
Keys: []string{"zset1", "zset2"},
|
||||
Weights: []int64{2, 3}
|
||||
}).Result()
|
||||
|
||||
// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
|
||||
vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
|
||||
|
||||
+10
-1
@@ -57,6 +57,7 @@ type ClusterOptions struct {
|
||||
|
||||
OnConnect func(*Conn) error
|
||||
|
||||
Username string
|
||||
Password string
|
||||
|
||||
MaxRetries int
|
||||
@@ -67,6 +68,9 @@ type ClusterOptions struct {
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
|
||||
// NewClient creates a cluster node client with provided name and options.
|
||||
NewClient func(opt *Options) *Client
|
||||
|
||||
// PoolSize applies per cluster node and not for the whole cluster.
|
||||
PoolSize int
|
||||
MinIdleConns int
|
||||
@@ -118,6 +122,10 @@ func (opt *ClusterOptions) init() {
|
||||
case 0:
|
||||
opt.MaxRetryBackoff = 512 * time.Millisecond
|
||||
}
|
||||
|
||||
if opt.NewClient == nil {
|
||||
opt.NewClient = NewClient
|
||||
}
|
||||
}
|
||||
|
||||
func (opt *ClusterOptions) clientOptions() *Options {
|
||||
@@ -130,6 +138,7 @@ func (opt *ClusterOptions) clientOptions() *Options {
|
||||
MaxRetries: opt.MaxRetries,
|
||||
MinRetryBackoff: opt.MinRetryBackoff,
|
||||
MaxRetryBackoff: opt.MaxRetryBackoff,
|
||||
Username: opt.Username,
|
||||
Password: opt.Password,
|
||||
readOnly: opt.ReadOnly,
|
||||
|
||||
@@ -162,7 +171,7 @@ func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode {
|
||||
opt := clOpt.clientOptions()
|
||||
opt.Addr = addr
|
||||
node := clusterNode{
|
||||
Client: NewClient(opt),
|
||||
Client: clOpt.NewClient(opt),
|
||||
}
|
||||
|
||||
node.latency = math.MaxUint32
|
||||
|
||||
+32
-3
@@ -671,7 +671,7 @@ func (cmd *StringCmd) Time() (time.Time, error) {
|
||||
if cmd.err != nil {
|
||||
return time.Time{}, cmd.err
|
||||
}
|
||||
return time.Parse(time.RFC3339, cmd.Val())
|
||||
return time.Parse(time.RFC3339Nano, cmd.Val())
|
||||
}
|
||||
|
||||
func (cmd *StringCmd) Scan(val interface{}) error {
|
||||
@@ -1885,6 +1885,7 @@ type CommandInfo struct {
|
||||
Name string
|
||||
Arity int8
|
||||
Flags []string
|
||||
ACLFlags []string
|
||||
FirstKeyPos int8
|
||||
LastKeyPos int8
|
||||
StepCount int8
|
||||
@@ -1934,8 +1935,14 @@ func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
|
||||
}
|
||||
|
||||
func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 6 {
|
||||
return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 6", n)
|
||||
const numArgRedis5 = 6
|
||||
const numArgRedis6 = 7
|
||||
|
||||
switch n {
|
||||
case numArgRedis5, numArgRedis6:
|
||||
// continue
|
||||
default:
|
||||
return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 7", n)
|
||||
}
|
||||
|
||||
var cmd CommandInfo
|
||||
@@ -1995,6 +2002,28 @@ func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if n == numArgRedis5 {
|
||||
return &cmd, nil
|
||||
}
|
||||
|
||||
_, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
cmd.ACLFlags = make([]string, n)
|
||||
for i := 0; i < len(cmd.ACLFlags); i++ {
|
||||
switch s, err := rd.ReadString(); {
|
||||
case err == Nil:
|
||||
cmd.ACLFlags[i] = ""
|
||||
case err != nil:
|
||||
return nil, err
|
||||
default:
|
||||
cmd.ACLFlags[i] = s
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cmd, nil
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -302,6 +302,7 @@ type Cmdable interface {
|
||||
type StatefulCmdable interface {
|
||||
Cmdable
|
||||
Auth(password string) *StatusCmd
|
||||
AuthACL(username, password string) *StatusCmd
|
||||
Select(index int) *StatusCmd
|
||||
SwapDB(index1, index2 int) *StatusCmd
|
||||
ClientSetName(name string) *BoolCmd
|
||||
@@ -324,6 +325,15 @@ func (c statefulCmdable) Auth(password string) *StatusCmd {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Perform an AUTH command, using the given user and pass.
|
||||
// Should be used to authenticate the current connection with one of the connections defined in the ACL list
|
||||
// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
||||
func (c statefulCmdable) AuthACL(username, password string) *StatusCmd {
|
||||
cmd := NewStatusCmd("auth", username, password)
|
||||
_ = c(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c cmdable) Echo(message interface{}) *StringCmd {
|
||||
cmd := NewStringCmd("echo", message)
|
||||
_ = c(cmd)
|
||||
|
||||
+15
@@ -6,9 +6,24 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/go-redis/redis/v7/internal/pool"
|
||||
"github.com/go-redis/redis/v7/internal/proto"
|
||||
)
|
||||
|
||||
var ErrClosed = pool.ErrClosed
|
||||
|
||||
type Error interface {
|
||||
error
|
||||
|
||||
// RedisError is a no-op function but
|
||||
// serves to distinguish types that are Redis
|
||||
// errors from ordinary errors: a type is a
|
||||
// Redis error if it has a RedisError method.
|
||||
RedisError()
|
||||
}
|
||||
|
||||
var _ Error = proto.RedisError("")
|
||||
|
||||
func isRetryableError(err error, retryTimeout bool) bool {
|
||||
switch err {
|
||||
case nil, context.Canceled, context.DeadlineExceeded:
|
||||
|
||||
+2
@@ -94,7 +94,9 @@ func NewConnPool(opt *Options) *ConnPool {
|
||||
closedCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
p.connsMu.Lock()
|
||||
p.checkMinIdleConns()
|
||||
p.connsMu.Unlock()
|
||||
|
||||
if opt.IdleTimeout > 0 && opt.IdleCheckFrequency > 0 {
|
||||
go p.reaper(opt.IdleCheckFrequency)
|
||||
|
||||
+2
@@ -24,6 +24,8 @@ type RedisError string
|
||||
|
||||
func (e RedisError) Error() string { return string(e) }
|
||||
|
||||
func (RedisError) RedisError() {}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type MultiBulkParse func(*Reader, int64) (interface{}, error)
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ func (w *Writer) writeArg(v interface{}) error {
|
||||
}
|
||||
return w.int(0)
|
||||
case time.Time:
|
||||
return w.string(v.Format(time.RFC3339))
|
||||
return w.string(v.Format(time.RFC3339Nano))
|
||||
case encoding.BinaryMarshaler:
|
||||
b, err := v.MarshalBinary()
|
||||
if err != nil {
|
||||
|
||||
+7
-1
@@ -40,8 +40,13 @@ type Options struct {
|
||||
// Hook that is called when new connection is established.
|
||||
OnConnect func(*Conn) error
|
||||
|
||||
// Use the specified Username to authenticate the current connection with one of the connections defined in the ACL
|
||||
// list when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
||||
Username string
|
||||
|
||||
// Optional password. Must match the password specified in the
|
||||
// requirepass server configuration option.
|
||||
// requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),
|
||||
// or the User Password when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
||||
Password string
|
||||
// Database to be selected after connecting to the server.
|
||||
DB int
|
||||
@@ -187,6 +192,7 @@ func ParseURL(redisURL string) (*Options, error) {
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
o.Username = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
o.Password = p
|
||||
}
|
||||
|
||||
+3
-1
@@ -309,9 +309,11 @@ func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
|
||||
case []interface{}:
|
||||
switch kind := reply[0].(string); kind {
|
||||
case "subscribe", "unsubscribe", "psubscribe", "punsubscribe":
|
||||
// Can be nil in case of "unsubscribe".
|
||||
channel, _ := reply[1].(string)
|
||||
return &Subscription{
|
||||
Kind: kind,
|
||||
Channel: reply[1].(string),
|
||||
Channel: channel,
|
||||
Count: int(reply[2].(int64)),
|
||||
}, nil
|
||||
case "message":
|
||||
|
||||
+5
-1
@@ -241,7 +241,11 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
||||
|
||||
_, err := conn.Pipelined(func(pipe Pipeliner) error {
|
||||
if c.opt.Password != "" {
|
||||
pipe.Auth(c.opt.Password)
|
||||
if c.opt.Username != "" {
|
||||
pipe.AuthACL(c.opt.Username, c.opt.Password)
|
||||
} else {
|
||||
pipe.Auth(c.opt.Password)
|
||||
}
|
||||
}
|
||||
|
||||
if c.opt.DB > 0 {
|
||||
|
||||
+8
@@ -98,6 +98,14 @@ func NewStringIntMapCmdResult(val map[string]int64, err error) *StringIntMapCmd
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// NewTimeCmdResult returns a TimeCmd initialised with val and err for testing
|
||||
func NewTimeCmdResult(val time.Time, err error) *TimeCmd {
|
||||
var cmd TimeCmd
|
||||
cmd.val = val
|
||||
cmd.SetErr(err)
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// NewZSliceCmdResult returns a ZSliceCmd initialised with val and err for testing
|
||||
func NewZSliceCmdResult(val []Z, err error) *ZSliceCmd {
|
||||
var cmd ZSliceCmd
|
||||
|
||||
+9
-1
@@ -56,6 +56,9 @@ type RingOptions struct {
|
||||
// See https://arxiv.org/abs/1406.2294 for reference
|
||||
HashReplicas int
|
||||
|
||||
// NewClient creates a shard client with provided name and options.
|
||||
NewClient func(name string, opt *Options) *Client
|
||||
|
||||
// Optional hook that is called when a new shard is created.
|
||||
OnNewShard func(*Client)
|
||||
|
||||
@@ -390,7 +393,12 @@ func NewRing(opt *RingOptions) *Ring {
|
||||
func newRingShard(opt *RingOptions, name, addr string) *Client {
|
||||
clopt := opt.clientOptions(name)
|
||||
clopt.Addr = addr
|
||||
shard := NewClient(clopt)
|
||||
var shard *Client
|
||||
if opt.NewClient != nil {
|
||||
shard = opt.NewClient(name, clopt)
|
||||
} else {
|
||||
shard = NewClient(clopt)
|
||||
}
|
||||
if opt.OnNewShard != nil {
|
||||
opt.OnNewShard(shard)
|
||||
}
|
||||
|
||||
+6
@@ -22,6 +22,7 @@ type FailoverOptions struct {
|
||||
MasterName string
|
||||
// A seed list of host:port addresses of sentinel nodes.
|
||||
SentinelAddrs []string
|
||||
SentinelUsername string
|
||||
SentinelPassword string
|
||||
|
||||
// Following options are copied from Options struct.
|
||||
@@ -29,6 +30,7 @@ type FailoverOptions struct {
|
||||
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
OnConnect func(*Conn) error
|
||||
|
||||
Username string
|
||||
Password string
|
||||
DB int
|
||||
|
||||
@@ -57,6 +59,7 @@ func (opt *FailoverOptions) options() *Options {
|
||||
OnConnect: opt.OnConnect,
|
||||
|
||||
DB: opt.DB,
|
||||
Username: opt.Username,
|
||||
Password: opt.Password,
|
||||
|
||||
MaxRetries: opt.MaxRetries,
|
||||
@@ -88,6 +91,7 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
|
||||
failover := &sentinelFailover{
|
||||
masterName: failoverOpt.MasterName,
|
||||
sentinelAddrs: failoverOpt.SentinelAddrs,
|
||||
username: failoverOpt.SentinelUsername,
|
||||
password: failoverOpt.SentinelPassword,
|
||||
|
||||
opt: opt,
|
||||
@@ -281,6 +285,7 @@ type sentinelFailover struct {
|
||||
sentinelAddrs []string
|
||||
|
||||
opt *Options
|
||||
username string
|
||||
password string
|
||||
|
||||
pool *pool.ConnPool
|
||||
@@ -372,6 +377,7 @@ func (c *sentinelFailover) masterAddr() (string, error) {
|
||||
Addr: sentinelAddr,
|
||||
Dialer: c.opt.Dialer,
|
||||
|
||||
Username: c.username,
|
||||
Password: c.password,
|
||||
|
||||
MaxRetries: c.opt.MaxRetries,
|
||||
|
||||
+4
@@ -22,6 +22,7 @@ type UniversalOptions struct {
|
||||
|
||||
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
OnConnect func(*Conn) error
|
||||
Username string
|
||||
Password string
|
||||
MaxRetries int
|
||||
MinRetryBackoff time.Duration
|
||||
@@ -60,6 +61,7 @@ func (o *UniversalOptions) Cluster() *ClusterOptions {
|
||||
Dialer: o.Dialer,
|
||||
OnConnect: o.OnConnect,
|
||||
|
||||
Username: o.Username,
|
||||
Password: o.Password,
|
||||
|
||||
MaxRedirects: o.MaxRedirects,
|
||||
@@ -99,6 +101,7 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
|
||||
OnConnect: o.OnConnect,
|
||||
|
||||
DB: o.DB,
|
||||
Username: o.Username,
|
||||
Password: o.Password,
|
||||
|
||||
MaxRetries: o.MaxRetries,
|
||||
@@ -133,6 +136,7 @@ func (o *UniversalOptions) Simple() *Options {
|
||||
OnConnect: o.OnConnect,
|
||||
|
||||
DB: o.DB,
|
||||
Username: o.Username,
|
||||
Password: o.Password,
|
||||
|
||||
MaxRetries: o.MaxRetries,
|
||||
|
||||
+1
-1
@@ -16,4 +16,4 @@ change is the ability to represent an invalid UUID (vs a NIL UUID).
|
||||
|
||||
Full `go doc` style documentation for the package can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
http://godoc.org/github.com/google/uuid
|
||||
http://pkg.go.dev/github.com/google/uuid
|
||||
|
||||
+4
-3
@@ -16,10 +16,11 @@ func (uuid UUID) MarshalText() ([]byte, error) {
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (uuid *UUID) UnmarshalText(data []byte) error {
|
||||
id, err := ParseBytes(data)
|
||||
if err == nil {
|
||||
*uuid = id
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
*uuid = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||
|
||||
+6
-6
@@ -17,12 +17,6 @@ import (
|
||||
//
|
||||
// In most cases, New should be used.
|
||||
func NewUUID() (UUID, error) {
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nodeMu.Unlock()
|
||||
|
||||
var uuid UUID
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
@@ -38,7 +32,13 @@ func NewUUID() (UUID, error) {
|
||||
binary.BigEndian.PutUint16(uuid[4:], timeMid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], timeHi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
copy(uuid[10:], nodeID[:])
|
||||
nodeMu.Unlock()
|
||||
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
+6
-1
@@ -27,8 +27,13 @@ func New() UUID {
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() (UUID, error) {
|
||||
return NewRandomFromReader(rander)
|
||||
}
|
||||
|
||||
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
|
||||
func NewRandomFromReader(r io.Reader) (UUID, error) {
|
||||
var uuid UUID
|
||||
_, err := io.ReadFull(rander, uuid[:])
|
||||
_, err := io.ReadFull(r, uuid[:])
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
|
||||
+67
-23
@@ -1,4 +1,6 @@
|
||||
Blackfriday [](https://travis-ci.org/russross/blackfriday)
|
||||
Blackfriday
|
||||
[![Build Status][BuildV2SVG]][BuildV2URL]
|
||||
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
|
||||
===========
|
||||
|
||||
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
|
||||
@@ -16,19 +18,21 @@ It started as a translation from C of [Sundown][3].
|
||||
Installation
|
||||
------------
|
||||
|
||||
Blackfriday is compatible with any modern Go release. With Go 1.7 and git
|
||||
installed:
|
||||
Blackfriday is compatible with modern Go releases in module mode.
|
||||
With Go installed:
|
||||
|
||||
go get gopkg.in/russross/blackfriday.v2
|
||||
go get github.com/russross/blackfriday/v2
|
||||
|
||||
will download, compile, and install the package into your `$GOPATH`
|
||||
directory hierarchy. Alternatively, you can achieve the same if you
|
||||
import it into a project:
|
||||
will resolve and add the package to the current development module,
|
||||
then build and install it. Alternatively, you can achieve the same
|
||||
if you import it in a package:
|
||||
|
||||
import "gopkg.in/russross/blackfriday.v2"
|
||||
import "github.com/russross/blackfriday/v2"
|
||||
|
||||
and `go get` without parameters.
|
||||
|
||||
Legacy GOPATH mode is unsupported.
|
||||
|
||||
|
||||
Versions
|
||||
--------
|
||||
@@ -36,13 +40,9 @@ Versions
|
||||
Currently maintained and recommended version of Blackfriday is `v2`. It's being
|
||||
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
|
||||
documentation is available at
|
||||
https://godoc.org/gopkg.in/russross/blackfriday.v2.
|
||||
https://pkg.go.dev/github.com/russross/blackfriday/v2.
|
||||
|
||||
It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`,
|
||||
but we highly recommend using package management tool like [dep][7] or
|
||||
[Glide][8] and make use of semantic versioning. With package management you
|
||||
should import `github.com/russross/blackfriday` and specify that you're using
|
||||
version 2.0.0.
|
||||
It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
|
||||
|
||||
Version 2 offers a number of improvements over v1:
|
||||
|
||||
@@ -62,6 +62,11 @@ Potential drawbacks:
|
||||
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
|
||||
tracking.
|
||||
|
||||
If you are still interested in the legacy `v1`, you can import it from
|
||||
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
|
||||
here: https://pkg.go.dev/github.com/russross/blackfriday.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@@ -91,7 +96,7 @@ Here's an example of simple usage of Blackfriday together with Bluemonday:
|
||||
```go
|
||||
import (
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// ...
|
||||
@@ -104,6 +109,8 @@ html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
|
||||
If you want to customize the set of options, use `blackfriday.WithExtensions`,
|
||||
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
|
||||
|
||||
### `blackfriday-tool`
|
||||
|
||||
You can also check out `blackfriday-tool` for a more complete example
|
||||
of how to use it. Download and install it using:
|
||||
|
||||
@@ -114,7 +121,7 @@ markdown file using a standalone program. You can also browse the
|
||||
source directly on github if you are just looking for some example
|
||||
code:
|
||||
|
||||
* <http://github.com/russross/blackfriday-tool>
|
||||
* <https://github.com/russross/blackfriday-tool>
|
||||
|
||||
Note that if you have not already done so, installing
|
||||
`blackfriday-tool` will be sufficient to download and install
|
||||
@@ -123,6 +130,22 @@ installed in `$GOPATH/bin`. This is a statically-linked binary that
|
||||
can be copied to wherever you need it without worrying about
|
||||
dependencies and library versions.
|
||||
|
||||
### Sanitized anchor names
|
||||
|
||||
Blackfriday includes an algorithm for creating sanitized anchor names
|
||||
corresponding to a given input text. This algorithm is used to create
|
||||
anchors for headings when `AutoHeadingIDs` extension is enabled. The
|
||||
algorithm has a specification, so that other packages can create
|
||||
compatible anchor names and links to those anchors.
|
||||
|
||||
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.
|
||||
|
||||
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
|
||||
create compatible links to the anchor names generated by blackfriday.
|
||||
This algorithm is also implemented in a small standalone package at
|
||||
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
|
||||
that want a small package and don't need full functionality of blackfriday.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
@@ -199,6 +222,15 @@ implements the following extensions:
|
||||
You can use 3 or more backticks to mark the beginning of the
|
||||
block, and the same number to mark the end of the block.
|
||||
|
||||
To preserve classes of fenced code blocks while using the bluemonday
|
||||
HTML sanitizer, use the following policy:
|
||||
|
||||
```go
|
||||
p := bluemonday.UGCPolicy()
|
||||
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
|
||||
html := p.SanitizeBytes(unsafe)
|
||||
```
|
||||
|
||||
* **Definition lists**. A simple definition list is made of a single-line
|
||||
term followed by a colon and the definition for that term.
|
||||
|
||||
@@ -250,7 +282,7 @@ Other renderers
|
||||
Blackfriday is structured to allow alternative rendering engines. Here
|
||||
are a few of note:
|
||||
|
||||
* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown):
|
||||
* [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
|
||||
provides a GitHub Flavored Markdown renderer with fenced code block
|
||||
highlighting, clickable heading anchor links.
|
||||
|
||||
@@ -261,20 +293,28 @@ are a few of note:
|
||||
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
||||
but for markdown.
|
||||
|
||||
* [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX):
|
||||
* [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
|
||||
renders output as LaTeX.
|
||||
|
||||
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
|
||||
integration with the [Chroma](https://github.com/alecthomas/chroma) code
|
||||
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
|
||||
provides a drop-in renderer ready to use with Blackfriday, as well as
|
||||
options and means for further customization.
|
||||
|
||||
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
|
||||
|
||||
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
|
||||
|
||||
Todo
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
* More unit testing
|
||||
* Improve unicode support. It does not understand all unicode
|
||||
* Improve Unicode support. It does not understand all Unicode
|
||||
rules (about what constitutes a letter, a punctuation symbol,
|
||||
etc.), so it may fail to detect word boundaries correctly in
|
||||
some instances. It is safe on all utf-8 input.
|
||||
some instances. It is safe on all UTF-8 input.
|
||||
|
||||
|
||||
License
|
||||
@@ -286,6 +326,10 @@ License
|
||||
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
|
||||
[2]: https://golang.org/ "Go Language"
|
||||
[3]: https://github.com/vmg/sundown "Sundown"
|
||||
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func"
|
||||
[4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
|
||||
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
|
||||
[6]: https://labix.org/gopkg.in "gopkg.in"
|
||||
|
||||
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
|
||||
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
|
||||
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
|
||||
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2
|
||||
|
||||
+26
-4
@@ -18,8 +18,7 @@ import (
|
||||
"html"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/shurcooL/sanitized_anchor_name"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -259,7 +258,7 @@ func (p *Markdown) prefixHeading(data []byte) int {
|
||||
}
|
||||
if end > i {
|
||||
if id == "" && p.extensions&AutoHeadingIDs != 0 {
|
||||
id = sanitized_anchor_name.Create(string(data[i:end]))
|
||||
id = SanitizedAnchorName(string(data[i:end]))
|
||||
}
|
||||
block := p.addBlock(Heading, data[i:end])
|
||||
block.HeadingID = id
|
||||
@@ -673,6 +672,7 @@ func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
|
||||
if beg == 0 || beg >= len(data) {
|
||||
return 0
|
||||
}
|
||||
fenceLength := beg - 1
|
||||
|
||||
var work bytes.Buffer
|
||||
work.Write([]byte(info))
|
||||
@@ -706,6 +706,7 @@ func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
|
||||
if doRender {
|
||||
block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer
|
||||
block.IsFenced = true
|
||||
block.FenceLength = fenceLength
|
||||
finalizeCodeBlock(block)
|
||||
}
|
||||
|
||||
@@ -1503,7 +1504,7 @@ func (p *Markdown) paragraph(data []byte) int {
|
||||
|
||||
id := ""
|
||||
if p.extensions&AutoHeadingIDs != 0 {
|
||||
id = sanitized_anchor_name.Create(string(data[prev:eol]))
|
||||
id = SanitizedAnchorName(string(data[prev:eol]))
|
||||
}
|
||||
|
||||
block := p.addBlock(Heading, data[prev:eol])
|
||||
@@ -1588,3 +1589,24 @@ func skipUntilChar(text []byte, start int, char byte) int {
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// SanitizedAnchorName returns a sanitized anchor name for the given text.
|
||||
//
|
||||
// It implements the algorithm specified in the package comment.
|
||||
func SanitizedAnchorName(text string) string {
|
||||
var anchorName []rune
|
||||
futureDash := false
|
||||
for _, r := range text {
|
||||
switch {
|
||||
case unicode.IsLetter(r) || unicode.IsNumber(r):
|
||||
if futureDash && len(anchorName) > 0 {
|
||||
anchorName = append(anchorName, '-')
|
||||
}
|
||||
futureDash = false
|
||||
anchorName = append(anchorName, unicode.ToLower(r))
|
||||
default:
|
||||
futureDash = true
|
||||
}
|
||||
}
|
||||
return string(anchorName)
|
||||
}
|
||||
|
||||
+28
@@ -15,4 +15,32 @@
|
||||
//
|
||||
// If you're interested in calling Blackfriday from command line, see
|
||||
// https://github.com/russross/blackfriday-tool.
|
||||
//
|
||||
// Sanitized Anchor Names
|
||||
//
|
||||
// Blackfriday includes an algorithm for creating sanitized anchor names
|
||||
// corresponding to a given input text. This algorithm is used to create
|
||||
// anchors for headings when AutoHeadingIDs extension is enabled. The
|
||||
// algorithm is specified below, so that other packages can create
|
||||
// compatible anchor names and links to those anchors.
|
||||
//
|
||||
// The algorithm iterates over the input text, interpreted as UTF-8,
|
||||
// one Unicode code point (rune) at a time. All runes that are letters (category L)
|
||||
// or numbers (category N) are considered valid characters. They are mapped to
|
||||
// lower case, and included in the output. All other runes are considered
|
||||
// invalid characters. Invalid characters that precede the first valid character,
|
||||
// as well as invalid character that follow the last valid character
|
||||
// are dropped completely. All other sequences of invalid characters
|
||||
// between two valid characters are replaced with a single dash character '-'.
|
||||
//
|
||||
// SanitizedAnchorName exposes this functionality, and can be used to
|
||||
// create compatible links to the anchor names generated by blackfriday.
|
||||
// This algorithm is also implemented in a small standalone package at
|
||||
// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients
|
||||
// that want a small package and don't need full functionality of blackfriday.
|
||||
package blackfriday
|
||||
|
||||
// NOTE: Keep Sanitized Anchor Name algorithm in sync with package
|
||||
// github.com/shurcooL/sanitized_anchor_name.
|
||||
// Otherwise, users of sanitized_anchor_name will get anchor names
|
||||
// that are incompatible with those generated by blackfriday.
|
||||
|
||||
+2236
File diff suppressed because it is too large
Load Diff
+39
-3
@@ -13,13 +13,27 @@ var htmlEscaper = [256][]byte{
|
||||
}
|
||||
|
||||
func escapeHTML(w io.Writer, s []byte) {
|
||||
escapeEntities(w, s, false)
|
||||
}
|
||||
|
||||
func escapeAllHTML(w io.Writer, s []byte) {
|
||||
escapeEntities(w, s, true)
|
||||
}
|
||||
|
||||
func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) {
|
||||
var start, end int
|
||||
for end < len(s) {
|
||||
escSeq := htmlEscaper[s[end]]
|
||||
if escSeq != nil {
|
||||
w.Write(s[start:end])
|
||||
w.Write(escSeq)
|
||||
start = end + 1
|
||||
isEntity, entityEnd := nodeIsEntity(s, end)
|
||||
if isEntity && !escapeValidEntities {
|
||||
w.Write(s[start : entityEnd+1])
|
||||
start = entityEnd + 1
|
||||
} else {
|
||||
w.Write(s[start:end])
|
||||
w.Write(escSeq)
|
||||
start = end + 1
|
||||
}
|
||||
}
|
||||
end++
|
||||
}
|
||||
@@ -28,6 +42,28 @@ func escapeHTML(w io.Writer, s []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) {
|
||||
isEntity = false
|
||||
endEntityPos = end + 1
|
||||
|
||||
if s[end] == '&' {
|
||||
for endEntityPos < len(s) {
|
||||
if s[endEntityPos] == ';' {
|
||||
if entities[string(s[end:endEntityPos+1])] {
|
||||
isEntity = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' {
|
||||
break
|
||||
}
|
||||
endEntityPos++
|
||||
}
|
||||
}
|
||||
|
||||
return isEntity, endEntityPos
|
||||
}
|
||||
|
||||
func escLink(w io.Writer, text []byte) {
|
||||
unesc := html.UnescapeString(string(text))
|
||||
escapeHTML(w, []byte(unesc))
|
||||
|
||||
+6
-3
@@ -132,7 +132,10 @@ func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||
}
|
||||
|
||||
if params.FootnoteReturnLinkContents == "" {
|
||||
params.FootnoteReturnLinkContents = `<sup>[return]</sup>`
|
||||
// U+FE0E is VARIATION SELECTOR-15.
|
||||
// It suppresses automatic emoji presentation of the preceding
|
||||
// U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS.
|
||||
params.FootnoteReturnLinkContents = "<span aria-label='Return'>↩\ufe0e</span>"
|
||||
}
|
||||
|
||||
return &HTMLRenderer{
|
||||
@@ -616,7 +619,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
||||
}
|
||||
case Code:
|
||||
r.out(w, codeTag)
|
||||
escapeHTML(w, node.Literal)
|
||||
escapeAllHTML(w, node.Literal)
|
||||
r.out(w, codeCloseTag)
|
||||
case Document:
|
||||
break
|
||||
@@ -762,7 +765,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
||||
r.cr(w)
|
||||
r.out(w, preTag)
|
||||
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
||||
escapeHTML(w, node.Literal)
|
||||
escapeAllHTML(w, node.Literal)
|
||||
r.out(w, codeCloseTag)
|
||||
r.out(w, preCloseTag)
|
||||
if node.Parent.Type != Item {
|
||||
|
||||
+1
-1
@@ -278,7 +278,7 @@ func link(p *Markdown, data []byte, offset int) (int, *Node) {
|
||||
case data[i] == '\n':
|
||||
textHasNl = true
|
||||
|
||||
case data[i-1] == '\\':
|
||||
case isBackslashEscaped(data, i):
|
||||
continue
|
||||
|
||||
case data[i] == '[':
|
||||
|
||||
+9
-3
@@ -199,7 +199,8 @@ func (n *Node) InsertBefore(sibling *Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) isContainer() bool {
|
||||
// IsContainer returns true if 'n' can contain children.
|
||||
func (n *Node) IsContainer() bool {
|
||||
switch n.Type {
|
||||
case Document:
|
||||
fallthrough
|
||||
@@ -238,6 +239,11 @@ func (n *Node) isContainer() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// IsLeaf returns true if 'n' is a leaf node.
|
||||
func (n *Node) IsLeaf() bool {
|
||||
return !n.IsContainer()
|
||||
}
|
||||
|
||||
func (n *Node) canContain(t NodeType) bool {
|
||||
if n.Type == List {
|
||||
return t == Item
|
||||
@@ -309,11 +315,11 @@ func newNodeWalker(root *Node) *nodeWalker {
|
||||
}
|
||||
|
||||
func (nw *nodeWalker) next() {
|
||||
if (!nw.current.isContainer() || !nw.entering) && nw.current == nw.root {
|
||||
if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root {
|
||||
nw.current = nil
|
||||
return
|
||||
}
|
||||
if nw.entering && nw.current.isContainer() {
|
||||
if nw.entering && nw.current.IsContainer() {
|
||||
if nw.current.FirstChild != nil {
|
||||
nw.current = nw.current.FirstChild
|
||||
nw.entering = true
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.x
|
||||
- master
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
||||
install:
|
||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||
- go tool vet .
|
||||
- go test -v -race ./...
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015 Dmitri Shuralyov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
sanitized_anchor_name
|
||||
=====================
|
||||
|
||||
[](https://travis-ci.org/shurcooL/sanitized_anchor_name) [](https://godoc.org/github.com/shurcooL/sanitized_anchor_name)
|
||||
|
||||
Package sanitized_anchor_name provides a func to create sanitized anchor names.
|
||||
|
||||
Its logic can be reused by multiple packages to create interoperable anchor names
|
||||
and links to those anchors.
|
||||
|
||||
At this time, it does not try to ensure that generated anchor names
|
||||
are unique, that responsibility falls on the caller.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```bash
|
||||
go get -u github.com/shurcooL/sanitized_anchor_name
|
||||
```
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
```Go
|
||||
anchorName := sanitized_anchor_name.Create("This is a header")
|
||||
|
||||
fmt.Println(anchorName)
|
||||
|
||||
// Output:
|
||||
// this-is-a-header
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
- [MIT License](LICENSE)
|
||||
-1
@@ -1 +0,0 @@
|
||||
module github.com/shurcooL/sanitized_anchor_name
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
// Package sanitized_anchor_name provides a func to create sanitized anchor names.
|
||||
//
|
||||
// Its logic can be reused by multiple packages to create interoperable anchor names
|
||||
// and links to those anchors.
|
||||
//
|
||||
// At this time, it does not try to ensure that generated anchor names
|
||||
// are unique, that responsibility falls on the caller.
|
||||
package sanitized_anchor_name // import "github.com/shurcooL/sanitized_anchor_name"
|
||||
|
||||
import "unicode"
|
||||
|
||||
// Create returns a sanitized anchor name for the given text.
|
||||
func Create(text string) string {
|
||||
var anchorName []rune
|
||||
var futureDash = false
|
||||
for _, r := range text {
|
||||
switch {
|
||||
case unicode.IsLetter(r) || unicode.IsNumber(r):
|
||||
if futureDash && len(anchorName) > 0 {
|
||||
anchorName = append(anchorName, '-')
|
||||
}
|
||||
futureDash = false
|
||||
anchorName = append(anchorName, unicode.ToLower(r))
|
||||
default:
|
||||
futureDash = true
|
||||
}
|
||||
}
|
||||
return string(anchorName)
|
||||
}
|
||||
+6
-5
@@ -152,11 +152,12 @@ are not thread safe. To work with data in multiple goroutines you must start
|
||||
a transaction for each one or use locking to ensure only one goroutine accesses
|
||||
a transaction at a time. Creating transaction from the `DB` is thread safe.
|
||||
|
||||
Read-only transactions and read-write transactions should not depend on one
|
||||
another and generally shouldn't be opened simultaneously in the same goroutine.
|
||||
This can cause a deadlock as the read-write transaction needs to periodically
|
||||
re-map the data file but it cannot do so while a read-only transaction is open.
|
||||
|
||||
Transactions should not depend on one another and generally shouldn't be opened
|
||||
simultaneously in the same goroutine. This can cause a deadlock as the read-write
|
||||
transaction needs to periodically re-map the data file but it cannot do so while
|
||||
any read-only transaction is open. Even a nested read-only transaction can cause
|
||||
a deadlock, as the child transaction can block the parent transaction from releasing
|
||||
its resources.
|
||||
|
||||
#### Read-write transactions
|
||||
|
||||
|
||||
+24
-33
@@ -2,7 +2,6 @@ package bbolt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -94,24 +93,8 @@ func (f *freelist) pending_count() int {
|
||||
return count
|
||||
}
|
||||
|
||||
// copyallunsafe copies a list of all free ids and all pending ids in one sorted list.
|
||||
// copyall copies a list of all free ids and all pending ids in one sorted list.
|
||||
// f.count returns the minimum length required for dst.
|
||||
func (f *freelist) copyallunsafe(dstptr unsafe.Pointer) { // dstptr is []pgid data pointer
|
||||
m := make(pgids, 0, f.pending_count())
|
||||
for _, txp := range f.pending {
|
||||
m = append(m, txp.ids...)
|
||||
}
|
||||
sort.Sort(m)
|
||||
fpgids := f.getFreePageIDs()
|
||||
sz := len(fpgids) + len(m)
|
||||
dst := *(*[]pgid)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(dstptr),
|
||||
Len: sz,
|
||||
Cap: sz,
|
||||
}))
|
||||
mergepgids(dst, fpgids, m)
|
||||
}
|
||||
|
||||
func (f *freelist) copyall(dst []pgid) {
|
||||
m := make(pgids, 0, f.pending_count())
|
||||
for _, txp := range f.pending {
|
||||
@@ -284,21 +267,23 @@ func (f *freelist) read(p *page) {
|
||||
}
|
||||
// If the page.count is at the max uint16 value (64k) then it's considered
|
||||
// an overflow and the size of the freelist is stored as the first element.
|
||||
var idx, count uintptr = 0, uintptr(p.count)
|
||||
var idx, count = 0, int(p.count)
|
||||
if count == 0xFFFF {
|
||||
idx = 1
|
||||
count = uintptr(*(*pgid)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p))))
|
||||
c := *(*pgid)(unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)))
|
||||
count = int(c)
|
||||
if count < 0 {
|
||||
panic(fmt.Sprintf("leading element count %d overflows int", c))
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the list of page ids from the freelist.
|
||||
if count == 0 {
|
||||
f.ids = nil
|
||||
} else {
|
||||
ids := *(*[]pgid)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p) + idx*unsafe.Sizeof(pgid(0)),
|
||||
Len: int(count),
|
||||
Cap: int(count),
|
||||
}))
|
||||
var ids []pgid
|
||||
data := unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p), unsafe.Sizeof(ids[0]), idx)
|
||||
unsafeSlice(unsafe.Pointer(&ids), data, count)
|
||||
|
||||
// copy the ids, so we don't modify on the freelist page directly
|
||||
idsCopy := make([]pgid, count)
|
||||
@@ -331,16 +316,22 @@ func (f *freelist) write(p *page) error {
|
||||
|
||||
// The page.count can only hold up to 64k elements so if we overflow that
|
||||
// number then we handle it by putting the size in the first element.
|
||||
lenids := f.count()
|
||||
if lenids == 0 {
|
||||
p.count = uint16(lenids)
|
||||
} else if lenids < 0xFFFF {
|
||||
p.count = uint16(lenids)
|
||||
f.copyallunsafe(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p)))
|
||||
l := f.count()
|
||||
if l == 0 {
|
||||
p.count = uint16(l)
|
||||
} else if l < 0xFFFF {
|
||||
p.count = uint16(l)
|
||||
var ids []pgid
|
||||
data := unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))
|
||||
unsafeSlice(unsafe.Pointer(&ids), data, l)
|
||||
f.copyall(ids)
|
||||
} else {
|
||||
p.count = 0xFFFF
|
||||
*(*pgid)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p))) = pgid(lenids)
|
||||
f.copyallunsafe(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p) + unsafe.Sizeof(pgid(0))))
|
||||
var ids []pgid
|
||||
data := unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))
|
||||
unsafeSlice(unsafe.Pointer(&ids), data, l+1)
|
||||
ids[0] = pgid(l)
|
||||
f.copyall(ids[1:])
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+10
-15
@@ -3,7 +3,6 @@ package bbolt
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -208,36 +207,32 @@ func (n *node) write(p *page) {
|
||||
}
|
||||
|
||||
// Loop over each item and write it to the page.
|
||||
bp := uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p) + n.pageElementSize()*uintptr(len(n.inodes))
|
||||
// off tracks the offset into p of the start of the next data.
|
||||
off := unsafe.Sizeof(*p) + n.pageElementSize()*uintptr(len(n.inodes))
|
||||
for i, item := range n.inodes {
|
||||
_assert(len(item.key) > 0, "write: zero-length inode key")
|
||||
|
||||
// Create a slice to write into of needed size and advance
|
||||
// byte pointer for next iteration.
|
||||
sz := len(item.key) + len(item.value)
|
||||
b := unsafeByteSlice(unsafe.Pointer(p), off, 0, sz)
|
||||
off += uintptr(sz)
|
||||
|
||||
// Write the page element.
|
||||
if n.isLeaf {
|
||||
elem := p.leafPageElement(uint16(i))
|
||||
elem.pos = uint32(bp - uintptr(unsafe.Pointer(elem)))
|
||||
elem.pos = uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(elem)))
|
||||
elem.flags = item.flags
|
||||
elem.ksize = uint32(len(item.key))
|
||||
elem.vsize = uint32(len(item.value))
|
||||
} else {
|
||||
elem := p.branchPageElement(uint16(i))
|
||||
elem.pos = uint32(bp - uintptr(unsafe.Pointer(elem)))
|
||||
elem.pos = uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(elem)))
|
||||
elem.ksize = uint32(len(item.key))
|
||||
elem.pgid = item.pgid
|
||||
_assert(elem.pgid != p.id, "write: circular dependency occurred")
|
||||
}
|
||||
|
||||
// Create a slice to write into of needed size and advance
|
||||
// byte pointer for next iteration.
|
||||
klen, vlen := len(item.key), len(item.value)
|
||||
sz := klen + vlen
|
||||
b := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: bp,
|
||||
Len: sz,
|
||||
Cap: sz,
|
||||
}))
|
||||
bp += uintptr(sz)
|
||||
|
||||
// Write data for the element to the end of the page.
|
||||
l := copy(b, item.key)
|
||||
copy(b[l:], item.value)
|
||||
|
||||
+21
-36
@@ -3,7 +3,6 @@ package bbolt
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -51,13 +50,13 @@ func (p *page) typ() string {
|
||||
|
||||
// meta returns a pointer to the metadata section of the page.
|
||||
func (p *page) meta() *meta {
|
||||
return (*meta)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p)))
|
||||
return (*meta)(unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)))
|
||||
}
|
||||
|
||||
// leafPageElement retrieves the leaf node by index
|
||||
func (p *page) leafPageElement(index uint16) *leafPageElement {
|
||||
off := uintptr(index) * unsafe.Sizeof(leafPageElement{})
|
||||
return (*leafPageElement)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p) + off))
|
||||
return (*leafPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
|
||||
leafPageElementSize, int(index)))
|
||||
}
|
||||
|
||||
// leafPageElements retrieves a list of leaf nodes.
|
||||
@@ -65,17 +64,16 @@ func (p *page) leafPageElements() []leafPageElement {
|
||||
if p.count == 0 {
|
||||
return nil
|
||||
}
|
||||
return *(*[]leafPageElement)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p),
|
||||
Len: int(p.count),
|
||||
Cap: int(p.count),
|
||||
}))
|
||||
var elems []leafPageElement
|
||||
data := unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))
|
||||
unsafeSlice(unsafe.Pointer(&elems), data, int(p.count))
|
||||
return elems
|
||||
}
|
||||
|
||||
// branchPageElement retrieves the branch node by index
|
||||
func (p *page) branchPageElement(index uint16) *branchPageElement {
|
||||
off := uintptr(index) * unsafe.Sizeof(branchPageElement{})
|
||||
return (*branchPageElement)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p) + off))
|
||||
return (*branchPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
|
||||
unsafe.Sizeof(branchPageElement{}), int(index)))
|
||||
}
|
||||
|
||||
// branchPageElements retrieves a list of branch nodes.
|
||||
@@ -83,20 +81,15 @@ func (p *page) branchPageElements() []branchPageElement {
|
||||
if p.count == 0 {
|
||||
return nil
|
||||
}
|
||||
return *(*[]branchPageElement)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(*p),
|
||||
Len: int(p.count),
|
||||
Cap: int(p.count),
|
||||
}))
|
||||
var elems []branchPageElement
|
||||
data := unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))
|
||||
unsafeSlice(unsafe.Pointer(&elems), data, int(p.count))
|
||||
return elems
|
||||
}
|
||||
|
||||
// dump writes n bytes of the page to STDERR as hex output.
|
||||
func (p *page) hexdump(n int) {
|
||||
buf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(p)),
|
||||
Len: n,
|
||||
Cap: n,
|
||||
}))
|
||||
buf := unsafeByteSlice(unsafe.Pointer(p), 0, 0, n)
|
||||
fmt.Fprintf(os.Stderr, "%x\n", buf)
|
||||
}
|
||||
|
||||
@@ -115,11 +108,7 @@ type branchPageElement struct {
|
||||
|
||||
// key returns a byte slice of the node key.
|
||||
func (n *branchPageElement) key() []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(n)) + uintptr(n.pos),
|
||||
Len: int(n.ksize),
|
||||
Cap: int(n.ksize),
|
||||
}))
|
||||
return unsafeByteSlice(unsafe.Pointer(n), 0, int(n.pos), int(n.pos)+int(n.ksize))
|
||||
}
|
||||
|
||||
// leafPageElement represents a node on a leaf page.
|
||||
@@ -132,20 +121,16 @@ type leafPageElement struct {
|
||||
|
||||
// key returns a byte slice of the node key.
|
||||
func (n *leafPageElement) key() []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(n)) + uintptr(n.pos),
|
||||
Len: int(n.ksize),
|
||||
Cap: int(n.ksize),
|
||||
}))
|
||||
i := int(n.pos)
|
||||
j := i + int(n.ksize)
|
||||
return unsafeByteSlice(unsafe.Pointer(n), 0, i, j)
|
||||
}
|
||||
|
||||
// value returns a byte slice of the node value.
|
||||
func (n *leafPageElement) value() []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(n)) + uintptr(n.pos) + uintptr(n.ksize),
|
||||
Len: int(n.vsize),
|
||||
Cap: int(n.vsize),
|
||||
}))
|
||||
i := int(n.pos) + int(n.ksize)
|
||||
j := i + int(n.vsize)
|
||||
return unsafeByteSlice(unsafe.Pointer(n), 0, i, j)
|
||||
}
|
||||
|
||||
// PageInfo represents human readable information about a page.
|
||||
|
||||
+8
-19
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -524,24 +523,18 @@ func (tx *Tx) write() error {
|
||||
|
||||
// Write pages to disk in order.
|
||||
for _, p := range pages {
|
||||
size := (int(p.overflow) + 1) * tx.db.pageSize
|
||||
rem := (uint64(p.overflow) + 1) * uint64(tx.db.pageSize)
|
||||
offset := int64(p.id) * int64(tx.db.pageSize)
|
||||
var written uintptr
|
||||
|
||||
// Write out page in "max allocation" sized chunks.
|
||||
ptr := uintptr(unsafe.Pointer(p))
|
||||
for {
|
||||
// Limit our write to our max allocation size.
|
||||
sz := size
|
||||
sz := rem
|
||||
if sz > maxAllocSize-1 {
|
||||
sz = maxAllocSize - 1
|
||||
}
|
||||
buf := unsafeByteSlice(unsafe.Pointer(p), written, 0, int(sz))
|
||||
|
||||
// Write chunk to disk.
|
||||
buf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: ptr,
|
||||
Len: sz,
|
||||
Cap: sz,
|
||||
}))
|
||||
if _, err := tx.db.ops.writeAt(buf, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -550,14 +543,14 @@ func (tx *Tx) write() error {
|
||||
tx.stats.Write++
|
||||
|
||||
// Exit inner for loop if we've written all the chunks.
|
||||
size -= sz
|
||||
if size == 0 {
|
||||
rem -= sz
|
||||
if rem == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Otherwise move offset forward and move pointer to next chunk.
|
||||
offset += int64(sz)
|
||||
ptr += uintptr(sz)
|
||||
written += uintptr(sz)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,11 +569,7 @@ func (tx *Tx) write() error {
|
||||
continue
|
||||
}
|
||||
|
||||
buf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(p)),
|
||||
Len: tx.db.pageSize,
|
||||
Cap: tx.db.pageSize,
|
||||
}))
|
||||
buf := unsafeByteSlice(unsafe.Pointer(p), 0, 0, tx.db.pageSize)
|
||||
|
||||
// See https://go.googlesource.com/go/+/f03c9202c43e0abb130669852082117ca50aa9b1
|
||||
for i := range buf {
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package bbolt
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func unsafeAdd(base unsafe.Pointer, offset uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(base) + offset)
|
||||
}
|
||||
|
||||
func unsafeIndex(base unsafe.Pointer, offset uintptr, elemsz uintptr, n int) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(base) + offset + uintptr(n)*elemsz)
|
||||
}
|
||||
|
||||
func unsafeByteSlice(base unsafe.Pointer, offset uintptr, i, j int) []byte {
|
||||
// See: https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices
|
||||
//
|
||||
// This memory is not allocated from C, but it is unmanaged by Go's
|
||||
// garbage collector and should behave similarly, and the compiler
|
||||
// should produce similar code. Note that this conversion allows a
|
||||
// subslice to begin after the base address, with an optional offset,
|
||||
// while the URL above does not cover this case and only slices from
|
||||
// index 0. However, the wiki never says that the address must be to
|
||||
// the beginning of a C allocation (or even that malloc was used at
|
||||
// all), so this is believed to be correct.
|
||||
return (*[maxAllocSize]byte)(unsafeAdd(base, offset))[i:j:j]
|
||||
}
|
||||
|
||||
// unsafeSlice modifies the data, len, and cap of a slice variable pointed to by
|
||||
// the slice parameter. This helper should be used over other direct
|
||||
// manipulation of reflect.SliceHeader to prevent misuse, namely, converting
|
||||
// from reflect.SliceHeader to a Go slice type.
|
||||
func unsafeSlice(slice, data unsafe.Pointer, len int) {
|
||||
s := (*reflect.SliceHeader)(slice)
|
||||
s.Data = uintptr(data)
|
||||
s.Cap = len
|
||||
s.Len = len
|
||||
}
|
||||
Vendored
+6
-8
@@ -104,12 +104,12 @@ github.com/go-pkgz/expirable-cache
|
||||
# github.com/go-pkgz/jrpc v0.2.0
|
||||
## explicit
|
||||
github.com/go-pkgz/jrpc
|
||||
# github.com/go-pkgz/lcw v0.7.1
|
||||
# github.com/go-pkgz/lcw v0.8.1
|
||||
## explicit
|
||||
github.com/go-pkgz/lcw
|
||||
github.com/go-pkgz/lcw/eventbus
|
||||
github.com/go-pkgz/lcw/internal/cache
|
||||
# github.com/go-pkgz/lgr v0.7.0
|
||||
# github.com/go-pkgz/lgr v0.10.4
|
||||
## explicit
|
||||
github.com/go-pkgz/lgr
|
||||
# github.com/go-pkgz/repeater v1.1.3
|
||||
@@ -123,7 +123,7 @@ github.com/go-pkgz/rest/logger
|
||||
# github.com/go-pkgz/syncs v1.1.1
|
||||
## explicit
|
||||
github.com/go-pkgz/syncs
|
||||
# github.com/go-redis/redis/v7 v7.2.0
|
||||
# github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/go-redis/redis/v7
|
||||
github.com/go-redis/redis/v7/internal
|
||||
github.com/go-redis/redis/v7/internal/consistenthash
|
||||
@@ -137,7 +137,7 @@ github.com/go-stack/stack
|
||||
github.com/golang/protobuf/proto
|
||||
# github.com/golang/snappy v0.0.1
|
||||
github.com/golang/snappy
|
||||
# github.com/google/uuid v1.1.1
|
||||
# github.com/google/uuid v1.1.2
|
||||
## explicit
|
||||
github.com/google/uuid
|
||||
# github.com/gorilla/feeds v1.1.1
|
||||
@@ -178,11 +178,9 @@ github.com/rakyll/statik/fs
|
||||
# github.com/rs/xid v1.2.1
|
||||
## explicit
|
||||
github.com/rs/xid
|
||||
# github.com/russross/blackfriday/v2 v2.0.1
|
||||
# github.com/russross/blackfriday/v2 v2.1.0
|
||||
## explicit
|
||||
github.com/russross/blackfriday/v2
|
||||
# github.com/shurcooL/sanitized_anchor_name v1.0.0
|
||||
github.com/shurcooL/sanitized_anchor_name
|
||||
# github.com/stretchr/objx v0.1.1
|
||||
github.com/stretchr/objx
|
||||
# github.com/stretchr/testify v1.6.1
|
||||
@@ -197,7 +195,7 @@ github.com/umputun/go-flags
|
||||
github.com/xdg/scram
|
||||
# github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc
|
||||
github.com/xdg/stringprep
|
||||
# go.etcd.io/bbolt v1.3.4
|
||||
# go.etcd.io/bbolt v1.3.5
|
||||
## explicit
|
||||
go.etcd.io/bbolt
|
||||
# go.mongodb.org/mongo-driver v1.4.4
|
||||
|
||||
Reference in New Issue
Block a user