Compare commits
58
Commits
058bcd7334
...
v0.12.24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4f941784f | ||
|
|
62142c8ccd | ||
|
|
53f00ce02a | ||
|
|
a53ec582a6 | ||
|
|
8b8c48900e | ||
|
|
c0abde3157 | ||
|
|
94c6110f2e | ||
|
|
55e80c3883 | ||
|
|
6d280e7b64 | ||
|
|
e6918a5857 | ||
|
|
dca8245ba4 | ||
|
|
9550817078 | ||
|
|
7a91860735 | ||
|
|
0f4abac8c2 | ||
|
|
b16050b978 | ||
|
|
aced62fec3 | ||
|
|
5155e24bc7 | ||
|
|
3aabe3a51d | ||
|
|
17de015b90 | ||
|
|
f19e71b333 | ||
|
|
8fea354b74 | ||
|
|
7a81c27cc6 | ||
|
|
ea5808d833 | ||
|
|
8b045a826a | ||
|
|
1a6e078510 | ||
|
|
daff8010cd | ||
|
|
4035930e0e | ||
|
|
fc63a384b2 | ||
|
|
2f3449b651 | ||
|
|
31c65a39e4 | ||
|
|
7ba20928c3 | ||
|
|
c482cbbe70 | ||
|
|
4042a3e406 | ||
|
|
392132dc3b | ||
|
|
459a3dc58c | ||
|
|
194a8b0922 | ||
|
|
8a8336ae08 | ||
|
|
458e0b3b8b | ||
|
|
9758dc6422 | ||
|
|
e4a9fb8a27 | ||
|
|
f0473fde17 | ||
|
|
e2980a5210 | ||
|
|
764da14440 | ||
|
|
7da6c627fe | ||
|
|
0a092b5b7f | ||
|
|
9f8af2327d | ||
|
|
345e24142e | ||
|
|
0fdd03ddee | ||
|
|
a0fa288cb6 | ||
|
|
70bac24832 | ||
|
|
1df40fbdeb | ||
|
|
91e9ffff85 | ||
|
|
853df14e2f | ||
|
|
e17a772bb6 | ||
|
|
90e027d9a4 | ||
|
|
fdc9a5d8b0 | ||
|
|
543891a0a0 | ||
|
|
06921443fc |
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# Except the dependencies, which we leave alone
|
# Except the dependencies, which we leave alone
|
||||||
vendor/** -text=auto
|
Godeps/** -text=auto
|
||||||
|
|
||||||
# Diffs on these files are meaningless
|
# Diffs on these files are meaningless
|
||||||
gui.files.go -diff
|
gui.files.go -diff
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ Chris Howie <me@chrishowie.com>
|
|||||||
Chris Joel <chris@scriptolo.gy>
|
Chris Joel <chris@scriptolo.gy>
|
||||||
Colin Kennedy <moshen.colin@gmail.com>
|
Colin Kennedy <moshen.colin@gmail.com>
|
||||||
Daniel Bergmann <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
|
Daniel Bergmann <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
|
||||||
Daniel Harte <daniel@harte.me> <daniel@danielharte.co.uk> <norgeous@users.noreply.github.com>
|
|
||||||
Daniel Martí <mvdan@mvdan.cc>
|
Daniel Martí <mvdan@mvdan.cc>
|
||||||
David Rimmer <dinosore@dbrsoftware.co.uk>
|
|
||||||
Denis A. <denisva@gmail.com>
|
Denis A. <denisva@gmail.com>
|
||||||
Dennis Wilson <dw@risu.io>
|
Dennis Wilson <dw@risu.io>
|
||||||
Dominik Heidler <dominik@heidler.eu>
|
Dominik Heidler <dominik@heidler.eu>
|
||||||
@@ -49,10 +47,8 @@ Jens Diemer <github.com@jensdiemer.de> <git@jensdiemer.de>
|
|||||||
Jochen Voss <voss@seehuhn.de>
|
Jochen Voss <voss@seehuhn.de>
|
||||||
Johan Vromans <jvromans@squirrel.nl>
|
Johan Vromans <jvromans@squirrel.nl>
|
||||||
Karol Różycki <rozycki.karol@gmail.com>
|
Karol Różycki <rozycki.karol@gmail.com>
|
||||||
Kelong Cong <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
|
|
||||||
Ken'ichi Kamada <kamada@nanohz.org>
|
Ken'ichi Kamada <kamada@nanohz.org>
|
||||||
Kevin Allen <kma1660@gmail.com>
|
Kevin Allen <kma1660@gmail.com>
|
||||||
Lars K.W. Gohlke <lkwg82@gmx.de>
|
|
||||||
Laurent Etiemble <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
Laurent Etiemble <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
||||||
Lode Hoste <zillode@zillode.be>
|
Lode Hoste <zillode@zillode.be>
|
||||||
Lord Landon Agahnim <lordlandon@gmail.com>
|
Lord Landon Agahnim <lordlandon@gmail.com>
|
||||||
|
|||||||
Generated
+90
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"ImportPath": "github.com/syncthing/syncthing",
|
||||||
|
"GoVersion": "go1.5.2",
|
||||||
|
"Packages": [
|
||||||
|
"./cmd/..."
|
||||||
|
],
|
||||||
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bkaradzic/go-lz4",
|
||||||
|
"Rev": "74ddf82598bc4745b965729e9c6a463bedd33049"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/calmh/du",
|
||||||
|
"Rev": "3c0690cca16228b97741327b1b6781397afbdb24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/calmh/luhn",
|
||||||
|
"Rev": "0c8388ff95fa92d4094011e5a04fc99dea3d1632"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/calmh/xdr",
|
||||||
|
"Rev": "9eb3e1a622d9364deb39c831f7e5f164393d7e37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/golang/snappy",
|
||||||
|
"Rev": "723cc1e459b8eea2dea4583200fd60757d40097a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/juju/ratelimit",
|
||||||
|
"Rev": "772f5c38e468398c4511514f4f6aa9a4185bc0a0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/kardianos/osext",
|
||||||
|
"Rev": "29ae4ffbc9a6fe9fb2bc5029050ce6996ea1d3bc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/rcrowley/go-metrics",
|
||||||
|
"Rev": "1ce93efbc8f9c568886b2ef85ce305b2217b3de3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||||
|
"Rev": "1a9d62f03ea92815b46fcaab357cfd4df264b1a0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/thejerf/suture",
|
||||||
|
"Comment": "v1.0.1",
|
||||||
|
"Rev": "99c1f2d613756768fc4299acd9dc621e11ed3fd7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/vitrun/qart/coding",
|
||||||
|
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/vitrun/qart/gf256",
|
||||||
|
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/vitrun/qart/qr",
|
||||||
|
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||||
|
"Rev": "575fdbe86e5dd89229707ebec0575ce7d088a4a6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
|
"Rev": "575fdbe86e5dd89229707ebec0575ce7d088a4a6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/internal/iana",
|
||||||
|
"Rev": "042ba42fa6633b34205efc66ba5719cd3afd8d38"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/ipv6",
|
||||||
|
"Rev": "042ba42fa6633b34205efc66ba5719cd3afd8d38"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/proxy",
|
||||||
|
"Rev": "042ba42fa6633b34205efc66ba5719cd3afd8d38"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/text/transform",
|
||||||
|
"Rev": "5eb8d4684c4796dd36c74f6452f2c0fa6c79597e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/text/unicode/norm",
|
||||||
|
"Rev": "5eb8d4684c4796dd36c74f6452f2c0fa6c79597e"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
||||||
Generated
+2
@@ -0,0 +1,2 @@
|
|||||||
|
/pkg
|
||||||
|
/bin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/lz4-example/lz4-example
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.1
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- tip
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
@@ -0,0 +1 @@
|
|||||||
|
coverage.out
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
install:
|
||||||
|
- export PATH=$PATH:$HOME/gopath/bin
|
||||||
|
- go get golang.org/x/tools/cover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./generate.sh
|
||||||
|
- go test -coverprofile=coverage.out
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- goveralls -coverprofile=coverage.out -service=travis-ci -package=calmh/xdr -repotoken="$COVERALLS_TOKEN"
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
secure: SmgnrGfp2zLrA44ChRMpjPeujubt9veZ8Fx/OseMWECmacyV5N/TuDhzIbwo6QwV4xB0sBacoPzvxQbJRVjNKsPiSu72UbcQmQ7flN4Tf7nW09tSh1iW8NgrpBCq/3UYLoBu2iPBEBKm93IK0aGNAKs6oEkB0fU27iTVBwiTXOY=
|
||||||
Generated
Vendored
Generated
Vendored
+4
-2
@@ -6,5 +6,7 @@ xdr
|
|||||||
[](http://godoc.org/github.com/calmh/xdr)
|
[](http://godoc.org/github.com/calmh/xdr)
|
||||||
[](http://opensource.org/licenses/MIT)
|
[](http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
This is an XDR marshalling/unmarshalling library. It uses code generation and
|
This is an XDR encoding/decoding library. It uses code generation and
|
||||||
not reflection.
|
not reflection. It supports the IPDR bastardized XDR format when built
|
||||||
|
with `-tags ipdr`.
|
||||||
|
|
||||||
Generated
Vendored
Generated
Vendored
+159
-215
@@ -36,73 +36,31 @@ type structInfo struct {
|
|||||||
Fields []fieldInfo
|
Fields []fieldInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i structInfo) SizeExpr() string {
|
var headerTpl = template.Must(template.New("header").Parse(`// ************************************************************
|
||||||
var xdrSizes = map[string]int{
|
|
||||||
"int8": 4,
|
|
||||||
"uint8": 4,
|
|
||||||
"int16": 4,
|
|
||||||
"uint16": 4,
|
|
||||||
"int32": 4,
|
|
||||||
"uint32": 4,
|
|
||||||
"int64": 8,
|
|
||||||
"uint64": 8,
|
|
||||||
"int": 8,
|
|
||||||
"bool": 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
var terms []string
|
|
||||||
nl := ""
|
|
||||||
for _, f := range i.Fields {
|
|
||||||
if size := xdrSizes[f.FieldType]; size > 0 {
|
|
||||||
if f.IsSlice {
|
|
||||||
terms = append(terms, nl+"4+len(o."+f.Name+")*"+strconv.Itoa(size))
|
|
||||||
} else {
|
|
||||||
terms = append(terms, strconv.Itoa(size))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch f.FieldType {
|
|
||||||
case "string", "[]byte":
|
|
||||||
if f.IsSlice {
|
|
||||||
terms = append(terms, nl+"4+xdr.SizeOfSlice(o."+f.Name+")")
|
|
||||||
} else {
|
|
||||||
terms = append(terms, nl+"4+len(o."+f.Name+")+xdr.Padding(len(o."+f.Name+"))")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if f.IsSlice {
|
|
||||||
terms = append(terms, nl+"4+xdr.SizeOfSlice(o."+f.Name+")")
|
|
||||||
} else {
|
|
||||||
terms = append(terms, nl+"o."+f.Name+".XDRSize()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nl = "\n"
|
|
||||||
}
|
|
||||||
return strings.Join(terms, "+")
|
|
||||||
}
|
|
||||||
|
|
||||||
var headerData = `// ************************************************************
|
|
||||||
// This file is automatically generated by genxdr. Do not edit.
|
// This file is automatically generated by genxdr. Do not edit.
|
||||||
// ************************************************************
|
// ************************************************************
|
||||||
|
|
||||||
package {{.Package}}
|
package {{.Package}}
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/calmh/xdr"
|
"github.com/calmh/xdr"
|
||||||
)
|
)
|
||||||
`
|
`))
|
||||||
|
|
||||||
var encoderData = `
|
var encodeTpl = template.Must(template.New("encoder").Parse(`
|
||||||
func (o {{.Name}}) XDRSize() int {
|
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
|
||||||
return {{.SizeExpr}}
|
var xw = xdr.NewWriter(w)
|
||||||
|
return o.EncodeXDRInto(xw)
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.Name}}) MarshalXDR() ([]byte, error) {
|
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
|
||||||
buf:= make([]byte, o.XDRSize())
|
return o.AppendXDR(make([]byte, 0, 128))
|
||||||
m := &xdr.Marshaller{Data: buf}
|
|
||||||
return buf, o.MarshalXDRInto(m)
|
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.Name}}) MustMarshalXDR() []byte {
|
func (o {{.TypeName}}) MustMarshalXDR() []byte {
|
||||||
bs, err := o.MarshalXDR()
|
bs, err := o.MarshalXDR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -110,155 +68,141 @@ func (o {{.Name}}) MustMarshalXDR() []byte {
|
|||||||
return bs
|
return bs
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.Name}}) MarshalXDRInto(m *xdr.Marshaller) error {
|
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
{{range $fi := .Fields}}
|
var aw = xdr.AppendWriter(bs)
|
||||||
{{if $fi.IsSlice}}
|
var xw = xdr.NewWriter(&aw)
|
||||||
{{template "marshalSlice" $fi}}
|
_, err := o.EncodeXDRInto(xw)
|
||||||
{{else}}
|
return []byte(aw), err
|
||||||
{{template "marshalValue" $fi}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
return m.Error
|
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
{{define "marshalValue"}}
|
func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
{{if ne .Convert ""}}
|
{{range $fieldInfo := .Fields}}
|
||||||
m.Marshal{{.Encoder}}({{.Convert}}(o.{{.Name}}))
|
{{if not $fieldInfo.IsSlice}}
|
||||||
{{else if .IsBasic}}
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
{{if ge .Max 1}}
|
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}))
|
||||||
if l := len(o.{{.Name}}); l > {{.Max}} {
|
{{else if $fieldInfo.IsBasic}}
|
||||||
return xdr.ElementSizeExceeded("{{.Name}}", l, {{.Max}})
|
{{if ge $fieldInfo.Max 1}}
|
||||||
|
if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}})
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
|
||||||
|
{{else}}
|
||||||
|
_, err := o.{{$fieldInfo.Name}}.EncodeXDRInto(xw)
|
||||||
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
{{if ge $fieldInfo.Max 1}}
|
||||||
|
if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}})
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}})))
|
||||||
|
for i := range o.{{$fieldInfo.Name}} {
|
||||||
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
|
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}[i]))
|
||||||
|
{{else if $fieldInfo.IsBasic}}
|
||||||
|
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
|
||||||
|
{{else}}
|
||||||
|
_, err := o.{{$fieldInfo.Name}}[i].EncodeXDRInto(xw)
|
||||||
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
m.Marshal{{.Encoder}}(o.{{.Name}})
|
|
||||||
{{else}}
|
|
||||||
if err := o.{{.Name}}.MarshalXDRInto(m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
return xw.Tot(), xw.Error()
|
||||||
|
|
||||||
{{define "marshalSlice"}}
|
|
||||||
{{if ge .Max 1}}
|
|
||||||
if l := len(o.{{.Name}}); l > {{.Max}} {
|
|
||||||
return xdr.ElementSizeExceeded("{{.Name}}", l, {{.Max}})
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
m.MarshalUint32(uint32(len(o.{{.Name}})))
|
|
||||||
for i := range o.{{.Name}} {
|
|
||||||
{{if ne .Convert ""}}
|
|
||||||
m.Marshal{{.Encoder}}({{.Convert}}(o.{{.Name}}[i]))
|
|
||||||
{{else if .IsBasic}}
|
|
||||||
m.Marshal{{.Encoder}}(o.{{.Name}}[i])
|
|
||||||
{{else}}
|
|
||||||
if err := o.{{.Name}}[i].MarshalXDRInto(m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
func (o *{{.Name}}) UnmarshalXDR(bs []byte) error {
|
|
||||||
u := &xdr.Unmarshaller{Data: bs}
|
|
||||||
return o.UnmarshalXDRFrom(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *{{.Name}}) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
|
||||||
{{range $fi := .Fields}}
|
|
||||||
{{if $fi.IsSlice}}
|
|
||||||
{{template "unmarshalSlice" $fi}}
|
|
||||||
{{else}}
|
|
||||||
{{template "unmarshalValue" $fi}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
return u.Error
|
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
{{define "unmarshalValue"}}
|
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
|
||||||
{{if ne .Convert ""}}
|
xr := xdr.NewReader(r)
|
||||||
o.{{.Name}} = {{.FieldType}}(u.Unmarshal{{.Encoder}}())
|
return o.DecodeXDRFrom(xr)
|
||||||
{{else if .IsBasic}}
|
}//+n
|
||||||
{{if ge .Max 1}}
|
|
||||||
o.{{.Name}} = u.Unmarshal{{.Encoder}}Max({{.Max}})
|
|
||||||
{{else}}
|
|
||||||
o.{{.Name}} = u.Unmarshal{{.Encoder}}()
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
|
||||||
(&o.{{.Name}}).UnmarshalXDRFrom(u)
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{define "unmarshalSlice"}}
|
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
|
||||||
_{{.Name}}Size := int(u.UnmarshalUint32())
|
var br = bytes.NewReader(bs)
|
||||||
if _{{.Name}}Size < 0 {
|
var xr = xdr.NewReader(br)
|
||||||
return xdr.ElementSizeExceeded("{{.Name}}", _{{.Name}}Size, {{.Max}})
|
return o.DecodeXDRFrom(xr)
|
||||||
} else if _{{.Name}}Size == 0 {
|
}//+n
|
||||||
o.{{.Name}} = nil
|
|
||||||
} else {
|
func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
{{if ge .Max 1}}
|
{{range $fieldInfo := .Fields}}
|
||||||
if _{{.Name}}Size > {{.Max}} {
|
{{if not $fieldInfo.IsSlice}}
|
||||||
return xdr.ElementSizeExceeded("{{.Name}}", _{{.Name}}Size, {{.Max}})
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
}
|
o.{{$fieldInfo.Name}} = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
|
||||||
{{end}}
|
{{else if $fieldInfo.IsBasic}}
|
||||||
if _{{.Name}}Size <= len(o.{{.Name}}) {
|
{{if ge $fieldInfo.Max 1}}
|
||||||
{{if eq .FieldType "string"}}
|
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Max}})
|
||||||
for i := _{{.Name}}Size; i < len(o.{{.Name}}); i++ { o.{{.Name}}[i] = "" }
|
|
||||||
{{end}}
|
|
||||||
{{if eq .FieldType "[]byte"}}
|
|
||||||
for i := _{{.Name}}Size; i < len(o.{{.Name}}); i++ { o.{{.Name}}[i] = nil }
|
|
||||||
{{end}}
|
|
||||||
o.{{.Name}} = o.{{.Name}}[:_{{.Name}}Size]
|
|
||||||
} else {
|
|
||||||
o.{{.Name}} = make([]{{.FieldType}}, _{{.Name}}Size)
|
|
||||||
}
|
|
||||||
for i := range o.{{.Name}} {
|
|
||||||
{{if ne .Convert ""}}
|
|
||||||
o.{{.Name}}[i] = {{.FieldType}}(u.Unmarshal{{.Encoder}}())
|
|
||||||
{{else if .IsBasic}}
|
|
||||||
{{if ge .Submax 1}}
|
|
||||||
o.{{.Name}}[i] = u.Unmarshal{{.Encoder}}Max({{.Submax}})
|
|
||||||
{{else}}
|
{{else}}
|
||||||
o.{{.Name}}[i] = u.Unmarshal{{.Encoder}}()
|
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
(&o.{{.Name}}[i]).UnmarshalXDRFrom(u)
|
(&o.{{$fieldInfo.Name}}).DecodeXDRFrom(xr)
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
{{else}}
|
||||||
}
|
_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
|
||||||
{{end}}
|
if _{{$fieldInfo.Name}}Size < 0 {
|
||||||
`
|
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
|
||||||
|
}
|
||||||
var (
|
{{if ge $fieldInfo.Max 1}}
|
||||||
encodeTpl = template.Must(template.New("encoder").Parse(encoderData))
|
if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
|
||||||
headerTpl = template.Must(template.New("header").Parse(headerData))
|
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
|
||||||
)
|
}
|
||||||
|
{{end}}
|
||||||
|
o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size)
|
||||||
|
for i := range o.{{$fieldInfo.Name}} {
|
||||||
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
|
o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
|
||||||
|
{{else if $fieldInfo.IsBasic}}
|
||||||
|
{{if ge $fieldInfo.Submax 1}}
|
||||||
|
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Submax}})
|
||||||
|
{{else}}
|
||||||
|
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
(&o.{{$fieldInfo.Name}}[i]).DecodeXDRFrom(xr)
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
return xr.Error()
|
||||||
|
}`))
|
||||||
|
|
||||||
var emptyTypeTpl = template.Must(template.New("encoder").Parse(`
|
var emptyTypeTpl = template.Must(template.New("encoder").Parse(`
|
||||||
func (o {{.Name}}) XDRSize() int {
|
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
|
||||||
return 0
|
return 0, nil
|
||||||
}
|
}//+n
|
||||||
|
|
||||||
func (o {{.Name}}) MarshalXDR() ([]byte, error) {
|
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.Name}}) MustMarshalXDR() []byte {
|
func (o {{.TypeName}}) MustMarshalXDR() []byte {
|
||||||
return nil
|
return nil
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.Name}}) MarshalXDRInto(m *xdr.Marshaller) error {
|
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
|
return bs, nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
|
return xw.Tot(), xw.Error()
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
|
||||||
return nil
|
return nil
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o *{{.Name}}) UnmarshalXDR(bs []byte) error {
|
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o *{{.Name}}) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
return nil
|
return xr.Error()
|
||||||
}//+n
|
}`))
|
||||||
`))
|
|
||||||
|
|
||||||
var maxRe = regexp.MustCompile(`(?:\Wmax:)(\d+)(?:\s*,\s*(\d+))?`)
|
var maxRe = regexp.MustCompile(`(?:\Wmax:)(\d+)(?:\s*,\s*(\d+))?`)
|
||||||
|
|
||||||
@@ -342,7 +286,7 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
f = fieldInfo{
|
f = fieldInfo{
|
||||||
Name: fn,
|
Name: fn,
|
||||||
IsBasic: true,
|
IsBasic: true,
|
||||||
FieldType: "[]" + tn,
|
FieldType: tn,
|
||||||
Encoder: enc.Encoder,
|
Encoder: enc.Encoder,
|
||||||
Convert: enc.Type,
|
Convert: enc.Type,
|
||||||
Max: max1,
|
Max: max1,
|
||||||
@@ -385,14 +329,17 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateCode(output io.Writer, s structInfo) {
|
func generateCode(output io.Writer, s structInfo) {
|
||||||
|
name := s.Name
|
||||||
|
fs := s.Fields
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
var err error
|
var err error
|
||||||
if len(s.Fields) == 0 {
|
if len(fs) == 0 {
|
||||||
// This is an empty type. We can create a quite simple codec for it.
|
// This is an empty type. We can create a quite simple codec for it.
|
||||||
err = emptyTypeTpl.Execute(&buf, s)
|
err = emptyTypeTpl.Execute(&buf, map[string]interface{}{"TypeName": name})
|
||||||
} else {
|
} else {
|
||||||
// Generate with the default template.
|
// Generate with the default template.
|
||||||
err = encodeTpl.Execute(&buf, s)
|
err = encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -400,7 +347,12 @@ func generateCode(output io.Writer, s structInfo) {
|
|||||||
|
|
||||||
bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
|
bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
|
||||||
bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)
|
bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)
|
||||||
output.Write(bs)
|
|
||||||
|
bs, err = format.Source(bs)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(output, string(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func uncamelize(s string) string {
|
func uncamelize(s string) string {
|
||||||
@@ -432,46 +384,46 @@ func generateDiagram(output io.Writer, s structInfo) {
|
|||||||
tn := f.FieldType
|
tn := f.FieldType
|
||||||
name := uncamelize(f.Name)
|
name := uncamelize(f.Name)
|
||||||
|
|
||||||
suffix := ""
|
|
||||||
if f.IsSlice {
|
if f.IsSlice {
|
||||||
fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61))
|
fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61))
|
||||||
fmt.Fprintln(output, line)
|
fmt.Fprintln(output, line)
|
||||||
suffix = " (n items)"
|
|
||||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
|
||||||
}
|
}
|
||||||
switch tn {
|
switch tn {
|
||||||
case "bool":
|
case "bool":
|
||||||
fmt.Fprintf(output, "| %s |V|\n", center(name+" (V=0 or 1)", 59))
|
fmt.Fprintf(output, "| %s |V|\n", center(name+" (V=0 or 1)", 59))
|
||||||
|
fmt.Fprintln(output, line)
|
||||||
case "int16", "uint16":
|
case "int16", "uint16":
|
||||||
fmt.Fprintf(output, "| %s | %s |\n", center("16 zero bits", 29), center(name, 29))
|
fmt.Fprintf(output, "| %s | %s |\n", center("0x0000", 29), center(name, 29))
|
||||||
case "int8", "uint8":
|
fmt.Fprintln(output, line)
|
||||||
fmt.Fprintf(output, "| %s | %s |\n", center("24 zero bits", 45), center(name, 13))
|
|
||||||
case "int32", "uint32":
|
case "int32", "uint32":
|
||||||
fmt.Fprintf(output, "| %s |\n", center(name+suffix, 61))
|
fmt.Fprintf(output, "| %s |\n", center(name, 61))
|
||||||
|
fmt.Fprintln(output, line)
|
||||||
case "int64", "uint64":
|
case "int64", "uint64":
|
||||||
fmt.Fprintf(output, "| %-61s |\n", "")
|
fmt.Fprintf(output, "| %-61s |\n", "")
|
||||||
fmt.Fprintf(output, "+ %s +\n", center(name+" (64 bits)", 61))
|
fmt.Fprintf(output, "+ %s +\n", center(name+" (64 bits)", 61))
|
||||||
fmt.Fprintf(output, "| %-61s |\n", "")
|
fmt.Fprintf(output, "| %-61s |\n", "")
|
||||||
case "string", "[]byte":
|
fmt.Fprintln(output, line)
|
||||||
|
case "string", "byte": // XXX We assume slice of byte!
|
||||||
|
fmt.Fprintf(output, "| %s |\n", center("Length of "+name, 61))
|
||||||
|
fmt.Fprintln(output, line)
|
||||||
fmt.Fprintf(output, "/ %61s /\n", "")
|
fmt.Fprintf(output, "/ %61s /\n", "")
|
||||||
fmt.Fprintf(output, "\\ %s \\\n", center(name+" (length + padded data)", 61))
|
fmt.Fprintf(output, "\\ %s \\\n", center(name+" (variable length)", 61))
|
||||||
fmt.Fprintf(output, "/ %61s /\n", "")
|
fmt.Fprintf(output, "/ %61s /\n", "")
|
||||||
|
fmt.Fprintln(output, line)
|
||||||
default:
|
default:
|
||||||
if f.IsSlice {
|
if f.IsSlice {
|
||||||
tn = "Zero or more " + tn + " Structures"
|
tn = "Zero or more " + tn + " Structures"
|
||||||
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
||||||
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
} else {
|
} else {
|
||||||
tn = tn + " Structure"
|
tn = tn + " Structure"
|
||||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
||||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
}
|
}
|
||||||
|
fmt.Fprintln(output, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.IsSlice {
|
|
||||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
|
||||||
}
|
|
||||||
fmt.Fprintln(output, line)
|
|
||||||
}
|
}
|
||||||
fmt.Fprintln(output)
|
fmt.Fprintln(output)
|
||||||
fmt.Fprintln(output)
|
fmt.Fprintln(output)
|
||||||
@@ -496,9 +448,9 @@ func generateXdr(output io.Writer, s structInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch tn {
|
switch tn {
|
||||||
case "int8", "int16", "int32":
|
case "int16", "int32":
|
||||||
fmt.Fprintf(output, "\tint %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\tint %s%s;\n", fn, suf)
|
||||||
case "uint8", "uint16", "uint32":
|
case "uint16", "uint32":
|
||||||
fmt.Fprintf(output, "\tunsigned int %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\tunsigned int %s%s;\n", fn, suf)
|
||||||
case "int64":
|
case "int64":
|
||||||
fmt.Fprintf(output, "\thyper %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\thyper %s%s;\n", fn, suf)
|
||||||
@@ -506,7 +458,7 @@ func generateXdr(output io.Writer, s structInfo) {
|
|||||||
fmt.Fprintf(output, "\tunsigned hyper %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\tunsigned hyper %s%s;\n", fn, suf)
|
||||||
case "string":
|
case "string":
|
||||||
fmt.Fprintf(output, "\tstring %s<%s>;\n", fn, l)
|
fmt.Fprintf(output, "\tstring %s<%s>;\n", fn, l)
|
||||||
case "[]byte":
|
case "byte":
|
||||||
fmt.Fprintf(output, "\topaque %s<%s>;\n", fn, l)
|
fmt.Fprintf(output, "\topaque %s<%s>;\n", fn, l)
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(output, "\t%s %s%s;\n", tn, fn, suf)
|
fmt.Fprintf(output, "\t%s %s%s;\n", tn, fn, suf)
|
||||||
@@ -558,22 +510,6 @@ func main() {
|
|||||||
i := inspector(&structs)
|
i := inspector(&structs)
|
||||||
ast.Inspect(f, i)
|
ast.Inspect(f, i)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
headerTpl.Execute(buf, map[string]string{"Package": f.Name.Name})
|
|
||||||
for _, s := range structs {
|
|
||||||
fmt.Fprintf(buf, "\n/*\n\n")
|
|
||||||
generateDiagram(buf, s)
|
|
||||||
generateXdr(buf, s)
|
|
||||||
fmt.Fprintf(buf, "*/\n")
|
|
||||||
generateCode(buf, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := format.Source(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
log.Print(buf.String())
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var output io.Writer = os.Stdout
|
var output io.Writer = os.Stdout
|
||||||
if *outputFile != "" {
|
if *outputFile != "" {
|
||||||
fd, err := os.Create(*outputFile)
|
fd, err := os.Create(*outputFile)
|
||||||
@@ -582,5 +518,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
output = fd
|
output = fd
|
||||||
}
|
}
|
||||||
output.Write(bs)
|
|
||||||
|
headerTpl.Execute(output, map[string]string{"Package": f.Name.Name})
|
||||||
|
for _, s := range structs {
|
||||||
|
fmt.Fprintf(output, "\n/*\n\n")
|
||||||
|
generateDiagram(output, s)
|
||||||
|
generateXdr(output, s)
|
||||||
|
fmt.Fprintf(output, "*/\n")
|
||||||
|
generateCode(output, s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
debug = len(os.Getenv("XDRTRACE")) > 0
|
||||||
|
dl = log.New(os.Stdout, "xdr: ", log.Lshortfile|log.Ltime|log.Lmicroseconds)
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxDebugBytes = 32
|
||||||
Generated
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package xdr implements an XDR (RFC 4506) marshaller/unmarshaller.
|
// Package xdr implements an XDR (RFC 4506) encoder/decoder.
|
||||||
package xdr
|
package xdr
|
||||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build ipdr
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
func pad(l int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !ipdr
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
func pad(l int) int {
|
||||||
|
d := l % 4
|
||||||
|
if d == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 4 - d
|
||||||
|
}
|
||||||
+171
@@ -0,0 +1,171 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||||
|
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
|
r io.Reader
|
||||||
|
err error
|
||||||
|
b [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReader(r io.Reader) *Reader {
|
||||||
|
return &Reader{
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadRaw(bs []byte) (int, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
n, r.err = io.ReadFull(r.r, bs)
|
||||||
|
return n, r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadString() string {
|
||||||
|
return r.ReadStringMax(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadStringMax(max int) string {
|
||||||
|
buf := r.ReadBytesMaxInto(max, nil)
|
||||||
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
||||||
|
sh := reflect.StringHeader{
|
||||||
|
Data: bh.Data,
|
||||||
|
Len: bh.Len,
|
||||||
|
}
|
||||||
|
return *((*string)(unsafe.Pointer(&sh)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBytes() []byte {
|
||||||
|
return r.ReadBytesInto(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBytesMax(max int) []byte {
|
||||||
|
return r.ReadBytesMaxInto(max, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBytesInto(dst []byte) []byte {
|
||||||
|
return r.ReadBytesMaxInto(0, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l := int(r.ReadUint32())
|
||||||
|
if r.err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if l < 0 || max > 0 && l > max {
|
||||||
|
// l may be negative on 32 bit builds
|
||||||
|
r.err = ElementSizeExceeded("bytes field", l, max)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if fullLen := l + pad(l); fullLen > len(dst) {
|
||||||
|
dst = make([]byte, fullLen)
|
||||||
|
} else {
|
||||||
|
dst = dst[:fullLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
n, r.err = io.ReadFull(r.r, dst)
|
||||||
|
if r.err != nil {
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd bytes (%d): %v", len(dst), r.err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
if n > maxDebugBytes {
|
||||||
|
dl.Printf("rd bytes (%d): %x...", len(dst), dst[:maxDebugBytes])
|
||||||
|
} else {
|
||||||
|
dl.Printf("rd bytes (%d): %x", len(dst), dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst[:l]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBool() bool {
|
||||||
|
return r.ReadUint8() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadUint32() uint32 {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_, r.err = io.ReadFull(r.r, r.b[:4])
|
||||||
|
if r.err != nil {
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint32: %v", r.err)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
v := uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint32=%d (0x%08x)", v, v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadUint64() uint64 {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_, r.err = io.ReadFull(r.r, r.b[:8])
|
||||||
|
if r.err != nil {
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint64: %v", r.err)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
v := uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 |
|
||||||
|
uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint64=%d (0x%016x)", v, v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type XDRError struct {
|
||||||
|
op string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e XDRError) Error() string {
|
||||||
|
return "xdr " + e.op + ": " + e.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e XDRError) IsEOF() bool {
|
||||||
|
return e.err == io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Error() error {
|
||||||
|
if r.err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return XDRError{"read", r.err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ElementSizeExceeded(field string, size, limit int) error {
|
||||||
|
return fmt.Errorf("%s exceeds size limit; %d > %d", field, size, limit)
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||||
|
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build ipdr
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (r *Reader) ReadUint8() uint8 {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_, r.err = io.ReadFull(r.r, r.b[:1])
|
||||||
|
if r.err != nil {
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint8: %v", r.err)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint8=%d (0x%02x)", r.b[0], r.b[0])
|
||||||
|
}
|
||||||
|
return r.b[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadUint16() uint16 {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_, r.err = io.ReadFull(r.r, r.b[:2])
|
||||||
|
if r.err != nil {
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint16: %v", r.err)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
v := uint16(r.b[1]) | uint16(r.b[0])<<8
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("rd uint16=%d (0x%04x)", v, v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||||
|
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !ipdr
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
func (r *Reader) ReadUint8() uint8 {
|
||||||
|
return uint8(r.ReadUint32())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadUint16() uint16 {
|
||||||
|
return uint16(r.ReadUint32())
|
||||||
|
}
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var padBytes = []byte{0, 0, 0}
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
w io.Writer
|
||||||
|
tot int
|
||||||
|
err error
|
||||||
|
b [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppendWriter []byte
|
||||||
|
|
||||||
|
func (w *AppendWriter) Write(bs []byte) (int, error) {
|
||||||
|
*w = append(*w, bs...)
|
||||||
|
return len(bs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(w io.Writer) *Writer {
|
||||||
|
return &Writer{
|
||||||
|
w: w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteRaw(bs []byte) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
n, w.err = w.w.Write(bs)
|
||||||
|
return n, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteString(s string) (int, error) {
|
||||||
|
sh := *((*reflect.StringHeader)(unsafe.Pointer(&s)))
|
||||||
|
bh := reflect.SliceHeader{
|
||||||
|
Data: sh.Data,
|
||||||
|
Len: sh.Len,
|
||||||
|
Cap: sh.Len,
|
||||||
|
}
|
||||||
|
return w.WriteBytes(*(*[]byte)(unsafe.Pointer(&bh)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteBytes(bs []byte) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteUint32(uint32(len(bs)))
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
if len(bs) > maxDebugBytes {
|
||||||
|
dl.Printf("wr bytes (%d): %x...", len(bs), bs[:maxDebugBytes])
|
||||||
|
} else {
|
||||||
|
dl.Printf("wr bytes (%d): %x", len(bs), bs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var l, n int
|
||||||
|
n, w.err = w.w.Write(bs)
|
||||||
|
l += n
|
||||||
|
|
||||||
|
if p := pad(len(bs)); w.err == nil && p > 0 {
|
||||||
|
n, w.err = w.w.Write(padBytes[:p])
|
||||||
|
l += n
|
||||||
|
}
|
||||||
|
|
||||||
|
w.tot += l
|
||||||
|
return l, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteBool(v bool) (int, error) {
|
||||||
|
if v {
|
||||||
|
return w.WriteUint8(1)
|
||||||
|
} else {
|
||||||
|
return w.WriteUint8(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUint32(v uint32) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("wr uint32=%d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.b[0] = byte(v >> 24)
|
||||||
|
w.b[1] = byte(v >> 16)
|
||||||
|
w.b[2] = byte(v >> 8)
|
||||||
|
w.b[3] = byte(v)
|
||||||
|
|
||||||
|
var l int
|
||||||
|
l, w.err = w.w.Write(w.b[:4])
|
||||||
|
w.tot += l
|
||||||
|
return l, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUint64(v uint64) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("wr uint64=%d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.b[0] = byte(v >> 56)
|
||||||
|
w.b[1] = byte(v >> 48)
|
||||||
|
w.b[2] = byte(v >> 40)
|
||||||
|
w.b[3] = byte(v >> 32)
|
||||||
|
w.b[4] = byte(v >> 24)
|
||||||
|
w.b[5] = byte(v >> 16)
|
||||||
|
w.b[6] = byte(v >> 8)
|
||||||
|
w.b[7] = byte(v)
|
||||||
|
|
||||||
|
var l int
|
||||||
|
l, w.err = w.w.Write(w.b[:8])
|
||||||
|
w.tot += l
|
||||||
|
return l, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Tot() int {
|
||||||
|
return w.tot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Error() error {
|
||||||
|
if w.err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return XDRError{"write", w.err}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build ipdr
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
func (w *Writer) WriteUint8(v uint8) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("wr uint8=%d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.b[0] = byte(v)
|
||||||
|
|
||||||
|
var l int
|
||||||
|
l, w.err = w.w.Write(w.b[:1])
|
||||||
|
w.tot += l
|
||||||
|
return l, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUint16(v uint16) (int, error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
dl.Printf("wr uint8=%d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.b[0] = byte(v >> 8)
|
||||||
|
w.b[1] = byte(v)
|
||||||
|
|
||||||
|
var l int
|
||||||
|
l, w.err = w.w.Write(w.b[:2])
|
||||||
|
w.tot += l
|
||||||
|
return l, w.err
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||||
|
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !ipdr
|
||||||
|
|
||||||
|
package xdr
|
||||||
|
|
||||||
|
func (w *Writer) WriteUint8(v uint8) (int, error) {
|
||||||
|
return w.WriteUint32(uint32(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUint16(v uint16) (int, error) {
|
||||||
|
return w.WriteUint32(uint32(v))
|
||||||
|
}
|
||||||
Generated
Vendored
-1
@@ -11,5 +11,4 @@
|
|||||||
Damian Gryski <dgryski@gmail.com>
|
Damian Gryski <dgryski@gmail.com>
|
||||||
Google Inc.
|
Google Inc.
|
||||||
Jan Mercl <0xjnml@gmail.com>
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
|
||||||
Sebastien Binet <seb.binet@gmail.com>
|
Sebastien Binet <seb.binet@gmail.com>
|
||||||
Generated
Vendored
-1
@@ -32,6 +32,5 @@ Kai Backman <kaib@golang.org>
|
|||||||
Marc-Antoine Ruel <maruel@chromium.org>
|
Marc-Antoine Ruel <maruel@chromium.org>
|
||||||
Nigel Tao <nigeltao@golang.org>
|
Nigel Tao <nigeltao@golang.org>
|
||||||
Rob Pike <r@golang.org>
|
Rob Pike <r@golang.org>
|
||||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
|
||||||
Russ Cox <rsc@golang.org>
|
Russ Cox <rsc@golang.org>
|
||||||
Sebastien Binet <seb.binet@gmail.com>
|
Sebastien Binet <seb.binet@gmail.com>
|
||||||
Generated
Vendored
Generated
Vendored
vendor/github.com/golang/snappy/decode.go → Godeps/_workspace/src/github.com/golang/snappy/decode.go
Generated
Vendored
+82
-25
@@ -17,9 +17,6 @@ var (
|
|||||||
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||||
// ErrUnsupported reports that the input isn't supported.
|
// ErrUnsupported reports that the input isn't supported.
|
||||||
ErrUnsupported = errors.New("snappy: unsupported input")
|
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||||
|
|
||||||
errUnsupportedCopy4Tag = errors.New("snappy: unsupported COPY_4 tag")
|
|
||||||
errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecodedLen returns the length of the decoded block.
|
// DecodedLen returns the length of the decoded block.
|
||||||
@@ -43,36 +40,96 @@ func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
|||||||
return int(v), n, nil
|
return int(v), n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
decodeErrCodeCorrupt = 1
|
|
||||||
decodeErrCodeUnsupportedLiteralLength = 2
|
|
||||||
decodeErrCodeUnsupportedCopy4Tag = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||||
// Otherwise, a newly allocated slice will be returned.
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
//
|
// It is valid to pass a nil dst.
|
||||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
|
||||||
func Decode(dst, src []byte) ([]byte, error) {
|
func Decode(dst, src []byte) ([]byte, error) {
|
||||||
dLen, s, err := decodedLen(src)
|
dLen, s, err := decodedLen(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if dLen <= len(dst) {
|
if len(dst) < dLen {
|
||||||
dst = dst[:dLen]
|
|
||||||
} else {
|
|
||||||
dst = make([]byte, dLen)
|
dst = make([]byte, dLen)
|
||||||
}
|
}
|
||||||
switch decode(dst, src[s:]) {
|
|
||||||
case 0:
|
var d, offset, length int
|
||||||
return dst, nil
|
for s < len(src) {
|
||||||
case decodeErrCodeUnsupportedLiteralLength:
|
switch src[s] & 0x03 {
|
||||||
return nil, errUnsupportedLiteralLength
|
case tagLiteral:
|
||||||
case decodeErrCodeUnsupportedCopy4Tag:
|
x := uint(src[s] >> 2)
|
||||||
return nil, errUnsupportedCopy4Tag
|
switch {
|
||||||
|
case x < 60:
|
||||||
|
s++
|
||||||
|
case x == 60:
|
||||||
|
s += 2
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-1])
|
||||||
|
case x == 61:
|
||||||
|
s += 3
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-2]) | uint(src[s-1])<<8
|
||||||
|
case x == 62:
|
||||||
|
s += 4
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
|
||||||
|
case x == 63:
|
||||||
|
s += 5
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
|
||||||
|
}
|
||||||
|
length = int(x + 1)
|
||||||
|
if length <= 0 {
|
||||||
|
return nil, errors.New("snappy: unsupported literal length")
|
||||||
|
}
|
||||||
|
if length > len(dst)-d || length > len(src)-s {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
copy(dst[d:], src[s:s+length])
|
||||||
|
d += length
|
||||||
|
s += length
|
||||||
|
continue
|
||||||
|
|
||||||
|
case tagCopy1:
|
||||||
|
s += 2
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
length = 4 + int(src[s-2])>>2&0x7
|
||||||
|
offset = int(src[s-2])&0xe0<<3 | int(src[s-1])
|
||||||
|
|
||||||
|
case tagCopy2:
|
||||||
|
s += 3
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
length = 1 + int(src[s-3])>>2
|
||||||
|
offset = int(src[s-2]) | int(src[s-1])<<8
|
||||||
|
|
||||||
|
case tagCopy4:
|
||||||
|
return nil, errors.New("snappy: unsupported COPY_4 tag")
|
||||||
|
}
|
||||||
|
|
||||||
|
end := d + length
|
||||||
|
if offset > d || end > len(dst) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
for ; d < end; d++ {
|
||||||
|
dst[d] = dst[d-offset]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, ErrCorrupt
|
if d != dLen {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
return dst[:d], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReader returns a new Reader that decompresses from r, using the framing
|
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||||
@@ -81,12 +138,12 @@ func Decode(dst, src []byte) ([]byte, error) {
|
|||||||
func NewReader(r io.Reader) *Reader {
|
func NewReader(r io.Reader) *Reader {
|
||||||
return &Reader{
|
return &Reader{
|
||||||
r: r,
|
r: r,
|
||||||
decoded: make([]byte, maxBlockSize),
|
decoded: make([]byte, maxUncompressedChunkLen),
|
||||||
buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
|
buf: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
// Reader is an io.Reader than can read Snappy-compressed bytes.
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
err error
|
err error
|
||||||
+254
@@ -0,0 +1,254 @@
|
|||||||
|
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package snappy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We limit how far copy back-references can go, the same as the C++ code.
|
||||||
|
const maxOffset = 1 << 15
|
||||||
|
|
||||||
|
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||||
|
func emitLiteral(dst, lit []byte) int {
|
||||||
|
i, n := 0, uint(len(lit)-1)
|
||||||
|
switch {
|
||||||
|
case n < 60:
|
||||||
|
dst[0] = uint8(n)<<2 | tagLiteral
|
||||||
|
i = 1
|
||||||
|
case n < 1<<8:
|
||||||
|
dst[0] = 60<<2 | tagLiteral
|
||||||
|
dst[1] = uint8(n)
|
||||||
|
i = 2
|
||||||
|
case n < 1<<16:
|
||||||
|
dst[0] = 61<<2 | tagLiteral
|
||||||
|
dst[1] = uint8(n)
|
||||||
|
dst[2] = uint8(n >> 8)
|
||||||
|
i = 3
|
||||||
|
case n < 1<<24:
|
||||||
|
dst[0] = 62<<2 | tagLiteral
|
||||||
|
dst[1] = uint8(n)
|
||||||
|
dst[2] = uint8(n >> 8)
|
||||||
|
dst[3] = uint8(n >> 16)
|
||||||
|
i = 4
|
||||||
|
case int64(n) < 1<<32:
|
||||||
|
dst[0] = 63<<2 | tagLiteral
|
||||||
|
dst[1] = uint8(n)
|
||||||
|
dst[2] = uint8(n >> 8)
|
||||||
|
dst[3] = uint8(n >> 16)
|
||||||
|
dst[4] = uint8(n >> 24)
|
||||||
|
i = 5
|
||||||
|
default:
|
||||||
|
panic("snappy: source buffer is too long")
|
||||||
|
}
|
||||||
|
if copy(dst[i:], lit) != len(lit) {
|
||||||
|
panic("snappy: destination buffer is too short")
|
||||||
|
}
|
||||||
|
return i + len(lit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitCopy writes a copy chunk and returns the number of bytes written.
|
||||||
|
func emitCopy(dst []byte, offset, length int) int {
|
||||||
|
i := 0
|
||||||
|
for length > 0 {
|
||||||
|
x := length - 4
|
||||||
|
if 0 <= x && x < 1<<3 && offset < 1<<11 {
|
||||||
|
dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1
|
||||||
|
dst[i+1] = uint8(offset)
|
||||||
|
i += 2
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
x = length
|
||||||
|
if x > 1<<6 {
|
||||||
|
x = 1 << 6
|
||||||
|
}
|
||||||
|
dst[i+0] = uint8(x-1)<<2 | tagCopy2
|
||||||
|
dst[i+1] = uint8(offset)
|
||||||
|
dst[i+2] = uint8(offset >> 8)
|
||||||
|
i += 3
|
||||||
|
length -= x
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns the encoded form of src. The returned slice may be a sub-
|
||||||
|
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||||
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
|
// It is valid to pass a nil dst.
|
||||||
|
func Encode(dst, src []byte) []byte {
|
||||||
|
if n := MaxEncodedLen(len(src)); len(dst) < n {
|
||||||
|
dst = make([]byte, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||||
|
d := binary.PutUvarint(dst, uint64(len(src)))
|
||||||
|
|
||||||
|
// Return early if src is short.
|
||||||
|
if len(src) <= 4 {
|
||||||
|
if len(src) != 0 {
|
||||||
|
d += emitLiteral(dst[d:], src)
|
||||||
|
}
|
||||||
|
return dst[:d]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||||
|
const maxTableSize = 1 << 14
|
||||||
|
shift, tableSize := uint(32-8), 1<<8
|
||||||
|
for tableSize < maxTableSize && tableSize < len(src) {
|
||||||
|
shift--
|
||||||
|
tableSize *= 2
|
||||||
|
}
|
||||||
|
var table [maxTableSize]int
|
||||||
|
|
||||||
|
// Iterate over the source bytes.
|
||||||
|
var (
|
||||||
|
s int // The iterator position.
|
||||||
|
t int // The last position with the same hash as s.
|
||||||
|
lit int // The start position of any pending literal bytes.
|
||||||
|
)
|
||||||
|
for s+3 < len(src) {
|
||||||
|
// Update the hash table.
|
||||||
|
b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3]
|
||||||
|
h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24
|
||||||
|
p := &table[(h*0x1e35a7bd)>>shift]
|
||||||
|
// We need to to store values in [-1, inf) in table. To save
|
||||||
|
// some initialization time, (re)use the table's zero value
|
||||||
|
// and shift the values against this zero: add 1 on writes,
|
||||||
|
// subtract 1 on reads.
|
||||||
|
t, *p = *p-1, s+1
|
||||||
|
// If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte.
|
||||||
|
if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] {
|
||||||
|
s++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Otherwise, we have a match. First, emit any pending literal bytes.
|
||||||
|
if lit != s {
|
||||||
|
d += emitLiteral(dst[d:], src[lit:s])
|
||||||
|
}
|
||||||
|
// Extend the match to be as long as possible.
|
||||||
|
s0 := s
|
||||||
|
s, t = s+4, t+4
|
||||||
|
for s < len(src) && src[s] == src[t] {
|
||||||
|
s++
|
||||||
|
t++
|
||||||
|
}
|
||||||
|
// Emit the copied bytes.
|
||||||
|
d += emitCopy(dst[d:], s-t, s-s0)
|
||||||
|
lit = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit any final pending literal bytes and return.
|
||||||
|
if lit != len(src) {
|
||||||
|
d += emitLiteral(dst[d:], src[lit:])
|
||||||
|
}
|
||||||
|
return dst[:d]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||||
|
// uncompressed length.
|
||||||
|
func MaxEncodedLen(srcLen int) int {
|
||||||
|
// Compressed data can be defined as:
|
||||||
|
// compressed := item* literal*
|
||||||
|
// item := literal* copy
|
||||||
|
//
|
||||||
|
// The trailing literal sequence has a space blowup of at most 62/60
|
||||||
|
// since a literal of length 60 needs one tag byte + one extra byte
|
||||||
|
// for length information.
|
||||||
|
//
|
||||||
|
// Item blowup is trickier to measure. Suppose the "copy" op copies
|
||||||
|
// 4 bytes of data. Because of a special check in the encoding code,
|
||||||
|
// we produce a 4-byte copy only if the offset is < 65536. Therefore
|
||||||
|
// the copy op takes 3 bytes to encode, and this type of item leads
|
||||||
|
// to at most the 62/60 blowup for representing literals.
|
||||||
|
//
|
||||||
|
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
|
||||||
|
// enough, it will take 5 bytes to encode the copy op. Therefore the
|
||||||
|
// worst case here is a one-byte literal followed by a five-byte copy.
|
||||||
|
// That is, 6 bytes of input turn into 7 bytes of "compressed" data.
|
||||||
|
//
|
||||||
|
// This last factor dominates the blowup, so the final estimate is:
|
||||||
|
return 32 + srcLen + srcLen/6
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter returns a new Writer that compresses to w, using the framing
|
||||||
|
// format described at
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
func NewWriter(w io.Writer) *Writer {
|
||||||
|
return &Writer{
|
||||||
|
w: w,
|
||||||
|
enc: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer is an io.Writer than can write Snappy-compressed bytes.
|
||||||
|
type Writer struct {
|
||||||
|
w io.Writer
|
||||||
|
err error
|
||||||
|
enc []byte
|
||||||
|
buf [checksumSize + chunkHeaderSize]byte
|
||||||
|
wroteHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset discards the writer's state and switches the Snappy writer to write to
|
||||||
|
// w. This permits reusing a Writer rather than allocating a new one.
|
||||||
|
func (w *Writer) Reset(writer io.Writer) {
|
||||||
|
w.w = writer
|
||||||
|
w.err = nil
|
||||||
|
w.wroteHeader = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write satisfies the io.Writer interface.
|
||||||
|
func (w *Writer) Write(p []byte) (n int, errRet error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
if !w.wroteHeader {
|
||||||
|
copy(w.enc, magicChunk)
|
||||||
|
if _, err := w.w.Write(w.enc[:len(magicChunk)]); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
w.wroteHeader = true
|
||||||
|
}
|
||||||
|
for len(p) > 0 {
|
||||||
|
var uncompressed []byte
|
||||||
|
if len(p) > maxUncompressedChunkLen {
|
||||||
|
uncompressed, p = p[:maxUncompressedChunkLen], p[maxUncompressedChunkLen:]
|
||||||
|
} else {
|
||||||
|
uncompressed, p = p, nil
|
||||||
|
}
|
||||||
|
checksum := crc(uncompressed)
|
||||||
|
|
||||||
|
// Compress the buffer, discarding the result if the improvement
|
||||||
|
// isn't at least 12.5%.
|
||||||
|
chunkType := uint8(chunkTypeCompressedData)
|
||||||
|
chunkBody := Encode(w.enc, uncompressed)
|
||||||
|
if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 {
|
||||||
|
chunkType, chunkBody = chunkTypeUncompressedData, uncompressed
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkLen := 4 + len(chunkBody)
|
||||||
|
w.buf[0] = chunkType
|
||||||
|
w.buf[1] = uint8(chunkLen >> 0)
|
||||||
|
w.buf[2] = uint8(chunkLen >> 8)
|
||||||
|
w.buf[3] = uint8(chunkLen >> 16)
|
||||||
|
w.buf[4] = uint8(checksum >> 0)
|
||||||
|
w.buf[5] = uint8(checksum >> 8)
|
||||||
|
w.buf[6] = uint8(checksum >> 16)
|
||||||
|
w.buf[7] = uint8(checksum >> 24)
|
||||||
|
if _, err := w.w.Write(w.buf[:]); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if _, err := w.w.Write(chunkBody); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n += len(uncompressed)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
vendor/github.com/golang/snappy/snappy.go → Godeps/_workspace/src/github.com/golang/snappy/snappy.go
Generated
Vendored
+3
-19
@@ -6,7 +6,7 @@
|
|||||||
// It aims for very high speeds and reasonable compression.
|
// It aims for very high speeds and reasonable compression.
|
||||||
//
|
//
|
||||||
// The C++ snappy implementation is at https://github.com/google/snappy
|
// The C++ snappy implementation is at https://github.com/google/snappy
|
||||||
package snappy // import "github.com/golang/snappy"
|
package snappy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
@@ -46,25 +46,9 @@ const (
|
|||||||
chunkHeaderSize = 4
|
chunkHeaderSize = 4
|
||||||
magicChunk = "\xff\x06\x00\x00" + magicBody
|
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||||
magicBody = "sNaPpY"
|
magicBody = "sNaPpY"
|
||||||
|
|
||||||
// maxBlockSize is the maximum size of the input to encodeBlock. It is not
|
|
||||||
// part of the wire format per se, but some parts of the encoder assume
|
|
||||||
// that an offset fits into a uint16.
|
|
||||||
//
|
|
||||||
// Also, for the framing format (Writer type instead of Encode function),
|
|
||||||
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
||||||
// that "the uncompressed data in a chunk must be no longer than 65536
|
// that "the uncompressed data in a chunk must be no longer than 65536 bytes".
|
||||||
// bytes".
|
maxUncompressedChunkLen = 65536
|
||||||
maxBlockSize = 65536
|
|
||||||
|
|
||||||
// maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is
|
|
||||||
// hard coded to be a const instead of a variable, so that obufLen can also
|
|
||||||
// be a const. Their equivalence is confirmed by
|
|
||||||
// TestMaxEncodedLenOfMaxBlockSize.
|
|
||||||
maxEncodedLenOfMaxBlockSize = 76490
|
|
||||||
|
|
||||||
obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
|
|
||||||
obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
+1
-25
@@ -8,10 +8,10 @@
|
|||||||
package ratelimit
|
package ratelimit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bucket represents a token bucket that fills at a predetermined rate.
|
// Bucket represents a token bucket that fills at a predetermined rate.
|
||||||
@@ -171,30 +171,6 @@ func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available returns the number of available tokens. It will be negative
|
|
||||||
// when there are consumers waiting for tokens. Note that if this
|
|
||||||
// returns greater than zero, it does not guarantee that calls that take
|
|
||||||
// tokens from the buffer will succeed, as the number of available
|
|
||||||
// tokens could have changed in the meantime. This method is intended
|
|
||||||
// primarily for metrics reporting and debugging.
|
|
||||||
func (tb *Bucket) Available() int64 {
|
|
||||||
return tb.available(time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// available is the internal version of available - it takes the current time as
|
|
||||||
// an argument to enable easy testing.
|
|
||||||
func (tb *Bucket) available(now time.Time) int64 {
|
|
||||||
tb.mu.Lock()
|
|
||||||
defer tb.mu.Unlock()
|
|
||||||
tb.adjust(now)
|
|
||||||
return tb.avail
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capacity returns the capacity that the bucket was created with.
|
|
||||||
func (tb *Bucket) Capacity() int64 {
|
|
||||||
return tb.capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rate returns the fill rate of the bucket, in tokens per second.
|
// Rate returns the fill rate of the bucket, in tokens per second.
|
||||||
func (tb *Bucket) Rate() float64 {
|
func (tb *Bucket) Rate() float64 {
|
||||||
return 1e9 * float64(tb.quantum) / float64(tb.fillInterval)
|
return 1e9 * float64(tb.quantum) / float64(tb.fillInterval)
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Extensions to the standard "os" package.
|
// Extensions to the standard "os" package.
|
||||||
package osext // import "github.com/kardianos/osext"
|
package osext
|
||||||
|
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
@@ -0,0 +1,9 @@
|
|||||||
|
*.[68]
|
||||||
|
*.a
|
||||||
|
*.out
|
||||||
|
*.swp
|
||||||
|
_obj
|
||||||
|
_testmain.go
|
||||||
|
cmd/metrics-bench/metrics-bench
|
||||||
|
cmd/metrics-example/metrics-example
|
||||||
|
cmd/never-read/never-read
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./validate.sh
|
||||||
|
|
||||||
|
# this should give us faster builds according to
|
||||||
|
# http://docs.travis-ci.com/user/migrating-from-legacy/
|
||||||
|
sudo: false
|
||||||
Generated
Vendored
Generated
Vendored
+1
-14
@@ -39,7 +39,7 @@ t.Update(47)
|
|||||||
Periodically log every metric in human-readable form to standard error:
|
Periodically log every metric in human-readable form to standard error:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
go metrics.Log(metrics.DefaultRegistry, 5 * time.Second, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
|
go metrics.Log(metrics.DefaultRegistry, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
|
||||||
```
|
```
|
||||||
|
|
||||||
Periodically log every metric in slightly-more-parseable form to syslog:
|
Periodically log every metric in slightly-more-parseable form to syslog:
|
||||||
@@ -103,19 +103,6 @@ import "github.com/rcrowley/go-metrics/stathat"
|
|||||||
go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com")
|
go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com")
|
||||||
```
|
```
|
||||||
|
|
||||||
Maintain all metrics along with expvars at `/debug/metrics`:
|
|
||||||
|
|
||||||
This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/)
|
|
||||||
but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars
|
|
||||||
as well as all your go-metrics.
|
|
||||||
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/rcrowley/go-metrics/exp"
|
|
||||||
|
|
||||||
exp.Exp(metrics.DefaultRegistry)
|
|
||||||
```
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
+1
-5
@@ -23,7 +23,6 @@ func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
|
|||||||
|
|
||||||
type Reporter struct {
|
type Reporter struct {
|
||||||
Email, Token string
|
Email, Token string
|
||||||
Namespace string
|
|
||||||
Source string
|
Source string
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Registry metrics.Registry
|
Registry metrics.Registry
|
||||||
@@ -33,7 +32,7 @@ type Reporter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
|
func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
|
||||||
return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
|
return &Reporter{e, t, s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
|
func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
|
||||||
@@ -89,9 +88,6 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot
|
|||||||
snapshot.Counters = make([]Measurement, 0)
|
snapshot.Counters = make([]Measurement, 0)
|
||||||
histogramGaugeCount := 1 + len(self.Percentiles)
|
histogramGaugeCount := 1 + len(self.Percentiles)
|
||||||
r.Each(func(name string, metric interface{}) {
|
r.Each(func(name string, metric interface{}) {
|
||||||
if self.Namespace != "" {
|
|
||||||
name = fmt.Sprintf("%s.%s", self.Namespace, name)
|
|
||||||
}
|
|
||||||
measurement := Measurement{}
|
measurement := Measurement{}
|
||||||
measurement[Period] = self.Interval.Seconds()
|
measurement[Period] = self.Interval.Seconds()
|
||||||
switch m := metric.(type) {
|
switch m := metric.(type) {
|
||||||
Generated
Vendored
+12
-19
@@ -5,17 +5,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Log(r Registry, freq time.Duration, l *log.Logger) {
|
|
||||||
LogScaled(r, freq, time.Nanosecond, l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output each metric in the given registry periodically using the given
|
// Output each metric in the given registry periodically using the given
|
||||||
// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos.
|
// logger.
|
||||||
func LogScaled(r Registry, freq time.Duration, scale time.Duration, l *log.Logger) {
|
func Log(r Registry, d time.Duration, l *log.Logger) {
|
||||||
du := float64(scale)
|
for _ = range time.Tick(d) {
|
||||||
duSuffix := scale.String()[1:]
|
|
||||||
|
|
||||||
for _ = range time.Tick(freq) {
|
|
||||||
r.Each(func(name string, i interface{}) {
|
r.Each(func(name string, i interface{}) {
|
||||||
switch metric := i.(type) {
|
switch metric := i.(type) {
|
||||||
case Counter:
|
case Counter:
|
||||||
@@ -58,15 +51,15 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l *log.Logge
|
|||||||
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
|
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
|
||||||
l.Printf("timer %s\n", name)
|
l.Printf("timer %s\n", name)
|
||||||
l.Printf(" count: %9d\n", t.Count())
|
l.Printf(" count: %9d\n", t.Count())
|
||||||
l.Printf(" min: %12.2f%s\n", float64(t.Min())/du, duSuffix)
|
l.Printf(" min: %9d\n", t.Min())
|
||||||
l.Printf(" max: %12.2f%s\n", float64(t.Max())/du, duSuffix)
|
l.Printf(" max: %9d\n", t.Max())
|
||||||
l.Printf(" mean: %12.2f%s\n", t.Mean()/du, duSuffix)
|
l.Printf(" mean: %12.2f\n", t.Mean())
|
||||||
l.Printf(" stddev: %12.2f%s\n", t.StdDev()/du, duSuffix)
|
l.Printf(" stddev: %12.2f\n", t.StdDev())
|
||||||
l.Printf(" median: %12.2f%s\n", ps[0]/du, duSuffix)
|
l.Printf(" median: %12.2f\n", ps[0])
|
||||||
l.Printf(" 75%%: %12.2f%s\n", ps[1]/du, duSuffix)
|
l.Printf(" 75%%: %12.2f\n", ps[1])
|
||||||
l.Printf(" 95%%: %12.2f%s\n", ps[2]/du, duSuffix)
|
l.Printf(" 95%%: %12.2f\n", ps[2])
|
||||||
l.Printf(" 99%%: %12.2f%s\n", ps[3]/du, duSuffix)
|
l.Printf(" 99%%: %12.2f\n", ps[3])
|
||||||
l.Printf(" 99.9%%: %12.2f%s\n", ps[4]/du, duSuffix)
|
l.Printf(" 99.9%%: %12.2f\n", ps[4])
|
||||||
l.Printf(" 1-min rate: %12.2f\n", t.Rate1())
|
l.Printf(" 1-min rate: %12.2f\n", t.Rate1())
|
||||||
l.Printf(" 5-min rate: %12.2f\n", t.Rate5())
|
l.Printf(" 5-min rate: %12.2f\n", t.Rate5())
|
||||||
l.Printf(" 15-min rate: %12.2f\n", t.Rate15())
|
l.Printf(" 15-min rate: %12.2f\n", t.Rate15())
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
-7
@@ -157,13 +157,6 @@ func NewPrefixedRegistry(prefix string) Registry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrefixedChildRegistry(parent Registry, prefix string) Registry {
|
|
||||||
return &PrefixedRegistry{
|
|
||||||
underlying: parent,
|
|
||||||
prefix: prefix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the given function for each registered metric.
|
// Call the given function for each registered metric.
|
||||||
func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
|
func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
|
||||||
r.underlying.Each(fn)
|
r.underlying.Each(fn)
|
||||||
Generated
Vendored
+26
-38
@@ -2,7 +2,6 @@ package metrics
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,37 +9,35 @@ var (
|
|||||||
memStats runtime.MemStats
|
memStats runtime.MemStats
|
||||||
runtimeMetrics struct {
|
runtimeMetrics struct {
|
||||||
MemStats struct {
|
MemStats struct {
|
||||||
Alloc Gauge
|
Alloc Gauge
|
||||||
BuckHashSys Gauge
|
BuckHashSys Gauge
|
||||||
DebugGC Gauge
|
DebugGC Gauge
|
||||||
EnableGC Gauge
|
EnableGC Gauge
|
||||||
Frees Gauge
|
Frees Gauge
|
||||||
HeapAlloc Gauge
|
HeapAlloc Gauge
|
||||||
HeapIdle Gauge
|
HeapIdle Gauge
|
||||||
HeapInuse Gauge
|
HeapInuse Gauge
|
||||||
HeapObjects Gauge
|
HeapObjects Gauge
|
||||||
HeapReleased Gauge
|
HeapReleased Gauge
|
||||||
HeapSys Gauge
|
HeapSys Gauge
|
||||||
LastGC Gauge
|
LastGC Gauge
|
||||||
Lookups Gauge
|
Lookups Gauge
|
||||||
Mallocs Gauge
|
Mallocs Gauge
|
||||||
MCacheInuse Gauge
|
MCacheInuse Gauge
|
||||||
MCacheSys Gauge
|
MCacheSys Gauge
|
||||||
MSpanInuse Gauge
|
MSpanInuse Gauge
|
||||||
MSpanSys Gauge
|
MSpanSys Gauge
|
||||||
NextGC Gauge
|
NextGC Gauge
|
||||||
NumGC Gauge
|
NumGC Gauge
|
||||||
GCCPUFraction GaugeFloat64
|
PauseNs Histogram
|
||||||
PauseNs Histogram
|
PauseTotalNs Gauge
|
||||||
PauseTotalNs Gauge
|
StackInuse Gauge
|
||||||
StackInuse Gauge
|
StackSys Gauge
|
||||||
StackSys Gauge
|
Sys Gauge
|
||||||
Sys Gauge
|
TotalAlloc Gauge
|
||||||
TotalAlloc Gauge
|
|
||||||
}
|
}
|
||||||
NumCgoCall Gauge
|
NumCgoCall Gauge
|
||||||
NumGoroutine Gauge
|
NumGoroutine Gauge
|
||||||
NumThread Gauge
|
|
||||||
ReadMemStats Timer
|
ReadMemStats Timer
|
||||||
}
|
}
|
||||||
frees uint64
|
frees uint64
|
||||||
@@ -48,8 +45,6 @@ var (
|
|||||||
mallocs uint64
|
mallocs uint64
|
||||||
numGC uint32
|
numGC uint32
|
||||||
numCgoCalls int64
|
numCgoCalls int64
|
||||||
|
|
||||||
threadCreateProfile = pprof.Lookup("threadcreate")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Capture new values for the Go runtime statistics exported in
|
// Capture new values for the Go runtime statistics exported in
|
||||||
@@ -102,7 +97,6 @@ func CaptureRuntimeMemStatsOnce(r Registry) {
|
|||||||
runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
|
runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
|
||||||
runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
|
runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
|
||||||
runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
|
runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
|
||||||
runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats))
|
|
||||||
|
|
||||||
// <https://code.google.com/p/go/source/browse/src/pkg/runtime/mgc0.c>
|
// <https://code.google.com/p/go/source/browse/src/pkg/runtime/mgc0.c>
|
||||||
i := numGC % uint32(len(memStats.PauseNs))
|
i := numGC % uint32(len(memStats.PauseNs))
|
||||||
@@ -138,8 +132,6 @@ func CaptureRuntimeMemStatsOnce(r Registry) {
|
|||||||
numCgoCalls = currentNumCgoCalls
|
numCgoCalls = currentNumCgoCalls
|
||||||
|
|
||||||
runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
|
runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
|
||||||
|
|
||||||
runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register runtimeMetrics for the Go runtime statistics exported in runtime and
|
// Register runtimeMetrics for the Go runtime statistics exported in runtime and
|
||||||
@@ -166,7 +158,6 @@ func RegisterRuntimeMemStats(r Registry) {
|
|||||||
runtimeMetrics.MemStats.MSpanSys = NewGauge()
|
runtimeMetrics.MemStats.MSpanSys = NewGauge()
|
||||||
runtimeMetrics.MemStats.NextGC = NewGauge()
|
runtimeMetrics.MemStats.NextGC = NewGauge()
|
||||||
runtimeMetrics.MemStats.NumGC = NewGauge()
|
runtimeMetrics.MemStats.NumGC = NewGauge()
|
||||||
runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64()
|
|
||||||
runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
|
runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
|
||||||
runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
|
runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
|
||||||
runtimeMetrics.MemStats.StackInuse = NewGauge()
|
runtimeMetrics.MemStats.StackInuse = NewGauge()
|
||||||
@@ -175,7 +166,6 @@ func RegisterRuntimeMemStats(r Registry) {
|
|||||||
runtimeMetrics.MemStats.TotalAlloc = NewGauge()
|
runtimeMetrics.MemStats.TotalAlloc = NewGauge()
|
||||||
runtimeMetrics.NumCgoCall = NewGauge()
|
runtimeMetrics.NumCgoCall = NewGauge()
|
||||||
runtimeMetrics.NumGoroutine = NewGauge()
|
runtimeMetrics.NumGoroutine = NewGauge()
|
||||||
runtimeMetrics.NumThread = NewGauge()
|
|
||||||
runtimeMetrics.ReadMemStats = NewTimer()
|
runtimeMetrics.ReadMemStats = NewTimer()
|
||||||
|
|
||||||
r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
|
r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
|
||||||
@@ -198,7 +188,6 @@ func RegisterRuntimeMemStats(r Registry) {
|
|||||||
r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
|
r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
|
||||||
r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
|
r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
|
||||||
r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
|
r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
|
||||||
r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction)
|
|
||||||
r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
|
r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
|
||||||
r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
|
r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
|
||||||
r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
|
r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
|
||||||
@@ -207,6 +196,5 @@ func RegisterRuntimeMemStats(r Registry) {
|
|||||||
r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
|
r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
|
||||||
r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
|
r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
|
||||||
r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
|
r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
|
||||||
r.Register("runtime.NumThread", runtimeMetrics.NumThread)
|
|
||||||
r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
|
r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
|
||||||
}
|
}
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
+22
-31
@@ -12,10 +12,8 @@ import (
|
|||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrBatchCorrupted records reason of batch corruption.
|
|
||||||
type ErrBatchCorrupted struct {
|
type ErrBatchCorrupted struct {
|
||||||
Reason string
|
Reason string
|
||||||
}
|
}
|
||||||
@@ -25,7 +23,7 @@ func (e *ErrBatchCorrupted) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newErrBatchCorrupted(reason string) error {
|
func newErrBatchCorrupted(reason string) error {
|
||||||
return errors.NewErrCorrupted(storage.FileDesc{}, &ErrBatchCorrupted{reason})
|
return errors.NewErrCorrupted(nil, &ErrBatchCorrupted{reason})
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -33,7 +31,6 @@ const (
|
|||||||
batchGrowRec = 3000
|
batchGrowRec = 3000
|
||||||
)
|
)
|
||||||
|
|
||||||
// BatchReplay wraps basic batch operations.
|
|
||||||
type BatchReplay interface {
|
type BatchReplay interface {
|
||||||
Put(key, value []byte)
|
Put(key, value []byte)
|
||||||
Delete(key []byte)
|
Delete(key []byte)
|
||||||
@@ -70,20 +67,20 @@ func (b *Batch) grow(n int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Batch) appendRec(kt keyType, key, value []byte) {
|
func (b *Batch) appendRec(kt kType, key, value []byte) {
|
||||||
n := 1 + binary.MaxVarintLen32 + len(key)
|
n := 1 + binary.MaxVarintLen32 + len(key)
|
||||||
if kt == keyTypeVal {
|
if kt == ktVal {
|
||||||
n += binary.MaxVarintLen32 + len(value)
|
n += binary.MaxVarintLen32 + len(value)
|
||||||
}
|
}
|
||||||
b.grow(n)
|
b.grow(n)
|
||||||
off := len(b.data)
|
off := len(b.data)
|
||||||
data := b.data[:off+n]
|
data := b.data[:off+n]
|
||||||
data[off] = byte(kt)
|
data[off] = byte(kt)
|
||||||
off++
|
off += 1
|
||||||
off += binary.PutUvarint(data[off:], uint64(len(key)))
|
off += binary.PutUvarint(data[off:], uint64(len(key)))
|
||||||
copy(data[off:], key)
|
copy(data[off:], key)
|
||||||
off += len(key)
|
off += len(key)
|
||||||
if kt == keyTypeVal {
|
if kt == ktVal {
|
||||||
off += binary.PutUvarint(data[off:], uint64(len(value)))
|
off += binary.PutUvarint(data[off:], uint64(len(value)))
|
||||||
copy(data[off:], value)
|
copy(data[off:], value)
|
||||||
off += len(value)
|
off += len(value)
|
||||||
@@ -97,13 +94,13 @@ func (b *Batch) appendRec(kt keyType, key, value []byte) {
|
|||||||
// Put appends 'put operation' of the given key/value pair to the batch.
|
// Put appends 'put operation' of the given key/value pair to the batch.
|
||||||
// It is safe to modify the contents of the argument after Put returns.
|
// It is safe to modify the contents of the argument after Put returns.
|
||||||
func (b *Batch) Put(key, value []byte) {
|
func (b *Batch) Put(key, value []byte) {
|
||||||
b.appendRec(keyTypeVal, key, value)
|
b.appendRec(ktVal, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete appends 'delete operation' of the given key to the batch.
|
// Delete appends 'delete operation' of the given key to the batch.
|
||||||
// It is safe to modify the contents of the argument after Delete returns.
|
// It is safe to modify the contents of the argument after Delete returns.
|
||||||
func (b *Batch) Delete(key []byte) {
|
func (b *Batch) Delete(key []byte) {
|
||||||
b.appendRec(keyTypeDel, key, nil)
|
b.appendRec(ktDel, key, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump dumps batch contents. The returned slice can be loaded into the
|
// Dump dumps batch contents. The returned slice can be loaded into the
|
||||||
@@ -124,14 +121,13 @@ func (b *Batch) Load(data []byte) error {
|
|||||||
|
|
||||||
// Replay replays batch contents.
|
// Replay replays batch contents.
|
||||||
func (b *Batch) Replay(r BatchReplay) error {
|
func (b *Batch) Replay(r BatchReplay) error {
|
||||||
return b.decodeRec(func(i int, kt keyType, key, value []byte) error {
|
return b.decodeRec(func(i int, kt kType, key, value []byte) {
|
||||||
switch kt {
|
switch kt {
|
||||||
case keyTypeVal:
|
case ktVal:
|
||||||
r.Put(key, value)
|
r.Put(key, value)
|
||||||
case keyTypeDel:
|
case ktDel:
|
||||||
r.Delete(key)
|
r.Delete(key)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,19 +193,18 @@ func (b *Batch) decode(prevSeq uint64, data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Batch) decodeRec(f func(i int, kt keyType, key, value []byte) error) error {
|
func (b *Batch) decodeRec(f func(i int, kt kType, key, value []byte)) (err error) {
|
||||||
off := batchHdrLen
|
off := batchHdrLen
|
||||||
for i := 0; i < b.rLen; i++ {
|
for i := 0; i < b.rLen; i++ {
|
||||||
if off >= len(b.data) {
|
if off >= len(b.data) {
|
||||||
return newErrBatchCorrupted("invalid records length")
|
return newErrBatchCorrupted("invalid records length")
|
||||||
}
|
}
|
||||||
|
|
||||||
kt := keyType(b.data[off])
|
kt := kType(b.data[off])
|
||||||
if kt > keyTypeVal {
|
if kt > ktVal {
|
||||||
panic(kt)
|
|
||||||
return newErrBatchCorrupted("bad record: invalid type")
|
return newErrBatchCorrupted("bad record: invalid type")
|
||||||
}
|
}
|
||||||
off++
|
off += 1
|
||||||
|
|
||||||
x, n := binary.Uvarint(b.data[off:])
|
x, n := binary.Uvarint(b.data[off:])
|
||||||
off += n
|
off += n
|
||||||
@@ -219,7 +214,7 @@ func (b *Batch) decodeRec(f func(i int, kt keyType, key, value []byte) error) er
|
|||||||
key := b.data[off : off+int(x)]
|
key := b.data[off : off+int(x)]
|
||||||
off += int(x)
|
off += int(x)
|
||||||
var value []byte
|
var value []byte
|
||||||
if kt == keyTypeVal {
|
if kt == ktVal {
|
||||||
x, n := binary.Uvarint(b.data[off:])
|
x, n := binary.Uvarint(b.data[off:])
|
||||||
off += n
|
off += n
|
||||||
if n <= 0 || off+int(x) > len(b.data) {
|
if n <= 0 || off+int(x) > len(b.data) {
|
||||||
@@ -229,19 +224,16 @@ func (b *Batch) decodeRec(f func(i int, kt keyType, key, value []byte) error) er
|
|||||||
off += int(x)
|
off += int(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := f(i, kt, key, value); err != nil {
|
f(i, kt, key, value)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Batch) memReplay(to *memdb.DB) error {
|
func (b *Batch) memReplay(to *memdb.DB) error {
|
||||||
var ikScratch []byte
|
return b.decodeRec(func(i int, kt kType, key, value []byte) {
|
||||||
return b.decodeRec(func(i int, kt keyType, key, value []byte) error {
|
ikey := newIkey(key, b.seq+uint64(i), kt)
|
||||||
ikScratch = makeInternalKey(ikScratch, key, b.seq+uint64(i), kt)
|
to.Put(ikey, value)
|
||||||
return to.Put(ikScratch, value)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,9 +245,8 @@ func (b *Batch) memDecodeAndReplay(prevSeq uint64, data []byte, to *memdb.DB) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Batch) revertMemReplay(to *memdb.DB) error {
|
func (b *Batch) revertMemReplay(to *memdb.DB) error {
|
||||||
var ikScratch []byte
|
return b.decodeRec(func(i int, kt kType, key, value []byte) {
|
||||||
return b.decodeRec(func(i int, kt keyType, key, value []byte) error {
|
ikey := newIkey(key, b.seq+uint64(i), kt)
|
||||||
ikScratch := makeInternalKey(ikScratch, key, b.seq+uint64(i), kt)
|
to.Delete(ikey)
|
||||||
return to.Delete(ikScratch)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Generated
Vendored
+3
-11
@@ -47,21 +47,17 @@ type Cacher interface {
|
|||||||
// so the the Release method will be called once object is released.
|
// so the the Release method will be called once object is released.
|
||||||
type Value interface{}
|
type Value interface{}
|
||||||
|
|
||||||
// NamespaceGetter provides convenient wrapper for namespace.
|
type CacheGetter struct {
|
||||||
type NamespaceGetter struct {
|
|
||||||
Cache *Cache
|
Cache *Cache
|
||||||
NS uint64
|
NS uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get simply calls Cache.Get() method.
|
func (g *CacheGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle {
|
||||||
func (g *NamespaceGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle {
|
|
||||||
return g.Cache.Get(g.NS, key, setFunc)
|
return g.Cache.Get(g.NS, key, setFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The hash tables implementation is based on:
|
// The hash tables implementation is based on:
|
||||||
// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu,
|
// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu, Kunlong Zhang, and Michael Spear. ACM Symposium on Principles of Distributed Computing, Jul 2014.
|
||||||
// Kunlong Zhang, and Michael Spear.
|
|
||||||
// ACM Symposium on Principles of Distributed Computing, Jul 2014.
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mInitialSize = 1 << 4
|
mInitialSize = 1 << 4
|
||||||
@@ -614,12 +610,10 @@ func (n *Node) unrefLocked() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is a 'cache handle' of a 'cache node'.
|
|
||||||
type Handle struct {
|
type Handle struct {
|
||||||
n unsafe.Pointer // *Node
|
n unsafe.Pointer // *Node
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the value of the 'cache node'.
|
|
||||||
func (h *Handle) Value() Value {
|
func (h *Handle) Value() Value {
|
||||||
n := (*Node)(atomic.LoadPointer(&h.n))
|
n := (*Node)(atomic.LoadPointer(&h.n))
|
||||||
if n != nil {
|
if n != nil {
|
||||||
@@ -628,8 +622,6 @@ func (h *Handle) Value() Value {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release releases this 'cache handle'.
|
|
||||||
// It is safe to call release multiple times.
|
|
||||||
func (h *Handle) Release() {
|
func (h *Handle) Release() {
|
||||||
nPtr := atomic.LoadPointer(&h.n)
|
nPtr := atomic.LoadPointer(&h.n)
|
||||||
if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) {
|
if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) {
|
||||||
Generated
Vendored
Generated
Vendored
+6
-6
@@ -33,9 +33,9 @@ func (icmp *iComparer) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (icmp *iComparer) Compare(a, b []byte) int {
|
func (icmp *iComparer) Compare(a, b []byte) int {
|
||||||
x := icmp.ucmp.Compare(internalKey(a).ukey(), internalKey(b).ukey())
|
x := icmp.ucmp.Compare(iKey(a).ukey(), iKey(b).ukey())
|
||||||
if x == 0 {
|
if x == 0 {
|
||||||
if m, n := internalKey(a).num(), internalKey(b).num(); m > n {
|
if m, n := iKey(a).num(), iKey(b).num(); m > n {
|
||||||
x = -1
|
x = -1
|
||||||
} else if m < n {
|
} else if m < n {
|
||||||
x = 1
|
x = 1
|
||||||
@@ -45,13 +45,13 @@ func (icmp *iComparer) Compare(a, b []byte) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (icmp *iComparer) Separator(dst, a, b []byte) []byte {
|
func (icmp *iComparer) Separator(dst, a, b []byte) []byte {
|
||||||
ua, ub := internalKey(a).ukey(), internalKey(b).ukey()
|
ua, ub := iKey(a).ukey(), iKey(b).ukey()
|
||||||
dst = icmp.ucmp.Separator(dst, ua, ub)
|
dst = icmp.ucmp.Separator(dst, ua, ub)
|
||||||
if dst == nil {
|
if dst == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(dst) < len(ua) && icmp.uCompare(ua, dst) < 0 {
|
if len(dst) < len(ua) && icmp.uCompare(ua, dst) < 0 {
|
||||||
dst = append(dst, keyMaxNumBytes...)
|
dst = append(dst, kMaxNumBytes...)
|
||||||
} else {
|
} else {
|
||||||
// Did not close possibilities that n maybe longer than len(ub).
|
// Did not close possibilities that n maybe longer than len(ub).
|
||||||
dst = append(dst, a[len(a)-8:]...)
|
dst = append(dst, a[len(a)-8:]...)
|
||||||
@@ -60,13 +60,13 @@ func (icmp *iComparer) Separator(dst, a, b []byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (icmp *iComparer) Successor(dst, b []byte) []byte {
|
func (icmp *iComparer) Successor(dst, b []byte) []byte {
|
||||||
ub := internalKey(b).ukey()
|
ub := iKey(b).ukey()
|
||||||
dst = icmp.ucmp.Successor(dst, ub)
|
dst = icmp.ucmp.Successor(dst, ub)
|
||||||
if dst == nil {
|
if dst == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(dst) < len(ub) && icmp.uCompare(ub, dst) < 0 {
|
if len(dst) < len(ub) && icmp.uCompare(ub, dst) < 0 {
|
||||||
dst = append(dst, keyMaxNumBytes...)
|
dst = append(dst, kMaxNumBytes...)
|
||||||
} else {
|
} else {
|
||||||
// Did not close possibilities that n maybe longer than len(ub).
|
// Did not close possibilities that n maybe longer than len(ub).
|
||||||
dst = append(dst, b[len(b)-8:]...)
|
dst = append(dst, b[len(b)-8:]...)
|
||||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
+151
-171
@@ -36,14 +36,14 @@ type DB struct {
|
|||||||
s *session
|
s *session
|
||||||
|
|
||||||
// MemDB.
|
// MemDB.
|
||||||
memMu sync.RWMutex
|
memMu sync.RWMutex
|
||||||
memPool chan *memdb.DB
|
memPool chan *memdb.DB
|
||||||
mem, frozenMem *memDB
|
mem, frozenMem *memDB
|
||||||
journal *journal.Writer
|
journal *journal.Writer
|
||||||
journalWriter storage.Writer
|
journalWriter storage.Writer
|
||||||
journalFd storage.FileDesc
|
journalFile storage.File
|
||||||
frozenJournalFd storage.FileDesc
|
frozenJournalFile storage.File
|
||||||
frozenSeq uint64
|
frozenSeq uint64
|
||||||
|
|
||||||
// Snapshot.
|
// Snapshot.
|
||||||
snapsMu sync.Mutex
|
snapsMu sync.Mutex
|
||||||
@@ -61,10 +61,8 @@ type DB struct {
|
|||||||
writeDelayN int
|
writeDelayN int
|
||||||
journalC chan *Batch
|
journalC chan *Batch
|
||||||
journalAckC chan error
|
journalAckC chan error
|
||||||
tr *Transaction
|
|
||||||
|
|
||||||
// Compaction.
|
// Compaction.
|
||||||
compCommitLk sync.Mutex
|
|
||||||
tcompCmdC chan cCmd
|
tcompCmdC chan cCmd
|
||||||
tcompPauseC chan chan<- struct{}
|
tcompPauseC chan chan<- struct{}
|
||||||
mcompCmdC chan cCmd
|
mcompCmdC chan cCmd
|
||||||
@@ -72,8 +70,7 @@ type DB struct {
|
|||||||
compPerErrC chan error
|
compPerErrC chan error
|
||||||
compErrSetC chan error
|
compErrSetC chan error
|
||||||
compWriteLocking bool
|
compWriteLocking bool
|
||||||
compStats cStats
|
compStats []cStats
|
||||||
memdbMaxLevel int // For testing.
|
|
||||||
|
|
||||||
// Close.
|
// Close.
|
||||||
closeW sync.WaitGroup
|
closeW sync.WaitGroup
|
||||||
@@ -107,6 +104,7 @@ func openDB(s *session) (*DB, error) {
|
|||||||
compErrC: make(chan error),
|
compErrC: make(chan error),
|
||||||
compPerErrC: make(chan error),
|
compPerErrC: make(chan error),
|
||||||
compErrSetC: make(chan error),
|
compErrSetC: make(chan error),
|
||||||
|
compStats: make([]cStats, s.o.GetNumLevel()),
|
||||||
// Close
|
// Close
|
||||||
closeC: make(chan struct{}),
|
closeC: make(chan struct{}),
|
||||||
}
|
}
|
||||||
@@ -211,7 +209,7 @@ func Open(stor storage.Storage, o *opt.Options) (db *DB, err error) {
|
|||||||
// The returned DB instance is goroutine-safe.
|
// The returned DB instance is goroutine-safe.
|
||||||
// The DB must be closed after use, by calling Close method.
|
// The DB must be closed after use, by calling Close method.
|
||||||
func OpenFile(path string, o *opt.Options) (db *DB, err error) {
|
func OpenFile(path string, o *opt.Options) (db *DB, err error) {
|
||||||
stor, err := storage.OpenFile(path, o.GetReadOnly())
|
stor, err := storage.OpenFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -261,7 +259,7 @@ func Recover(stor storage.Storage, o *opt.Options) (db *DB, err error) {
|
|||||||
// The returned DB instance is goroutine-safe.
|
// The returned DB instance is goroutine-safe.
|
||||||
// The DB must be closed after use, by calling Close method.
|
// The DB must be closed after use, by calling Close method.
|
||||||
func RecoverFile(path string, o *opt.Options) (db *DB, err error) {
|
func RecoverFile(path string, o *opt.Options) (db *DB, err error) {
|
||||||
stor, err := storage.OpenFile(path, false)
|
stor, err := storage.OpenFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -280,11 +278,12 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
o.Strict &= ^opt.StrictReader
|
o.Strict &= ^opt.StrictReader
|
||||||
|
|
||||||
// Get all tables and sort it by file number.
|
// Get all tables and sort it by file number.
|
||||||
fds, err := s.stor.List(storage.TypeTable)
|
tableFiles_, err := s.getFiles(storage.TypeTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sortFds(fds)
|
tableFiles := files(tableFiles_)
|
||||||
|
tableFiles.sort()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
maxSeq uint64
|
maxSeq uint64
|
||||||
@@ -297,17 +296,17 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
rec = &sessionRecord{}
|
rec = &sessionRecord{}
|
||||||
bpool = util.NewBufferPool(o.GetBlockSize() + 5)
|
bpool = util.NewBufferPool(o.GetBlockSize() + 5)
|
||||||
)
|
)
|
||||||
buildTable := func(iter iterator.Iterator) (tmpFd storage.FileDesc, size int64, err error) {
|
buildTable := func(iter iterator.Iterator) (tmp storage.File, size int64, err error) {
|
||||||
tmpFd = s.newTemp()
|
tmp = s.newTemp()
|
||||||
writer, err := s.stor.Create(tmpFd)
|
writer, err := tmp.Create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
writer.Close()
|
writer.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.stor.Remove(tmpFd)
|
tmp.Remove()
|
||||||
tmpFd = storage.FileDesc{}
|
tmp = nil
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -315,7 +314,7 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
tw := table.NewWriter(writer, o)
|
tw := table.NewWriter(writer, o)
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
key := iter.Key()
|
key := iter.Key()
|
||||||
if validInternalKey(key) {
|
if validIkey(key) {
|
||||||
err = tw.Append(key, iter.Value())
|
err = tw.Append(key, iter.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -339,9 +338,9 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
size = int64(tw.BytesLen())
|
size = int64(tw.BytesLen())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
recoverTable := func(fd storage.FileDesc) error {
|
recoverTable := func(file storage.File) error {
|
||||||
s.logf("table@recovery recovering @%d", fd.Num)
|
s.logf("table@recovery recovering @%d", file.Num())
|
||||||
reader, err := s.stor.Open(fd)
|
reader, err := file.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -363,7 +362,7 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
tgoodKey, tcorruptedKey, tcorruptedBlock int
|
tgoodKey, tcorruptedKey, tcorruptedBlock int
|
||||||
imin, imax []byte
|
imin, imax []byte
|
||||||
)
|
)
|
||||||
tr, err := table.NewReader(reader, size, fd, nil, bpool, o)
|
tr, err := table.NewReader(reader, size, storage.NewFileInfo(file), nil, bpool, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -371,7 +370,7 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
if itererr, ok := iter.(iterator.ErrorCallbackSetter); ok {
|
if itererr, ok := iter.(iterator.ErrorCallbackSetter); ok {
|
||||||
itererr.SetErrorCallback(func(err error) {
|
itererr.SetErrorCallback(func(err error) {
|
||||||
if errors.IsCorrupted(err) {
|
if errors.IsCorrupted(err) {
|
||||||
s.logf("table@recovery block corruption @%d %q", fd.Num, err)
|
s.logf("table@recovery block corruption @%d %q", file.Num(), err)
|
||||||
tcorruptedBlock++
|
tcorruptedBlock++
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -380,7 +379,7 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
// Scan the table.
|
// Scan the table.
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
key := iter.Key()
|
key := iter.Key()
|
||||||
_, seq, _, kerr := parseInternalKey(key)
|
_, seq, _, kerr := parseIkey(key)
|
||||||
if kerr != nil {
|
if kerr != nil {
|
||||||
tcorruptedKey++
|
tcorruptedKey++
|
||||||
continue
|
continue
|
||||||
@@ -406,23 +405,23 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
|
|
||||||
if strict && (tcorruptedKey > 0 || tcorruptedBlock > 0) {
|
if strict && (tcorruptedKey > 0 || tcorruptedBlock > 0) {
|
||||||
droppedTable++
|
droppedTable++
|
||||||
s.logf("table@recovery dropped @%d Gk·%d Ck·%d Cb·%d S·%d Q·%d", fd.Num, tgoodKey, tcorruptedKey, tcorruptedBlock, size, tSeq)
|
s.logf("table@recovery dropped @%d Gk·%d Ck·%d Cb·%d S·%d Q·%d", file.Num(), tgoodKey, tcorruptedKey, tcorruptedBlock, size, tSeq)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if tgoodKey > 0 {
|
if tgoodKey > 0 {
|
||||||
if tcorruptedKey > 0 || tcorruptedBlock > 0 {
|
if tcorruptedKey > 0 || tcorruptedBlock > 0 {
|
||||||
// Rebuild the table.
|
// Rebuild the table.
|
||||||
s.logf("table@recovery rebuilding @%d", fd.Num)
|
s.logf("table@recovery rebuilding @%d", file.Num())
|
||||||
iter := tr.NewIterator(nil, nil)
|
iter := tr.NewIterator(nil, nil)
|
||||||
tmpFd, newSize, err := buildTable(iter)
|
tmp, newSize, err := buildTable(iter)
|
||||||
iter.Release()
|
iter.Release()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
closed = true
|
closed = true
|
||||||
reader.Close()
|
reader.Close()
|
||||||
if err := s.stor.Rename(tmpFd, fd); err != nil {
|
if err := file.Replace(tmp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
size = newSize
|
size = newSize
|
||||||
@@ -432,30 +431,30 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
}
|
}
|
||||||
recoveredKey += tgoodKey
|
recoveredKey += tgoodKey
|
||||||
// Add table to level 0.
|
// Add table to level 0.
|
||||||
rec.addTable(0, fd.Num, size, imin, imax)
|
rec.addTable(0, file.Num(), uint64(size), imin, imax)
|
||||||
s.logf("table@recovery recovered @%d Gk·%d Ck·%d Cb·%d S·%d Q·%d", fd.Num, tgoodKey, tcorruptedKey, tcorruptedBlock, size, tSeq)
|
s.logf("table@recovery recovered @%d Gk·%d Ck·%d Cb·%d S·%d Q·%d", file.Num(), tgoodKey, tcorruptedKey, tcorruptedBlock, size, tSeq)
|
||||||
} else {
|
} else {
|
||||||
droppedTable++
|
droppedTable++
|
||||||
s.logf("table@recovery unrecoverable @%d Ck·%d Cb·%d S·%d", fd.Num, tcorruptedKey, tcorruptedBlock, size)
|
s.logf("table@recovery unrecoverable @%d Ck·%d Cb·%d S·%d", file.Num(), tcorruptedKey, tcorruptedBlock, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recover all tables.
|
// Recover all tables.
|
||||||
if len(fds) > 0 {
|
if len(tableFiles) > 0 {
|
||||||
s.logf("table@recovery F·%d", len(fds))
|
s.logf("table@recovery F·%d", len(tableFiles))
|
||||||
|
|
||||||
// Mark file number as used.
|
// Mark file number as used.
|
||||||
s.markFileNum(fds[len(fds)-1].Num)
|
s.markFileNum(tableFiles[len(tableFiles)-1].Num())
|
||||||
|
|
||||||
for _, fd := range fds {
|
for _, file := range tableFiles {
|
||||||
if err := recoverTable(fd); err != nil {
|
if err := recoverTable(file); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logf("table@recovery recovered F·%d N·%d Gk·%d Ck·%d Q·%d", len(fds), recoveredKey, goodKey, corruptedKey, maxSeq)
|
s.logf("table@recovery recovered F·%d N·%d Gk·%d Ck·%d Q·%d", len(tableFiles), recoveredKey, goodKey, corruptedKey, maxSeq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sequence number.
|
// Set sequence number.
|
||||||
@@ -472,31 +471,31 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
|
|
||||||
func (db *DB) recoverJournal() error {
|
func (db *DB) recoverJournal() error {
|
||||||
// Get all journals and sort it by file number.
|
// Get all journals and sort it by file number.
|
||||||
rawFds, err := db.s.stor.List(storage.TypeJournal)
|
allJournalFiles, err := db.s.getFiles(storage.TypeJournal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sortFds(rawFds)
|
files(allJournalFiles).sort()
|
||||||
|
|
||||||
// Journals that will be recovered.
|
// Journals that will be recovered.
|
||||||
var fds []storage.FileDesc
|
var recJournalFiles []storage.File
|
||||||
for _, fd := range rawFds {
|
for _, jf := range allJournalFiles {
|
||||||
if fd.Num >= db.s.stJournalNum || fd.Num == db.s.stPrevJournalNum {
|
if jf.Num() >= db.s.stJournalNum || jf.Num() == db.s.stPrevJournalNum {
|
||||||
fds = append(fds, fd)
|
recJournalFiles = append(recJournalFiles, jf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ofd storage.FileDesc // Obsolete file.
|
of storage.File // Obsolete file.
|
||||||
rec = &sessionRecord{}
|
rec = &sessionRecord{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Recover journals.
|
// Recover journals.
|
||||||
if len(fds) > 0 {
|
if len(recJournalFiles) > 0 {
|
||||||
db.logf("journal@recovery F·%d", len(fds))
|
db.logf("journal@recovery F·%d", len(recJournalFiles))
|
||||||
|
|
||||||
// Mark file number as used.
|
// Mark file number as used.
|
||||||
db.s.markFileNum(fds[len(fds)-1].Num)
|
db.s.markFileNum(recJournalFiles[len(recJournalFiles)-1].Num())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Options.
|
// Options.
|
||||||
@@ -510,31 +509,31 @@ func (db *DB) recoverJournal() error {
|
|||||||
batch = &Batch{}
|
batch = &Batch{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, fd := range fds {
|
for _, jf := range recJournalFiles {
|
||||||
db.logf("journal@recovery recovering @%d", fd.Num)
|
db.logf("journal@recovery recovering @%d", jf.Num())
|
||||||
|
|
||||||
fr, err := db.s.stor.Open(fd)
|
fr, err := jf.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create or reset journal reader instance.
|
// Create or reset journal reader instance.
|
||||||
if jr == nil {
|
if jr == nil {
|
||||||
jr = journal.NewReader(fr, dropper{db.s, fd}, strict, checksum)
|
jr = journal.NewReader(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
} else {
|
} else {
|
||||||
jr.Reset(fr, dropper{db.s, fd}, strict, checksum)
|
jr.Reset(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush memdb and remove obsolete journal file.
|
// Flush memdb and remove obsolete journal file.
|
||||||
if !ofd.Nil() {
|
if of != nil {
|
||||||
if mdb.Len() > 0 {
|
if mdb.Len() > 0 {
|
||||||
if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil {
|
if _, err := db.s.flushMemdb(rec, mdb, -1); err != nil {
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rec.setJournalNum(fd.Num)
|
rec.setJournalNum(jf.Num())
|
||||||
rec.setSeqNum(db.seq)
|
rec.setSeqNum(db.seq)
|
||||||
if err := db.s.commit(rec); err != nil {
|
if err := db.s.commit(rec); err != nil {
|
||||||
fr.Close()
|
fr.Close()
|
||||||
@@ -542,8 +541,8 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
rec.resetAddedTables()
|
rec.resetAddedTables()
|
||||||
|
|
||||||
db.s.stor.Remove(ofd)
|
of.Remove()
|
||||||
ofd = storage.FileDesc{}
|
of = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replay journal to memdb.
|
// Replay journal to memdb.
|
||||||
@@ -556,7 +555,7 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return errors.SetFd(err, fd)
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
@@ -567,7 +566,7 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return errors.SetFd(err, fd)
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil {
|
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil {
|
||||||
if !strict && errors.IsCorrupted(err) {
|
if !strict && errors.IsCorrupted(err) {
|
||||||
@@ -577,7 +576,7 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return errors.SetFd(err, fd)
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save sequence number.
|
// Save sequence number.
|
||||||
@@ -595,7 +594,7 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
ofd = fd
|
of = jf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush the last memdb.
|
// Flush the last memdb.
|
||||||
@@ -612,7 +611,7 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Commit.
|
// Commit.
|
||||||
rec.setJournalNum(db.journalFd.Num)
|
rec.setJournalNum(db.journalFile.Num())
|
||||||
rec.setSeqNum(db.seq)
|
rec.setSeqNum(db.seq)
|
||||||
if err := db.s.commit(rec); err != nil {
|
if err := db.s.commit(rec); err != nil {
|
||||||
// Close journal on error.
|
// Close journal on error.
|
||||||
@@ -624,8 +623,8 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove the last obsolete journal file.
|
// Remove the last obsolete journal file.
|
||||||
if !ofd.Nil() {
|
if of != nil {
|
||||||
db.s.stor.Remove(ofd)
|
of.Remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -633,17 +632,17 @@ func (db *DB) recoverJournal() error {
|
|||||||
|
|
||||||
func (db *DB) recoverJournalRO() error {
|
func (db *DB) recoverJournalRO() error {
|
||||||
// Get all journals and sort it by file number.
|
// Get all journals and sort it by file number.
|
||||||
rawFds, err := db.s.stor.List(storage.TypeJournal)
|
allJournalFiles, err := db.s.getFiles(storage.TypeJournal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sortFds(rawFds)
|
files(allJournalFiles).sort()
|
||||||
|
|
||||||
// Journals that will be recovered.
|
// Journals that will be recovered.
|
||||||
var fds []storage.FileDesc
|
var recJournalFiles []storage.File
|
||||||
for _, fd := range rawFds {
|
for _, jf := range allJournalFiles {
|
||||||
if fd.Num >= db.s.stJournalNum || fd.Num == db.s.stPrevJournalNum {
|
if jf.Num() >= db.s.stJournalNum || jf.Num() == db.s.stPrevJournalNum {
|
||||||
fds = append(fds, fd)
|
recJournalFiles = append(recJournalFiles, jf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,8 +656,8 @@ func (db *DB) recoverJournalRO() error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Recover journals.
|
// Recover journals.
|
||||||
if len(fds) > 0 {
|
if len(recJournalFiles) > 0 {
|
||||||
db.logf("journal@recovery RO·Mode F·%d", len(fds))
|
db.logf("journal@recovery RO·Mode F·%d", len(recJournalFiles))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
jr *journal.Reader
|
jr *journal.Reader
|
||||||
@@ -666,19 +665,19 @@ func (db *DB) recoverJournalRO() error {
|
|||||||
batch = &Batch{}
|
batch = &Batch{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, fd := range fds {
|
for _, jf := range recJournalFiles {
|
||||||
db.logf("journal@recovery recovering @%d", fd.Num)
|
db.logf("journal@recovery recovering @%d", jf.Num())
|
||||||
|
|
||||||
fr, err := db.s.stor.Open(fd)
|
fr, err := jf.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create or reset journal reader instance.
|
// Create or reset journal reader instance.
|
||||||
if jr == nil {
|
if jr == nil {
|
||||||
jr = journal.NewReader(fr, dropper{db.s, fd}, strict, checksum)
|
jr = journal.NewReader(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
} else {
|
} else {
|
||||||
jr.Reset(fr, dropper{db.s, fd}, strict, checksum)
|
jr.Reset(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replay journal to memdb.
|
// Replay journal to memdb.
|
||||||
@@ -690,7 +689,7 @@ func (db *DB) recoverJournalRO() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return errors.SetFd(err, fd)
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
@@ -701,7 +700,7 @@ func (db *DB) recoverJournalRO() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return errors.SetFd(err, fd)
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil {
|
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil {
|
||||||
if !strict && errors.IsCorrupted(err) {
|
if !strict && errors.IsCorrupted(err) {
|
||||||
@@ -711,7 +710,7 @@ func (db *DB) recoverJournalRO() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fr.Close()
|
fr.Close()
|
||||||
return errors.SetFd(err, fd)
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save sequence number.
|
// Save sequence number.
|
||||||
@@ -728,35 +727,46 @@ func (db *DB) recoverJournalRO() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func memGet(mdb *memdb.DB, ikey internalKey, icmp *iComparer) (ok bool, mv []byte, err error) {
|
func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, err error) {
|
||||||
mk, mv, err := mdb.Find(ikey)
|
ikey := newIkey(key, seq, ktSeek)
|
||||||
if err == nil {
|
|
||||||
ukey, _, kt, kerr := parseInternalKey(mk)
|
em, fm := db.getMems()
|
||||||
if kerr != nil {
|
for _, m := range [...]*memDB{em, fm} {
|
||||||
// Shouldn't have had happen.
|
if m == nil {
|
||||||
panic(kerr)
|
continue
|
||||||
}
|
}
|
||||||
if icmp.uCompare(ukey, ikey.ukey()) == 0 {
|
defer m.decref()
|
||||||
if kt == keyTypeDel {
|
|
||||||
return true, nil, ErrNotFound
|
mk, mv, me := m.Find(ikey)
|
||||||
|
if me == nil {
|
||||||
|
ukey, _, kt, kerr := parseIkey(mk)
|
||||||
|
if kerr != nil {
|
||||||
|
// Shouldn't have had happen.
|
||||||
|
panic(kerr)
|
||||||
}
|
}
|
||||||
return true, mv, nil
|
if db.s.icmp.uCompare(ukey, key) == 0 {
|
||||||
|
if kt == ktDel {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return append([]byte{}, mv...), nil
|
||||||
|
}
|
||||||
|
} else if me != ErrNotFound {
|
||||||
|
return nil, me
|
||||||
}
|
}
|
||||||
} else if err != ErrNotFound {
|
}
|
||||||
return true, nil, err
|
|
||||||
|
v := db.s.version()
|
||||||
|
value, cSched, err := v.get(ikey, ro, false)
|
||||||
|
v.release()
|
||||||
|
if cSched {
|
||||||
|
// Trigger table compaction.
|
||||||
|
db.compSendTrigger(db.tcompCmdC)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) get(auxm *memdb.DB, auxt tFiles, key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, err error) {
|
func (db *DB) has(key []byte, seq uint64, ro *opt.ReadOptions) (ret bool, err error) {
|
||||||
ikey := makeInternalKey(nil, key, seq, keyTypeSeek)
|
ikey := newIkey(key, seq, ktSeek)
|
||||||
|
|
||||||
if auxm != nil {
|
|
||||||
if ok, mv, me := memGet(auxm, ikey, db.s.icmp); ok {
|
|
||||||
return append([]byte{}, mv...), me
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
em, fm := db.getMems()
|
em, fm := db.getMems()
|
||||||
for _, m := range [...]*memDB{em, fm} {
|
for _, m := range [...]*memDB{em, fm} {
|
||||||
@@ -765,55 +775,30 @@ func (db *DB) get(auxm *memdb.DB, auxt tFiles, key []byte, seq uint64, ro *opt.R
|
|||||||
}
|
}
|
||||||
defer m.decref()
|
defer m.decref()
|
||||||
|
|
||||||
if ok, mv, me := memGet(m.DB, ikey, db.s.icmp); ok {
|
mk, _, me := m.Find(ikey)
|
||||||
return append([]byte{}, mv...), me
|
if me == nil {
|
||||||
|
ukey, _, kt, kerr := parseIkey(mk)
|
||||||
|
if kerr != nil {
|
||||||
|
// Shouldn't have had happen.
|
||||||
|
panic(kerr)
|
||||||
|
}
|
||||||
|
if db.s.icmp.uCompare(ukey, key) == 0 {
|
||||||
|
if kt == ktDel {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
} else if me != ErrNotFound {
|
||||||
|
return false, me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v := db.s.version()
|
v := db.s.version()
|
||||||
value, cSched, err := v.get(auxt, ikey, ro, false)
|
_, cSched, err := v.get(ikey, ro, true)
|
||||||
v.release()
|
v.release()
|
||||||
if cSched {
|
if cSched {
|
||||||
// Trigger table compaction.
|
// Trigger table compaction.
|
||||||
db.compTrigger(db.tcompCmdC)
|
db.compSendTrigger(db.tcompCmdC)
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func nilIfNotFound(err error) error {
|
|
||||||
if err == ErrNotFound {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) has(auxm *memdb.DB, auxt tFiles, key []byte, seq uint64, ro *opt.ReadOptions) (ret bool, err error) {
|
|
||||||
ikey := makeInternalKey(nil, key, seq, keyTypeSeek)
|
|
||||||
|
|
||||||
if auxm != nil {
|
|
||||||
if ok, _, me := memGet(auxm, ikey, db.s.icmp); ok {
|
|
||||||
return me == nil, nilIfNotFound(me)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
em, fm := db.getMems()
|
|
||||||
for _, m := range [...]*memDB{em, fm} {
|
|
||||||
if m == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
defer m.decref()
|
|
||||||
|
|
||||||
if ok, _, me := memGet(m.DB, ikey, db.s.icmp); ok {
|
|
||||||
return me == nil, nilIfNotFound(me)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v := db.s.version()
|
|
||||||
_, cSched, err := v.get(auxt, ikey, ro, true)
|
|
||||||
v.release()
|
|
||||||
if cSched {
|
|
||||||
// Trigger table compaction.
|
|
||||||
db.compTrigger(db.tcompCmdC)
|
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ret = true
|
ret = true
|
||||||
@@ -837,7 +822,7 @@ func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
|
|||||||
|
|
||||||
se := db.acquireSnapshot()
|
se := db.acquireSnapshot()
|
||||||
defer db.releaseSnapshot(se)
|
defer db.releaseSnapshot(se)
|
||||||
return db.get(nil, nil, key, se.seq, ro)
|
return db.get(key, se.seq, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has returns true if the DB does contains the given key.
|
// Has returns true if the DB does contains the given key.
|
||||||
@@ -851,11 +836,11 @@ func (db *DB) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
|
|||||||
|
|
||||||
se := db.acquireSnapshot()
|
se := db.acquireSnapshot()
|
||||||
defer db.releaseSnapshot(se)
|
defer db.releaseSnapshot(se)
|
||||||
return db.has(nil, nil, key, se.seq, ro)
|
return db.has(key, se.seq, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIterator returns an iterator for the latest snapshot of the
|
// NewIterator returns an iterator for the latest snapshot of the
|
||||||
// underlying DB.
|
// uderlying DB.
|
||||||
// The returned iterator is not goroutine-safe, but it is safe to use
|
// The returned iterator is not goroutine-safe, but it is safe to use
|
||||||
// multiple iterators concurrently, with each in a dedicated goroutine.
|
// multiple iterators concurrently, with each in a dedicated goroutine.
|
||||||
// It is also safe to use an iterator concurrently with modifying its
|
// It is also safe to use an iterator concurrently with modifying its
|
||||||
@@ -879,7 +864,7 @@ func (db *DB) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Itera
|
|||||||
defer db.releaseSnapshot(se)
|
defer db.releaseSnapshot(se)
|
||||||
// Iterator holds 'version' lock, 'version' is immutable so snapshot
|
// Iterator holds 'version' lock, 'version' is immutable so snapshot
|
||||||
// can be released after iterator created.
|
// can be released after iterator created.
|
||||||
return db.newIterator(nil, nil, se.seq, slice, ro)
|
return db.newIterator(se.seq, slice, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSnapshot returns a latest snapshot of the underlying DB. A snapshot
|
// GetSnapshot returns a latest snapshot of the underlying DB. A snapshot
|
||||||
@@ -935,7 +920,7 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
var level uint
|
var level uint
|
||||||
var rest string
|
var rest string
|
||||||
n, _ := fmt.Sscanf(p[len(numFilesPrefix):], "%d%s", &level, &rest)
|
n, _ := fmt.Sscanf(p[len(numFilesPrefix):], "%d%s", &level, &rest)
|
||||||
if n != 1 {
|
if n != 1 || int(level) >= db.s.o.GetNumLevel() {
|
||||||
err = ErrNotFound
|
err = ErrNotFound
|
||||||
} else {
|
} else {
|
||||||
value = fmt.Sprint(v.tLen(int(level)))
|
value = fmt.Sprint(v.tLen(int(level)))
|
||||||
@@ -944,8 +929,8 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
value = "Compactions\n" +
|
value = "Compactions\n" +
|
||||||
" Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" +
|
" Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" +
|
||||||
"-------+------------+---------------+---------------+---------------+---------------\n"
|
"-------+------------+---------------+---------------+---------------+---------------\n"
|
||||||
for level, tables := range v.levels {
|
for level, tables := range v.tables {
|
||||||
duration, read, write := db.compStats.getStat(level)
|
duration, read, write := db.compStats[level].get()
|
||||||
if len(tables) == 0 && duration == 0 {
|
if len(tables) == 0 && duration == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -954,10 +939,10 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
float64(read)/1048576.0, float64(write)/1048576.0)
|
float64(read)/1048576.0, float64(write)/1048576.0)
|
||||||
}
|
}
|
||||||
case p == "sstables":
|
case p == "sstables":
|
||||||
for level, tables := range v.levels {
|
for level, tables := range v.tables {
|
||||||
value += fmt.Sprintf("--- level %d ---\n", level)
|
value += fmt.Sprintf("--- level %d ---\n", level)
|
||||||
for _, t := range tables {
|
for _, t := range tables {
|
||||||
value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.fd.Num, t.size, t.imin, t.imax)
|
value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.file.Num(), t.size, t.imin, t.imax)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case p == "blockpool":
|
case p == "blockpool":
|
||||||
@@ -997,8 +982,8 @@ func (db *DB) SizeOf(ranges []util.Range) (Sizes, error) {
|
|||||||
|
|
||||||
sizes := make(Sizes, 0, len(ranges))
|
sizes := make(Sizes, 0, len(ranges))
|
||||||
for _, r := range ranges {
|
for _, r := range ranges {
|
||||||
imin := makeInternalKey(nil, r.Start, keyMaxSeq, keyTypeSeek)
|
imin := newIkey(r.Start, kMaxSeq, ktSeek)
|
||||||
imax := makeInternalKey(nil, r.Limit, keyMaxSeq, keyTypeSeek)
|
imax := newIkey(r.Limit, kMaxSeq, ktSeek)
|
||||||
start, err := v.offsetOf(imin)
|
start, err := v.offsetOf(imin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1007,7 +992,7 @@ func (db *DB) SizeOf(ranges []util.Range) (Sizes, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var size int64
|
var size uint64
|
||||||
if limit >= start {
|
if limit >= start {
|
||||||
size = limit - start
|
size = limit - start
|
||||||
}
|
}
|
||||||
@@ -1017,8 +1002,8 @@ func (db *DB) SizeOf(ranges []util.Range) (Sizes, error) {
|
|||||||
return sizes, nil
|
return sizes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the DB. This will also releases any outstanding snapshot,
|
// Close closes the DB. This will also releases any outstanding snapshot and
|
||||||
// abort any in-flight compaction and discard open transaction.
|
// abort any in-flight compaction.
|
||||||
//
|
//
|
||||||
// It is not safe to close a DB until all outstanding iterators are released.
|
// It is not safe to close a DB until all outstanding iterators are released.
|
||||||
// It is valid to call Close multiple times. Other methods should not be
|
// It is valid to call Close multiple times. Other methods should not be
|
||||||
@@ -1047,18 +1032,11 @@ func (db *DB) Close() error {
|
|||||||
// Signal all goroutines.
|
// Signal all goroutines.
|
||||||
close(db.closeC)
|
close(db.closeC)
|
||||||
|
|
||||||
// Discard open transaction.
|
|
||||||
if db.tr != nil {
|
|
||||||
db.tr.Discard()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire writer lock.
|
|
||||||
db.writeLockC <- struct{}{}
|
|
||||||
|
|
||||||
// Wait for all gorotines to exit.
|
// Wait for all gorotines to exit.
|
||||||
db.closeW.Wait()
|
db.closeW.Wait()
|
||||||
|
|
||||||
// Closes journal.
|
// Lock writer and closes journal.
|
||||||
|
db.writeLockC <- struct{}{}
|
||||||
if db.journal != nil {
|
if db.journal != nil {
|
||||||
db.journal.Close()
|
db.journal.Close()
|
||||||
db.journalWriter.Close()
|
db.journalWriter.Close()
|
||||||
@@ -1085,6 +1063,8 @@ func (db *DB) Close() error {
|
|||||||
db.frozenMem = nil
|
db.frozenMem = nil
|
||||||
db.journal = nil
|
db.journal = nil
|
||||||
db.journalWriter = nil
|
db.journalWriter = nil
|
||||||
|
db.journalFile = nil
|
||||||
|
db.frozenJournalFile = nil
|
||||||
db.closer = nil
|
db.closer = nil
|
||||||
|
|
||||||
return err
|
return err
|
||||||
Generated
Vendored
+88
-123
@@ -12,76 +12,55 @@ import (
|
|||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errCompactionTransactExiting = errors.New("leveldb: compaction transact exiting")
|
errCompactionTransactExiting = errors.New("leveldb: compaction transact exiting")
|
||||||
)
|
)
|
||||||
|
|
||||||
type cStat struct {
|
type cStats struct {
|
||||||
|
sync.Mutex
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
read int64
|
read uint64
|
||||||
write int64
|
write uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cStat) add(n *cStatStaging) {
|
func (p *cStats) add(n *cStatsStaging) {
|
||||||
|
p.Lock()
|
||||||
p.duration += n.duration
|
p.duration += n.duration
|
||||||
p.read += n.read
|
p.read += n.read
|
||||||
p.write += n.write
|
p.write += n.write
|
||||||
|
p.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cStat) get() (duration time.Duration, read, write int64) {
|
func (p *cStats) get() (duration time.Duration, read, write uint64) {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
return p.duration, p.read, p.write
|
return p.duration, p.read, p.write
|
||||||
}
|
}
|
||||||
|
|
||||||
type cStatStaging struct {
|
type cStatsStaging struct {
|
||||||
start time.Time
|
start time.Time
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
on bool
|
on bool
|
||||||
read int64
|
read uint64
|
||||||
write int64
|
write uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cStatStaging) startTimer() {
|
func (p *cStatsStaging) startTimer() {
|
||||||
if !p.on {
|
if !p.on {
|
||||||
p.start = time.Now()
|
p.start = time.Now()
|
||||||
p.on = true
|
p.on = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cStatStaging) stopTimer() {
|
func (p *cStatsStaging) stopTimer() {
|
||||||
if p.on {
|
if p.on {
|
||||||
p.duration += time.Since(p.start)
|
p.duration += time.Since(p.start)
|
||||||
p.on = false
|
p.on = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cStats struct {
|
|
||||||
lk sync.Mutex
|
|
||||||
stats []cStat
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *cStats) addStat(level int, n *cStatStaging) {
|
|
||||||
p.lk.Lock()
|
|
||||||
if level >= len(p.stats) {
|
|
||||||
newStats := make([]cStat, level+1)
|
|
||||||
copy(newStats, p.stats)
|
|
||||||
p.stats = newStats
|
|
||||||
}
|
|
||||||
p.stats[level].add(n)
|
|
||||||
p.lk.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *cStats) getStat(level int) (duration time.Duration, read, write int64) {
|
|
||||||
p.lk.Lock()
|
|
||||||
defer p.lk.Unlock()
|
|
||||||
if level < len(p.stats) {
|
|
||||||
return p.stats[level].get()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) compactionError() {
|
func (db *DB) compactionError() {
|
||||||
var err error
|
var err error
|
||||||
noerr:
|
noerr:
|
||||||
@@ -256,14 +235,6 @@ func (db *DB) compactionExitTransact() {
|
|||||||
panic(errCompactionTransactExiting)
|
panic(errCompactionTransactExiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) compactionCommit(name string, rec *sessionRecord) {
|
|
||||||
db.compCommitLk.Lock()
|
|
||||||
defer db.compCommitLk.Unlock() // Defer is necessary.
|
|
||||||
db.compactionTransactFunc(name+"@commit", func(cnt *compactionTransactCounter) error {
|
|
||||||
return db.s.commit(rec)
|
|
||||||
}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) memCompaction() {
|
func (db *DB) memCompaction() {
|
||||||
mdb := db.getFrozenMem()
|
mdb := db.getFrozenMem()
|
||||||
if mdb == nil {
|
if mdb == nil {
|
||||||
@@ -294,40 +265,41 @@ func (db *DB) memCompaction() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
rec = &sessionRecord{}
|
rec = &sessionRecord{}
|
||||||
stats = &cStatStaging{}
|
stats = &cStatsStaging{}
|
||||||
flushLevel int
|
flushLevel int
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generate tables.
|
|
||||||
db.compactionTransactFunc("memdb@flush", func(cnt *compactionTransactCounter) (err error) {
|
db.compactionTransactFunc("memdb@flush", func(cnt *compactionTransactCounter) (err error) {
|
||||||
stats.startTimer()
|
stats.startTimer()
|
||||||
flushLevel, err = db.s.flushMemdb(rec, mdb.DB, db.memdbMaxLevel)
|
flushLevel, err = db.s.flushMemdb(rec, mdb.DB, -1)
|
||||||
stats.stopTimer()
|
stats.stopTimer()
|
||||||
return
|
return
|
||||||
}, func() error {
|
}, func() error {
|
||||||
for _, r := range rec.addedTables {
|
for _, r := range rec.addedTables {
|
||||||
db.logf("memdb@flush revert @%d", r.num)
|
db.logf("memdb@flush revert @%d", r.num)
|
||||||
if err := db.s.stor.Remove(storage.FileDesc{Type: storage.TypeTable, Num: r.num}); err != nil {
|
f := db.s.getTableFile(r.num)
|
||||||
|
if err := f.Remove(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
rec.setJournalNum(db.journalFd.Num)
|
db.compactionTransactFunc("memdb@commit", func(cnt *compactionTransactCounter) (err error) {
|
||||||
rec.setSeqNum(db.frozenSeq)
|
stats.startTimer()
|
||||||
|
rec.setJournalNum(db.journalFile.Num())
|
||||||
// Commit.
|
rec.setSeqNum(db.frozenSeq)
|
||||||
stats.startTimer()
|
err = db.s.commit(rec)
|
||||||
db.compactionCommit("memdb", rec)
|
stats.stopTimer()
|
||||||
stats.stopTimer()
|
return
|
||||||
|
}, nil)
|
||||||
|
|
||||||
db.logf("memdb@flush committed F·%d T·%v", len(rec.addedTables), stats.duration)
|
db.logf("memdb@flush committed F·%d T·%v", len(rec.addedTables), stats.duration)
|
||||||
|
|
||||||
for _, r := range rec.addedTables {
|
for _, r := range rec.addedTables {
|
||||||
stats.write += r.size
|
stats.write += r.size
|
||||||
}
|
}
|
||||||
db.compStats.addStat(flushLevel, stats)
|
db.compStats[flushLevel].add(stats)
|
||||||
|
|
||||||
// Drop frozen memdb.
|
// Drop frozen memdb.
|
||||||
db.dropFrozenMem()
|
db.dropFrozenMem()
|
||||||
@@ -343,7 +315,7 @@ func (db *DB) memCompaction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Trigger table compaction.
|
// Trigger table compaction.
|
||||||
db.compTrigger(db.tcompCmdC)
|
db.compSendTrigger(db.tcompCmdC)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tableCompactionBuilder struct {
|
type tableCompactionBuilder struct {
|
||||||
@@ -351,7 +323,7 @@ type tableCompactionBuilder struct {
|
|||||||
s *session
|
s *session
|
||||||
c *compaction
|
c *compaction
|
||||||
rec *sessionRecord
|
rec *sessionRecord
|
||||||
stat0, stat1 *cStatStaging
|
stat0, stat1 *cStatsStaging
|
||||||
|
|
||||||
snapHasLastUkey bool
|
snapHasLastUkey bool
|
||||||
snapLastUkey []byte
|
snapLastUkey []byte
|
||||||
@@ -405,9 +377,9 @@ func (b *tableCompactionBuilder) flush() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.rec.addTableFile(b.c.sourceLevel+1, t)
|
b.rec.addTableFile(b.c.level+1, t)
|
||||||
b.stat1.write += t.size
|
b.stat1.write += t.size
|
||||||
b.s.logf("table@build created L%d@%d N·%d S·%s %q:%q", b.c.sourceLevel+1, t.fd.Num, b.tw.tw.EntriesLen(), shortenb(int(t.size)), t.imin, t.imax)
|
b.s.logf("table@build created L%d@%d N·%d S·%s %q:%q", b.c.level+1, t.file.Num(), b.tw.tw.EntriesLen(), shortenb(int(t.size)), t.imin, t.imax)
|
||||||
b.tw = nil
|
b.tw = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -452,7 +424,7 @@ func (b *tableCompactionBuilder) run(cnt *compactionTransactCounter) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ikey := iter.Key()
|
ikey := iter.Key()
|
||||||
ukey, seq, kt, kerr := parseInternalKey(ikey)
|
ukey, seq, kt, kerr := parseIkey(ikey)
|
||||||
|
|
||||||
if kerr == nil {
|
if kerr == nil {
|
||||||
shouldStop := !resumed && b.c.shouldStopBefore(ikey)
|
shouldStop := !resumed && b.c.shouldStopBefore(ikey)
|
||||||
@@ -478,14 +450,14 @@ func (b *tableCompactionBuilder) run(cnt *compactionTransactCounter) error {
|
|||||||
|
|
||||||
hasLastUkey = true
|
hasLastUkey = true
|
||||||
lastUkey = append(lastUkey[:0], ukey...)
|
lastUkey = append(lastUkey[:0], ukey...)
|
||||||
lastSeq = keyMaxSeq
|
lastSeq = kMaxSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case lastSeq <= b.minSeq:
|
case lastSeq <= b.minSeq:
|
||||||
// Dropped because newer entry for same user key exist
|
// Dropped because newer entry for same user key exist
|
||||||
fallthrough // (A)
|
fallthrough // (A)
|
||||||
case kt == keyTypeDel && seq <= b.minSeq && b.c.baseLevelForKey(lastUkey):
|
case kt == ktDel && seq <= b.minSeq && b.c.baseLevelForKey(lastUkey):
|
||||||
// For this user key:
|
// For this user key:
|
||||||
// (1) there is no data in higher levels
|
// (1) there is no data in higher levels
|
||||||
// (2) data in lower levels will have larger seq numbers
|
// (2) data in lower levels will have larger seq numbers
|
||||||
@@ -507,7 +479,7 @@ func (b *tableCompactionBuilder) run(cnt *compactionTransactCounter) error {
|
|||||||
// Don't drop corrupted keys.
|
// Don't drop corrupted keys.
|
||||||
hasLastUkey = false
|
hasLastUkey = false
|
||||||
lastUkey = lastUkey[:0]
|
lastUkey = lastUkey[:0]
|
||||||
lastSeq = keyMaxSeq
|
lastSeq = kMaxSeq
|
||||||
b.kerrCnt++
|
b.kerrCnt++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,7 +502,8 @@ func (b *tableCompactionBuilder) run(cnt *compactionTransactCounter) error {
|
|||||||
func (b *tableCompactionBuilder) revert() error {
|
func (b *tableCompactionBuilder) revert() error {
|
||||||
for _, at := range b.rec.addedTables {
|
for _, at := range b.rec.addedTables {
|
||||||
b.s.logf("table@build revert @%d", at.num)
|
b.s.logf("table@build revert @%d", at.num)
|
||||||
if err := b.s.stor.Remove(storage.FileDesc{Type: storage.TypeTable, Num: at.num}); err != nil {
|
f := b.s.getTableFile(at.num)
|
||||||
|
if err := f.Remove(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -541,28 +514,30 @@ func (db *DB) tableCompaction(c *compaction, noTrivial bool) {
|
|||||||
defer c.release()
|
defer c.release()
|
||||||
|
|
||||||
rec := &sessionRecord{}
|
rec := &sessionRecord{}
|
||||||
rec.addCompPtr(c.sourceLevel, c.imax)
|
rec.addCompPtr(c.level, c.imax)
|
||||||
|
|
||||||
if !noTrivial && c.trivial() {
|
if !noTrivial && c.trivial() {
|
||||||
t := c.levels[0][0]
|
t := c.tables[0][0]
|
||||||
db.logf("table@move L%d@%d -> L%d", c.sourceLevel, t.fd.Num, c.sourceLevel+1)
|
db.logf("table@move L%d@%d -> L%d", c.level, t.file.Num(), c.level+1)
|
||||||
rec.delTable(c.sourceLevel, t.fd.Num)
|
rec.delTable(c.level, t.file.Num())
|
||||||
rec.addTableFile(c.sourceLevel+1, t)
|
rec.addTableFile(c.level+1, t)
|
||||||
db.compactionCommit("table-move", rec)
|
db.compactionTransactFunc("table@move", func(cnt *compactionTransactCounter) (err error) {
|
||||||
|
return db.s.commit(rec)
|
||||||
|
}, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var stats [2]cStatStaging
|
var stats [2]cStatsStaging
|
||||||
for i, tables := range c.levels {
|
for i, tables := range c.tables {
|
||||||
for _, t := range tables {
|
for _, t := range tables {
|
||||||
stats[i].read += t.size
|
stats[i].read += t.size
|
||||||
// Insert deleted tables into record
|
// Insert deleted tables into record
|
||||||
rec.delTable(c.sourceLevel+i, t.fd.Num)
|
rec.delTable(c.level+i, t.file.Num())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sourceSize := int(stats[0].read + stats[1].read)
|
sourceSize := int(stats[0].read + stats[1].read)
|
||||||
minSeq := db.minSeq()
|
minSeq := db.minSeq()
|
||||||
db.logf("table@compaction L%d·%d -> L%d·%d S·%s Q·%d", c.sourceLevel, len(c.levels[0]), c.sourceLevel+1, len(c.levels[1]), shortenb(sourceSize), minSeq)
|
db.logf("table@compaction L%d·%d -> L%d·%d S·%s Q·%d", c.level, len(c.tables[0]), c.level+1, len(c.tables[1]), shortenb(sourceSize), minSeq)
|
||||||
|
|
||||||
b := &tableCompactionBuilder{
|
b := &tableCompactionBuilder{
|
||||||
db: db,
|
db: db,
|
||||||
@@ -572,60 +547,49 @@ func (db *DB) tableCompaction(c *compaction, noTrivial bool) {
|
|||||||
stat1: &stats[1],
|
stat1: &stats[1],
|
||||||
minSeq: minSeq,
|
minSeq: minSeq,
|
||||||
strict: db.s.o.GetStrict(opt.StrictCompaction),
|
strict: db.s.o.GetStrict(opt.StrictCompaction),
|
||||||
tableSize: db.s.o.GetCompactionTableSize(c.sourceLevel + 1),
|
tableSize: db.s.o.GetCompactionTableSize(c.level + 1),
|
||||||
}
|
}
|
||||||
db.compactionTransact("table@build", b)
|
db.compactionTransact("table@build", b)
|
||||||
|
|
||||||
// Commit.
|
// Commit changes
|
||||||
stats[1].startTimer()
|
db.compactionTransactFunc("table@commit", func(cnt *compactionTransactCounter) (err error) {
|
||||||
db.compactionCommit("table", rec)
|
stats[1].startTimer()
|
||||||
stats[1].stopTimer()
|
defer stats[1].stopTimer()
|
||||||
|
return db.s.commit(rec)
|
||||||
|
}, nil)
|
||||||
|
|
||||||
resultSize := int(stats[1].write)
|
resultSize := int(stats[1].write)
|
||||||
db.logf("table@compaction committed F%s S%s Ke·%d D·%d T·%v", sint(len(rec.addedTables)-len(rec.deletedTables)), sshortenb(resultSize-sourceSize), b.kerrCnt, b.dropCnt, stats[1].duration)
|
db.logf("table@compaction committed F%s S%s Ke·%d D·%d T·%v", sint(len(rec.addedTables)-len(rec.deletedTables)), sshortenb(resultSize-sourceSize), b.kerrCnt, b.dropCnt, stats[1].duration)
|
||||||
|
|
||||||
// Save compaction stats
|
// Save compaction stats
|
||||||
for i := range stats {
|
for i := range stats {
|
||||||
db.compStats.addStat(c.sourceLevel+1, &stats[i])
|
db.compStats[c.level+1].add(&stats[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) tableRangeCompaction(level int, umin, umax []byte) error {
|
func (db *DB) tableRangeCompaction(level int, umin, umax []byte) {
|
||||||
db.logf("table@compaction range L%d %q:%q", level, umin, umax)
|
db.logf("table@compaction range L%d %q:%q", level, umin, umax)
|
||||||
|
|
||||||
if level >= 0 {
|
if level >= 0 {
|
||||||
if c := db.s.getCompactionRange(level, umin, umax, true); c != nil {
|
if c := db.s.getCompactionRange(level, umin, umax); c != nil {
|
||||||
db.tableCompaction(c, true)
|
db.tableCompaction(c, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Retry until nothing to compact.
|
v := db.s.version()
|
||||||
for {
|
m := 1
|
||||||
compacted := false
|
for i, t := range v.tables[1:] {
|
||||||
|
if t.overlaps(db.s.icmp, umin, umax, false) {
|
||||||
// Scan for maximum level with overlapped tables.
|
m = i + 1
|
||||||
v := db.s.version()
|
|
||||||
m := 1
|
|
||||||
for i := m; i < len(v.levels); i++ {
|
|
||||||
tables := v.levels[i]
|
|
||||||
if tables.overlaps(db.s.icmp, umin, umax, false) {
|
|
||||||
m = i
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
v.release()
|
}
|
||||||
|
v.release()
|
||||||
|
|
||||||
for level := 0; level < m; level++ {
|
for level := 0; level < m; level++ {
|
||||||
if c := db.s.getCompactionRange(level, umin, umax, false); c != nil {
|
if c := db.s.getCompactionRange(level, umin, umax); c != nil {
|
||||||
db.tableCompaction(c, true)
|
db.tableCompaction(c, true)
|
||||||
compacted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !compacted {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) tableAutoCompaction() {
|
func (db *DB) tableAutoCompaction() {
|
||||||
@@ -652,11 +616,11 @@ type cCmd interface {
|
|||||||
ack(err error)
|
ack(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cAuto struct {
|
type cIdle struct {
|
||||||
ackC chan<- error
|
ackC chan<- error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r cAuto) ack(err error) {
|
func (r cIdle) ack(err error) {
|
||||||
if r.ackC != nil {
|
if r.ackC != nil {
|
||||||
defer func() {
|
defer func() {
|
||||||
recover()
|
recover()
|
||||||
@@ -680,21 +644,13 @@ func (r cRange) ack(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will trigger auto compaction but will not wait for it.
|
|
||||||
func (db *DB) compTrigger(compC chan<- cCmd) {
|
|
||||||
select {
|
|
||||||
case compC <- cAuto{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will trigger auto compation and/or wait for all compaction to be done.
|
// This will trigger auto compation and/or wait for all compaction to be done.
|
||||||
func (db *DB) compTriggerWait(compC chan<- cCmd) (err error) {
|
func (db *DB) compSendIdle(compC chan<- cCmd) (err error) {
|
||||||
ch := make(chan error)
|
ch := make(chan error)
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
// Send cmd.
|
// Send cmd.
|
||||||
select {
|
select {
|
||||||
case compC <- cAuto{ch}:
|
case compC <- cIdle{ch}:
|
||||||
case err = <-db.compErrC:
|
case err = <-db.compErrC:
|
||||||
return
|
return
|
||||||
case _, _ = <-db.closeC:
|
case _, _ = <-db.closeC:
|
||||||
@@ -710,8 +666,16 @@ func (db *DB) compTriggerWait(compC chan<- cCmd) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will trigger auto compaction but will not wait for it.
|
||||||
|
func (db *DB) compSendTrigger(compC chan<- cCmd) {
|
||||||
|
select {
|
||||||
|
case compC <- cIdle{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send range compaction request.
|
// Send range compaction request.
|
||||||
func (db *DB) compTriggerRange(compC chan<- cCmd, level int, min, max []byte) (err error) {
|
func (db *DB) compSendRange(compC chan<- cCmd, level int, min, max []byte) (err error) {
|
||||||
ch := make(chan error)
|
ch := make(chan error)
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
// Send cmd.
|
// Send cmd.
|
||||||
@@ -751,7 +715,7 @@ func (db *DB) mCompaction() {
|
|||||||
select {
|
select {
|
||||||
case x = <-db.mcompCmdC:
|
case x = <-db.mcompCmdC:
|
||||||
switch x.(type) {
|
switch x.(type) {
|
||||||
case cAuto:
|
case cIdle:
|
||||||
db.memCompaction()
|
db.memCompaction()
|
||||||
x.ack(nil)
|
x.ack(nil)
|
||||||
x = nil
|
x = nil
|
||||||
@@ -812,10 +776,11 @@ func (db *DB) tCompaction() {
|
|||||||
}
|
}
|
||||||
if x != nil {
|
if x != nil {
|
||||||
switch cmd := x.(type) {
|
switch cmd := x.(type) {
|
||||||
case cAuto:
|
case cIdle:
|
||||||
ackQ = append(ackQ, x)
|
ackQ = append(ackQ, x)
|
||||||
case cRange:
|
case cRange:
|
||||||
x.ack(db.tableRangeCompaction(cmd.level, cmd.min, cmd.max))
|
db.tableRangeCompaction(cmd.level, cmd.min, cmd.max)
|
||||||
|
x.ack(nil)
|
||||||
default:
|
default:
|
||||||
panic("leveldb: unknown command")
|
panic("leveldb: unknown command")
|
||||||
}
|
}
|
||||||
Generated
Vendored
+21
-31
@@ -19,7 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInvalidInternalKey = errors.New("leveldb: Iterator: invalid internal key")
|
errInvalidIkey = errors.New("leveldb: Iterator: invalid internal key")
|
||||||
)
|
)
|
||||||
|
|
||||||
type memdbReleaser struct {
|
type memdbReleaser struct {
|
||||||
@@ -33,50 +33,40 @@ func (mr *memdbReleaser) Release() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) newRawIterator(auxm *memDB, auxt tFiles, slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
func (db *DB) newRawIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
||||||
strict := opt.GetStrict(db.s.o.Options, ro, opt.StrictReader)
|
|
||||||
em, fm := db.getMems()
|
em, fm := db.getMems()
|
||||||
v := db.s.version()
|
v := db.s.version()
|
||||||
|
|
||||||
tableIts := v.getIterators(slice, ro)
|
ti := v.getIterators(slice, ro)
|
||||||
n := len(tableIts) + len(auxt) + 3
|
n := len(ti) + 2
|
||||||
its := make([]iterator.Iterator, 0, n)
|
i := make([]iterator.Iterator, 0, n)
|
||||||
|
|
||||||
if auxm != nil {
|
|
||||||
ami := auxm.NewIterator(slice)
|
|
||||||
ami.SetReleaser(&memdbReleaser{m: auxm})
|
|
||||||
its = append(its, ami)
|
|
||||||
}
|
|
||||||
for _, t := range auxt {
|
|
||||||
its = append(its, v.s.tops.newIterator(t, slice, ro))
|
|
||||||
}
|
|
||||||
|
|
||||||
emi := em.NewIterator(slice)
|
emi := em.NewIterator(slice)
|
||||||
emi.SetReleaser(&memdbReleaser{m: em})
|
emi.SetReleaser(&memdbReleaser{m: em})
|
||||||
its = append(its, emi)
|
i = append(i, emi)
|
||||||
if fm != nil {
|
if fm != nil {
|
||||||
fmi := fm.NewIterator(slice)
|
fmi := fm.NewIterator(slice)
|
||||||
fmi.SetReleaser(&memdbReleaser{m: fm})
|
fmi.SetReleaser(&memdbReleaser{m: fm})
|
||||||
its = append(its, fmi)
|
i = append(i, fmi)
|
||||||
}
|
}
|
||||||
its = append(its, tableIts...)
|
i = append(i, ti...)
|
||||||
mi := iterator.NewMergedIterator(its, db.s.icmp, strict)
|
strict := opt.GetStrict(db.s.o.Options, ro, opt.StrictReader)
|
||||||
|
mi := iterator.NewMergedIterator(i, db.s.icmp, strict)
|
||||||
mi.SetReleaser(&versionReleaser{v: v})
|
mi.SetReleaser(&versionReleaser{v: v})
|
||||||
return mi
|
return mi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) newIterator(auxm *memDB, auxt tFiles, seq uint64, slice *util.Range, ro *opt.ReadOptions) *dbIter {
|
func (db *DB) newIterator(seq uint64, slice *util.Range, ro *opt.ReadOptions) *dbIter {
|
||||||
var islice *util.Range
|
var islice *util.Range
|
||||||
if slice != nil {
|
if slice != nil {
|
||||||
islice = &util.Range{}
|
islice = &util.Range{}
|
||||||
if slice.Start != nil {
|
if slice.Start != nil {
|
||||||
islice.Start = makeInternalKey(nil, slice.Start, keyMaxSeq, keyTypeSeek)
|
islice.Start = newIkey(slice.Start, kMaxSeq, ktSeek)
|
||||||
}
|
}
|
||||||
if slice.Limit != nil {
|
if slice.Limit != nil {
|
||||||
islice.Limit = makeInternalKey(nil, slice.Limit, keyMaxSeq, keyTypeSeek)
|
islice.Limit = newIkey(slice.Limit, kMaxSeq, ktSeek)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rawIter := db.newRawIterator(auxm, auxt, islice, ro)
|
rawIter := db.newRawIterator(islice, ro)
|
||||||
iter := &dbIter{
|
iter := &dbIter{
|
||||||
db: db,
|
db: db,
|
||||||
icmp: db.s.icmp,
|
icmp: db.s.icmp,
|
||||||
@@ -187,7 +177,7 @@ func (i *dbIter) Seek(key []byte) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ikey := makeInternalKey(nil, key, i.seq, keyTypeSeek)
|
ikey := newIkey(key, i.seq, ktSeek)
|
||||||
if i.iter.Seek(ikey) {
|
if i.iter.Seek(ikey) {
|
||||||
i.dir = dirSOI
|
i.dir = dirSOI
|
||||||
return i.next()
|
return i.next()
|
||||||
@@ -199,15 +189,15 @@ func (i *dbIter) Seek(key []byte) bool {
|
|||||||
|
|
||||||
func (i *dbIter) next() bool {
|
func (i *dbIter) next() bool {
|
||||||
for {
|
for {
|
||||||
if ukey, seq, kt, kerr := parseInternalKey(i.iter.Key()); kerr == nil {
|
if ukey, seq, kt, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
||||||
i.sampleSeek()
|
i.sampleSeek()
|
||||||
if seq <= i.seq {
|
if seq <= i.seq {
|
||||||
switch kt {
|
switch kt {
|
||||||
case keyTypeDel:
|
case ktDel:
|
||||||
// Skip deleted key.
|
// Skip deleted key.
|
||||||
i.key = append(i.key[:0], ukey...)
|
i.key = append(i.key[:0], ukey...)
|
||||||
i.dir = dirForward
|
i.dir = dirForward
|
||||||
case keyTypeVal:
|
case ktVal:
|
||||||
if i.dir == dirSOI || i.icmp.uCompare(ukey, i.key) > 0 {
|
if i.dir == dirSOI || i.icmp.uCompare(ukey, i.key) > 0 {
|
||||||
i.key = append(i.key[:0], ukey...)
|
i.key = append(i.key[:0], ukey...)
|
||||||
i.value = append(i.value[:0], i.iter.Value()...)
|
i.value = append(i.value[:0], i.iter.Value()...)
|
||||||
@@ -250,13 +240,13 @@ func (i *dbIter) prev() bool {
|
|||||||
del := true
|
del := true
|
||||||
if i.iter.Valid() {
|
if i.iter.Valid() {
|
||||||
for {
|
for {
|
||||||
if ukey, seq, kt, kerr := parseInternalKey(i.iter.Key()); kerr == nil {
|
if ukey, seq, kt, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
||||||
i.sampleSeek()
|
i.sampleSeek()
|
||||||
if seq <= i.seq {
|
if seq <= i.seq {
|
||||||
if !del && i.icmp.uCompare(ukey, i.key) < 0 {
|
if !del && i.icmp.uCompare(ukey, i.key) < 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
del = (kt == keyTypeDel)
|
del = (kt == ktDel)
|
||||||
if !del {
|
if !del {
|
||||||
i.key = append(i.key[:0], ukey...)
|
i.key = append(i.key[:0], ukey...)
|
||||||
i.value = append(i.value[:0], i.iter.Value()...)
|
i.value = append(i.value[:0], i.iter.Value()...)
|
||||||
@@ -292,7 +282,7 @@ func (i *dbIter) Prev() bool {
|
|||||||
return i.Last()
|
return i.Last()
|
||||||
case dirForward:
|
case dirForward:
|
||||||
for i.iter.Prev() {
|
for i.iter.Prev() {
|
||||||
if ukey, _, _, kerr := parseInternalKey(i.iter.Key()); kerr == nil {
|
if ukey, _, _, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
||||||
i.sampleSeek()
|
i.sampleSeek()
|
||||||
if i.icmp.uCompare(ukey, i.key) < 0 {
|
if i.icmp.uCompare(ukey, i.key) < 0 {
|
||||||
goto cont
|
goto cont
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user