dep ensure

pull/22/head
Miguel Mota 5 years ago
parent 98b6d2bfbc
commit 8e58d5a9b2

54
Gopkg.lock generated

@ -2,65 +2,103 @@
[[projects]]
digest = "1:289dd4d7abfb3ad2b5f728fbe9b1d5c1bf7d265a3eb9ef92869af1f7baba4c7a"
name = "github.com/BurntSushi/toml"
packages = ["."]
pruneopts = ""
revision = "b26d9c308763d68093482582cea63d69be07a0f0"
version = "v0.3.0"
[[projects]]
digest = "1:c742448be99f29f5233ab05bd50eea54ab53f438a3465afa8b247791f0cd885b"
name = "github.com/anaskhan96/soup"
packages = ["."]
pruneopts = ""
revision = "cd07662ec9300d16d2ece1c24b0b29ad89f65ff4"
version = "v1.1"
[[projects]]
digest = "1:d7d69c103aba5ea229451115b28ca06d061e3861b2f44bcd0100cbc65659c4d9"
name = "github.com/fatih/color"
packages = ["."]
pruneopts = ""
revision = "507f6050b8568533fb3f5504de8e5205fa62a114"
version = "v1.6.0"
[[projects]]
digest = "1:9ea83adf8e96d6304f394d40436f2eb44c1dc3250d223b74088cc253a6cd0a1c"
name = "github.com/mattn/go-colorable"
packages = ["."]
pruneopts = ""
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
digest = "1:78229b46ddb7434f881390029bd1af7661294af31f6802e0e1bedaad4ab0af3c"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = ""
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
digest = "1:81e673df85e765593a863f67cba4544cf40e8919590f04d67664940786c2b61a"
name = "github.com/mattn/go-runewidth"
packages = ["."]
pruneopts = ""
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
version = "v0.0.2"
[[projects]]
branch = "master"
name = "go4.org"
packages = ["reflectutil"]
revision = "9599cf28b011184741f249bd9f9330756b506cbc"
[[projects]]
branch = "master"
digest = "1:80ec738f420f13c23460cc92cd893d3e403588c60682afebbd4515eca5e19578"
name = "golang.org/x/net"
packages = [
"html",
"html/atom"
"html/atom",
]
pruneopts = ""
revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23"
[[projects]]
branch = "master"
digest = "1:ba4898d0f47bb5a26d7e1842bc1b29799441fbcc76aed65ba45d5b6918e35130"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = ""
revision = "bb9c189858d91f42db229b04d45a4c3d23a7662a"
[[projects]]
digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
name = "golang.org/x/text"
packages = [
"feature/plural",
"internal",
"internal/catmsg",
"internal/format",
"internal/gen",
"internal/number",
"internal/stringset",
"internal/tag",
"language",
"message",
"message/catalog",
"unicode/cldr",
]
pruneopts = ""
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "f8fa24cf45636d4d6991f0a805aba5b6ad13ce7807be80cc3baf17909e6b855b"
input-imports = [
"github.com/BurntSushi/toml",
"github.com/anaskhan96/soup",
"github.com/fatih/color",
"github.com/mattn/go-runewidth",
"golang.org/x/text/language",
"golang.org/x/text/message",
]
solver-name = "gps-cdcl"
solver-version = 1

24
vendor/go4.org/.gitignore generated vendored

@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

10
vendor/go4.org/.travis.yml generated vendored

@ -1,10 +0,0 @@
go_import_path: go4.org
language: go
go:
- "1.10.x"
- tip
before_install:
- go get -u cloud.google.com/go/storage
- cd $HOME/gopath/src/cloud.google.com/go
- git reset --hard 4d445121f7d55f37b70187e796b16e64f6eea7a0
- cd $HOME/gopath/src/go4.org

8
vendor/go4.org/AUTHORS generated vendored

@ -1,8 +0,0 @@
# This is the official list of go4 authors for copyright purposes.
# This is distinct from the CONTRIBUTORS file, which is the list of
# people who have contributed, even if they don't own the copyright on
# their work.
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
Daniel Theophanes <kardianos@gmail.com>
Google

202
vendor/go4.org/LICENSE generated vendored

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

57
vendor/go4.org/README.md generated vendored

@ -1,57 +0,0 @@
# go4
[![travis badge](https://travis-ci.org/camlistore/go4.svg?branch=master)](https://travis-ci.org/camlistore/go4 "Travis CI")
[go4.org](http://go4.org) is a collection of packages for
Go programmers.
They started out living in [Perkeep](https://perkeep.org)'s repo
and elsewhere but they have nothing to do with Perkeep, so we're
moving them here.
## Details
* **single repo**. go4 is a single repo. That means things can be
changed and rearranged globally atomically with ease and
confidence.
* **no backwards compatibility**. go4 makes no backwards compatibility
promises. If you want to use go4, vendor it. And next time you
update your vendor tree, update to the latest API if things in go4
changed. The plan is to eventually provide tools to make this
easier.
* **forward progress** because we have no backwards compatibility,
it's always okay to change things to make things better. That also
means the bar for contributions is lower. We don't have to get the
API 100% correct in the first commit.
* **no Go version policy** go4 packages are usually built and tested
with the latest Go stable version. However, go4 has no overarching
version policy; each package can declare its own set of supported
Go versions.
* **code review** contributions must be code-reviewed. We're trying
out Gerrithub, to see if we can find a mix of Github Pull Requests
and Gerrit that works well for many people. We'll see.
* **CLA compliant** contributors must agree to the Google CLA (the
same as Go itself). This ensures we can move things into Go as
necessary in the future. It also makes lawyers at various
companies happy. The CLA is **not** a copyright *assignment*; you
retain the copyright on your work. The CLA just says that your
work is open source and you have permission to open source it. See
https://golang.org/doc/contribute.html#cla
* **docs, tests, portability** all code should be documented in the
normal Go style, have tests, and be portable to different
operating systems and architectures. We'll try to get builders in
place to help run the tests on different OS/arches. For now we
have Travis at least.
## Contact
For any question, or communication when a Github issue is not appropriate,
please contact the [Perkeep mailing
list](https://groups.google.com/forum/#!forum/perkeep).

@ -1,286 +0,0 @@
/*
Copyright 2015 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package bytereplacer provides a utility for replacing parts of byte slices.
package bytereplacer // import "go4.org/bytereplacer"
import "bytes"
// Replacer replaces a list of strings with replacements.
// It is safe for concurrent use by multiple goroutines.
type Replacer struct {
r replacer
}
// replacer is the interface that a replacement algorithm needs to implement.
type replacer interface {
// Replace performs all replacements, in-place if possible.
Replace(s []byte) []byte
}
// New returns a new Replacer from a list of old, new string pairs.
// Replacements are performed in order, without overlapping matches.
func New(oldnew ...string) *Replacer {
if len(oldnew)%2 == 1 {
panic("bytes.NewReplacer: odd argument count")
}
allNewBytes := true
for i := 0; i < len(oldnew); i += 2 {
if len(oldnew[i]) != 1 {
return &Replacer{r: makeGenericReplacer(oldnew)}
}
if len(oldnew[i+1]) != 1 {
allNewBytes = false
}
}
if allNewBytes {
r := byteReplacer{}
for i := range r {
r[i] = byte(i)
}
// The first occurrence of old->new map takes precedence
// over the others with the same old string.
for i := len(oldnew) - 2; i >= 0; i -= 2 {
o := oldnew[i][0]
n := oldnew[i+1][0]
r[o] = n
}
return &Replacer{r: &r}
}
return &Replacer{r: makeGenericReplacer(oldnew)}
}
// Replace performs all replacements in-place on s. If the capacity
// of s is not sufficient, a new slice is allocated, otherwise Replace
// returns s.
func (r *Replacer) Replace(s []byte) []byte {
return r.r.Replace(s)
}
type trieNode struct {
value []byte
priority int
prefix []byte
next *trieNode
table []*trieNode
}
func (t *trieNode) add(key, val []byte, priority int, r *genericReplacer) {
if len(key) == 0 {
if t.priority == 0 {
t.value = val
t.priority = priority
}
return
}
if len(t.prefix) > 0 {
// Need to split the prefix among multiple nodes.
var n int // length of the longest common prefix
for ; n < len(t.prefix) && n < len(key); n++ {
if t.prefix[n] != key[n] {
break
}
}
if n == len(t.prefix) {
t.next.add(key[n:], val, priority, r)
} else if n == 0 {
// First byte differs, start a new lookup table here. Looking up
// what is currently t.prefix[0] will lead to prefixNode, and
// looking up key[0] will lead to keyNode.
var prefixNode *trieNode
if len(t.prefix) == 1 {
prefixNode = t.next
} else {
prefixNode = &trieNode{
prefix: t.prefix[1:],
next: t.next,
}
}
keyNode := new(trieNode)
t.table = make([]*trieNode, r.tableSize)
t.table[r.mapping[t.prefix[0]]] = prefixNode
t.table[r.mapping[key[0]]] = keyNode
t.prefix = nil
t.next = nil
keyNode.add(key[1:], val, priority, r)
} else {
// Insert new node after the common section of the prefix.
next := &trieNode{
prefix: t.prefix[n:],
next: t.next,
}
t.prefix = t.prefix[:n]
t.next = next
next.add(key[n:], val, priority, r)
}
} else if t.table != nil {
// Insert into existing table.
m := r.mapping[key[0]]
if t.table[m] == nil {
t.table[m] = new(trieNode)
}
t.table[m].add(key[1:], val, priority, r)
} else {
t.prefix = key
t.next = new(trieNode)
t.next.add(nil, val, priority, r)
}
}
func (r *genericReplacer) lookup(s []byte, ignoreRoot bool) (val []byte, keylen int, found bool) {
// Iterate down the trie to the end, and grab the value and keylen with
// the highest priority.
bestPriority := 0
node := &r.root
n := 0
for node != nil {
if node.priority > bestPriority && !(ignoreRoot && node == &r.root) {
bestPriority = node.priority
val = node.value
keylen = n
found = true
}
if len(s) == 0 {
break
}
if node.table != nil {
index := r.mapping[s[0]]
if int(index) == r.tableSize {
break
}
node = node.table[index]
s = s[1:]
n++
} else if len(node.prefix) > 0 && bytes.HasPrefix(s, node.prefix) {
n += len(node.prefix)
s = s[len(node.prefix):]
node = node.next
} else {
break
}
}
return
}
// genericReplacer is the fully generic algorithm.
// It's used as a fallback when nothing faster can be used.
type genericReplacer struct {
root trieNode
// tableSize is the size of a trie node's lookup table. It is the number
// of unique key bytes.
tableSize int
// mapping maps from key bytes to a dense index for trieNode.table.
mapping [256]byte
}
func makeGenericReplacer(oldnew []string) *genericReplacer {
r := new(genericReplacer)
// Find each byte used, then assign them each an index.
for i := 0; i < len(oldnew); i += 2 {
key := oldnew[i]
for j := 0; j < len(key); j++ {
r.mapping[key[j]] = 1
}
}
for _, b := range r.mapping {
r.tableSize += int(b)
}
var index byte
for i, b := range r.mapping {
if b == 0 {
r.mapping[i] = byte(r.tableSize)
} else {
r.mapping[i] = index
index++
}
}
// Ensure root node uses a lookup table (for performance).
r.root.table = make([]*trieNode, r.tableSize)
for i := 0; i < len(oldnew); i += 2 {
r.root.add([]byte(oldnew[i]), []byte(oldnew[i+1]), len(oldnew)-i, r)
}
return r
}
func (r *genericReplacer) Replace(s []byte) []byte {
var last int
var prevMatchEmpty bool
dst := s[:0]
grown := false
for i := 0; i <= len(s); {
// Fast path: s[i] is not a prefix of any pattern.
if i != len(s) && r.root.priority == 0 {
index := int(r.mapping[s[i]])
if index == r.tableSize || r.root.table[index] == nil {
i++
continue
}
}
// Ignore the empty match iff the previous loop found the empty match.
val, keylen, match := r.lookup(s[i:], prevMatchEmpty)
prevMatchEmpty = match && keylen == 0
if match {
dst = append(dst, s[last:i]...)
if diff := len(val) - keylen; grown || diff < 0 {
dst = append(dst, val...)
i += keylen
} else if diff <= cap(s)-len(s) {
// The replacement is larger than the original, but can still fit in the original buffer.
copy(s[i+len(val):cap(dst)], s[i+keylen:])
dst = append(dst, val...)
s = s[:len(s)+diff]
i += len(val)
} else {
// The output will grow larger than the original buffer. Allocate a new one.
grown = true
newDst := make([]byte, len(dst), cap(dst)+diff)
copy(newDst, dst)
dst = newDst
dst = append(dst, val...)
i += keylen
}
last = i
continue
}
i++
}
if last != len(s) {
dst = append(dst, s[last:]...)
}
return dst
}
// byteReplacer is the implementation that's used when all the "old"
// and "new" values are single ASCII bytes.
// The array contains replacement bytes indexed by old byte.
type byteReplacer [256]byte
func (r *byteReplacer) Replace(s []byte) []byte {
for i, b := range s {
s[i] = r[b]
}
return s
}

@ -1,423 +0,0 @@
/*
Copyright 2015 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bytereplacer
import (
"bytes"
"strings"
"testing"
)
var htmlEscaper = New(
"&", "&amp;",
"<", "&lt;",
">", "&gt;",
`"`, "&quot;",
"'", "&apos;",
)
var htmlUnescaper = New(
"&amp;", "&",
"&lt;", "<",
"&gt;", ">",
"&quot;", `"`,
"&apos;", "'",
)
var capitalLetters = New("a", "A", "b", "B")
func TestReplacer(t *testing.T) {
type testCase struct {
r *Replacer
in, out string
}
var testCases []testCase
// str converts 0xff to "\xff". This isn't just string(b) since that converts to UTF-8.
str := func(b byte) string {
return string([]byte{b})
}
var s []string
// inc maps "\x00"->"\x01", ..., "a"->"b", "b"->"c", ..., "\xff"->"\x00".
s = nil
for i := 0; i < 256; i++ {
s = append(s, str(byte(i)), str(byte(i+1)))
}
inc := New(s...)
// Test cases with 1-byte old strings, 1-byte new strings.
testCases = append(testCases,
testCase{capitalLetters, "brad", "BrAd"},
testCase{capitalLetters, strings.Repeat("a", (32<<10)+123), strings.Repeat("A", (32<<10)+123)},
testCase{capitalLetters, "", ""},
testCase{inc, "brad", "csbe"},
testCase{inc, "\x00\xff", "\x01\x00"},
testCase{inc, "", ""},
testCase{New("a", "1", "a", "2"), "brad", "br1d"},
)
// repeat maps "a"->"a", "b"->"bb", "c"->"ccc", ...
s = nil
for i := 0; i < 256; i++ {
n := i + 1 - 'a'
if n < 1 {
n = 1
}
s = append(s, str(byte(i)), strings.Repeat(str(byte(i)), n))
}
repeat := New(s...)
// Test cases with 1-byte old strings, variable length new strings.
testCases = append(testCases,
testCase{htmlEscaper, "No changes", "No changes"},
testCase{htmlEscaper, "I <3 escaping & stuff", "I &lt;3 escaping &amp; stuff"},
testCase{htmlEscaper, "&&&", "&amp;&amp;&amp;"},
testCase{htmlEscaper, "", ""},
testCase{repeat, "brad", "bbrrrrrrrrrrrrrrrrrradddd"},
testCase{repeat, "abba", "abbbba"},
testCase{repeat, "", ""},
testCase{New("a", "11", "a", "22"), "brad", "br11d"},
)
// The remaining test cases have variable length old strings.
testCases = append(testCases,
testCase{htmlUnescaper, "&amp;amp;", "&amp;"},
testCase{htmlUnescaper, "&lt;b&gt;HTML&apos;s neat&lt;/b&gt;", "<b>HTML's neat</b>"},
testCase{htmlUnescaper, "", ""},
testCase{New("a", "1", "a", "2", "xxx", "xxx"), "brad", "br1d"},
testCase{New("a", "1", "aa", "2", "aaa", "3"), "aaaa", "1111"},
testCase{New("aaa", "3", "aa", "2", "a", "1"), "aaaa", "31"},
)
// gen1 has multiple old strings of variable length. There is no
// overall non-empty common prefix, but some pairwise common prefixes.
gen1 := New(
"aaa", "3[aaa]",
"aa", "2[aa]",
"a", "1[a]",
"i", "i",
"longerst", "most long",
"longer", "medium",
"long", "short",
"xx", "xx",
"x", "X",
"X", "Y",
"Y", "Z",
)
testCases = append(testCases,
testCase{gen1, "fooaaabar", "foo3[aaa]b1[a]r"},
testCase{gen1, "long, longerst, longer", "short, most long, medium"},
testCase{gen1, "xxxxx", "xxxxX"},
testCase{gen1, "XiX", "YiY"},
testCase{gen1, "", ""},
)
// gen2 has multiple old strings with no pairwise common prefix.
gen2 := New(
"roses", "red",
"violets", "blue",
"sugar", "sweet",
)
testCases = append(testCases,
testCase{gen2, "roses are red, violets are blue...", "red are red, blue are blue..."},
testCase{gen2, "", ""},
)
// gen3 has multiple old strings with an overall common prefix.
gen3 := New(
"abracadabra", "poof",
"abracadabrakazam", "splat",
"abraham", "lincoln",
"abrasion", "scrape",
"abraham", "isaac",
)
testCases = append(testCases,
testCase{gen3, "abracadabrakazam abraham", "poofkazam lincoln"},
testCase{gen3, "abrasion abracad", "scrape abracad"},
testCase{gen3, "abba abram abrasive", "abba abram abrasive"},
testCase{gen3, "", ""},
)
// foo{1,2,3,4} have multiple old strings with an overall common prefix
// and 1- or 2- byte extensions from the common prefix.
foo1 := New(
"foo1", "A",
"foo2", "B",
"foo3", "C",
)
foo2 := New(
"foo1", "A",
"foo2", "B",
"foo31", "C",
"foo32", "D",
)
foo3 := New(
"foo11", "A",
"foo12", "B",
"foo31", "C",
"foo32", "D",
)
foo4 := New(
"foo12", "B",
"foo32", "D",
)
testCases = append(testCases,
testCase{foo1, "fofoofoo12foo32oo", "fofooA2C2oo"},
testCase{foo1, "", ""},
testCase{foo2, "fofoofoo12foo32oo", "fofooA2Doo"},
testCase{foo2, "", ""},
testCase{foo3, "fofoofoo12foo32oo", "fofooBDoo"},
testCase{foo3, "", ""},
testCase{foo4, "fofoofoo12foo32oo", "fofooBDoo"},
testCase{foo4, "", ""},
)
// genAll maps "\x00\x01\x02...\xfe\xff" to "[all]", amongst other things.
allBytes := make([]byte, 256)
for i := range allBytes {
allBytes[i] = byte(i)
}
allString := string(allBytes)
genAll := New(
allString, "[all]",
"\xff", "[ff]",
"\x00", "[00]",
)
testCases = append(testCases,
testCase{genAll, allString, "[all]"},
testCase{genAll, "a\xff" + allString + "\x00", "a[ff][all][00]"},
testCase{genAll, "", ""},
)
// Test cases with empty old strings.
blankToX1 := New("", "X")
blankToX2 := New("", "X", "", "")
blankHighPriority := New("", "X", "o", "O")
blankLowPriority := New("o", "O", "", "X")
blankNoOp1 := New("", "")
blankNoOp2 := New("", "", "", "A")
blankFoo := New("", "X", "foobar", "R", "foobaz", "Z")
testCases = append(testCases,
testCase{blankToX1, "foo", "XfXoXoX"},
testCase{blankToX1, "", "X"},
testCase{blankToX2, "foo", "XfXoXoX"},
testCase{blankToX2, "", "X"},
testCase{blankHighPriority, "oo", "XOXOX"},
testCase{blankHighPriority, "ii", "XiXiX"},
testCase{blankHighPriority, "oiio", "XOXiXiXOX"},
testCase{blankHighPriority, "iooi", "XiXOXOXiX"},
testCase{blankHighPriority, "", "X"},
testCase{blankLowPriority, "oo", "OOX"},
testCase{blankLowPriority, "ii", "XiXiX"},
testCase{blankLowPriority, "oiio", "OXiXiOX"},
testCase{blankLowPriority, "iooi", "XiOOXiX"},
testCase{blankLowPriority, "", "X"},
testCase{blankNoOp1, "foo", "foo"},
testCase{blankNoOp1, "", ""},
testCase{blankNoOp2, "foo", "foo"},
testCase{blankNoOp2, "", ""},
testCase{blankFoo, "foobarfoobaz", "XRXZX"},
testCase{blankFoo, "foobar-foobaz", "XRX-XZX"},
testCase{blankFoo, "", "X"},
)
// single string replacer
abcMatcher := New("abc", "[match]")
testCases = append(testCases,
testCase{abcMatcher, "", ""},
testCase{abcMatcher, "ab", "ab"},
testCase{abcMatcher, "abc", "[match]"},
testCase{abcMatcher, "abcd", "[match]d"},
testCase{abcMatcher, "cabcabcdabca", "c[match][match]d[match]a"},
)
// Issue 6659 cases (more single string replacer)
noHello := New("Hello", "")
testCases = append(testCases,
testCase{noHello, "Hello", ""},
testCase{noHello, "Hellox", "x"},
testCase{noHello, "xHello", "x"},
testCase{noHello, "xHellox", "xx"},
)
// No-arg test cases.
nop := New()
testCases = append(testCases,
testCase{nop, "abc", "abc"},
testCase{nop, "", ""},
)
// Run the test cases.
for i, tc := range testCases {
{
// Replace with len(in) == cap(in)
in := make([]byte, len(tc.in))
copy(in, tc.in)
if s := string(tc.r.Replace(in)); s != tc.out {
t.Errorf("%d. Replace(%q /* len == cap */) = %q, want %q", i, tc.in, s, tc.out)
}
}
{
// Replace with len(in) < cap(in)
in := make([]byte, len(tc.in), len(tc.in)*2)
copy(in, tc.in)
if s := string(tc.r.Replace(in)); s != tc.out {
t.Errorf("%d. Replace(%q /* len < cap */) = %q, want %q", i, tc.in, s, tc.out)
}
}
}
}
func BenchmarkGenericNoMatch(b *testing.B) {
str := []byte(strings.Repeat("A", 100) + strings.Repeat("B", 100))
generic := New("a", "A", "b", "B", "12", "123") // varying lengths forces generic
for i := 0; i < b.N; i++ {
generic.Replace(str)
}
}
func BenchmarkGenericMatch1(b *testing.B) {
str := []byte(strings.Repeat("a", 100) + strings.Repeat("b", 100))
generic := New("a", "A", "b", "B", "12", "123")
for i := 0; i < b.N; i++ {
generic.Replace(str)
}
}
func BenchmarkGenericMatch2(b *testing.B) {
str := bytes.Repeat([]byte("It&apos;s &lt;b&gt;HTML&lt;/b&gt;!"), 100)
for i := 0; i < b.N; i++ {
htmlUnescaper.Replace(str)
}
}
func benchmarkSingleString(b *testing.B, pattern, text string) {
r := New(pattern, "[match]")
buf := make([]byte, len(text), len(text)*7)
b.SetBytes(int64(len(text)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
copy(buf, text)
r.Replace(buf)
}
}
func BenchmarkSingleMaxSkipping(b *testing.B) {
benchmarkSingleString(b, strings.Repeat("b", 25), strings.Repeat("a", 10000))
}
func BenchmarkSingleLongSuffixFail(b *testing.B) {
benchmarkSingleString(b, "b"+strings.Repeat("a", 500), strings.Repeat("a", 1002))
}
func BenchmarkSingleMatch(b *testing.B) {
benchmarkSingleString(b, "abcdef", strings.Repeat("abcdefghijklmno", 1000))
}
func benchmarkReplacer(b *testing.B, r *Replacer, str string) {
buf := make([]byte, len(str))
b.ResetTimer()
for i := 0; i < b.N; i++ {
copy(buf, str)
r.Replace(buf)
}
}
func BenchmarkByteByteNoMatch(b *testing.B) {
benchmarkReplacer(b, capitalLetters, strings.Repeat("A", 100)+strings.Repeat("B", 100))
}
func BenchmarkByteByteMatch(b *testing.B) {
benchmarkReplacer(b, capitalLetters, strings.Repeat("a", 100)+strings.Repeat("b", 100))
}
func BenchmarkByteStringMatch(b *testing.B) {
benchmarkReplacer(b, htmlEscaper, "<"+strings.Repeat("a", 99)+strings.Repeat("b", 99)+">")
}
func BenchmarkHTMLEscapeNew(b *testing.B) {
benchmarkReplacer(b, htmlEscaper, "I <3 to escape HTML & other text too.")
}
func BenchmarkHTMLEscapeOld(b *testing.B) {
str := "I <3 to escape HTML & other text too."
buf := make([]byte, len(str))
for i := 0; i < b.N; i++ {
copy(buf, str)
oldHTMLEscape(buf)
}
}
// The http package's old HTML escaping function in bytes form.
func oldHTMLEscape(s []byte) []byte {
s = bytes.Replace(s, []byte("&"), []byte("&amp;"), -1)
s = bytes.Replace(s, []byte("<"), []byte("&lt;"), -1)
s = bytes.Replace(s, []byte(">"), []byte("&gt;"), -1)
s = bytes.Replace(s, []byte(`"`), []byte("&quot;"), -1)
s = bytes.Replace(s, []byte("'"), []byte("&apos;"), -1)
return s
}
// BenchmarkByteByteReplaces compares byteByteImpl against multiple Replaces.
func BenchmarkByteByteReplaces(b *testing.B) {
str := strings.Repeat("a", 100) + strings.Repeat("b", 100)
for i := 0; i < b.N; i++ {
bytes.Replace(bytes.Replace([]byte(str), []byte{'a'}, []byte{'A'}, -1), []byte{'b'}, []byte{'B'}, -1)
}
}
// BenchmarkByteByteMap compares byteByteImpl against Map.
func BenchmarkByteByteMap(b *testing.B) {
str := strings.Repeat("a", 100) + strings.Repeat("b", 100)
fn := func(r rune) rune {
switch r {
case 'a':
return 'A'
case 'b':
return 'B'
}
return r
}
for i := 0; i < b.N; i++ {
bytes.Map(fn, []byte(str))
}
}

@ -1,457 +0,0 @@
/*
Copyright 2015 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package cloudlaunch helps binaries run themselves on The Cloud, copying
// themselves to GCE.
package cloudlaunch // import "go4.org/cloud/cloudlaunch"
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"time"
"go4.org/cloud/google/gceutil"
"cloud.google.com/go/compute/metadata"
"cloud.google.com/go/storage"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
storageapi "google.golang.org/api/storage/v1"
)
func readFile(v string) string {
slurp, err := ioutil.ReadFile(v)
if err != nil {
log.Fatalf("Error reading %s: %v", v, err)
}
return strings.TrimSpace(string(slurp))
}
const baseConfig = `#cloud-config
coreos:
update:
group: stable
reboot-strategy: $REBOOT
units:
- name: $NAME.service
command: start
content: |
[Unit]
Description=$NAME service
After=network.target
[Service]
Type=simple
ExecStartPre=/bin/sh -c 'mkdir -p /opt/bin && /usr/bin/curl --silent -f -o /opt/bin/$NAME $URL?$(date +%s) && chmod +x /opt/bin/$NAME'
ExecStart=/opt/bin/$NAME
RestartSec=10
Restart=always
StartLimitInterval=0
[Install]
WantedBy=network-online.target
`
// RestartPolicy controls whether the binary automatically restarts.
type RestartPolicy int
const (
RestartOnUpdates RestartPolicy = iota
RestartNever
// TODO: more graceful restarts; make systemd own listening on network sockets,
// don't break connections.
)
type Config struct {
// Name is the name of a service to run.
// This is the name of the systemd service (without .service)
// and the name of the GCE instance.
Name string
// RestartPolicy controls whether the binary automatically restarts
// on updates. The zero value means automatic.
RestartPolicy RestartPolicy
// UpdateStrategy sets the CoreOS automatic update strategy, and the
// associated reboots. Possible values are "best-effort", "etcd-lock",
// "reboot", "off", with "best-effort" being the default. See
// https://coreos.com/os/docs/latest/update-strategies.html
UpdateStrategy string
// BinaryBucket and BinaryObject are the GCS bucket and object
// within that bucket containing the Linux binary to download
// on boot and occasionally run. This binary must be public
// (at least for now).
BinaryBucket string
BinaryObject string // defaults to Name
GCEProjectID string
Zone string // defaults to us-central1-f
SSD bool
Scopes []string // any additional scopes
MachineType string
InstanceName string
}
// cloudLaunch is a launch of a Config.
type cloudLaunch struct {
*Config
oauthClient *http.Client
computeService *compute.Service
}
func (c *Config) binaryURL() string {
return "https://storage.googleapis.com/" + c.BinaryBucket + "/" + c.binaryObject()
}
func (c *Config) instName() string { return c.Name } // for now
func (c *Config) zone() string { return strDefault(c.Zone, "us-central1-f") }
func (c *Config) machineType() string { return strDefault(c.MachineType, "g1-small") }
func (c *Config) binaryObject() string { return strDefault(c.BinaryObject, c.Name) }
func (c *Config) updateStrategy() string { return strDefault(c.UpdateStrategy, "best-effort") }
func (c *Config) projectAPIURL() string {
return "https://www.googleapis.com/compute/v1/projects/" + c.GCEProjectID
}
func (c *Config) machineTypeURL() string {
return c.projectAPIURL() + "/zones/" + c.zone() + "/machineTypes/" + c.machineType()
}
func strDefault(a, b string) string {
if a != "" {
return a
}
return b
}
var (
doLaunch = flag.Bool("cloudlaunch", false, "Deploy or update this binary to the cloud. Must be on Linux, for now.")
)
func (c *Config) MaybeDeploy() {
flag.Parse()
if !*doLaunch {
go c.restartLoop()
return
}
defer os.Exit(1) // backup, in case we return without Fatal or os.Exit later
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
log.Fatal("Can only use --cloudlaunch on linux/amd64, for now.")
}
if c.GCEProjectID == "" {
log.Fatal("cloudconfig.GCEProjectID is empty")
}
filename := filepath.Join(os.Getenv("HOME"), "keys", c.GCEProjectID+".key.json")
log.Printf("Using OAuth config from JSON service file: %s", filename)
jwtConf, err := google.JWTConfigFromJSON([]byte(readFile(filename)), append([]string{
storageapi.DevstorageFullControlScope,
compute.ComputeScope,
"https://www.googleapis.com/auth/cloud-platform",
}, c.Scopes...)...)
if err != nil {
log.Fatalf("ConfigFromJSON: %v", err)
}
cl := &cloudLaunch{
Config: c,
oauthClient: jwtConf.Client(oauth2.NoContext),
}
cl.computeService, _ = compute.New(cl.oauthClient)
cl.uploadBinary()
cl.createInstance()
os.Exit(0)
}
func (c *Config) restartLoop() {
if !metadata.OnGCE() {
return
}
if c.RestartPolicy == RestartNever {
return
}
url := c.binaryURL()
var lastEtag string
for {
res, err := http.Head(url + "?" + fmt.Sprint(time.Now().Unix()))
if err != nil {
log.Printf("Warning: %v", err)
time.Sleep(15 * time.Second)
continue
}
etag := res.Header.Get("Etag")
if etag == "" {
log.Printf("Warning, no ETag in response: %v", res)
time.Sleep(15 * time.Second)
continue
}
if lastEtag != "" && etag != lastEtag {
log.Printf("Binary updated; restarting.")
// TODO: more graceful restart, letting systemd own the network connections.
// Then we can finish up requests here.
os.Exit(0)
}
lastEtag = etag
time.Sleep(15 * time.Second)
}
}
// uploadBinary uploads the currently-running Linux binary.
// It crashes if it fails.
func (cl *cloudLaunch) uploadBinary() {
ctx := context.Background()
if cl.BinaryBucket == "" {
log.Fatal("cloudlaunch: Config.BinaryBucket is empty")
}
stoClient, err := storage.NewClient(ctx, option.WithHTTPClient(cl.oauthClient))
if err != nil {
log.Fatal(err)
}
w := stoClient.Bucket(cl.BinaryBucket).Object(cl.binaryObject()).NewWriter(ctx)
if err != nil {
log.Fatal(err)
}
w.ACL = []storage.ACLRule{
// If you don't give the owners access, the web UI seems to
// have a bug and doesn't have access to see that it's public, so
// won't render the "Shared Publicly" link. So we do that, even
// though it's dumb and unnecessary otherwise:
{
Entity: storage.ACLEntity("project-owners-" + cl.GCEProjectID),
Role: storage.RoleOwner,
},
// Public, so our systemd unit can get it easily:
{
Entity: storage.AllUsers,
Role: storage.RoleReader,
},
}
w.CacheControl = "no-cache"
selfPath := getSelfPath()
log.Printf("Uploading %q to %v", selfPath, cl.binaryURL())
f, err := os.Open(selfPath)
if err != nil {
log.Fatal(err)
}
defer f.Close()
n, err := io.Copy(w, f)
if err != nil {
log.Fatal(err)
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
log.Printf("Uploaded %d bytes", n)
}
func getSelfPath() string {
if runtime.GOOS != "linux" {
panic("TODO")
}
v, err := os.Readlink("/proc/self/exe")
if err != nil {
log.Fatal(err)
}
return v
}
func zoneInRegion(zone, regionURL string) bool {
if zone == "" {
panic("empty zone")
}
if regionURL == "" {
panic("empty regionURL")
}
// zone is like "us-central1-f"
// regionURL is like "https://www.googleapis.com/compute/v1/projects/camlistore-website/regions/us-central1"
region := path.Base(regionURL) // "us-central1"
if region == "" {
panic("empty region")
}
return strings.HasPrefix(zone, region)
}
// findIP finds an IP address to use, or returns the empty string if none is found.
// It tries to find a reserved one in the same region where the name of the reserved IP
// is "NAME-ip" and the IP is not in use.
func (cl *cloudLaunch) findIP() string {
// Try to find it by name.
aggAddrList, err := cl.computeService.Addresses.AggregatedList(cl.GCEProjectID).Do()
if err != nil {
log.Fatal(err)
}
// https://godoc.org/google.golang.org/api/compute/v1#AddressAggregatedList
var ip string
IPLoop:
for _, asl := range aggAddrList.Items {
for _, addr := range asl.Addresses {
log.Printf(" addr: %#v", addr)
if addr.Name == cl.Name+"-ip" && addr.Status == "RESERVED" && zoneInRegion(cl.zone(), addr.Region) {
ip = addr.Address
break IPLoop
}
}
}
return ip
}
func (cl *cloudLaunch) createInstance() {
inst := cl.lookupInstance()
if inst != nil {
log.Printf("Instance exists; not re-creating.")
return
}
log.Printf("Instance doesn't exist; creating...")
ip := cl.findIP()
log.Printf("Found IP: %v", ip)
cloudConfig := strings.NewReplacer(
"$NAME", cl.Name,
"$URL", cl.binaryURL(),
"$REBOOT", cl.updateStrategy(),
).Replace(baseConfig)
instance := &compute.Instance{
Name: cl.instName(),
Description: cl.Name,
MachineType: cl.machineTypeURL(),
Disks: []*compute.AttachedDisk{cl.instanceDisk()},
Tags: &compute.Tags{
Items: []string{"http-server", "https-server"},
},
Metadata: &compute.Metadata{
Items: []*compute.MetadataItems{
{
Key: "user-data",
Value: googleapi.String(cloudConfig),
},
},
},
NetworkInterfaces: []*compute.NetworkInterface{
&compute.NetworkInterface{
AccessConfigs: []*compute.AccessConfig{
&compute.AccessConfig{
Type: "ONE_TO_ONE_NAT",
Name: "External NAT",
NatIP: ip,
},
},
Network: cl.projectAPIURL() + "/global/networks/default",
},
},
ServiceAccounts: []*compute.ServiceAccount{
{
Email: "default",
Scopes: cl.Scopes,
},
},
}
log.Printf("Creating instance...")
op, err := cl.computeService.Instances.Insert(cl.GCEProjectID, cl.zone(), instance).Do()
if err != nil {
log.Fatalf("Failed to create instance: %v", err)
}
opName := op.Name
log.Printf("Created. Waiting on operation %v", opName)
OpLoop:
for {
time.Sleep(2 * time.Second)
op, err := cl.computeService.ZoneOperations.Get(cl.GCEProjectID, cl.zone(), opName).Do()
if err != nil {
log.Fatalf("Failed to get op %s: %v", opName, err)
}
switch op.Status {
case "PENDING", "RUNNING":
log.Printf("Waiting on operation %v", opName)
continue
case "DONE":
if op.Error != nil {
for _, operr := range op.Error.Errors {
log.Printf("Error: %+v", operr)
}
log.Fatalf("Failed to start.")
}
log.Printf("Success. %+v", op)
break OpLoop
default:
log.Fatalf("Unknown status %q: %+v", op.Status, op)
}
}
inst, err = cl.computeService.Instances.Get(cl.GCEProjectID, cl.zone(), cl.instName()).Do()
if err != nil {
log.Fatalf("Error getting instance after creation: %v", err)
}
ij, _ := json.MarshalIndent(inst, "", " ")
log.Printf("%s", ij)
log.Printf("Instance created.")
os.Exit(0)
}
// returns nil if instance doesn't exist.
func (cl *cloudLaunch) lookupInstance() *compute.Instance {
inst, err := cl.computeService.Instances.Get(cl.GCEProjectID, cl.zone(), cl.instName()).Do()
if ae, ok := err.(*googleapi.Error); ok && ae.Code == 404 {
return nil
} else if err != nil {
log.Fatalf("Instances.Get: %v", err)
}
return inst
}
func (cl *cloudLaunch) instanceDisk() *compute.AttachedDisk {
imageURL, err := gceutil.CoreOSImageURL(cl.oauthClient)
if err != nil {
log.Fatalf("error looking up latest CoreOS stable image: %v", err)
}
diskName := cl.instName() + "-coreos-stateless-pd"
var diskType string
if cl.SSD {
diskType = cl.projectAPIURL() + "/zones/" + cl.zone() + "/diskTypes/pd-ssd"
}
return &compute.AttachedDisk{
AutoDelete: true,
Boot: true,
Type: "PERSISTENT",
InitializeParams: &compute.AttachedDiskInitializeParams{
DiskName: diskName,
SourceImage: imageURL,
DiskSizeGb: 50,
DiskType: diskType,
},
}
}

@ -1,110 +0,0 @@
/*
Copyright 2015 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package gceutil provides utility functions to help with instances on
// Google Compute Engine.
package gceutil // import "go4.org/cloud/google/gceutil"
import (
"encoding/json"
"errors"
"net/http"
"strings"
"time"
"google.golang.org/api/compute/v1"
)
// CoreOSImageURL returns the URL of the latest stable CoreOS image for running on Google Compute Engine.
func CoreOSImageURL(cl *http.Client) (string, error) {
resp, err := cl.Get("https://www.googleapis.com/compute/v1/projects/coreos-cloud/global/images")
if err != nil {
return "", err
}
defer resp.Body.Close()
type coreOSImage struct {
SelfLink string
CreationTimestamp time.Time
Name string
}
type coreOSImageList struct {
Items []coreOSImage
}
imageList := &coreOSImageList{}
if err := json.NewDecoder(resp.Body).Decode(imageList); err != nil {
return "", err
}
if imageList == nil || len(imageList.Items) == 0 {
return "", errors.New("no images list in response")
}
imageURL := ""
var max time.Time // latest stable image creation time
for _, v := range imageList.Items {
if !strings.HasPrefix(v.Name, "coreos-stable") {
continue
}
if v.CreationTimestamp.After(max) {
max = v.CreationTimestamp
imageURL = v.SelfLink
}
}
if imageURL == "" {
return "", errors.New("no stable coreOS image found")
}
return imageURL, nil
}
// InstanceGroupAndManager contains both an InstanceGroup and
// its InstanceGroupManager, if any.
type InstanceGroupAndManager struct {
Group *compute.InstanceGroup
// Manager is the manager of the Group. It may be nil.
Manager *compute.InstanceGroupManager
}
// InstanceGroups returns all the instance groups in a project's zone, along
// with their associated InstanceGroupManagers.
// The returned map is keyed by the instance group identifier URL.
func InstanceGroups(svc *compute.Service, proj, zone string) (map[string]InstanceGroupAndManager, error) {
managerList, err := svc.InstanceGroupManagers.List(proj, zone).Do()
if err != nil {
return nil, err
}
if managerList.NextPageToken != "" {
return nil, errors.New("too many managers; pagination not supported")
}
managedBy := make(map[string]*compute.InstanceGroupManager) // instance group URL -> its manager
for _, it := range managerList.Items {
managedBy[it.InstanceGroup] = it
}
groupList, err := svc.InstanceGroups.List(proj, zone).Do()
if err != nil {
return nil, err
}
if groupList.NextPageToken != "" {
return nil, errors.New("too many instance groups; pagination not supported")
}
ret := make(map[string]InstanceGroupAndManager)
for _, it := range groupList.Items {
ret[it.SelfLink] = InstanceGroupAndManager{it, managedBy[it.SelfLink]}
}
return ret, nil
}

@ -1,180 +0,0 @@
/*
Copyright 2015 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package gcsutil provides tools for accessing Google Cloud Storage until they can be
// completely replaced by cloud.google.com/go/storage.
package gcsutil // import "go4.org/cloud/google/gcsutil"
import (
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"cloud.google.com/go/storage"
"go4.org/ctxutil"
"golang.org/x/net/context"
)
const gsAccessURL = "https://storage.googleapis.com"
// An Object holds the name of an object (its bucket and key) within
// Google Cloud Storage.
type Object struct {
Bucket string
Key string
}
func (o *Object) valid() error {
if o == nil {
return errors.New("invalid nil Object")
}
if o.Bucket == "" {
return errors.New("missing required Bucket field in Object")
}
if o.Key == "" {
return errors.New("missing required Key field in Object")
}
return nil
}
// A SizedObject holds the bucket, key, and size of an object.
type SizedObject struct {
Object
Size int64
}
func (o *Object) String() string {
if o == nil {
return "<nil *Object>"
}
return fmt.Sprintf("%v/%v", o.Bucket, o.Key)
}
func (so SizedObject) String() string {
return fmt.Sprintf("%v/%v (%vB)", so.Bucket, so.Key, so.Size)
}
// Makes a simple body-less google storage request
func simpleRequest(method, url_ string) (*http.Request, error) {
req, err := http.NewRequest(method, url_, nil)
if err != nil {
return nil, err
}
req.Header.Set("x-goog-api-version", "2")
return req, err
}
// ErrInvalidRange is used when the server has returned http.StatusRequestedRangeNotSatisfiable.
var ErrInvalidRange = errors.New("gcsutil: requested range not satisfiable")
// GetPartialObject fetches part of a Google Cloud Storage object.
// This function relies on the ctx ctxutil.HTTPClient value being set to an OAuth2
// authorized and authenticated HTTP client.
// If length is negative, the rest of the object is returned.
// It returns ErrInvalidRange if the server replies with http.StatusRequestedRangeNotSatisfiable.
// The caller must call Close on the returned value.
func GetPartialObject(ctx context.Context, obj Object, offset, length int64) (io.ReadCloser, error) {
if offset < 0 {
return nil, errors.New("invalid negative offset")
}
if err := obj.valid(); err != nil {
return nil, err
}
req, err := simpleRequest("GET", gsAccessURL+"/"+obj.Bucket+"/"+obj.Key)
if err != nil {
return nil, err
}
if length >= 0 {
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1))
} else {
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
}
req.Cancel = ctx.Done()
res, err := ctxutil.Client(ctx).Do(req)
if err != nil {
return nil, fmt.Errorf("GET (offset=%d, length=%d) failed: %v\n", offset, length, err)
}
if res.StatusCode == http.StatusNotFound {
res.Body.Close()
return nil, os.ErrNotExist
}
if !(res.StatusCode == http.StatusPartialContent || (offset == 0 && res.StatusCode == http.StatusOK)) {
res.Body.Close()
if res.StatusCode == http.StatusRequestedRangeNotSatisfiable {
return nil, ErrInvalidRange
}
return nil, fmt.Errorf("GET (offset=%d, length=%d) got failed status: %v\n", offset, length, res.Status)
}
return res.Body, nil
}
// EnumerateObjects lists the objects in a bucket.
// This function relies on the ctx oauth2.HTTPClient value being set to an OAuth2
// authorized and authenticated HTTP client.
// If after is non-empty, listing will begin with lexically greater object names.
// If limit is non-zero, the length of the list will be limited to that number.
func EnumerateObjects(ctx context.Context, bucket, after string, limit int) ([]*storage.ObjectAttrs, error) {
// Build url, with query params
var params []string
if after != "" {
params = append(params, "marker="+url.QueryEscape(after))
}
if limit > 0 {
params = append(params, fmt.Sprintf("max-keys=%v", limit))
}
query := ""
if len(params) > 0 {
query = "?" + strings.Join(params, "&")
}
req, err := simpleRequest("GET", gsAccessURL+"/"+bucket+"/"+query)
if err != nil {
return nil, err
}
req.Cancel = ctx.Done()
res, err := ctxutil.Client(ctx).Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("gcsutil: bad enumerate response code: %v", res.Status)
}
var xres struct {
Contents []SizedObject
}
if err = xml.NewDecoder(res.Body).Decode(&xres); err != nil {
return nil, err
}
objAttrs := make([]*storage.ObjectAttrs, len(xres.Contents))
for k, o := range xres.Contents {
objAttrs[k] = &storage.ObjectAttrs{
Name: o.Key,
Size: o.Size,
}
}
return objAttrs, nil
}

44
vendor/go4.org/ctxutil/ctxutil.go generated vendored

@ -1,44 +0,0 @@
/*
Copyright 2015 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package ctxutil contains golang.org/x/net/context related utilities.
package ctxutil // import "go4.org/ctxutil"
import (
"net/http"
"golang.org/x/net/context"
"golang.org/x/oauth2"
)
// HTTPClient is the context key to use with golang.org/x/net/context's WithValue function
// to associate an *http.Client value with a context.
//
// We use the same value as the oauth2 package (which first introduced this key) rather
// than creating a new one and forcing users to possibly set two.
var HTTPClient = oauth2.HTTPClient
// Client returns the HTTP client to use for the provided context.
// If ctx is non-nil and has an associated HTTP client, that client is returned.
// Otherwise, http.DefaultClient is returned.
func Client(ctx context.Context) *http.Client {
if ctx != nil {
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
return hc
}
}
return http.DefaultClient
}

@ -1,58 +0,0 @@
/*
Copyright 2011 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package errorutil helps make better error messages.
package errorutil // import "go4.org/errorutil"
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
// HighlightBytePosition takes a reader and the location in bytes of a parse
// error (for instance, from json.SyntaxError.Offset) and returns the line, column,
// and pretty-printed context around the error with an arrow indicating the exact
// position of the syntax error.
func HighlightBytePosition(f io.Reader, pos int64) (line, col int, highlight string) {
line = 1
br := bufio.NewReader(f)
lastLine := ""
thisLine := new(bytes.Buffer)
for n := int64(0); n < pos; n++ {
b, err := br.ReadByte()
if err != nil {
break
}
if b == '\n' {
lastLine = thisLine.String()
thisLine.Reset()
line++
col = 1
} else {
col++
thisLine.WriteByte(b)
}
}
if line > 1 {
highlight += fmt.Sprintf("%5d: %s\n", line-1, lastLine)
}
highlight += fmt.Sprintf("%5d: %s\n", line, thisLine.String())
highlight += fmt.Sprintf("%s^\n", strings.Repeat(" ", col+5))
return
}

59
vendor/go4.org/fault/fault.go generated vendored

@ -1,59 +0,0 @@
/*
Copyright 2014 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package fault handles fault injection for testing.
package fault // import "go4.org/fault"
import (
"errors"
"math/rand"
"os"
"strconv"
"strings"
)
var fakeErr = errors.New("fake injected error for testing")
// An Injector reports whether fake errors should be returned.
type Injector struct {
failPercent int
}
// NewInjector returns a new fault injector with the given name. The
// environment variable "FAULT_" + capital(name) + "_FAIL_PERCENT"
// controls the percentage of requests that fail. If undefined or
// zero, no requests fail.
func NewInjector(name string) *Injector {
var failPercent, _ = strconv.Atoi(os.Getenv("FAULT_" + strings.ToUpper(name) + "_FAIL_PERCENT"))
return &Injector{
failPercent: failPercent,
}
}
// ShouldFail reports whether a fake error should be returned.
func (in *Injector) ShouldFail() bool {
return in.failPercent > 0 && in.failPercent > rand.Intn(100)
}
// FailErr checks ShouldFail and, if true, assigns a fake error to err
// and returns true.
func (in *Injector) FailErr(err *error) bool {
if !in.ShouldFail() {
return false
}
*err = fakeErr
return true
}

@ -1,95 +0,0 @@
// +build ignore
/*
Copyright 2016 The Go4 Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// The serve_on_cloud program deploys an HTTP server on Google Compute Engine,
// serving from Google Cloud Storage. Its purpose is to help testing
// go4.org/cloud/cloudlaunch and go4.org/wkfs/gcs.
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"path"
"regexp"
"time"
"go4.org/cloud/cloudlaunch"
"go4.org/wkfs"
_ "go4.org/wkfs/gcs"
"cloud.google.com/go/compute/metadata"
storageapi "google.golang.org/api/storage/v1"
compute "google.golang.org/api/compute/v1"
)
var httpAddr = flag.String("http", ":80", "HTTP address")
var gcsBucket string
func serveHTTP(w http.ResponseWriter, r *http.Request) {
rc, err := wkfs.Open(path.Join("/gcs", gcsBucket, r.URL.Path))
if err != nil {
http.Error(w, fmt.Sprintf("could not open %v: %v", r.URL.Path, err), 500)
return
}
defer rc.Close()
http.ServeContent(w, r, r.URL.Path, time.Now(), rc)
}
func main() {
if !metadata.OnGCE() {
bucket := os.Getenv("GCSBUCKET")
if bucket == "" {
log.Fatal("You need to set the GCSBUCKET env var to specify the Google Cloud Storage bucket to serve from.")
}
projectID := os.Getenv("GCEPROJECTID")
if projectID == "" {
log.Fatal("You need to set the GCEPROJECTID env var to specify the Google Cloud project where the instance will run.")
}
(&cloudlaunch.Config{
Name: "serveoncloud",
BinaryBucket: bucket,
GCEProjectID: projectID,
Scopes: []string{
storageapi.DevstorageFullControlScope,
compute.ComputeScope,
},
}).MaybeDeploy()
return
}
flag.Parse()
storageURLRxp := regexp.MustCompile(`https://storage.googleapis.com/(.+?)/serveoncloud.*`)
cloudConfig, err := metadata.InstanceAttributeValue("user-data")
if err != nil || cloudConfig == "" {
log.Fatalf("could not get cloud config from metadata: %v", err)
}
m := storageURLRxp.FindStringSubmatch(cloudConfig)
if len(m) < 2 {
log.Fatal("storage URL not found in cloud config")
}
gcsBucket = m[1]
http.HandleFunc("/", serveHTTP)
log.Fatal(http.ListenAndServe(*httpAddr, nil))
}

321
vendor/go4.org/jsonconfig/eval.go generated vendored

@ -1,321 +0,0 @@
/*
Copyright 2011 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package jsonconfig
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"go4.org/errorutil"
"go4.org/wkfs"
)
type stringVector struct {
v []string
}
func (v *stringVector) Push(s string) {
v.v = append(v.v, s)
}
func (v *stringVector) Pop() {
v.v = v.v[:len(v.v)-1]
}
func (v *stringVector) Last() string {
return v.v[len(v.v)-1]
}
// A File is the type returned by ConfigParser.Open.
type File interface {
io.ReadSeeker
io.Closer
Name() string
}
// ConfigParser specifies the environment for parsing a config file
// and evaluating expressions.
type ConfigParser struct {
rootJSON Obj
touchedFiles map[string]bool
includeStack stringVector
// Open optionally specifies an opener function.
Open func(filename string) (File, error)
// IncludeDirs optionally specifies where to find the other config files which are child
// objects of this config, if any. Even if nil, the working directory is always searched
// first.
IncludeDirs []string
}
func (c *ConfigParser) open(filename string) (File, error) {
if c.Open == nil {
return wkfs.Open(filename)
}
return c.Open(filename)
}
// Validates variable names for config _env expresssions
var envPattern = regexp.MustCompile(`\$\{[A-Za-z0-9_]+\}`)
// ReadFile parses the provided path and returns the config file.
// If path is empty, the c.Open function must be defined.
func (c *ConfigParser) ReadFile(path string) (Obj, error) {
if path == "" && c.Open == nil {
return nil, errors.New("ReadFile of empty string but Open hook not defined")
}
c.touchedFiles = make(map[string]bool)
var err error
c.rootJSON, err = c.recursiveReadJSON(path)
return c.rootJSON, err
}
// Decodes and evaluates a json config file, watching for include cycles.
func (c *ConfigParser) recursiveReadJSON(configPath string) (decodedObject map[string]interface{}, err error) {
if configPath != "" {
absConfigPath, err := filepath.Abs(configPath)
if err != nil {
return nil, fmt.Errorf("Failed to expand absolute path for %s", configPath)
}
if c.touchedFiles[absConfigPath] {
return nil, fmt.Errorf("ConfigParser include cycle detected reading config: %v",
absConfigPath)
}
c.touchedFiles[absConfigPath] = true
c.includeStack.Push(absConfigPath)
defer c.includeStack.Pop()
}
var f File
if f, err = c.open(configPath); err != nil {
return nil, fmt.Errorf("Failed to open config: %v", err)
}
defer f.Close()
decodedObject = make(map[string]interface{})
dj := json.NewDecoder(f)
if err = dj.Decode(&decodedObject); err != nil {
extra := ""
if serr, ok := err.(*json.SyntaxError); ok {
if _, serr := f.Seek(0, os.SEEK_SET); serr != nil {
log.Fatalf("seek error: %v", serr)
}
line, col, highlight := errorutil.HighlightBytePosition(f, serr.Offset)
extra = fmt.Sprintf(":\nError at line %d, column %d (file offset %d):\n%s",
line, col, serr.Offset, highlight)
}
return nil, fmt.Errorf("error parsing JSON object in config file %s%s\n%v",
f.Name(), extra, err)
}
if err = c.evaluateExpressions(decodedObject, nil, false); err != nil {
return nil, fmt.Errorf("error expanding JSON config expressions in %s:\n%v",
f.Name(), err)
}
return decodedObject, nil
}
var regFunc = map[string]expanderFunc{}
// RegisterFunc registers a new function that may be called from JSON
// configs using an array of the form ["_name", arg0, argN...].
// The provided name must begin with an underscore.
func RegisterFunc(name string, fn func(c *ConfigParser, v []interface{}) (interface{}, error)) {
if len(name) < 2 || !strings.HasPrefix(name, "_") {
panic("illegal name")
}
if _, dup := regFunc[name]; dup {
panic("duplicate registration of " + name)
}
regFunc[name] = fn
}
type expanderFunc func(c *ConfigParser, v []interface{}) (interface{}, error)
func namedExpander(name string) (fn expanderFunc, ok bool) {
switch name {
case "_env":
return (*ConfigParser).expandEnv, true
case "_fileobj":
return (*ConfigParser).expandFile, true
}
fn, ok = regFunc[name]
return
}
func (c *ConfigParser) evalValue(v interface{}) (interface{}, error) {
sl, ok := v.([]interface{})
if !ok {
return v, nil
}
if name, ok := sl[0].(string); ok {
if expander, ok := namedExpander(name); ok {
newval, err := expander(c, sl[1:])
if err != nil {
return nil, err
}
return newval, nil
}
}
for i, oldval := range sl {
newval, err := c.evalValue(oldval)
if err != nil {
return nil, err
}
sl[i] = newval
}
return v, nil
}
// CheckTypes parses m and returns an error if it encounters a type or value
// that is not supported by this package.
func (c *ConfigParser) CheckTypes(m map[string]interface{}) error {
return c.evaluateExpressions(m, nil, true)
}
// evaluateExpressions parses recursively m, populating it with the values
// that are found, unless testOnly is true.
func (c *ConfigParser) evaluateExpressions(m map[string]interface{}, seenKeys []string, testOnly bool) error {
for k, ei := range m {
thisPath := append(seenKeys, k)
switch subval := ei.(type) {
case string, bool, float64, nil:
continue
case []interface{}:
if len(subval) == 0 {
continue
}
evaled, err := c.evalValue(subval)
if err != nil {
return fmt.Errorf("%s: value error %v", strings.Join(thisPath, "."), err)
}
if !testOnly {
m[k] = evaled
}
case map[string]interface{}:
if err := c.evaluateExpressions(subval, thisPath, testOnly); err != nil {
return err
}
default:
return fmt.Errorf("%s: unhandled type %T", strings.Join(thisPath, "."), ei)
}
}
return nil
}
// Permit either:
// ["_env", "VARIABLE"] (required to be set)
// or ["_env", "VARIABLE", "default_value"]
func (c *ConfigParser) expandEnv(v []interface{}) (interface{}, error) {
hasDefault := false
def := ""
if len(v) < 1 || len(v) > 2 {
return "", fmt.Errorf("_env expansion expected 1 or 2 args, got %d", len(v))
}
s, ok := v[0].(string)
if !ok {
return "", fmt.Errorf("Expected a string after _env expansion; got %#v", v[0])
}
boolDefault, wantsBool := false, false
if len(v) == 2 {
hasDefault = true
switch vdef := v[1].(type) {
case string:
def = vdef
case bool:
wantsBool = true
boolDefault = vdef
default:
return "", fmt.Errorf("Expected default value in %q _env expansion; got %#v", s, v[1])
}
}
var err error
expanded := envPattern.ReplaceAllStringFunc(s, func(match string) string {
envVar := match[2 : len(match)-1]
val := os.Getenv(envVar)
// Special case:
if val == "" && envVar == "USER" && runtime.GOOS == "windows" {
val = os.Getenv("USERNAME")
}
if val == "" {
if hasDefault {
return def
}
err = fmt.Errorf("couldn't expand environment variable %q", envVar)
}
return val
})
if wantsBool {
if expanded == "" {
return boolDefault, nil
}
return strconv.ParseBool(expanded)
}
return expanded, err
}
func (c *ConfigParser) expandFile(v []interface{}) (exp interface{}, err error) {
if len(v) != 1 {
return "", fmt.Errorf("_file expansion expected 1 arg, got %d", len(v))
}
var incPath string
if incPath, err = c.ConfigFilePath(v[0].(string)); err != nil {
return "", fmt.Errorf("Included config does not exist: %v", v[0])
}
if exp, err = c.recursiveReadJSON(incPath); err != nil {
return "", fmt.Errorf("In file included from %s:\n%v",
c.includeStack.Last(), err)
}
return exp, nil
}
// ConfigFilePath checks if configFile is found and returns a usable path to it.
// It first checks if configFile is an absolute path, or if it's found in the
// current working directory. If not, it then checks if configFile is in one of
// c.IncludeDirs. It returns an error if configFile is absolute and could not be
// statted, or os.ErrNotExist if configFile was not found.
func (c *ConfigParser) ConfigFilePath(configFile string) (path string, err error) {
// Try to open as absolute / relative to CWD
_, err = os.Stat(configFile)
if err != nil && filepath.IsAbs(configFile) {
return "", err
}
if err == nil {
return configFile, nil
}
for _, d := range c.IncludeDirs {
if _, err := os.Stat(filepath.Join(d, configFile)); err == nil {
return filepath.Join(d, configFile), nil
}
}
return "", os.ErrNotExist
}

@ -1,297 +0,0 @@
/*
Copyright 2011 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package jsonconfig defines a helper type for JSON objects to be
// used for configuration.
package jsonconfig // import "go4.org/jsonconfig"
import (
"fmt"
"sort"
"strconv"
"strings"
)
// Obj is a JSON configuration map.
type Obj map[string]interface{}
// ReadFile reads JSON config data from the specified open file, expanding
// all expressions. Use *ConfigParser.ReadFile instead if you
// need to set c.IncludeDirs.
func ReadFile(configPath string) (Obj, error) {
var c ConfigParser
return c.ReadFile(configPath)
}
func (jc Obj) RequiredObject(key string) Obj {
return jc.obj(key, false)
}
func (jc Obj) OptionalObject(key string) Obj {
return jc.obj(key, true)
}
func (jc Obj) obj(key string, optional bool) Obj {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if optional {
return make(Obj)
}
jc.appendError(fmt.Errorf("Missing required config key %q (object)", key))
return make(Obj)
}
m, ok := ei.(map[string]interface{})
if !ok {
jc.appendError(fmt.Errorf("Expected config key %q to be an object, not %T", key, ei))
return make(Obj)
}
return m
}
func (jc Obj) RequiredString(key string) string {
return jc.string(key, nil)
}
func (jc Obj) OptionalString(key, def string) string {
return jc.string(key, &def)
}
func (jc Obj) string(key string, def *string) string {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if def != nil {
return *def
}
jc.appendError(fmt.Errorf("Missing required config key %q (string)", key))
return ""
}
s, ok := ei.(string)
if !ok {
jc.appendError(fmt.Errorf("Expected config key %q to be a string", key))
return ""
}
return s
}
func (jc Obj) RequiredStringOrObject(key string) interface{} {
return jc.stringOrObject(key, true)
}
func (jc Obj) OptionalStringOrObject(key string) interface{} {
return jc.stringOrObject(key, false)
}
func (jc Obj) stringOrObject(key string, required bool) interface{} {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if !required {
return nil
}
jc.appendError(fmt.Errorf("Missing required config key %q (string or object)", key))
return ""
}
if _, ok := ei.(map[string]interface{}); ok {
return ei
}
if _, ok := ei.(string); ok {
return ei
}
jc.appendError(fmt.Errorf("Expected config key %q to be a string or object", key))
return ""
}
func (jc Obj) RequiredBool(key string) bool {
return jc.bool(key, nil)
}
func (jc Obj) OptionalBool(key string, def bool) bool {
return jc.bool(key, &def)
}
func (jc Obj) bool(key string, def *bool) bool {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if def != nil {
return *def
}
jc.appendError(fmt.Errorf("Missing required config key %q (boolean)", key))
return false
}
switch v := ei.(type) {
case bool:
return v
case string:
b, err := strconv.ParseBool(v)
if err != nil {
jc.appendError(fmt.Errorf("Config key %q has bad boolean format %q", key, v))
}
return b
default:
jc.appendError(fmt.Errorf("Expected config key %q to be a boolean", key))
return false
}
}
func (jc Obj) RequiredInt(key string) int {
return jc.int(key, nil)
}
func (jc Obj) OptionalInt(key string, def int) int {
return jc.int(key, &def)
}
func (jc Obj) int(key string, def *int) int {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if def != nil {
return *def
}
jc.appendError(fmt.Errorf("Missing required config key %q (integer)", key))
return 0
}
b, ok := ei.(float64)
if !ok {
jc.appendError(fmt.Errorf("Expected config key %q to be a number", key))
return 0
}
return int(b)
}
func (jc Obj) RequiredInt64(key string) int64 {
return jc.int64(key, nil)
}
func (jc Obj) OptionalInt64(key string, def int64) int64 {
return jc.int64(key, &def)
}
func (jc Obj) int64(key string, def *int64) int64 {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if def != nil {
return *def
}
jc.appendError(fmt.Errorf("Missing required config key %q (integer)", key))
return 0
}
b, ok := ei.(float64)
if !ok {
jc.appendError(fmt.Errorf("Expected config key %q to be a number", key))
return 0
}
return int64(b)
}
func (jc Obj) RequiredList(key string) []string {
return jc.requiredList(key, true)
}
func (jc Obj) OptionalList(key string) []string {
return jc.requiredList(key, false)
}
func (jc Obj) requiredList(key string, required bool) []string {
jc.noteKnownKey(key)
ei, ok := jc[key]
if !ok {
if required {
jc.appendError(fmt.Errorf("Missing required config key %q (list of strings)", key))
}
return nil
}
eil, ok := ei.([]interface{})
if !ok {
jc.appendError(fmt.Errorf("Expected config key %q to be a list, not %T", key, ei))
return nil
}
sl := make([]string, len(eil))
for i, ei := range eil {
s, ok := ei.(string)
if !ok {
jc.appendError(fmt.Errorf("Expected config key %q index %d to be a string, not %T", key, i, ei))
return nil
}
sl[i] = s
}
return sl
}
func (jc Obj) noteKnownKey(key string) {
_, ok := jc["_knownkeys"]
if !ok {
jc["_knownkeys"] = make(map[string]bool)
}
jc["_knownkeys"].(map[string]bool)[key] = true
}
func (jc Obj) appendError(err error) {
ei, ok := jc["_errors"]
if ok {
jc["_errors"] = append(ei.([]error), err)
} else {
jc["_errors"] = []error{err}
}
}
// UnknownKeys returns the keys from the config that have not yet been discovered by one of the RequiredT or OptionalT calls.
func (jc Obj) UnknownKeys() []string {
ei, ok := jc["_knownkeys"]
var known map[string]bool
if ok {
known = ei.(map[string]bool)
}
var unknown []string
for k, _ := range jc {
if ok && known[k] {
continue
}
if strings.HasPrefix(k, "_") {
// Permit keys with a leading underscore as a
// form of comments.
continue
}
unknown = append(unknown, k)
}
sort.Strings(unknown)
return unknown
}
func (jc Obj) Validate() error {
unknown := jc.UnknownKeys()
for _, k := range unknown {
jc.appendError(fmt.Errorf("Unknown key %q", k))
}
ei, ok := jc["_errors"]
if !ok {
return nil
}
errList := ei.([]error)
if len(errList) == 1 {
return errList[0]
}
strs := make([]string, 0)
for _, v := range errList {
strs = append(strs, v.Error())
}
return fmt.Errorf("Multiple errors: " + strings.Join(strs, ", "))
}

@ -1,114 +0,0 @@
/*
Copyright 2011 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package jsonconfig
import (
"os"
"reflect"
"strings"
"testing"
)
func testIncludes(configFile string, t *testing.T) {
var c ConfigParser
c.IncludeDirs = []string{"testdata"}
obj, err := c.ReadFile(configFile)
if err != nil {
t.Fatal(err)
}
two := obj.RequiredObject("two")
if err := obj.Validate(); err != nil {
t.Error(err)
}
if g, e := two.RequiredString("key"), "value"; g != e {
t.Errorf("sub object key = %q; want %q", g, e)
}
}
func TestIncludesCWD(t *testing.T) {
testIncludes("testdata/include1.json", t)
}
func TestIncludesIncludeDirs(t *testing.T) {
testIncludes("testdata/include1bis.json", t)
}
func TestIncludeLoop(t *testing.T) {
_, err := ReadFile("testdata/loop1.json")
if err == nil {
t.Fatal("expected an error about import cycles.")
}
if !strings.Contains(err.Error(), "include cycle detected") {
t.Fatalf("expected an error about import cycles; got: %v", err)
}
}
func TestBoolEnvs(t *testing.T) {
os.Setenv("TEST_EMPTY", "")
os.Setenv("TEST_TRUE", "true")
os.Setenv("TEST_ONE", "1")
os.Setenv("TEST_ZERO", "0")
os.Setenv("TEST_FALSE", "false")
obj, err := ReadFile("testdata/boolenv.json")
if err != nil {
t.Fatal(err)
}
if str := obj.RequiredString("emptystr"); str != "" {
t.Errorf("str = %q, want empty", str)
}
tests := []struct {
key string
want bool
}{
{"def_false", false},
{"def_true", true},
{"set_true_def_false", true},
{"set_false_def_true", false},
{"lit_true", true},
{"lit_false", false},
{"one", true},
{"zero", false},
}
for _, tt := range tests {
if v := obj.RequiredBool(tt.key); v != tt.want {
t.Errorf("key %q = %v; want %v", tt.key, v, tt.want)
}
}
if err := obj.Validate(); err != nil {
t.Error(err)
}
}
func TestListExpansion(t *testing.T) {
os.Setenv("TEST_BAR", "bar")
obj, err := ReadFile("testdata/listexpand.json")
if err != nil {
t.Fatal(err)
}
s := obj.RequiredString("str")
l := obj.RequiredList("list")
if err := obj.Validate(); err != nil {
t.Error(err)
}
want := []string{"foo", "bar"}
if !reflect.DeepEqual(l, want) {
t.Errorf("got = %#v\nwant = %#v", l, want)
}
if s != "bar" {
t.Errorf("str = %q, want %q", s, "bar")
}
}

@ -1,11 +0,0 @@
{
"emptystr": ["_env", "${TEST_EMPTY}", ""],
"def_false": ["_env", "${TEST_EMPTY}", false],
"def_true": ["_env", "${TEST_EMPTY}", true],
"set_true_def_false": ["_env", "${TEST_TRUE}", false],
"set_false_def_true": ["_env", "${TEST_FALSE}", true],
"one": ["_env", "${TEST_ONE}"],
"zero": ["_env", "${TEST_ZERO}"],
"lit_true": true,
"lit_false": false
}

@ -1,3 +0,0 @@
{
"two": ["_fileobj", "testdata/include2.json"]
}

@ -1,3 +0,0 @@
{
"two": ["_fileobj", "include2.json"]
}

@ -1,3 +0,0 @@
{
"key": "value"
}

@ -1,4 +0,0 @@
{
"list": ["foo", ["_env", "${TEST_BAR}"]],
"str": ["_env", "${TEST_BAR}"]
}

@ -1,3 +0,0 @@
{
"obj": ["_fileobj", "testdata/loop2.json"]
}

@ -1,3 +0,0 @@
{
"obj": ["_fileobj", "testdata/loop1.json"]
}

32
vendor/go4.org/legal/legal.go generated vendored

@ -1,32 +0,0 @@
/*
Copyright 2014 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package legal provides in-process storage for compiled-in licenses.
package legal // import "go4.org/legal"
var licenses []string
// RegisterLicense stores the license text.
// It doesn't check whether the text was already present.
func RegisterLicense(text string) {
licenses = append(licenses, text)
return
}
// Licenses returns a slice of the licenses.
func Licenses() []string {
return licenses
}

@ -1,29 +0,0 @@
/*
Copyright 2014 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package legal
import (
"testing"
)
func TestRegisterLicense(t *testing.T) {
initial := len(licenses)
RegisterLicense("dummy")
if initial+1 != len(licenses) {
t.Fatal("didn't add a license")
}
}

1
vendor/go4.org/lock/.gitignore generated vendored

@ -1 +0,0 @@
*~

186
vendor/go4.org/lock/lock.go generated vendored

@ -1,186 +0,0 @@
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package lock is a file locking library.
package lock // import "go4.org/lock"
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"sync"
)
// Lock locks the given file, creating the file if necessary. If the
// file already exists, it must have zero size or an error is returned.
// The lock is an exclusive lock (a write lock), but locked files
// should neither be read from nor written to. Such files should have
// zero size and only exist to co-ordinate ownership across processes.
//
// A nil Closer is returned if an error occurred. Otherwise, close that
// Closer to release the lock.
//
// On Linux, FreeBSD and OSX, a lock has the same semantics as fcntl(2)'s
// advisory locks. In particular, closing any other file descriptor for the
// same file will release the lock prematurely.
//
// Attempting to lock a file that is already locked by the current process
// has undefined behavior.
//
// On other operating systems, lock will fallback to using the presence and
// content of a file named name + '.lock' to implement locking behavior.
func Lock(name string) (io.Closer, error) {
abs, err := filepath.Abs(name)
if err != nil {
return nil, err
}
lockmu.Lock()
defer lockmu.Unlock()
if locked[abs] {
return nil, fmt.Errorf("file %q already locked", abs)
}
c, err := lockFn(abs)
if err != nil {
return nil, fmt.Errorf("cannot acquire lock: %v", err)
}
locked[abs] = true
return c, nil
}
var lockFn = lockPortable
// lockPortable is a portable version not using fcntl. Doesn't handle crashes as gracefully,
// since it can leave stale lock files.
func lockPortable(name string) (io.Closer, error) {
fi, err := os.Stat(name)
if err == nil && fi.Size() > 0 {
st := portableLockStatus(name)
switch st {
case statusLocked:
return nil, fmt.Errorf("file %q already locked", name)
case statusStale:
os.Remove(name)
case statusInvalid:
return nil, fmt.Errorf("can't Lock file %q: has invalid contents", name)
}
}
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0666)
if err != nil {
return nil, fmt.Errorf("failed to create lock file %s %v", name, err)
}
if err := json.NewEncoder(f).Encode(&pidLockMeta{OwnerPID: os.Getpid()}); err != nil {
return nil, fmt.Errorf("cannot write owner pid: %v", err)
}
return &unlocker{
f: f,
abs: name,
portable: true,
}, nil
}
type lockStatus int
const (
statusInvalid lockStatus = iota
statusLocked
statusUnlocked
statusStale
)
type pidLockMeta struct {
OwnerPID int
}
func portableLockStatus(path string) lockStatus {
f, err := os.Open(path)
if err != nil {
return statusUnlocked
}
defer f.Close()
var meta pidLockMeta
if json.NewDecoder(f).Decode(&meta) != nil {
return statusInvalid
}
if meta.OwnerPID == 0 {
return statusInvalid
}
p, err := os.FindProcess(meta.OwnerPID)
if err != nil {
// e.g. on Windows
return statusStale
}
// On unix, os.FindProcess always is true, so we have to send
// it a signal to see if it's alive.
if signalZero != nil {
if p.Signal(signalZero) != nil {
return statusStale
}
}
return statusLocked
}
var signalZero os.Signal // nil or set by lock_sigzero.go
var (
lockmu sync.Mutex
locked = map[string]bool{} // abs path -> true
)
type unlocker struct {
portable bool
f *os.File
abs string
// once guards the close method call.
once sync.Once
// err holds the error returned by Close.
err error
}
func (u *unlocker) Close() error {
u.once.Do(u.close)
return u.err
}
func (u *unlocker) close() {
lockmu.Lock()
defer lockmu.Unlock()
delete(locked, u.abs)
if u.portable {
// In the portable lock implementation, it's
// important to close before removing because
// Windows won't allow us to remove an open
// file.
if err := u.f.Close(); err != nil {
u.err = err
}
if err := os.Remove(u.abs); err != nil {
// Note that if both Close and Remove fail,
// we care more about the latter than the former
// so we'll return that error.
u.err = err
}
return
}
// In other implementatioons, it's nice for us to clean up.
// If we do do this, though, it needs to be before the
// u.f.Close below.
os.Remove(u.abs)
u.err = u.f.Close()
}

@ -1,32 +0,0 @@
// +build appengine
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lock
import (
"errors"
"io"
)
func init() {
lockFn = lockAppEngine
}
func lockAppEngine(name string) (io.Closer, error) {
return nil, errors.New("Lock not available on App Engine")
}

41
vendor/go4.org/lock/lock_plan9.go generated vendored

@ -1,41 +0,0 @@
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lock
import (
"fmt"
"io"
"os"
)
func init() {
lockFn = lockPlan9
}
func lockPlan9(name string) (io.Closer, error) {
fi, err := os.Stat(name)
if err == nil && fi.Size() > 0 {
return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name)
}
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, os.ModeExclusive|0644)
if err != nil {
return nil, fmt.Errorf("Lock Create of %s failed: %v", name, err)
}
return &unlocker{f: f, abs: name}, nil
}

@ -1,26 +0,0 @@
// +build !appengine
// +build linux darwin freebsd openbsd netbsd dragonfly
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lock
import "syscall"
func init() {
signalZero = syscall.Signal(0)
}

222
vendor/go4.org/lock/lock_test.go generated vendored

@ -1,222 +0,0 @@
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lock
import (
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"testing"
)
func TestLock(t *testing.T) {
testLock(t, false)
}
func TestLockPortable(t *testing.T) {
testLock(t, true)
}
func TestLockInChild(t *testing.T) {
f := os.Getenv("TEST_LOCK_FILE")
if f == "" {
// not child
return
}
lock := Lock
if v, _ := strconv.ParseBool(os.Getenv("TEST_LOCK_PORTABLE")); v {
lock = lockPortable
}
var lk io.Closer
for scan := bufio.NewScanner(os.Stdin); scan.Scan(); {
var err error
switch scan.Text() {
case "lock":
lk, err = lock(f)
case "unlock":
err = lk.Close()
lk = nil
case "exit":
// Simulate a crash, or at least not unlocking the lock.
os.Exit(0)
default:
err = fmt.Errorf("unexpected child command %q", scan.Text())
}
if err != nil {
fmt.Println(err)
} else {
fmt.Println("")
}
}
}
func testLock(t *testing.T, portable bool) {
lock := Lock
if portable {
lock = lockPortable
}
t.Logf("test lock, portable %v", portable)
td, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(td)
path := filepath.Join(td, "foo.lock")
proc := newChildProc(t, path, portable)
defer proc.kill()
t.Logf("First lock in child")
if err := proc.do("lock"); err != nil {
t.Fatalf("first lock in child process: %v", err)
}
t.Logf("Crash child")
if err := proc.do("exit"); err != nil {
t.Fatalf("crash in child process: %v", err)
}
proc = newChildProc(t, path, portable)
defer proc.kill()
t.Logf("Locking+unlocking in child...")
if err := proc.do("lock"); err != nil {
t.Fatalf("lock in child process after crashing child: %v", err)
}
if err := proc.do("unlock"); err != nil {
t.Fatalf("lock in child process after crashing child: %v", err)
}
t.Logf("Locking in parent...")
lk1, err := lock(path)
if err != nil {
t.Fatal(err)
}
t.Logf("Again in parent...")
_, err = lock(path)
if err == nil {
t.Fatal("expected second lock to fail")
}
t.Logf("Locking in child...")
if err := proc.do("lock"); err == nil {
t.Fatalf("expected lock in child process to fail")
}
t.Logf("Unlocking lock in parent")
if err := lk1.Close(); err != nil {
t.Fatal(err)
}
t.Logf("Trying lock again in child...")
if err := proc.do("lock"); err != nil {
t.Fatal(err)
}
if err := proc.do("unlock"); err != nil {
t.Fatal(err)
}
lk3, err := lock(path)
if err != nil {
t.Fatal(err)
}
lk3.Close()
}
type childLockCmd struct {
op string
reply chan<- error
}
type childProc struct {
proc *os.Process
c chan childLockCmd
}
func (c *childProc) kill() {
c.proc.Kill()
}
func (c *childProc) do(op string) error {
reply := make(chan error)
c.c <- childLockCmd{
op: op,
reply: reply,
}
return <-reply
}
func newChildProc(t *testing.T, path string, portable bool) *childProc {
cmd := exec.Command(os.Args[0], "-test.run=LockInChild$")
cmd.Env = []string{"TEST_LOCK_FILE=" + path}
toChild, err := cmd.StdinPipe()
if err != nil {
t.Fatalf("cannot make pipe: %v", err)
}
fromChild, err := cmd.StdoutPipe()
if err != nil {
t.Fatalf("cannot make pipe: %v", err)
}
cmd.Stderr = os.Stderr
if portable {
cmd.Env = append(cmd.Env, "TEST_LOCK_PORTABLE=1")
}
if err := cmd.Start(); err != nil {
t.Fatalf("cannot start child: %v", err)
}
cmdChan := make(chan childLockCmd)
go func() {
defer fromChild.Close()
defer toChild.Close()
inScan := bufio.NewScanner(fromChild)
for c := range cmdChan {
fmt.Fprintln(toChild, c.op)
ok := inScan.Scan()
if c.op == "exit" {
if ok {
c.reply <- errors.New("child did not exit")
} else {
cmd.Wait()
c.reply <- nil
}
break
}
if !ok {
panic("child exited early")
}
if errText := inScan.Text(); errText != "" {
c.reply <- errors.New(errText)
} else {
c.reply <- nil
}
}
}()
return &childProc{
c: cmdChan,
proc: cmd.Process,
}
}

58
vendor/go4.org/lock/lock_unix.go generated vendored

@ -1,58 +0,0 @@
// +build linux darwin freebsd openbsd netbsd dragonfly solaris
// +build !appengine
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lock
import (
"fmt"
"io"
"os"
"golang.org/x/sys/unix"
)
func init() {
lockFn = lockFcntl
}
func lockFcntl(name string) (io.Closer, error) {
fi, err := os.Stat(name)
if err == nil && fi.Size() > 0 {
return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name)
}
f, err := os.Create(name)
if err != nil {
return nil, fmt.Errorf("Lock Create of %s failed: %v", name, err)
}
err = unix.FcntlFlock(f.Fd(), unix.F_SETLK, &unix.Flock_t{
Type: unix.F_WRLCK,
Whence: int16(os.SEEK_SET),
Start: 0,
Len: 0, // 0 means to lock the entire file.
Pid: 0, // only used by F_GETLK
})
if err != nil {
f.Close()
return nil, fmt.Errorf("Lock FcntlFlock of %s failed: %v", name, err)
}
return &unlocker{f: f, abs: name}, nil
}

@ -1,78 +0,0 @@
/*
Copyright 2013 The Go Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lock
import (
"fmt"
"io"
"os"
"sync"
"golang.org/x/sys/windows"
)
func init() {
lockFn = lockWindows
}
type winUnlocker struct {
h windows.Handle
abs string
// err holds the error returned by Close.
err error
// once guards the close method call.
once sync.Once
}
func (u *winUnlocker) Close() error {
u.once.Do(u.close)
return u.err
}
func (u *winUnlocker) close() {
lockmu.Lock()
defer lockmu.Unlock()
delete(locked, u.abs)
u.err = windows.CloseHandle(u.h)
}
func lockWindows(name string) (io.Closer, error) {
fi, err := os.Stat(name)
if err == nil && fi.Size() > 0 {
return nil, fmt.Errorf("can't lock file %q: %s", name, "has non-zero size")
}
handle, err := winCreateEphemeral(name)
if err != nil {
return nil, fmt.Errorf("creation of lock %s failed: %v", name, err)
}
return &winUnlocker{h: handle, abs: name}, nil
}
func winCreateEphemeral(name string) (windows.Handle, error) {
const (
FILE_ATTRIBUTE_TEMPORARY = 0x100
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
)
handle, err := windows.CreateFile(windows.StringToUTF16Ptr(name), 0, 0, nil, windows.OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, 0)
if err != nil {
return 0, err
}
return handle, nil
}

@ -1,833 +0,0 @@
/*
Copyright 2018 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package bmff reads ISO BMFF boxes, as used by HEIF, etc.
//
// This is not so much as a generic BMFF reader as it is a BMFF reader
// as needed by HEIF, though that may change in time. For now, only
// boxes necessary for the go4.org/media/heif package have explicit
// parsers.
//
// This package makes no API compatibility promises; it exists
// primarily for use by the go4.org/media/heif package.
package bmff
import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
)
func NewReader(r io.Reader) *Reader {
br, ok := r.(*bufio.Reader)
if !ok {
br = bufio.NewReader(r)
}
return &Reader{br: bufReader{Reader: br}}
}
type Reader struct {
br bufReader
lastBox Box // or nil
noMoreBoxes bool // a box with size 0 (the final box) was seen
}
type BoxType [4]byte
// Common box types.
var (
TypeFtyp = BoxType{'f', 't', 'y', 'p'}
TypeMeta = BoxType{'m', 'e', 't', 'a'}
)
func (t BoxType) String() string { return string(t[:]) }
func (t BoxType) EqualString(s string) bool {
// Could be cleaner, but see ohttps://github.com/golang/go/issues/24765
return len(s) == 4 && s[0] == t[0] && s[1] == t[1] && s[2] == t[2] && s[3] == t[3]
}
type parseFunc func(b box, br *bufio.Reader) (Box, error)
// Box represents a BMFF box.
type Box interface {
Size() int64 // 0 means unknown (will read to end of file)
Type() BoxType
// Parses parses the box, populating the fields
// in the returned concrete type.
//
// If Parse has already been called, Parse returns nil.
// If the box type is unknown, the returned error is ErrUnknownBox
// and it's guaranteed that no bytes have been read from the box.
Parse() (Box, error)
// Body returns the inner bytes of the box, ignoring the header.
// The body may start with the 4 byte header of a "Full Box" if the
// box's type derives from a full box. Most users will use Parse
// instead.
// Body will return a new reader at the beginning of the box if the
// outer box has already been parsed.
Body() io.Reader
}
// ErrUnknownBox is returned by Box.Parse for unrecognized box types.
var ErrUnknownBox = errors.New("heif: unknown box")
type parserFunc func(b *box, br *bufReader) (Box, error)
func boxType(s string) BoxType {
if len(s) != 4 {
panic("bogus boxType length")
}
return BoxType{s[0], s[1], s[2], s[3]}
}
var parsers = map[BoxType]parserFunc{
boxType("dinf"): parseDataInformationBox,
boxType("dref"): parseDataReferenceBox,
boxType("ftyp"): parseFileTypeBox,
boxType("hdlr"): parseHandlerBox,
boxType("iinf"): parseItemInfoBox,
boxType("infe"): parseItemInfoEntry,
boxType("iloc"): parseItemLocationBox,
boxType("ipco"): parseItemPropertyContainerBox,
boxType("ipma"): parseItemPropertyAssociation,
boxType("iprp"): parseItemPropertiesBox,
boxType("irot"): parseImageRotation,
boxType("ispe"): parseImageSpatialExtentsProperty,
boxType("meta"): parseMetaBox,
boxType("pitm"): parsePrimaryItemBox,
}
type box struct {
size int64 // 0 means unknown, will read to end of file (box container)
boxType BoxType
body io.Reader
parsed Box // if non-nil, the Parsed result
slurp []byte // if non-nil, the contents slurped to memory
}
func (b *box) Size() int64 { return b.size }
func (b *box) Type() BoxType { return b.boxType }
func (b *box) Body() io.Reader {
if b.slurp != nil {
return bytes.NewReader(b.slurp)
}
return b.body
}
func (b *box) Parse() (Box, error) {
if b.parsed != nil {
return b.parsed, nil
}
parser, ok := parsers[b.Type()]
if !ok {
return nil, ErrUnknownBox
}
v, err := parser(b, &bufReader{Reader: bufio.NewReader(b.Body())})
if err != nil {
return nil, err
}
b.parsed = v
return v, nil
}
type FullBox struct {
*box
Version uint8
Flags uint32 // 24 bits
}
// ReadBox reads the next box.
//
// If the previously read box was not read to completion, ReadBox consumes
// the rest of its data.
//
// At the end, the error is io.EOF.
func (r *Reader) ReadBox() (Box, error) {
if r.noMoreBoxes {
return nil, io.EOF
}
if r.lastBox != nil {
if _, err := io.Copy(ioutil.Discard, r.lastBox.Body()); err != nil {
return nil, err
}
}
var buf [8]byte
_, err := io.ReadFull(r.br, buf[:4])
if err != nil {
return nil, err
}
box := &box{
size: int64(binary.BigEndian.Uint32(buf[:4])),
}
_, err = io.ReadFull(r.br, box.boxType[:]) // 4 more bytes
if err != nil {
return nil, err
}
// Special cases for size:
var remain int64
switch box.size {
case 1:
// 1 means it's actually a 64-bit size, after the type.
_, err = io.ReadFull(r.br, buf[:8])
if err != nil {
return nil, err
}
box.size = int64(binary.BigEndian.Uint64(buf[:8]))
if box.size < 0 {
// Go uses int64 for sizes typically, but BMFF uses uint64.
// We assume for now that nobody actually uses boxes larger
// than int64.
return nil, fmt.Errorf("unexpectedly large box %q", box.boxType)
}
remain = box.size - 2*4 - 8
case 0:
// 0 means unknown & to read to end of file. No more boxes.
r.noMoreBoxes = true
default:
remain = box.size - 2*4
}
if remain < 0 {
return nil, fmt.Errorf("Box header for %q has size %d, suggesting %d (negative) bytes remain", box.boxType, box.size, remain)
}
if box.size > 0 {
box.body = io.LimitReader(r.br, remain)
} else {
box.body = r.br
}
r.lastBox = box
return box, nil
}
// ReadAndParseBox wraps the ReadBox method, ensuring that the read box is of type typ
// and parses successfully. It returns the parsed box.
func (r *Reader) ReadAndParseBox(typ BoxType) (Box, error) {
box, err := r.ReadBox()
if err != nil {
return nil, fmt.Errorf("error reading %q box: %v", typ, err)
}
if box.Type() != typ {
return nil, fmt.Errorf("error reading %q box: got box type %q instead", typ, box.Type())
}
pbox, err := box.Parse()
if err != nil {
return nil, fmt.Errorf("error parsing read %q box: %v", typ, err)
}
return pbox, nil
}
func readFullBox(outer *box, br *bufReader) (fb FullBox, err error) {
fb.box = outer
// Parse FullBox header.
buf, err := br.Peek(4)
if err != nil {
return FullBox{}, fmt.Errorf("failed to read 4 bytes of FullBox: %v", err)
}
fb.Version = buf[0]
buf[0] = 0
fb.Flags = binary.BigEndian.Uint32(buf[:4])
br.Discard(4)
return fb, nil
}
type FileTypeBox struct {
*box
MajorBrand string // 4 bytes
MinorVersion string // 4 bytes
Compatible []string // all 4 bytes
}
func parseFileTypeBox(outer *box, br *bufReader) (Box, error) {
buf, err := br.Peek(8)
if err != nil {
return nil, err
}
ft := &FileTypeBox{
box: outer,
MajorBrand: string(buf[:4]),
MinorVersion: string(buf[4:8]),
}
br.Discard(8)
for {
buf, err := br.Peek(4)
if err == io.EOF {
return ft, nil
}
if err != nil {
return nil, err
}
ft.Compatible = append(ft.Compatible, string(buf[:4]))
br.Discard(4)
}
}
type MetaBox struct {
FullBox
Children []Box
}
func parseMetaBox(outer *box, br *bufReader) (Box, error) {
fb, err := readFullBox(outer, br)
if err != nil {
return nil, err
}
mb := &MetaBox{FullBox: fb}
return mb, br.parseAppendBoxes(&mb.Children)
}
func (br *bufReader) parseAppendBoxes(dst *[]Box) error {
if br.err != nil {
return br.err
}
boxr := NewReader(br.Reader)
for {
inner, err := boxr.ReadBox()
if err == io.EOF {
return nil
}
if err != nil {
br.err = err
return err
}
slurp, err := ioutil.ReadAll(inner.Body())
if err != nil {
br.err = err
return err
}
inner.(*box).slurp = slurp
*dst = append(*dst, inner)
}
}
// ItemInfoEntry represents an "infe" box.
//
// TODO: currently only parses Version 2 boxes.
type ItemInfoEntry struct {
FullBox
ItemID uint16
ProtectionIndex uint16
ItemType string // always 4 bytes
Name string
// If Type == "mime":
ContentType string
ContentEncoding string
// If Type == "uri ":
ItemURIType string
}
func parseItemInfoEntry(outer *box, br *bufReader) (Box, error) {
fb, err := readFullBox(outer, br)
if err != nil {
return nil, err
}
ie := &ItemInfoEntry{FullBox: fb}
if fb.Version != 2 {
return nil, fmt.Errorf("TODO: found version %d infe box. Only 2 is supported now.", fb.Version)
}
ie.ItemID, _ = br.readUint16()
ie.ProtectionIndex, _ = br.readUint16()
if !br.ok() {
return nil, br.err
}
buf, err := br.Peek(4)
if err != nil {
return nil, err
}
ie.ItemType = string(buf[:4])
ie.Name, _ = br.readString()
switch ie.ItemType {
case "mime":
ie.ContentType, _ = br.readString()
if br.anyRemain() {
ie.ContentEncoding, _ = br.readString()
}
case "uri ":
ie.ItemURIType, _ = br.readString()
}
if !br.ok() {
return nil, br.err
}
return ie, nil
}
// ItemInfoBox represents an "iinf" box.
type ItemInfoBox struct {
FullBox
Count uint16
ItemInfos []*ItemInfoEntry
}
func parseItemInfoBox(outer *box, br *bufReader) (Box, error) {
fb, err := readFullBox(outer, br)
if err != nil {
return nil, err
}
ib := &ItemInfoBox{FullBox: fb}
ib.Count, _ = br.readUint16()
var itemInfos []Box
br.parseAppendBoxes(&itemInfos)
if br.ok() {
for _, box := range itemInfos {
pb, err := box.Parse()
if err != nil {
return nil, fmt.Errorf("error parsing ItemInfoEntry in ItemInfoBox: %v", err)
}
if iie, ok := pb.(*ItemInfoEntry); ok {
ib.ItemInfos = append(ib.ItemInfos, iie)
}
}
}
if !br.ok() {
return FullBox{}, br.err
}
return ib, nil
}
// bufReader adds some HEIF/BMFF-specific methods around a *bufio.Reader.
type bufReader struct {
*bufio.Reader
err error // sticky error
}
// ok reports whether all previous reads have been error-free.
func (br *bufReader) ok() bool { return br.err == nil }
func (br *bufReader) anyRemain() bool {
if br.err != nil {
return false
}
_, err := br.Peek(1)
return err == nil
}
func (br *bufReader) readUintN(bits uint8) (uint64, error) {
if br.err != nil {
return 0, br.err
}
if bits == 0 {
return 0, nil
}
nbyte := bits / 8
buf, err := br.Peek(int(nbyte))
if err != nil {
br.err = err
return 0, err
}
defer br.Discard(int(nbyte))
switch bits {
case 8:
return uint64(buf[0]), nil
case 16:
return uint64(binary.BigEndian.Uint16(buf[:2])), nil
case 32:
return uint64(binary.BigEndian.Uint32(buf[:4])), nil
case 64:
return binary.BigEndian.Uint64(buf[:8]), nil
default:
br.err = fmt.Errorf("invalid uintn read size")
return 0, br.err
}
}
func (br *bufReader) readUint8() (uint8, error) {
if br.err != nil {
return 0, br.err
}
v, err := br.ReadByte()
if err != nil {
br.err = err
return 0, err
}
return v, nil
}
func (br *bufReader) readUint16() (uint16, error) {
if br.err != nil {
return 0, br.err
}
buf, err := br.Peek(2)
if err != nil {
br.err = err
return 0, err
}
v := binary.BigEndian.Uint16(buf[:2])
br.Discard(2)
return v, nil
}
func (br *bufReader) readUint32() (uint32, error) {
if br.err != nil {
return 0, br.err
}
buf, err := br.Peek(4)
if err != nil {
br.err = err
return 0, err
}
v := binary.BigEndian.Uint32(buf[:4])
br.Discard(4)
return v, nil
}
func (br *bufReader) readString() (string, error) {
if br.err != nil {
return "", br.err
}
s0, err := br.ReadString(0)
if err != nil {
br.err = err
return "", err
}
s := strings.TrimSuffix(s0, "\x00")
if len(s) == len(s0) {
err = fmt.Errorf("unexpected non-null terminated string")
br.err = err
return "", err
}
return s, nil
}
// HEIF: ipco
type ItemPropertyContainerBox struct {
*box
Properties []Box // of ItemProperty or ItemFullProperty
}
func parseItemPropertyContainerBox(outer *box, br *bufReader) (Box, error) {
ipc := &ItemPropertyContainerBox{box: outer}
return ipc, br.parseAppendBoxes(&ipc.Properties)
}
// HEIF: iprp
type ItemPropertiesBox struct {
*box
PropertyContainer *ItemPropertyContainerBox
Associations []*ItemPropertyAssociation // at least 1
}
func parseItemPropertiesBox(outer *box, br *bufReader) (Box, error) {
ip := &ItemPropertiesBox{
box: outer,
}
var boxes []Box
err := br.parseAppendBoxes(&boxes)
if err != nil {
return nil, err
}
if len(boxes) < 2 {
return nil, fmt.Errorf("expect at least 2 boxes in children; got 0")
}
cb, err := boxes[0].Parse()
if err != nil {
return nil, fmt.Errorf("failed to parse first box, %q: %v", boxes[0].Type(), err)
}
var ok bool
ip.PropertyContainer, ok = cb.(*ItemPropertyContainerBox)
if !ok {
return nil, fmt.Errorf("unexpected type %T for ItemPropertieBox.PropertyContainer", cb)
}
// Association boxes
ip.Associations = make([]*ItemPropertyAssociation, 0, len(boxes)-1)
for _, box := range boxes[1:] {
boxp, err := box.Parse()
if err != nil {
return nil, fmt.Errorf("failed to parse association box: %v", err)
}
ipa, ok := boxp.(*ItemPropertyAssociation)
if !ok {
return nil, fmt.Errorf("unexpected box %q instead of ItemPropertyAssociation", boxp.Type())
}
ip.Associations = append(ip.Associations, ipa)
}
return ip, nil
}
type ItemPropertyAssociation struct {
FullBox
EntryCount uint32
Entries []ItemPropertyAssociationItem
}
// not a box
type ItemProperty struct {
Essential bool
Index uint16
}
// not a box
type ItemPropertyAssociationItem struct {
ItemID uint32
AssociationsCount int // as declared
Associations []ItemProperty // as parsed
}
func parseItemPropertyAssociation(outer *box, br *bufReader) (Box, error) {
fb, err := readFullBox(outer, br)
if err != nil {
return nil, err
}
ipa := &ItemPropertyAssociation{FullBox: fb}
count, _ := br.readUint32()
ipa.EntryCount = count
for i := uint64(0); i < uint64(count) && br.ok(); i++ {
var itemID uint32
if fb.Version < 1 {
itemID16, _ := br.readUint16()
itemID = uint32(itemID16)
} else {
itemID, _ = br.readUint32()
}
assocCount, _ := br.readUint8()
ipai := ItemPropertyAssociationItem{
ItemID: itemID,
AssociationsCount: int(assocCount),
}
for j := 0; j < int(assocCount) && br.ok(); j++ {
first, _ := br.readUint8()
essential := first&(1<<7) != 0
first &^= byte(1 << 7)
var index uint16
if fb.Flags&1 != 0 {
second, _ := br.readUint8()
index = uint16(first)<<8 | uint16(second)
} else {
index = uint16(first)
}
ipai.Associations = append(ipai.Associations, ItemProperty{
Essential: essential,
Index: index,
})
}
ipa.Entries = append(ipa.Entries, ipai)
}
if !br.ok() {
return nil, br.err
}
return ipa, nil
}
type ImageSpatialExtentsProperty struct {
FullBox
ImageWidth uint32
ImageHeight uint32
}
func parseImageSpatialExtentsProperty(outer *box, br *bufReader) (Box, error) {
fb, err := readFullBox(outer, br)
if err != nil {
return nil, err
}
w, err := br.readUint32()
if err != nil {
return nil, err
}
h, err := br.readUint32()
if err != nil {
return nil, err
}
return &ImageSpatialExtentsProperty{
FullBox: fb,
ImageWidth: w,
ImageHeight: h,
}, nil
}
type OffsetLength struct {
Offset, Length uint64
}
// not a box
type ItemLocationBoxEntry struct {
ItemID uint16
ConstructionMethod uint8 // actually uint4
DataReferenceIndex uint16
BaseOffset uint64 // uint32 or uint64, depending on encoding
ExtentCount uint16
Extents []OffsetLength
}
// box "iloc"
type ItemLocationBox struct {
FullBox
offsetSize, lengthSize, baseOffsetSize, indexSize uint8 // actually uint4
ItemCount uint16
Items []ItemLocationBoxEntry
}
func parseItemLocationBox(outer *box, br *bufReader) (Box, error) {
fb, err := readFullBox(outer, br)
if err != nil {
return nil, err
}
ilb := &ItemLocationBox{
FullBox: fb,
}
buf, err := br.Peek(4)
if err != nil {
return nil, err
}
ilb.offsetSize = buf[0] >> 4
ilb.lengthSize = buf[0] & 15
ilb.baseOffsetSize = buf[1] >> 4
if fb.Version > 0 { // version 1
ilb.indexSize = buf[1] & 15
}
ilb.ItemCount = binary.BigEndian.Uint16(buf[2:4])
br.Discard(4)
for i := 0; br.ok() && i < int(ilb.ItemCount); i++ {
var ent ItemLocationBoxEntry
ent.ItemID, _ = br.readUint16()
if fb.Version > 0 { // version 1
cmeth, _ := br.readUint16()
ent.ConstructionMethod = byte(cmeth & 15)
}
ent.DataReferenceIndex, _ = br.readUint16()
if br.ok() && ilb.baseOffsetSize > 0 {
br.Discard(int(ilb.baseOffsetSize) / 8)
}
ent.ExtentCount, _ = br.readUint16()
for j := 0; br.ok() && j < int(ent.ExtentCount); j++ {
var ol OffsetLength
ol.Offset, _ = br.readUintN(ilb.offsetSize * 8)
ol.Length, _ = br.readUintN(ilb.lengthSize * 8)
if br.err != nil {
return nil, br.err
}
ent.Extents = append(ent.Extents, ol)
}
ilb.Items = append(ilb.Items, ent)
}
if !br.ok() {
return nil, br.err
}
return ilb, nil
}
// a "hdlr" box.
type HandlerBox struct {
FullBox
HandlerType string // always 4 bytes; usually "pict" for iOS Camera images
Name string
}
func parseHandlerBox(gen *box, br *bufReader) (Box, error) {
fb, err := readFullBox(gen, br)
if err != nil {
return nil, err
}
hb := &HandlerBox{
FullBox: fb,
}
buf, err := br.Peek(20)
if err != nil {
return nil, err
}
hb.HandlerType = string(buf[4:8])
br.Discard(20)
hb.Name, _ = br.readString()
return hb, br.err
}
// a "dinf" box
type DataInformationBox struct {
*box
Children []Box
}
func parseDataInformationBox(gen *box, br *bufReader) (Box, error) {
dib := &DataInformationBox{box: gen}
return dib, br.parseAppendBoxes(&dib.Children)
}
// a "dref" box.
type DataReferenceBox struct {
FullBox
EntryCount uint32
Children []Box
}
func parseDataReferenceBox(gen *box, br *bufReader) (Box, error) {
fb, err := readFullBox(gen, br)
if err != nil {
return nil, err
}
drb := &DataReferenceBox{FullBox: fb}
drb.EntryCount, _ = br.readUint32()
return drb, br.parseAppendBoxes(&drb.Children)
}
// "pitm" box
type PrimaryItemBox struct {
FullBox
ItemID uint16
}
func parsePrimaryItemBox(gen *box, br *bufReader) (Box, error) {
fb, err := readFullBox(gen, br)
if err != nil {
return nil, err
}
pib := &PrimaryItemBox{FullBox: fb}
pib.ItemID, _ = br.readUint16()
if !br.ok() {
return nil, br.err
}
return pib, nil
}
// ImageRotation is a HEIF "irot" rotation property.
type ImageRotation struct {
*box
Angle uint8 // 1 means 90 degrees counter-clockwise, 2 means 180 counter-clockwise
}
func parseImageRotation(gen *box, br *bufReader) (Box, error) {
v, err := br.readUint8()
if err != nil {
return nil, err
}
return &ImageRotation{box: gen, Angle: v & 3}, nil
}

@ -1,200 +0,0 @@
/*
Copyright 2018 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// The dumpheif program dumps the structure and metadata of a HEIF file.
//
// It exists purely for debugging the go4.org/media/heif and
// go4.org/media/heif/bmff packages; it makes no backwards
// compatibility promises.
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
"github.com/rwcarlsen/goexif/exif"
"github.com/rwcarlsen/goexif/tiff"
"go4.org/media/heif"
"go4.org/media/heif/bmff"
)
var (
exifItemID uint16
exifLoc bmff.ItemLocationBoxEntry
)
func main() {
flag.Parse()
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "usage: dumpheif <file>\n")
os.Exit(1)
}
f, err := os.Open(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
defer f.Close()
hf := heif.Open(f)
it, err := hf.PrimaryItem()
if err != nil {
log.Fatalf("PrimaryItem: %v", err)
}
fmt.Printf("primary item: %v\n", it.ID)
width, height, ok := it.SpatialExtents()
if ok {
fmt.Printf("spatial extents: %d x %d\n", width, height)
}
fmt.Printf("properties:\n")
for _, prop := range it.Properties {
fmt.Printf("\t%q: %#v\n", prop.Type(), prop)
}
if len(it.Properties) == 0 {
fmt.Printf("\t(no properties)\n")
}
if ex, err := hf.EXIF(); err == nil {
fmt.Printf("EXIF dump:\n")
ex, err := exif.Decode(bytes.NewReader(ex))
if err != nil {
log.Fatalf("EXIF decode: %v", err)
}
ex.Walk(exifWalkFunc(func(name exif.FieldName, tag *tiff.Tag) error {
fmt.Printf("\t%v = %v\n", name, tag)
return nil
}))
fmt.Printf("\n")
}
fmt.Printf("BMFF boxes:\n")
r := bmff.NewReader(f)
for {
box, err := r.ReadBox()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("ReadBox: %v", err)
}
dumpBox(box, 0)
}
}
type exifWalkFunc func(exif.FieldName, *tiff.Tag) error
func (f exifWalkFunc) Walk(name exif.FieldName, tag *tiff.Tag) error {
return f(name, tag)
}
func dumpBox(box bmff.Box, depth int) {
indent := strings.Repeat(" ", depth)
fmt.Printf("%sBox: type %q, size %v\n", indent, box.Type(), box.Size())
box2, err := box.Parse()
if err == bmff.ErrUnknownBox {
slurp, err := ioutil.ReadAll(box.Body())
if err != nil {
log.Fatalf("%sreading body: %v", indent, err)
}
if len(slurp) < 5000 {
fmt.Printf("%s- contents: %q\n", indent, slurp)
} else {
fmt.Printf("%s- contents: (... %d bytes, starting with %q ...)\n", indent, len(slurp), slurp[:100])
}
return
}
if err != nil {
slurp, _ := ioutil.ReadAll(box.Body())
log.Fatalf("Parse box type %q: %v; slurp: %q", box.Type(), err, slurp)
}
switch v := box2.(type) {
case *bmff.FileTypeBox, *bmff.HandlerBox, *bmff.PrimaryItemBox:
fmt.Printf("%s- %T: %+v\n", indent, v, v)
case *bmff.MetaBox:
fmt.Printf("%s- %T, %d children:\n", indent, v, len(v.Children))
for _, child := range v.Children {
dumpBox(child, depth+1)
}
case *bmff.ItemInfoBox:
//slurp, _ := ioutil.ReadAll(box.Body())
//fmt.Printf("%s- %T raw: %q\n", indent, v, slurp)
fmt.Printf("%s- %T, %d children (%d in slice):\n", indent, v, v.Count, len(v.ItemInfos))
for _, child := range v.ItemInfos {
dumpBox(child, depth+1)
}
case *bmff.ItemInfoEntry:
fmt.Printf("%s- %T, %+v\n", indent, v, v)
if v.ItemType == "Exif" {
exifItemID = v.ItemID
}
case *bmff.ItemPropertiesBox:
fmt.Printf("%s- %T\n", indent, v)
if v.PropertyContainer != nil {
dumpBox(v.PropertyContainer, depth+1)
}
for _, child := range v.Associations {
dumpBox(child, depth+1)
}
case *bmff.ItemPropertyAssociation:
fmt.Printf("%s- %T: %d declared entries, %d parsed:\n", indent, v, v.EntryCount, len(v.Entries))
for _, ai := range v.Entries {
fmt.Printf("%s for Item ID %d, %d associations declared, %d parsed:\n", indent, ai.ItemID, ai.AssociationsCount, len(ai.Associations))
for _, ass := range ai.Associations {
fmt.Printf("%s index: %d, essential: %v\n", indent, ass.Index, ass.Essential)
}
}
case *bmff.DataInformationBox:
fmt.Printf("%s- %T\n", indent, v)
for _, child := range v.Children {
dumpBox(child, depth+1)
}
case *bmff.DataReferenceBox:
fmt.Printf("%s- %T\n", indent, v)
for _, child := range v.Children {
dumpBox(child, depth+1)
}
case *bmff.ItemPropertyContainerBox:
fmt.Printf("%s- %T\n", indent, v)
for _, child := range v.Properties {
dumpBox(child, depth+1)
}
case *bmff.ItemLocationBox:
fmt.Printf("%s- %T: %d items declared, %d parsed:\n", indent, v, v.ItemCount, len(v.Items))
for _, lbe := range v.Items {
fmt.Printf("%s %+v\n", indent, lbe)
if exifItemID != 0 && lbe.ItemID == exifItemID {
exifLoc = lbe
}
}
case *bmff.ImageSpatialExtentsProperty:
fmt.Printf("%s- %T dimensions: %d x %d\n", indent, v, v.ImageWidth, v.ImageHeight)
default:
fmt.Printf("%s- gotype: %T\n", indent, box2)
}
}

292
vendor/go4.org/media/heif/heif.go generated vendored

@ -1,292 +0,0 @@
/*
Copyright 2018 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package heif reads HEIF containers, as found in Apple HEIC/HEVC images.
// This package does not decode images; it only reads the metadata.
//
// This package is a work in progress and makes no API compatibility
// promises.
package heif
import (
"errors"
"fmt"
"io"
"log"
"go4.org/media/heif/bmff"
)
// File represents a HEIF file.
//
// Methods on File should not be called concurrently.
type File struct {
ra io.ReaderAt
primary *Item
// Populated lazily, by getMeta:
metaErr error
meta *BoxMeta
}
// BoxMeta contains the low-level BMFF metadata boxes.
type BoxMeta struct {
FileType *bmff.FileTypeBox
Handler *bmff.HandlerBox
PrimaryItem *bmff.PrimaryItemBox
ItemInfo *bmff.ItemInfoBox
Properties *bmff.ItemPropertiesBox
ItemLocation *bmff.ItemLocationBox
}
// EXIFItemID returns the item ID of the EXIF part, or 0 if not found.
func (m *BoxMeta) EXIFItemID() uint32 {
if m.ItemInfo == nil {
return 0
}
for _, ife := range m.ItemInfo.ItemInfos {
if ife.ItemType == "Exif" {
return uint32(ife.ItemID)
}
}
return 0
}
// Item represents an item in a HEIF file.
type Item struct {
f *File
ID uint32
Info *bmff.ItemInfoEntry
Location *bmff.ItemLocationBoxEntry // location in file
Properties []bmff.Box
}
// SpatialExtents returns the item's spatial extents property values, if present,
// not correcting from any camera rotation metadata.
func (it *Item) SpatialExtents() (width, height int, ok bool) {
for _, p := range it.Properties {
if p, ok := p.(*bmff.ImageSpatialExtentsProperty); ok {
return int(p.ImageWidth), int(p.ImageHeight), true
}
}
return
}
// Rotations returns the number of 90 degree rotations counter-clockwise that this
// image should be rendered at, in the range [0,3].
func (it *Item) Rotations() int {
for _, p := range it.Properties {
if p, ok := p.(*bmff.ImageRotation); ok {
return int(p.Angle)
}
}
return 0
}
// VisualDimensions returns the item's width and height after correcting
// for any rotations.
func (it *Item) VisualDimensions() (width, height int, ok bool) {
width, height, ok = it.SpatialExtents()
for i := 0; i < it.Rotations(); i++ {
width, height = height, width
}
return
}
// TODO: add HEIF imir (mirroring) accessor, like Image.SpatialExtents.
// Open returns a handle to access a HEIF file.
func Open(f io.ReaderAt) *File {
return &File{ra: f}
}
// ErrNoEXIF is returned by File.EXIF when a file does not contain an EXIF item.
var ErrNoEXIF = errors.New("heif: no EXIF found")
// ErrUnknownItem is returned by File.ItemByID for unknown items.
var ErrUnknownItem = errors.New("heif: unknown item")
// EXIF returns the raw EXIF data from the file.
// The error is ErrNoEXIF if the file did not contain EXIF.
//
// The raw EXIF data can be parsed by the
// github.com/rwcarlsen/goexif/exif package's Decode function.
func (f *File) EXIF() ([]byte, error) {
meta, err := f.getMeta()
if err != nil {
return nil, err
}
exifID := meta.EXIFItemID()
if exifID == 0 {
return nil, ErrNoEXIF
}
it, err := f.ItemByID(exifID)
if err != nil {
return nil, err
}
if it.Location == nil {
return nil, errors.New("heif: file said it contained EXIF, but didn't say where")
}
if n := len(it.Location.Extents); n != 1 {
return nil, fmt.Errorf("heif: expected 1 EXIF section, saw %d", n)
}
offLen := it.Location.Extents[0]
const maxSize = 20 << 10 // 20MB of EXIF seems excessive; cap it for sanity
if offLen.Length > maxSize {
return nil, fmt.Errorf("heif: declared EXIF size %d exceeds threshold of %d bytes", offLen.Length, maxSize)
}
buf := make([]byte, offLen.Length-4)
n, err := f.ra.ReadAt(buf, int64(offLen.Offset)+4) // TODO: why 4? did I miss something?
if err != nil {
log.Printf("Read %d bytes + %v: %q", n, err, buf)
return nil, err
}
return buf, nil
}
func (f *File) setMetaErr(err error) error {
if f.metaErr != nil {
f.metaErr = err
}
return err
}
func (f *File) getMeta() (*BoxMeta, error) {
if f.metaErr != nil {
return nil, f.metaErr
}
if f.meta != nil {
return f.meta, nil
}
const assumedMaxSize = 5 << 40 // arbitrary
sr := io.NewSectionReader(f.ra, 0, assumedMaxSize)
bmr := bmff.NewReader(sr)
meta := &BoxMeta{}
pbox, err := bmr.ReadAndParseBox(bmff.TypeFtyp)
if err != nil {
return nil, f.setMetaErr(err)
}
meta.FileType = pbox.(*bmff.FileTypeBox)
pbox, err = bmr.ReadAndParseBox(bmff.TypeMeta)
if err != nil {
return nil, f.setMetaErr(err)
}
metabox := pbox.(*bmff.MetaBox)
for _, box := range metabox.Children {
boxp, err := box.Parse()
if err == bmff.ErrUnknownBox {
continue
}
if err != nil {
return nil, f.setMetaErr(err)
}
switch v := boxp.(type) {
case *bmff.HandlerBox:
meta.Handler = v
case *bmff.PrimaryItemBox:
meta.PrimaryItem = v
case *bmff.ItemInfoBox:
meta.ItemInfo = v
case *bmff.ItemPropertiesBox:
meta.Properties = v
case *bmff.ItemLocationBox:
meta.ItemLocation = v
}
}
f.meta = meta
return f.meta, nil
}
// PrimaryItem returns the HEIF file's primary item.
func (f *File) PrimaryItem() (*Item, error) {
meta, err := f.getMeta()
if err != nil {
return nil, err
}
if meta.PrimaryItem == nil {
return nil, errors.New("heif: HEIF file lacks primary item box")
}
return f.ItemByID(uint32(meta.PrimaryItem.ItemID))
}
// ItemByID by returns the file's Item of a given ID.
// If the ID is known, the returned error is ErrUnknownItem.
func (f *File) ItemByID(id uint32) (*Item, error) {
meta, err := f.getMeta()
if err != nil {
return nil, err
}
it := &Item{
f: f,
ID: id,
}
if meta.ItemLocation != nil {
for _, ilbe := range meta.ItemLocation.Items {
if uint32(ilbe.ItemID) == id {
shallowCopy := ilbe
it.Location = &shallowCopy
}
}
}
if meta.ItemInfo != nil {
for _, iie := range meta.ItemInfo.ItemInfos {
if uint32(iie.ItemID) == id {
it.Info = iie
}
}
}
if it.Info == nil {
return nil, ErrUnknownItem
}
if meta.Properties != nil {
allProps := meta.Properties.PropertyContainer.Properties
for _, ipa := range meta.Properties.Associations {
// TODO: I've never seen a file with more than
// top-level ItemPropertyAssociation box, but
// apparently they can exist with different
// versions/flags. For now we just merge them
// all together, but that's not really right.
// So for now, just bail once a previous loop
// found anything.
if len(it.Properties) > 0 {
break
}
for _, ipai := range ipa.Entries {
if ipai.ItemID != id {
continue
}
for _, ass := range ipai.Associations {
if ass.Index != 0 && int(ass.Index) <= len(allProps) {
box := allProps[ass.Index-1]
boxp, err := box.Parse()
if err == nil {
box = boxp
}
it.Properties = append(it.Properties, box)
}
}
}
}
}
return it, nil
}

@ -1,113 +0,0 @@
package heif
import (
"bytes"
"fmt"
"os"
"testing"
"github.com/rwcarlsen/goexif/exif"
"github.com/rwcarlsen/goexif/tiff"
)
func TestAll(t *testing.T) {
f, err := os.Open("testdata/park.heic")
if err != nil {
t.Fatal(err)
}
defer f.Close()
h := Open(f)
// meta
_, err = h.getMeta()
if err != nil {
t.Fatalf("getMeta: %v", err)
}
it, err := h.PrimaryItem()
if err != nil {
t.Fatalf("PrimaryItem: %v", err)
}
if want := uint32(49); it.ID != want {
t.Errorf("PrimaryIem ID = %v; want %v", it.ID, want)
}
if it.Location == nil {
t.Errorf("Item.Location is nil")
}
if it.Info == nil {
t.Errorf("Item.Info is nil")
}
if len(it.Properties) == 0 {
t.Errorf("Item.Properties is empty")
}
for _, prop := range it.Properties {
t.Logf(" property: %q, %#v", prop.Type(), prop)
}
if w, h, ok := it.SpatialExtents(); !ok || w == 0 || h == 0 {
t.Errorf("no spatial extents found")
} else {
t.Logf("dimensions: %v x %v", w, h)
}
// exif
exbuf, err := h.EXIF()
if err != nil {
t.Errorf("EXIF: %v", err)
} else {
const magic = "Exif\x00\x00"
if !bytes.HasPrefix(exbuf, []byte(magic)) {
t.Errorf("Exif buffer doesn't start with %q: got %q", magic, exbuf)
}
x, err := exif.Decode(bytes.NewReader(exbuf))
if err != nil {
t.Fatalf("EXIF decode: %v", err)
}
got := map[string]string{}
if err := x.Walk(walkFunc(func(name exif.FieldName, tag *tiff.Tag) error {
got[fmt.Sprint(name)] = fmt.Sprint(tag)
return nil
})); err != nil {
t.Fatalf("EXIF walk: %v", err)
}
if g, w := len(got), 56; g < w {
t.Errorf("saw %v EXIF tags; want at least %v", g, w)
}
if g, w := got["GPSLongitude"], `["122/1","21/1","3776/100"]`; g != w {
t.Errorf("GPSLongitude = %#q; want %#q", g, w)
}
}
}
func TestRotations(t *testing.T) {
f, err := os.Open("testdata/rotate.heic")
if err != nil {
t.Fatal(err)
}
defer f.Close()
h := Open(f)
it, err := h.PrimaryItem()
if err != nil {
t.Fatalf("PrimaryItem: %v", err)
}
if r := it.Rotations(); r != 3 {
t.Errorf("Rotations = %v; want %v", r, 3)
}
sw, sh, ok := it.SpatialExtents()
if !ok {
t.Fatalf("expected spatial extents")
}
vw, vh, ok := it.VisualDimensions()
if !ok {
t.Fatalf("expected visual dimensions")
}
if vw != sh || vh != sw {
t.Errorf("visual dimensions = %v, %v; want %v, %v", vw, vh, sh, sw)
}
}
type walkFunc func(exif.FieldName, *tiff.Tag) error
func (f walkFunc) Walk(name exif.FieldName, tag *tiff.Tag) error {
return f(name, tag)
}

Binary file not shown.

Binary file not shown.

@ -1,137 +0,0 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package throttle provides a net.Listener that returns
// artificially-delayed connections for testing real-world
// connectivity.
package throttle // import "go4.org/net/throttle"
import (
"fmt"
"net"
"sync"
"time"
)
const unitSize = 1400 // read/write chunk size. ~MTU size.
type Rate struct {
KBps int // or 0, to not rate-limit bandwidth
Latency time.Duration
}
// byteTime returns the time required for n bytes.
func (r Rate) byteTime(n int) time.Duration {
if r.KBps == 0 {
return 0
}
return time.Duration(float64(n)/1024/float64(r.KBps)) * time.Second
}
type Listener struct {
net.Listener
Down Rate // server Writes to Client
Up Rate // server Reads from client
}
func (ln *Listener) Accept() (net.Conn, error) {
c, err := ln.Listener.Accept()
time.Sleep(ln.Up.Latency)
if err != nil {
return nil, err
}
tc := &conn{Conn: c, Down: ln.Down, Up: ln.Up}
tc.start()
return tc, nil
}
type nErr struct {
n int
err error
}
type writeReq struct {
writeAt time.Time
p []byte
resc chan nErr
}
type conn struct {
net.Conn
Down Rate // for reads
Up Rate // for writes
wchan chan writeReq
closeOnce sync.Once
closeErr error
}
func (c *conn) start() {
c.wchan = make(chan writeReq, 1024)
go c.writeLoop()
}
func (c *conn) writeLoop() {
for req := range c.wchan {
time.Sleep(req.writeAt.Sub(time.Now()))
var res nErr
for len(req.p) > 0 && res.err == nil {
writep := req.p
if len(writep) > unitSize {
writep = writep[:unitSize]
}
n, err := c.Conn.Write(writep)
time.Sleep(c.Up.byteTime(len(writep)))
res.n += n
res.err = err
req.p = req.p[n:]
}
req.resc <- res
}
}
func (c *conn) Close() error {
c.closeOnce.Do(func() {
err := c.Conn.Close()
close(c.wchan)
c.closeErr = err
})
return c.closeErr
}
func (c *conn) Write(p []byte) (n int, err error) {
defer func() {
if e := recover(); e != nil {
n = 0
err = fmt.Errorf("%v", err)
return
}
}()
resc := make(chan nErr, 1)
c.wchan <- writeReq{time.Now().Add(c.Up.Latency), p, resc}
res := <-resc
return res.n, res.err
}
func (c *conn) Read(p []byte) (n int, err error) {
const max = 1024
if len(p) > max {
p = p[:max]
}
n, err = c.Conn.Read(p)
time.Sleep(c.Down.byteTime(n))
return
}

121
vendor/go4.org/oauthutil/oauth.go generated vendored

@ -1,121 +0,0 @@
/*
Copyright 2015 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package oauthutil contains OAuth 2 related utilities.
package oauthutil // import "go4.org/oauthutil"
import (
"encoding/json"
"errors"
"fmt"
"time"
"go4.org/wkfs"
"golang.org/x/oauth2"
)
// TitleBarRedirectURL is the OAuth2 redirect URL to use when the authorization
// code should be returned in the title bar of the browser, with the page text
// prompting the user to copy the code and paste it in the application.
const TitleBarRedirectURL = "urn:ietf:wg:oauth:2.0:oob"
// ErrNoAuthCode is returned when Token() has not found any valid cached token
// and TokenSource does not have an AuthCode for getting a new token.
var ErrNoAuthCode = errors.New("oauthutil: unspecified TokenSource.AuthCode")
// TokenSource is an implementation of oauth2.TokenSource. It uses CacheFile to store and
// reuse the the acquired token, and AuthCode to provide the authorization code that will be
// exchanged for a token otherwise.
type TokenSource struct {
Config *oauth2.Config
// CacheFile is where the token will be stored JSON-encoded. Any call to Token
// first tries to read a valid token from CacheFile.
CacheFile string
// AuthCode provides the authorization code that Token will exchange for a token.
// It usually is a way to prompt the user for the code. If CacheFile does not provide
// a token and AuthCode is nil, Token returns ErrNoAuthCode.
AuthCode func() string
}
var errExpiredToken = errors.New("expired token")
// cachedToken returns the token saved in cacheFile. It specifically returns
// errTokenExpired if the token is expired.
func cachedToken(cacheFile string) (*oauth2.Token, error) {
tok := new(oauth2.Token)
tokenData, err := wkfs.ReadFile(cacheFile)
if err != nil {
return nil, err
}
if err = json.Unmarshal(tokenData, tok); err != nil {
return nil, err
}
if !tok.Valid() {
if tok != nil && time.Now().After(tok.Expiry) {
return nil, errExpiredToken
}
return nil, errors.New("invalid token")
}
return tok, nil
}
// Token first tries to find a valid token in CacheFile, and otherwise uses
// Config and AuthCode to fetch a new token. This new token is saved in CacheFile
// (if not blank). If CacheFile did not provide a token and AuthCode is nil,
// ErrNoAuthCode is returned.
func (src TokenSource) Token() (*oauth2.Token, error) {
var tok *oauth2.Token
var err error
if src.CacheFile != "" {
tok, err = cachedToken(src.CacheFile)
if err == nil {
return tok, nil
}
if err != errExpiredToken {
fmt.Printf("Error getting token from %s: %v\n", src.CacheFile, err)
}
}
if src.AuthCode == nil {
return nil, ErrNoAuthCode
}
tok, err = src.Config.Exchange(oauth2.NoContext, src.AuthCode())
if err != nil {
return nil, fmt.Errorf("could not exchange auth code for a token: %v", err)
}
if src.CacheFile == "" {
return tok, nil
}
tokenData, err := json.Marshal(&tok)
if err != nil {
return nil, fmt.Errorf("could not encode token as json: %v", err)
}
if err := wkfs.WriteFile(src.CacheFile, tokenData, 0600); err != nil {
return nil, fmt.Errorf("could not cache token in %v: %v", src.CacheFile, err)
}
return tok, nil
}
// NewRefreshTokenSource returns a token source that obtains its initial token
// based on the provided config and the refresh token.
func NewRefreshTokenSource(config *oauth2.Config, refreshToken string) oauth2.TokenSource {
var noInitialToken *oauth2.Token = nil
return oauth2.ReuseTokenSource(noInitialToken, config.TokenSource(
oauth2.NoContext, // TODO: maybe accept a context later.
&oauth2.Token{RefreshToken: refreshToken},
))
}

@ -1,35 +0,0 @@
// Copyright 2015 The go4 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build plan9
package osutil
import (
"fmt"
"os"
"path/filepath"
"syscall"
)
func executable() (string, error) {
fn := fmt.Sprintf("/proc/%d/text", os.Getpid())
f, err := os.Open(fn)
if err != nil {
return "", err
}
defer f.Close()
p, err := syscall.Fd2path(int(f.Fd()))
return filepath.Clean(p), err
}

@ -1,42 +0,0 @@
// Copyright 2015 The go4 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build linux netbsd openbsd dragonfly nacl
package osutil
import (
"errors"
"os"
"path/filepath"
"runtime"
)
func executable() (string, error) {
var procfn string
switch runtime.GOOS {
default:
return "", errors.New("Executable not implemented for " + runtime.GOOS)
case "linux":
procfn = "/proc/self/exe"
case "netbsd":
procfn = "/proc/curproc/exe"
case "openbsd":
procfn = "/proc/curproc/file"
case "dragonfly":
procfn = "/proc/curproc/file"
}
p, err := os.Readlink(procfn)
return filepath.Clean(p), err
}

@ -1,71 +0,0 @@
// Copyright 2015 The go4 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build amd64,solaris
package osutil
import (
"os"
"syscall"
"unsafe"
)
//go:cgo_import_dynamic libc_getexecname getexecname "libc.so"
//go:linkname libc_getexecname libc_getexecname
var libc_getexecname uintptr
func getexecname() (path unsafe.Pointer, err error) {
r0, _, e1 := syscall.Syscall6(uintptr(unsafe.Pointer(&libc_getexecname)), 0, 0, 0, 0, 0, 0)
path = unsafe.Pointer(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}
func syscallGetexecname() (path string, err error) {
ptr, err := getexecname()
if err != nil {
return "", err
}
bytes := (*[1 << 29]byte)(ptr)[:]
for i, b := range bytes {
if b == 0 {
return string(bytes[:i]), nil
}
}
panic("unreachable")
}
var initCwd, initCwdErr = os.Getwd()
func executable() (string, error) {
path, err := syscallGetexecname()
if err != nil {
return path, err
}
if len(path) > 0 && path[0] != '/' {
if initCwdErr != nil {
return path, initCwdErr
}
if len(path) > 2 && path[0:2] == "./" {
// skip "./"
path = path[2:]
}
return initCwd + "/" + path, nil
}
return path, nil
}

@ -1,63 +0,0 @@
// Copyright 2015 The go4 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build freebsd darwin
package osutil
import (
"os"
"path/filepath"
"runtime"
"syscall"
"unsafe"
)
var cacheWD, cacheWDErr = os.Getwd()
func executable() (string, error) {
var mib [4]int32
switch runtime.GOOS {
case "freebsd":
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
case "darwin":
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
}
n := uintptr(0)
// get length
_, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if err != 0 {
return "", err
}
if n == 0 { // shouldn't happen
return "", nil
}
buf := make([]byte, n)
_, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
if err != 0 {
return "", err
}
if n == 0 { // shouldn't happen
return "", nil
}
p := string(buf[:n-1])
if !filepath.IsAbs(p) {
if cacheWDErr != nil {
return p, cacheWDErr
}
p = filepath.Join(cacheWD, filepath.Clean(p))
}
return filepath.EvalSymlinks(p)
}

@ -1,94 +0,0 @@
// Copyright 2015 The go4 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package osutil
import (
"fmt"
"os"
osexec "os/exec"
"path/filepath"
"runtime"
"testing"
)
const executable_EnvVar = "OSTEST_OUTPUT_EXECPATH"
func TestExecutable(t *testing.T) {
if runtime.GOOS == "nacl" {
t.Skip()
}
ep, err := Executable()
if err != nil {
switch goos := runtime.GOOS; goos {
case "openbsd": // procfs is not mounted by default
t.Skipf("Executable failed on %s: %v, expected", goos, err)
}
t.Fatalf("Executable failed: %v", err)
}
// we want fn to be of the form "dir/prog"
dir := filepath.Dir(filepath.Dir(ep))
fn, err := filepath.Rel(dir, ep)
if err != nil {
t.Fatalf("filepath.Rel: %v", err)
}
cmd := &osexec.Cmd{}
// make child start with a relative program path
cmd.Dir = dir
cmd.Path = fn
// forge argv[0] for child, so that we can verify we could correctly
// get real path of the executable without influenced by argv[0].
cmd.Args = []string{"-", "-test.run=XXXX"}
cmd.Env = []string{fmt.Sprintf("%s=1", executable_EnvVar)}
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("exec(self) failed: %v", err)
}
outs := string(out)
if !filepath.IsAbs(outs) {
t.Fatalf("Child returned %q, want an absolute path", out)
}
if !sameFile(outs, ep) {
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
}
}
func sameFile(fn1, fn2 string) bool {
fi1, err := os.Stat(fn1)
if err != nil {
return false
}
fi2, err := os.Stat(fn2)
if err != nil {
return false
}
return os.SameFile(fi1, fi2)
}
func init() {
if e := os.Getenv(executable_EnvVar); e != "" {
// first chdir to another path
dir := "/"
if runtime.GOOS == "windows" {
dir = filepath.VolumeName(".")
}
os.Chdir(dir)
if ep, err := Executable(); err != nil {
fmt.Fprint(os.Stderr, "ERROR: ", err)
} else {
fmt.Fprint(os.Stderr, ep)
}
os.Exit(0)
}
}

@ -1,64 +0,0 @@
/*
Copyright 2015 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package osutil
import (
"path/filepath"
"syscall"
"unsafe"
)
var (
modkernel32 = syscall.MustLoadDLL("kernel32.dll")
procGetModuleFileNameW = modkernel32.MustFindProc("GetModuleFileNameW")
)
func getModuleFileName(handle syscall.Handle) (string, error) {
n := uint32(1024)
var buf []uint16
for {
buf = make([]uint16, n)
r, err := syscallGetModuleFileName(handle, &buf[0], n)
if err != nil {
return "", err
}
if r < n {
break
}
// r == n means n not big enough
n += 1024
}
return syscall.UTF16ToString(buf), nil
}
func executable() (string, error) {
p, err := getModuleFileName(0)
return filepath.Clean(p), err
}
func syscallGetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) {
r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(fn)), uintptr(len))
n = uint32(r0)
if n == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}

32
vendor/go4.org/osutil/osutil.go generated vendored

@ -1,32 +0,0 @@
/*
Copyright 2015 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package osutil contains os level functions.
package osutil // import "go4.org/osutil"
// capture executable on package init to work around various os issues if
// captured after executable has been renamed.
var execPath, execError = executable()
// Executable returns the path name for the executable that starts the
// current process. The result is the path that was used to start the
// current process, but there is no guarantee that the path is still
// pointing to the correct executable.
//
// OpenBSD is currently unsupported.
func Executable() (string, error) {
return execPath, execError
}

@ -1,48 +0,0 @@
/*
Copyright 2018 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import "io"
// NewBufferingReaderAt returns an io.ReaderAt that reads from r as
// necessary and keeps a copy of all data read in memory.
func NewBufferingReaderAt(r io.Reader) io.ReaderAt {
return &bufReaderAt{r: r}
}
type bufReaderAt struct {
r io.Reader
buf []byte
}
func (br *bufReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
endOff := off + int64(len(p))
need := endOff - int64(len(br.buf))
if need > 0 {
buf := make([]byte, need)
var rn int
rn, err = io.ReadFull(br.r, buf)
br.buf = append(br.buf, buf[:rn]...)
}
if int64(len(br.buf)) >= off {
n = copy(p, br.buf[off:])
}
if n == len(p) {
err = nil
}
return
}

@ -1,70 +0,0 @@
/*
Copyright 2018 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import "testing"
type trackingReader struct {
off int
reads int
readBytes int
}
func (t *trackingReader) Read(p []byte) (n int, err error) {
t.reads++
t.readBytes += len(p)
for len(p) > 0 {
p[0] = '0' + byte(t.off%10)
t.off++
p = p[1:]
n++
}
return
}
func TestBufferingReaderAt(t *testing.T) {
tr := new(trackingReader)
ra := NewBufferingReaderAt(tr)
for i, tt := range []struct {
off int64
want string
wantReads int
wantReadBytes int
}{
{off: 0, want: "0123456789", wantReads: 1, wantReadBytes: 10},
{off: 5, want: "56789", wantReads: 1, wantReadBytes: 10}, // already buffered
{off: 6, want: "67890", wantReads: 2, wantReadBytes: 11}, // need 1 more byte
{off: 0, want: "0123456789", wantReads: 2, wantReadBytes: 11}, // already buffered
} {
got := make([]byte, len(tt.want))
n, err := ra.ReadAt(got, tt.off)
if err != nil || n != len(tt.want) {
t.Errorf("step %d: ReadAt = %v, %v; want %v, %v", i, n, err, len(tt.want), nil)
continue
}
if string(got) != tt.want {
t.Errorf("step %d: ReadAt read %q; want %q", i, got, tt.want)
}
if tr.reads != tt.wantReads {
t.Errorf("step %d: num reads = %d; want %d", i, tr.reads, tt.wantReads)
}
if tr.readBytes != tt.wantReadBytes {
t.Errorf("step %d: read bytes = %d; want %d", i, tr.reads, tt.wantReads)
}
}
}

@ -1,32 +0,0 @@
/*
Copyright 2011 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import "io"
// CountingReader wraps a Reader, incrementing N by the number of
// bytes read. No locking is performed.
type CountingReader struct {
Reader io.Reader
N *int64
}
func (cr CountingReader) Read(p []byte) (n int, err error) {
n, err = cr.Reader.Read(p)
*cr.N += int64(n)
return
}

@ -1,70 +0,0 @@
/*
Copyright 2014 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import (
"errors"
"fmt"
"io"
"os"
)
// fakeSeeker can seek to the ends but any read not at the current
// position will fail.
type fakeSeeker struct {
r io.Reader
size int64
fakePos int64
realPos int64
}
// NewFakeSeeker returns a ReadSeeker that can pretend to Seek (based
// on the provided total size of the reader's content), but any reads
// will fail if the fake seek position doesn't match reality.
func NewFakeSeeker(r io.Reader, size int64) io.ReadSeeker {
return &fakeSeeker{r: r, size: size}
}
func (fs *fakeSeeker) Seek(offset int64, whence int) (int64, error) {
var newo int64
switch whence {
default:
return 0, errors.New("invalid whence")
case os.SEEK_SET:
newo = offset
case os.SEEK_CUR:
newo = fs.fakePos + offset
case os.SEEK_END:
newo = fs.size + offset
}
if newo < 0 {
return 0, errors.New("negative seek")
}
fs.fakePos = newo
return newo, nil
}
func (fs *fakeSeeker) Read(p []byte) (n int, err error) {
if fs.fakePos != fs.realPos {
return 0, fmt.Errorf("attempt to read from fake seek offset %d; real offset is %d", fs.fakePos, fs.realPos)
}
n, err = fs.r.Read(p)
fs.fakePos += int64(n)
fs.realPos += int64(n)
return
}

@ -1,55 +0,0 @@
/*
Copyright 2014 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import (
"os"
"strings"
"testing"
)
func TestFakeSeeker(t *testing.T) {
rs := NewFakeSeeker(strings.NewReader("foobar"), 6)
if pos, err := rs.Seek(0, os.SEEK_END); err != nil || pos != 6 {
t.Fatalf("SEEK_END = %d, %v; want 6, nil", pos, err)
}
if pos, err := rs.Seek(0, os.SEEK_CUR); err != nil || pos != 6 {
t.Fatalf("SEEK_CUR = %d, %v; want 6, nil", pos, err)
}
if pos, err := rs.Seek(0, os.SEEK_SET); err != nil || pos != 0 {
t.Fatalf("SEEK_SET = %d, %v; want 0, nil", pos, err)
}
buf := make([]byte, 3)
if n, err := rs.Read(buf); n != 3 || err != nil || string(buf) != "foo" {
t.Fatalf("First read = %d, %v (buf = %q); want foo", n, err, buf)
}
if pos, err := rs.Seek(0, os.SEEK_CUR); err != nil || pos != 3 {
t.Fatalf("Seek cur pos after first read = %d, %v; want 3, nil", pos, err)
}
if n, err := rs.Read(buf); n != 3 || err != nil || string(buf) != "bar" {
t.Fatalf("Second read = %d, %v (buf = %q); want foo", n, err, buf)
}
if pos, err := rs.Seek(1, os.SEEK_SET); err != nil || pos != 1 {
t.Fatalf("SEEK_SET = %d, %v; want 1, nil", pos, err)
}
const msg = "attempt to read from fake seek offset"
if _, err := rs.Read(buf); err == nil || !strings.Contains(err.Error(), msg) {
t.Fatalf("bogus Read after seek = %v; want something containing %q", err, msg)
}
}

@ -1,91 +0,0 @@
/*
Copyright 2016 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import (
"io"
"sort"
)
// NewMultiReaderAt is like io.MultiReader but produces a ReaderAt
// (and Size), instead of just a reader.
func NewMultiReaderAt(parts ...SizeReaderAt) SizeReaderAt {
m := &multiRA{
parts: make([]offsetAndSource, 0, len(parts)),
}
var off int64
for _, p := range parts {
m.parts = append(m.parts, offsetAndSource{off, p})
off += p.Size()
}
m.size = off
return m
}
type offsetAndSource struct {
off int64
SizeReaderAt
}
type multiRA struct {
parts []offsetAndSource
size int64
}
func (m *multiRA) Size() int64 { return m.size }
func (m *multiRA) ReadAt(p []byte, off int64) (n int, err error) {
wantN := len(p)
// Skip past the requested offset.
skipParts := sort.Search(len(m.parts), func(i int) bool {
// This function returns whether parts[i] will
// contribute any bytes to our output.
part := m.parts[i]
return part.off+part.Size() > off
})
parts := m.parts[skipParts:]
// How far to skip in the first part.
needSkip := off
if len(parts) > 0 {
needSkip -= parts[0].off
}
for len(parts) > 0 && len(p) > 0 {
readP := p
partSize := parts[0].Size()
if int64(len(readP)) > partSize-needSkip {
readP = readP[:partSize-needSkip]
}
pn, err0 := parts[0].ReadAt(readP, needSkip)
if err0 != nil {
return n, err0
}
n += pn
p = p[pn:]
if int64(pn)+needSkip == partSize {
parts = parts[1:]
}
needSkip = 0
}
if n != wantN {
err = io.ErrUnexpectedEOF
}
return
}

@ -1,48 +0,0 @@
/*
Copyright 2016 The Go4 Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import (
"io"
"io/ioutil"
"strings"
"testing"
)
func TestMultiReaderAt(t *testing.T) {
sra := NewMultiReaderAt(
io.NewSectionReader(strings.NewReader("xaaax"), 1, 3),
io.NewSectionReader(strings.NewReader("xxbbbbxx"), 2, 3),
io.NewSectionReader(strings.NewReader("cccx"), 0, 3),
)
if sra.Size() != 9 {
t.Fatalf("Size = %d; want 9", sra.Size())
}
const full = "aaabbbccc"
for start := 0; start < len(full); start++ {
for end := start; end < len(full); end++ {
want := full[start:end]
got, err := ioutil.ReadAll(io.NewSectionReader(sra, int64(start), int64(end-start)))
if err != nil {
t.Fatal(err)
}
if string(got) != want {
t.Errorf("for start=%d, end=%d: ReadAll = %q; want %q", start, end, got, want)
}
}
}
}

@ -1,58 +0,0 @@
/*
Copyright 2012 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package readerutil provides and operates on io.Readers.
package readerutil // import "go4.org/readerutil"
import (
"bytes"
"io"
"os"
"strings"
)
// Size tries to determine the length of r. If r is an io.Seeker, Size may seek
// to guess the length.
func Size(r io.Reader) (size int64, ok bool) {
switch rt := r.(type) {
case *bytes.Buffer:
return int64(rt.Len()), true
case *bytes.Reader:
return int64(rt.Len()), true
case *strings.Reader:
return int64(rt.Len()), true
case io.Seeker:
pos, err := rt.Seek(0, os.SEEK_CUR)
if err != nil {
return
}
end, err := rt.Seek(0, os.SEEK_END)
if err != nil {
return
}
size = end - pos
pos1, err := rt.Seek(pos, os.SEEK_SET)
if err != nil || pos1 != pos {
msg := "failed to restore seek position"
if err != nil {
msg += ": " + err.Error()
}
panic(msg)
}
return size, true
}
return 0, false
}

@ -1,68 +0,0 @@
/*
Copyright 2012 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import (
"bytes"
"io"
"io/ioutil"
"os"
"testing"
)
const text = "HelloWorld"
type testSrc struct {
name string
src io.Reader
want int64
}
func (tsrc *testSrc) run(t *testing.T) {
n, ok := Size(tsrc.src)
if !ok {
t.Fatalf("failed to read size for %q", tsrc.name)
}
if n != tsrc.want {
t.Fatalf("wanted %v, got %v", tsrc.want, n)
}
}
func TestBytesBuffer(t *testing.T) {
buf := bytes.NewBuffer([]byte(text))
tsrc := &testSrc{"buffer", buf, int64(len(text))}
tsrc.run(t)
}
func TestSeeker(t *testing.T) {
f, err := ioutil.TempFile("", "camliTestReaderSize")
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
defer f.Close()
size, err := f.Write([]byte(text))
if err != nil {
t.Fatal(err)
}
pos, err := f.Seek(5, 0)
if err != nil {
t.Fatal(err)
}
tsrc := &testSrc{"seeker", f, int64(size) - pos}
tsrc.run(t)
}

@ -1,84 +0,0 @@
/*
Copyright 2016 The go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package readerutil contains io.Reader types.
package readerutil // import "go4.org/readerutil"
import (
"expvar"
"io"
)
// A SizeReaderAt is a ReaderAt with a Size method.
//
// An io.SectionReader implements SizeReaderAt.
type SizeReaderAt interface {
Size() int64
io.ReaderAt
}
// A ReadSeekCloser can Read, Seek, and Close.
type ReadSeekCloser interface {
io.Reader
io.Seeker
io.Closer
}
type ReaderAtCloser interface {
io.ReaderAt
io.Closer
}
// TODO(wathiede): make sure all the stat readers work with code that
// type asserts ReadFrom/WriteTo.
type varStatReader struct {
*expvar.Int
r io.Reader
}
// NewReaderStats returns an io.Reader that will have the number of bytes
// read from r added to v.
func NewStatsReader(v *expvar.Int, r io.Reader) io.Reader {
return &varStatReader{v, r}
}
func (v *varStatReader) Read(p []byte) (int, error) {
n, err := v.r.Read(p)
v.Int.Add(int64(n))
return n, err
}
type varStatReadSeeker struct {
*expvar.Int
rs io.ReadSeeker
}
// NewReaderStats returns an io.ReadSeeker that will have the number of bytes
// read from rs added to v.
func NewStatsReadSeeker(v *expvar.Int, rs io.ReadSeeker) io.ReadSeeker {
return &varStatReadSeeker{v, rs}
}
func (v *varStatReadSeeker) Read(p []byte) (int, error) {
n, err := v.rs.Read(p)
v.Int.Add(int64(n))
return n, err
}
func (v *varStatReadSeeker) Seek(offset int64, whence int) (int64, error) {
return v.rs.Seek(offset, whence)
}

@ -1,38 +0,0 @@
/*
Copyright 2016 The Go4 Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readerutil
import (
"expvar"
"fmt"
"io"
"io/ioutil"
"strings"
)
func ExampleNewStatsReader() {
var (
// r is the io.Reader we'd like to count read from.
r = strings.NewReader("Hello world")
v = expvar.NewInt("read-bytes")
sw = NewStatsReader(v, r)
)
// Read from the wrapped io.Reader, StatReader will count the bytes.
io.Copy(ioutil.Discard, sw)
fmt.Printf("Read %s bytes\n", v.String())
// Output: Read 11 bytes
}

@ -1,118 +0,0 @@
/*
Copyright 2013 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// package singlereader provides Open and Close operations, reusing existing
// file descriptors when possible.
package singlereader // import "go4.org/readerutil/singlereader"
import (
"sync"
"go4.org/readerutil"
"go4.org/syncutil/singleflight"
"go4.org/wkfs"
)
var (
openerGroup singleflight.Group
openFileMu sync.Mutex // guards openFiles
openFiles = make(map[string]*openFile)
)
type openFile struct {
wkfs.File
path string // map key of openFiles
refCount int
}
type openFileHandle struct {
closed bool
*openFile
}
func (f *openFileHandle) Close() error {
openFileMu.Lock()
if f.closed {
openFileMu.Unlock()
return nil
}
f.closed = true
f.refCount--
if f.refCount < 0 {
panic("unexpected negative refcount")
}
zero := f.refCount == 0
if zero {
delete(openFiles, f.path)
}
openFileMu.Unlock()
if !zero {
return nil
}
return f.openFile.File.Close()
}
// Open opens the given file path for reading, reusing existing file descriptors
// when possible.
func Open(path string) (readerutil.ReaderAtCloser, error) {
openFileMu.Lock()
of := openFiles[path]
if of != nil {
of.refCount++
openFileMu.Unlock()
return &openFileHandle{false, of}, nil
}
openFileMu.Unlock() // release the lock while we call os.Open
winner := false // this goroutine made it into Do's func
// Returns an *openFile
resi, err := openerGroup.Do(path, func() (interface{}, error) {
winner = true
f, err := wkfs.Open(path)
if err != nil {
return nil, err
}
of := &openFile{
File: f,
path: path,
refCount: 1,
}
openFileMu.Lock()
openFiles[path] = of
openFileMu.Unlock()
return of, nil
})
if err != nil {
return nil, err
}
of = resi.(*openFile)
// If our os.Open was dup-suppressed, we have to increment our
// reference count.
if !winner {
openFileMu.Lock()
if of.refCount == 0 {
// Winner already closed it. Try again (rare).
openFileMu.Unlock()
return Open(path)
}
of.refCount++
openFileMu.Unlock()
}
return &openFileHandle{false, of}, nil
}

@ -1,77 +0,0 @@
/*
Copyright 2013 The Go4 Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package singlereader
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"runtime"
"testing"
)
func TestOpenSingle(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode")
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
f, err := ioutil.TempFile("", "foo")
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
contents := []byte("Some file contents")
if _, err := f.Write(contents); err != nil {
t.Fatal(err)
}
f.Close()
const j = 4
errc := make(chan error, j)
for i := 1; i < j; i++ {
go func() {
buf := make([]byte, len(contents))
for i := 0; i < 400; i++ {
rac, err := Open(f.Name())
if err != nil {
errc <- err
return
}
n, err := rac.ReadAt(buf, 0)
if err != nil {
errc <- err
return
}
if n != len(contents) || !bytes.Equal(buf, contents) {
errc <- fmt.Errorf("read %d, %q; want %d, %q", n, buf, len(contents), contents)
return
}
if err := rac.Close(); err != nil {
errc <- err
return
}
}
errc <- nil
}()
}
for i := 1; i < j; i++ {
if err := <-errc; err != nil {
t.Error(err)
}
}
}

@ -1,17 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.6
// +build arm
#include "textflag.h"
#include "funcdata.h"
// func typedmemmove(reflect_rtype, src unsafe.Pointer, size uintptr)
TEXT ·typedmemmove(SB),(NOSPLIT|WRAPPER),$0-24
B runtime·typedmemmove(SB)
// func memmove(dst, src unsafe.Pointer, size uintptr)
TEXT ·memmove(SB),(NOSPLIT|WRAPPER),$0-24
B runtime·memmove(SB)

@ -1,13 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.5
// +build arm
#include "textflag.h"
#include "funcdata.h"
// func memmove(dst, src unsafe.Pointer, size uintptr)
TEXT ·memmove(SB),(NOSPLIT|WRAPPER),$0-24
B runtime·memmove(SB)

@ -1,17 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.5,!js,!safe,!appengine
// +build amd64 386
#include "textflag.h"
#include "funcdata.h"
// func typedmemmove(reflect_rtype, src unsafe.Pointer, size uintptr)
TEXT ·typedmemmove(SB),(NOSPLIT|WRAPPER),$0-24
JMP runtime·typedmemmove(SB)
// func memmove(dst, src unsafe.Pointer, size uintptr)
TEXT ·memmove(SB),(NOSPLIT|WRAPPER),$0-24
JMP runtime·memmove(SB)

@ -1,13 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.5,!js,!safe,!appengine
// +build amd64 386
#include "textflag.h"
#include "funcdata.h"
// func memmove(dst, src unsafe.Pointer, size uintptr)
TEXT ·memmove(SB),(NOSPLIT|WRAPPER),$0-24
JMP runtime·memmove(SB)

@ -1,40 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package reflectutil contains reflect utilities.
package reflectutil
import "reflect"
// hasPointers reports whether the given type contains any pointers,
// including any internal pointers in slices, funcs, maps, channels,
// etc.
//
// This function exists for Swapper's internal use, instead of reaching
// into the runtime's *reflect._rtype kind&kindNoPointers flag.
func hasPointers(t reflect.Type) bool {
if t == nil {
panic("nil Type")
}
k := t.Kind()
if k <= reflect.Complex128 {
return false
}
switch k {
default:
// chan, func, interface, map, ptr, slice, string, unsafepointer
// And anything else. It's safer to err on the side of true.
return true
case reflect.Array:
return hasPointers(t.Elem())
case reflect.Struct:
num := t.NumField()
for i := 0; i < num; i++ {
if hasPointers(t.Field(i).Type) {
return true
}
}
return false
}
}

@ -1,71 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package reflectutil
import (
"reflect"
"testing"
)
func TestHasPointers(t *testing.T) {
tests := []struct {
val interface{}
want bool
}{
{false, false},
{int(1), false},
{int8(1), false},
{int16(1), false},
{int32(1), false},
{int64(1), false},
{uint(1), false},
{uint8(1), false},
{uint16(1), false},
{uint32(1), false},
{uint64(1), false},
{uintptr(1), false},
{float32(1.0), false},
{float64(1.0), false},
{complex64(1.0i), false},
{complex128(1.0i), false},
{[...]int{1, 2}, false},
{[...]*int{nil, nil}, true},
{make(chan bool), true},
{TestHasPointers, true},
{map[string]string{"foo": "bar"}, true},
{new(int), true},
{[]int{1, 2}, true},
{"foo", true},
{struct{}{}, false},
{struct{ int }{0}, false},
{struct {
a int
b bool
}{0, false}, false},
{struct {
a int
b string
}{0, ""}, true},
{struct{ *int }{nil}, true},
{struct {
a *int
b int
}{nil, 0}, true},
}
for i, tt := range tests {
got := hasPointers(reflect.TypeOf(tt.val))
if got != tt.want {
t.Errorf("%d. hasPointers(%T) = %v; want %v", i, tt.val, got, tt.want)
}
}
}

@ -1,21 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package reflectutil
import "reflect"
// Swapper returns a function which swaps the elements in slice.
// Swapper panics if the provided interface is not a slice.
//
// Its goal is to work safely and efficiently for all versions and
// variants of Go: pre-Go1.5, Go1.5+, safe, unsafe, App Engine,
// GopherJS, etc.
func Swapper(slice interface{}) func(i, j int) {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
panic(&reflect.ValueError{Method: "reflectutil.Swapper", Kind: v.Kind()})
}
return swapper(v)
}

@ -1,20 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js appengine safe ppc64 ppc64le arm64 mips mipsle mips64 mips64le
package reflectutil
import "reflect"
func swapper(slice reflect.Value) func(i, j int) {
tmp := reflect.New(slice.Type().Elem()).Elem()
return func(i, j int) {
v1 := slice.Index(i)
v2 := slice.Index(j)
tmp.Set(v1)
v1.Set(v2)
v2.Set(tmp)
}
}

@ -1,110 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package reflectutil
import (
"fmt"
"reflect"
"strconv"
"testing"
)
func TestSwapper(t *testing.T) {
type I int
var a, b, c I
type pair struct {
x, y int
}
type pairPtr struct {
x, y int
p *I
}
type S string
tests := []struct {
in interface{}
i, j int
want interface{}
}{
{
in: []int{1, 20, 300},
i: 0,
j: 2,
want: []int{300, 20, 1},
},
{
in: []uintptr{1, 20, 300},
i: 0,
j: 2,
want: []uintptr{300, 20, 1},
},
{
in: []int16{1, 20, 300},
i: 0,
j: 2,
want: []int16{300, 20, 1},
},
{
in: []int8{1, 20, 100},
i: 0,
j: 2,
want: []int8{100, 20, 1},
},
{
in: []*I{&a, &b, &c},
i: 0,
j: 2,
want: []*I{&c, &b, &a},
},
{
in: []string{"eric", "sergey", "larry"},
i: 0,
j: 2,
want: []string{"larry", "sergey", "eric"},
},
{
in: []S{"eric", "sergey", "larry"},
i: 0,
j: 2,
want: []S{"larry", "sergey", "eric"},
},
{
in: []pair{{1, 2}, {3, 4}, {5, 6}},
i: 0,
j: 2,
want: []pair{{5, 6}, {3, 4}, {1, 2}},
},
{
in: []pairPtr{{1, 2, &a}, {3, 4, &b}, {5, 6, &c}},
i: 0,
j: 2,
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
},
}
for i, tt := range tests {
inStr := fmt.Sprint(tt.in)
Swapper(tt.in)(tt.i, tt.j)
if !reflect.DeepEqual(tt.in, tt.want) {
t.Errorf("%d. swapping %v and %v of %v = %v; want %v", i, tt.i, tt.j, inStr, tt.in, tt.want)
}
}
}
func BenchmarkSwap(b *testing.B) {
const N = 1024
strs := make([]string, N)
for i := range strs {
strs[i] = strconv.Itoa(i)
}
swap := Swapper(strs)
b.ResetTimer()
i, j := 0, 1
for n := 0; n < b.N; n++ {
i = (i + 1) % N
j = (j + 2) % N
swap(i, j)
}
}

@ -1,106 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !ppc64,!ppc64le,!arm64,!mips,!mipsle,!mips64,!mips64le
// +build !js,!appengine,!safe
package reflectutil
import (
"reflect"
"unsafe"
)
const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
// arrayAt returns the i-th element of p, a C-array whose elements are
// eltSize wide (in bytes).
func arrayAt(p unsafe.Pointer, i int, eltSize uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(p) + uintptr(i)*eltSize)
}
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
func swapper(v reflect.Value) func(i, j int) {
maxLen := uint(v.Len())
s := sliceHeader{unsafe.Pointer(v.Pointer()), int(maxLen), int(maxLen)}
tt := v.Type()
elemt := tt.Elem()
typ := unsafe.Pointer(reflect.ValueOf(elemt).Pointer())
size := elemt.Size()
hasPtr := hasPointers(elemt)
// Some common & small cases, without using memmove:
if hasPtr {
if size == ptrSize {
var ps []unsafe.Pointer
*(*sliceHeader)(unsafe.Pointer(&ps)) = s
return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
}
if elemt.Kind() == reflect.String {
var ss []string
*(*sliceHeader)(unsafe.Pointer(&ss)) = s
return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
}
} else {
switch size {
case 8:
var is []int64
*(*sliceHeader)(unsafe.Pointer(&is)) = s
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 4:
var is []int32
*(*sliceHeader)(unsafe.Pointer(&is)) = s
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 2:
var is []int16
*(*sliceHeader)(unsafe.Pointer(&is)) = s
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 1:
var is []int8
*(*sliceHeader)(unsafe.Pointer(&is)) = s
return func(i, j int) { is[i], is[j] = is[j], is[i] }
}
}
// Allocate scratch space for swaps:
tmpVal := reflect.New(elemt)
tmp := unsafe.Pointer(tmpVal.Pointer())
// If no pointers (or Go 1.4 or below), we don't require typedmemmove:
if !haveTypedMemmove || !hasPtr {
return func(i, j int) {
if uint(i) >= maxLen || uint(j) >= maxLen {
panic("reflect: slice index out of range")
}
val1 := arrayAt(s.Data, i, size)
val2 := arrayAt(s.Data, j, size)
memmove(tmp, val1, size)
memmove(val1, val2, size)
memmove(val2, tmp, size)
}
}
return func(i, j int) {
if uint(i) >= maxLen || uint(j) >= maxLen {
panic("reflect: slice index out of range")
}
val1 := arrayAt(s.Data, i, size)
val2 := arrayAt(s.Data, j, size)
typedmemmove(typ, tmp, val1)
typedmemmove(typ, val1, val2)
typedmemmove(typ, val2, tmp)
}
}
// memmove copies size bytes from src to dst.
// The memory must not contain any pointers.
//go:noescape
func memmove(dst, src unsafe.Pointer, size uintptr)

@ -1,16 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !ppc64,!ppc64le,!arm64,!mips,!mipsle,!mips64,!mips64le
// +build !go1.5,!js,!appengine,!safe
package reflectutil
import "unsafe"
const haveTypedMemmove = false
func typedmemmove(reflect_rtype, dst, src unsafe.Pointer) {
panic("never called") // only here so swapper_unsafe.go compiles
}

@ -1,16 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !ppc64,!ppc64le,!arm64,!mips,!mipsle,!mips64,!mips64le
// +build go1.5,!js,!appengine,!safe
package reflectutil
import "unsafe"
const haveTypedMemmove = true
// typedmemmove copies a value of type t to dst from src.
//go:noescape
func typedmemmove(reflect_rtype, dst, src unsafe.Pointer)

@ -1,45 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"go4.org/sort"
)
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s: %d", p.Name, p.Age)
}
// ByAge implements sort.Interface for []Person based on
// the Age field.
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func ExampleSort() {
people := []Person{
{"Bob", 31},
{"John", 42},
{"Michael", 17},
{"Jenny", 26},
}
fmt.Println(people)
sort.Sort(ByAge(people))
fmt.Println(people)
// Output:
// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}

@ -1,96 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"sort"
)
// A couple of type definitions to make the units clear.
type earthMass float64
type au float64
// A Planet defines the properties of a solar system object.
type Planet struct {
name string
mass earthMass
distance au
}
// By is the type of a "less" function that defines the ordering of its Planet arguments.
type By func(p1, p2 *Planet) bool
// Sort is a method on the function type, By, that sorts the argument slice according to the function.
func (by By) Sort(planets []Planet) {
ps := &planetSorter{
planets: planets,
by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
}
sort.Sort(ps)
}
// planetSorter joins a By function and a slice of Planets to be sorted.
type planetSorter struct {
planets []Planet
by func(p1, p2 *Planet) bool // Closure used in the Less method.
}
// Len is part of sort.Interface.
func (s *planetSorter) Len() int {
return len(s.planets)
}
// Swap is part of sort.Interface.
func (s *planetSorter) Swap(i, j int) {
s.planets[i], s.planets[j] = s.planets[j], s.planets[i]
}
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s *planetSorter) Less(i, j int) bool {
return s.by(&s.planets[i], &s.planets[j])
}
var planets = []Planet{
{"Mercury", 0.055, 0.4},
{"Venus", 0.815, 0.7},
{"Earth", 1.0, 1.0},
{"Mars", 0.107, 1.5},
}
// ExampleSortKeys demonstrates a technique for sorting a struct type using programmable sort criteria.
func Example_sortKeys() {
// Closures that order the Planet structure.
name := func(p1, p2 *Planet) bool {
return p1.name < p2.name
}
mass := func(p1, p2 *Planet) bool {
return p1.mass < p2.mass
}
distance := func(p1, p2 *Planet) bool {
return p1.distance < p2.distance
}
decreasingDistance := func(p1, p2 *Planet) bool {
return !distance(p1, p2)
}
// Sort the planets by the various criteria.
By(name).Sort(planets)
fmt.Println("By name:", planets)
By(mass).Sort(planets)
fmt.Println("By mass:", planets)
By(distance).Sort(planets)
fmt.Println("By distance:", planets)
By(decreasingDistance).Sort(planets)
fmt.Println("By decreasing distance:", planets)
// Output: By name: [{Earth 1 1} {Mars 0.107 1.5} {Mercury 0.055 0.4} {Venus 0.815 0.7}]
// By mass: [{Mercury 0.055 0.4} {Mars 0.107 1.5} {Venus 0.815 0.7} {Earth 1 1}]
// By distance: [{Mercury 0.055 0.4} {Venus 0.815 0.7} {Earth 1 1} {Mars 0.107 1.5}]
// By decreasing distance: [{Mars 0.107 1.5} {Earth 1 1} {Venus 0.815 0.7} {Mercury 0.055 0.4}]
}

@ -1,133 +0,0 @@
// +build go1.6
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"sort"
)
// A Change is a record of source code changes, recording user, language, and delta size.
type Change struct {
user string
language string
lines int
}
type lessFunc func(p1, p2 *Change) bool
// multiSorter implements the Sort interface, sorting the changes within.
type multiSorter struct {
changes []Change
less []lessFunc
}
// Sort sorts the argument slice according to the less functions passed to OrderedBy.
func (ms *multiSorter) Sort(changes []Change) {
ms.changes = changes
sort.Sort(ms)
}
// OrderedBy returns a Sorter that sorts using the less functions, in order.
// Call its Sort method to sort the data.
func OrderedBy(less ...lessFunc) *multiSorter {
return &multiSorter{
less: less,
}
}
// Len is part of sort.Interface.
func (ms *multiSorter) Len() int {
return len(ms.changes)
}
// Swap is part of sort.Interface.
func (ms *multiSorter) Swap(i, j int) {
ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i]
}
// Less is part of sort.Interface. It is implemented by looping along the
// less functions until it finds a comparison that is either Less or
// !Less. Note that it can call the less functions twice per call. We
// could change the functions to return -1, 0, 1 and reduce the
// number of calls for greater efficiency: an exercise for the reader.
func (ms *multiSorter) Less(i, j int) bool {
p, q := &ms.changes[i], &ms.changes[j]
// Try all but the last comparison.
var k int
for k = 0; k < len(ms.less)-1; k++ {
less := ms.less[k]
switch {
case less(p, q):
// p < q, so we have a decision.
return true
case less(q, p):
// p > q, so we have a decision.
return false
}
// p == q; try the next comparison.
}
// All comparisons to here said "equal", so just return whatever
// the final comparison reports.
return ms.less[k](p, q)
}
var changes = []Change{
{"gri", "Go", 100},
{"ken", "C", 150},
{"glenda", "Go", 200},
{"rsc", "Go", 200},
{"r", "Go", 100},
{"ken", "Go", 200},
{"dmr", "C", 100},
{"r", "C", 150},
{"gri", "Smalltalk", 80},
}
// ExampleMultiKeys demonstrates a technique for sorting a struct type using different
// sets of multiple fields in the comparison. We chain together "Less" functions, each of
// which compares a single field.
func Example_sortMultiKeys() {
// Closures that order the Change structure.
user := func(c1, c2 *Change) bool {
return c1.user < c2.user
}
language := func(c1, c2 *Change) bool {
return c1.language < c2.language
}
increasingLines := func(c1, c2 *Change) bool {
return c1.lines < c2.lines
}
decreasingLines := func(c1, c2 *Change) bool {
return c1.lines > c2.lines // Note: > orders downwards.
}
// Simple use: Sort by user.
OrderedBy(user).Sort(changes)
fmt.Println("By user:", changes)
// More examples.
OrderedBy(user, increasingLines).Sort(changes)
fmt.Println("By user,<lines:", changes)
OrderedBy(user, decreasingLines).Sort(changes)
fmt.Println("By user,>lines:", changes)
OrderedBy(language, increasingLines).Sort(changes)
fmt.Println("By language,<lines:", changes)
OrderedBy(language, increasingLines, user).Sort(changes)
fmt.Println("By language,<lines,user:", changes)
// Output:
// By user: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
// By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
// By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
// By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {r Go 100} {gri Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
// By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
}

@ -1,45 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"go4.org/sort"
)
func Example() {
people := []Person{
{Name: "Bob", Age: 31},
{Name: "John", Age: 42},
{Name: "Michael", Age: 17},
{Name: "Jenny", Age: 26},
}
fmt.Println(people)
sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
fmt.Println(people)
// Output:
// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}
func ExampleSlice() {
people := []Person{
{Name: "Bob", Age: 31},
{Name: "John", Age: 42},
{Name: "Michael", Age: 17},
{Name: "Jenny", Age: 26},
}
fmt.Println(people)
sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
fmt.Println(people)
// Output:
// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}

@ -1,24 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"sort"
)
func ExampleInts() {
s := []int{5, 2, 6, 3, 1, 4} // unsorted
sort.Ints(s)
fmt.Println(s)
// Output: [1 2 3 4 5 6]
}
func ExampleReverse() {
s := []int{5, 2, 6, 3, 1, 4} // unsorted
sort.Sort(sort.Reverse(sort.IntSlice(s)))
fmt.Println(s)
// Output: [6 5 4 3 2 1]
}

@ -1,77 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"sort"
)
type Grams int
func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
type Organ struct {
Name string
Weight Grams
}
type Organs []*Organ
func (s Organs) Len() int { return len(s) }
func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// ByName implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded Organs value.
type ByName struct{ Organs }
func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }
// ByWeight implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded Organs value.
type ByWeight struct{ Organs }
func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }
func Example_sortWrapper() {
s := []*Organ{
{"brain", 1340},
{"heart", 290},
{"liver", 1494},
{"pancreas", 131},
{"prostate", 62},
{"spleen", 162},
}
sort.Sort(ByWeight{s})
fmt.Println("Organs by weight:")
printOrgans(s)
sort.Sort(ByName{s})
fmt.Println("Organs by name:")
printOrgans(s)
// Output:
// Organs by weight:
// prostate (62g)
// pancreas (131g)
// spleen (162g)
// heart (290g)
// brain (1340g)
// liver (1494g)
// Organs by name:
// brain (1340g)
// heart (290g)
// liver (1494g)
// pancreas (131g)
// prostate (62g)
// spleen (162g)
}
func printOrgans(s []*Organ) {
for _, o := range s {
fmt.Printf("%-8s (%v)\n", o.Name, o.Weight)
}
}

@ -1,9 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort
func Heapsort(data Interface) {
heapSort(data, 0, data.Len())
}

122
vendor/go4.org/sort/genzfunc.go generated vendored

@ -1,122 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// This program is run via "go generate" (via a directive in sort.go)
// to generate zfuncversion.go.
//
// It copies sort.go to zfuncversion.go, only retaining funcs which
// take a "data Interface" parameter, and renaming each to have a
// "_func" suffix and taking a "data lessSwap" instead. It then rewrites
// each internal function call to the appropriate _func variants.
package main
import (
"bytes"
"go/ast"
"go/format"
"go/parser"
"go/token"
"io/ioutil"
"log"
"regexp"
)
var fset = token.NewFileSet()
func main() {
af, err := parser.ParseFile(fset, "sort.go", nil, 0)
if err != nil {
log.Fatal(err)
}
af.Doc = nil
af.Imports = nil
af.Comments = nil
var newDecl []ast.Decl
for _, d := range af.Decls {
fd, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
if fd.Recv != nil || fd.Name.IsExported() {
continue
}
typ := fd.Type
if len(typ.Params.List) < 1 {
continue
}
arg0 := typ.Params.List[0]
arg0Name := arg0.Names[0].Name
arg0Type := arg0.Type.(*ast.Ident)
if arg0Name != "data" || arg0Type.Name != "Interface" {
continue
}
arg0Type.Name = "lessSwap"
newDecl = append(newDecl, fd)
}
af.Decls = newDecl
ast.Walk(visitFunc(rewriteCalls), af)
var out bytes.Buffer
if err := format.Node(&out, fset, af); err != nil {
log.Fatalf("format.Node: %v", err)
}
// Get rid of blank lines after removal of comments.
src := regexp.MustCompile(`\n{2,}`).ReplaceAll(out.Bytes(), []byte("\n"))
// Add comments to each func, for the lost reader.
// This is so much easier than adding comments via the AST
// and trying to get position info correct.
src = regexp.MustCompile(`(?m)^func (\w+)`).ReplaceAll(src, []byte("\n// Auto-generated variant of sort.go:$1\nfunc ${1}_func"))
// Final gofmt.
src, err = format.Source(src)
if err != nil {
log.Fatalf("format.Source: %v on\n%s", err, src)
}
out.Reset()
out.WriteString(`// DO NOT EDIT; AUTO-GENERATED from sort.go using genzfunc.go
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
`)
out.Write(src)
const target = "zfuncversion.go"
if err := ioutil.WriteFile(target, out.Bytes(), 0644); err != nil {
log.Fatal(err)
}
}
type visitFunc func(ast.Node) ast.Visitor
func (f visitFunc) Visit(n ast.Node) ast.Visitor { return f(n) }
func rewriteCalls(n ast.Node) ast.Visitor {
ce, ok := n.(*ast.CallExpr)
if ok {
rewriteCall(ce)
}
return visitFunc(rewriteCalls)
}
func rewriteCall(ce *ast.CallExpr) {
ident, ok := ce.Fun.(*ast.Ident)
if !ok {
// e.g. skip SelectorExpr (data.Less(..) calls)
return
}
if len(ce.Args) < 1 {
return
}
ident.Name += "_func"
}

112
vendor/go4.org/sort/search.go generated vendored

@ -1,112 +0,0 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements binary search.
package sort
// Search uses binary search to find and return the smallest index i
// in [0, n) at which f(i) is true, assuming that on the range [0, n),
// f(i) == true implies f(i+1) == true. That is, Search requires that
// f is false for some (possibly empty) prefix of the input range [0, n)
// and then true for the (possibly empty) remainder; Search returns
// the first true index. If there is no such index, Search returns n.
// (Note that the "not found" return value is not -1 as in, for instance,
// strings.Index.)
// Search calls f(i) only for i in the range [0, n).
//
// A common use of Search is to find the index i for a value x in
// a sorted, indexable data structure such as an array or slice.
// In this case, the argument f, typically a closure, captures the value
// to be searched for, and how the data structure is indexed and
// ordered.
//
// For instance, given a slice data sorted in ascending order,
// the call Search(len(data), func(i int) bool { return data[i] >= 23 })
// returns the smallest index i such that data[i] >= 23. If the caller
// wants to find whether 23 is in the slice, it must test data[i] == 23
// separately.
//
// Searching data sorted in descending order would use the <=
// operator instead of the >= operator.
//
// To complete the example above, the following code tries to find the value
// x in an integer slice data sorted in ascending order:
//
// x := 23
// i := sort.Search(len(data), func(i int) bool { return data[i] >= x })
// if i < len(data) && data[i] == x {
// // x is present at data[i]
// } else {
// // x is not present in data,
// // but i is the index where it would be inserted.
// }
//
// As a more whimsical example, this program guesses your number:
//
// func GuessingGame() {
// var s string
// fmt.Printf("Pick an integer from 0 to 100.\n")
// answer := sort.Search(100, func(i int) bool {
// fmt.Printf("Is your number <= %d? ", i)
// fmt.Scanf("%s", &s)
// return s != "" && s[0] == 'y'
// })
// fmt.Printf("Your number is %d.\n", answer)
// }
//
func Search(n int, f func(int) bool) int {
// Define f(-1) == false and f(n) == true.
// Invariant: f(i-1) == false, f(j) == true.
i, j := 0, n
for i < j {
h := i + (j-i)/2 // avoid overflow when computing h
// i ≤ h < j
if !f(h) {
i = h + 1 // preserves f(i-1) == false
} else {
j = h // preserves f(j) == true
}
}
// i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
return i
}
// Convenience wrappers for common cases.
// SearchInts searches for x in a sorted slice of ints and returns the index
// as specified by Search. The return value is the index to insert x if x is
// not present (it could be len(a)).
// The slice must be sorted in ascending order.
//
func SearchInts(a []int, x int) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
// SearchFloat64s searches for x in a sorted slice of float64s and returns the index
// as specified by Search. The return value is the index to insert x if x is not
// present (it could be len(a)).
// The slice must be sorted in ascending order.
//
func SearchFloat64s(a []float64, x float64) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
// SearchStrings searches for x in a sorted slice of strings and returns the index
// as specified by Search. The return value is the index to insert x if x is not
// present (it could be len(a)).
// The slice must be sorted in ascending order.
//
func SearchStrings(a []string, x string) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
// Search returns the result of applying SearchInts to the receiver and x.
func (p IntSlice) Search(x int) int { return SearchInts(p, x) }
// Search returns the result of applying SearchFloat64s to the receiver and x.
func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }
// Search returns the result of applying SearchStrings to the receiver and x.
func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }

161
vendor/go4.org/sort/search_test.go generated vendored

@ -1,161 +0,0 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"runtime"
. "sort"
"testing"
)
func f(a []int, x int) func(int) bool {
return func(i int) bool {
return a[i] >= x
}
}
var data = []int{0: -10, 1: -5, 2: 0, 3: 1, 4: 2, 5: 3, 6: 5, 7: 7, 8: 11, 9: 100, 10: 100, 11: 100, 12: 1000, 13: 10000}
var tests = []struct {
name string
n int
f func(int) bool
i int
}{
{"empty", 0, nil, 0},
{"1 1", 1, func(i int) bool { return i >= 1 }, 1},
{"1 true", 1, func(i int) bool { return true }, 0},
{"1 false", 1, func(i int) bool { return false }, 1},
{"1e9 991", 1e9, func(i int) bool { return i >= 991 }, 991},
{"1e9 true", 1e9, func(i int) bool { return true }, 0},
{"1e9 false", 1e9, func(i int) bool { return false }, 1e9},
{"data -20", len(data), f(data, -20), 0},
{"data -10", len(data), f(data, -10), 0},
{"data -9", len(data), f(data, -9), 1},
{"data -6", len(data), f(data, -6), 1},
{"data -5", len(data), f(data, -5), 1},
{"data 3", len(data), f(data, 3), 5},
{"data 11", len(data), f(data, 11), 8},
{"data 99", len(data), f(data, 99), 9},
{"data 100", len(data), f(data, 100), 9},
{"data 101", len(data), f(data, 101), 12},
{"data 10000", len(data), f(data, 10000), 13},
{"data 10001", len(data), f(data, 10001), 14},
{"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] <= 7 }, 4},
{"descending 7", 1e9, func(i int) bool { return 1e9-i <= 7 }, 1e9 - 7},
{"overflow", 2e9, func(i int) bool { return false }, 2e9},
}
func TestSearch(t *testing.T) {
for _, e := range tests {
i := Search(e.n, e.f)
if i != e.i {
t.Errorf("%s: expected index %d; got %d", e.name, e.i, i)
}
}
}
// log2 computes the binary logarithm of x, rounded up to the next integer.
// (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.)
//
func log2(x int) int {
n := 0
for p := 1; p < x; p += p {
// p == 2**n
n++
}
// p/2 < x <= p == 2**n
return n
}
func TestSearchEfficiency(t *testing.T) {
n := 100
step := 1
for exp := 2; exp < 10; exp++ {
// n == 10**exp
// step == 10**(exp-2)
max := log2(n)
for x := 0; x < n; x += step {
count := 0
i := Search(n, func(i int) bool { count++; return i >= x })
if i != x {
t.Errorf("n = %d: expected index %d; got %d", n, x, i)
}
if count > max {
t.Errorf("n = %d, x = %d: expected <= %d calls; got %d", n, x, max, count)
}
}
n *= 10
step *= 10
}
}
// Smoke tests for convenience wrappers - not comprehensive.
var fdata = []float64{0: -3.14, 1: 0, 2: 1, 3: 2, 4: 1000.7}
var sdata = []string{0: "f", 1: "foo", 2: "foobar", 3: "x"}
var wrappertests = []struct {
name string
result int
i int
}{
{"SearchInts", SearchInts(data, 11), 8},
{"SearchFloat64s", SearchFloat64s(fdata, 2.1), 4},
{"SearchStrings", SearchStrings(sdata, ""), 0},
{"IntSlice.Search", IntSlice(data).Search(0), 2},
{"Float64Slice.Search", Float64Slice(fdata).Search(2.0), 3},
{"StringSlice.Search", StringSlice(sdata).Search("x"), 3},
}
func TestSearchWrappers(t *testing.T) {
for _, e := range wrappertests {
if e.result != e.i {
t.Errorf("%s: expected index %d; got %d", e.name, e.i, e.result)
}
}
}
func runSearchWrappers() {
SearchInts(data, 11)
SearchFloat64s(fdata, 2.1)
SearchStrings(sdata, "")
IntSlice(data).Search(0)
Float64Slice(fdata).Search(2.0)
StringSlice(sdata).Search("x")
}
func TestSearchWrappersDontAlloc(t *testing.T) {
if testing.Short() {
t.Skip("skipping malloc count in short mode")
}
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
allocs := testing.AllocsPerRun(100, runSearchWrappers)
if allocs != 0 {
t.Errorf("expected no allocs for runSearchWrappers, got %v", allocs)
}
}
func BenchmarkSearchWrappers(b *testing.B) {
for i := 0; i < b.N; i++ {
runSearchWrappers()
}
}
// Abstract exhaustive test: all sizes up to 100,
// all possible return values. If there are any small
// corner cases, this test exercises them.
func TestSearchExhaustive(t *testing.T) {
for size := 0; size <= 100; size++ {
for targ := 0; targ <= size; targ++ {
i := Search(size, func(i int) bool { return i >= targ })
if i != targ {
t.Errorf("Search(%d, %d) = %d", size, targ, i)
}
}
}
}

623
vendor/go4.org/sort/sort.go generated vendored

@ -1,623 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run genzfunc.go
// Package sort provides primitives for sorting slices and user-defined
// collections.
//
// This is a copy of the Go standard library's sort package with the
// addition of some helpers for sorting slices and using func literals
// to sort, rather than having to create a sorter type. See the
// additional MakeInterface, SliceSorter, and Slice functions.
// Discussion of moving such helpers into the standard library is
// at:
//
// https://golang.org/issue/16721
//
// Per Go's "no +1 policy", please only leave a comment on that issue
// if you have something unique to add. Use Github's emoji reactions
// otherwise.
package sort
import (
"reflect"
"go4.org/reflectutil"
)
// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
// lessSwap is a pair of Less and Swap function for use with the
// auto-generated func-optimized variant of sort.go in
// zfuncversion.go.
type lessSwap struct {
Less func(i, j int) bool
Swap func(i, j int)
}
// MakeInterface returns a sort Interface using the provided length
// and pair of swap and less functions.
func MakeInterface(length int, swap func(i, j int), less func(i, j int) bool) Interface {
return &funcs{length, lessSwap{less, swap}}
}
// SliceSorter returns a sort.Interface to sort the provided slice
// using the provided less function.
// If the provided interface is not a slice, the function panics.
func SliceSorter(slice interface{}, less func(i, j int) bool) Interface {
return MakeInterface(reflect.ValueOf(slice).Len(), reflectutil.Swapper(slice), less)
}
// Slice sorts the provided slice using less.
// If the provided interface is not a slice, the function panics.
// The sort is not stable. For a stable sort, use sort.Stable with sort.SliceSorter.
func Slice(slice interface{}, less func(i, j int) bool) {
Sort(SliceSorter(slice, less))
}
// funcs implements Interface, but is recognized by Sort and Stable
// which use its lessSwap field with the non-interface sorting
// routines in zfuncversion.go.
type funcs struct {
length int
lessSwap
}
func (f *funcs) Len() int { return f.length }
func (f *funcs) Swap(i, j int) { f.lessSwap.Swap(i, j) }
func (f *funcs) Less(i, j int) bool { return f.lessSwap.Less(i, j) }
// Insertion sort
func insertionSort(data Interface, a, b int) {
for i := a + 1; i < b; i++ {
for j := i; j > a && data.Less(j, j-1); j-- {
data.Swap(j, j-1)
}
}
}
// siftDown implements the heap property on data[lo, hi).
// first is an offset into the array where the root of the heap lies.
func siftDown(data Interface, lo, hi, first int) {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && data.Less(first+child, first+child+1) {
child++
}
if !data.Less(first+root, first+child) {
return
}
data.Swap(first+root, first+child)
root = child
}
}
func heapSort(data Interface, a, b int) {
first := a
lo := 0
hi := b - a
// Build heap with greatest element at top.
for i := (hi - 1) / 2; i >= 0; i-- {
siftDown(data, i, hi, first)
}
// Pop elements, largest first, into end of data.
for i := hi - 1; i >= 0; i-- {
data.Swap(first, first+i)
siftDown(data, lo, i, first)
}
}
// Quicksort, loosely following Bentley and McIlroy,
// ``Engineering a Sort Function,'' SP&E November 1993.
// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
func medianOfThree(data Interface, m1, m0, m2 int) {
// sort 3 elements
if data.Less(m1, m0) {
data.Swap(m1, m0)
}
// data[m0] <= data[m1]
if data.Less(m2, m1) {
data.Swap(m2, m1)
// data[m0] <= data[m2] && data[m1] < data[m2]
if data.Less(m1, m0) {
data.Swap(m1, m0)
}
}
// now data[m0] <= data[m1] <= data[m2]
}
func swapRange(data Interface, a, b, n int) {
for i := 0; i < n; i++ {
data.Swap(a+i, b+i)
}
}
func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
m := lo + (hi-lo)/2 // Written like this to avoid integer overflow.
if hi-lo > 40 {
// Tukey's ``Ninther,'' median of three medians of three.
s := (hi - lo) / 8
medianOfThree(data, lo, lo+s, lo+2*s)
medianOfThree(data, m, m-s, m+s)
medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
}
medianOfThree(data, lo, m, hi-1)
// Invariants are:
// data[lo] = pivot (set up by ChoosePivot)
// data[lo < i < a] < pivot
// data[a <= i < b] <= pivot
// data[b <= i < c] unexamined
// data[c <= i < hi-1] > pivot
// data[hi-1] >= pivot
pivot := lo
a, c := lo+1, hi-1
for ; a < c && data.Less(a, pivot); a++ {
}
b := a
for {
for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot
}
for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
}
if b >= c {
break
}
// data[b] > pivot; data[c-1] <= pivot
data.Swap(b, c-1)
b++
c--
}
// If hi-c<3 then there are duplicates (by property of median of nine).
// Let be a bit more conservative, and set border to 5.
protect := hi-c < 5
if !protect && hi-c < (hi-lo)/4 {
// Lets test some points for equality to pivot
dups := 0
if !data.Less(pivot, hi-1) { // data[hi-1] = pivot
data.Swap(c, hi-1)
c++
dups++
}
if !data.Less(b-1, pivot) { // data[b-1] = pivot
b--
dups++
}
// m-lo = (hi-lo)/2 > 6
// b-lo > (hi-lo)*3/4-1 > 8
// ==> m < b ==> data[m] <= pivot
if !data.Less(m, pivot) { // data[m] = pivot
data.Swap(m, b-1)
b--
dups++
}
// if at least 2 points are equal to pivot, assume skewed distribution
protect = dups > 1
}
if protect {
// Protect against a lot of duplicates
// Add invariant:
// data[a <= i < b] unexamined
// data[b <= i < c] = pivot
for {
for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot
}
for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot
}
if a >= b {
break
}
// data[a] == pivot; data[b-1] < pivot
data.Swap(a, b-1)
a++
b--
}
}
// Swap pivot into middle
data.Swap(pivot, b-1)
return b - 1, c
}
func quickSort(data Interface, a, b, maxDepth int) {
for b-a > 12 { // Use ShellSort for slices <= 12 elements
if maxDepth == 0 {
heapSort(data, a, b)
return
}
maxDepth--
mlo, mhi := doPivot(data, a, b)
// Avoiding recursion on the larger subproblem guarantees
// a stack depth of at most lg(b-a).
if mlo-a < b-mhi {
quickSort(data, a, mlo, maxDepth)
a = mhi // i.e., quickSort(data, mhi, b)
} else {
quickSort(data, mhi, b, maxDepth)
b = mlo // i.e., quickSort(data, a, mlo)
}
}
if b-a > 1 {
// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
if data.Less(i, i-6) {
data.Swap(i, i-6)
}
}
insertionSort(data, a, b)
}
}
// Sort sorts data.
//
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
//
// To sort slices without creating a type, see Slice.
func Sort(data Interface) {
n := data.Len()
if fs, ok := data.(*funcs); ok {
quickSort_func(fs.lessSwap, 0, n, maxDepth(n))
} else {
quickSort(data, 0, n, maxDepth(n))
}
}
// With sorts data given the provided length, swap, and less
// functions.
// The sort is not guaranteed to be stable.
func With(length int, swap func(i, j int), less func(i, j int) bool) {
quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
}
// maxDepth returns a threshold at which quicksort should switch
// to heapsort. It returns 2*ceil(lg(n+1)).
func maxDepth(n int) int {
var depth int
for i := n; i > 0; i >>= 1 {
depth++
}
return depth * 2
}
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
}
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
// IsSorted reports whether data is sorted.
func IsSorted(data Interface) bool {
n := data.Len()
for i := n - 1; i > 0; i-- {
if data.Less(i, i-1) {
return false
}
}
return true
}
// Convenience types for common cases
// IntSlice attaches the methods of Interface to []int, sorting in increasing order.
type IntSlice []int
func (p IntSlice) Len() int { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// Sort is a convenience method.
func (p IntSlice) Sort() { Sort(p) }
// Float64Slice attaches the methods of Interface to []float64, sorting in increasing order.
type Float64Slice []float64
func (p Float64Slice) Len() int { return len(p) }
func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }
func (p Float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// isNaN is a copy of math.IsNaN to avoid a dependency on the math package.
func isNaN(f float64) bool {
return f != f
}
// Sort is a convenience method.
func (p Float64Slice) Sort() { Sort(p) }
// StringSlice attaches the methods of Interface to []string, sorting in increasing order.
type StringSlice []string
func (p StringSlice) Len() int { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// Sort is a convenience method.
func (p StringSlice) Sort() { Sort(p) }
// Convenience wrappers for common cases
// Ints sorts a slice of ints in increasing order.
func Ints(a []int) { Sort(IntSlice(a)) }
// Float64s sorts a slice of float64s in increasing order.
func Float64s(a []float64) { Sort(Float64Slice(a)) }
// Strings sorts a slice of strings in increasing order.
func Strings(a []string) { Sort(StringSlice(a)) }
// IntsAreSorted tests whether a slice of ints is sorted in increasing order.
func IntsAreSorted(a []int) bool { return IsSorted(IntSlice(a)) }
// Float64sAreSorted tests whether a slice of float64s is sorted in increasing order.
func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) }
// StringsAreSorted tests whether a slice of strings is sorted in increasing order.
func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) }
// Notes on stable sorting:
// The used algorithms are simple and provable correct on all input and use
// only logarithmic additional stack space. They perform well if compared
// experimentally to other stable in-place sorting algorithms.
//
// Remarks on other algorithms evaluated:
// - GCC's 4.6.3 stable_sort with merge_without_buffer from libstdc++:
// Not faster.
// - GCC's __rotate for block rotations: Not faster.
// - "Practical in-place mergesort" from Jyrki Katajainen, Tomi A. Pasanen
// and Jukka Teuhola; Nordic Journal of Computing 3,1 (1996), 27-40:
// The given algorithms are in-place, number of Swap and Assignments
// grow as n log n but the algorithm is not stable.
// - "Fast Stable In-Place Sorting with O(n) Data Moves" J.I. Munro and
// V. Raman in Algorithmica (1996) 16, 115-160:
// This algorithm either needs additional 2n bits or works only if there
// are enough different elements available to encode some permutations
// which have to be undone later (so not stable on any input).
// - All the optimal in-place sorting/merging algorithms I found are either
// unstable or rely on enough different elements in each step to encode the
// performed block rearrangements. See also "In-Place Merging Algorithms",
// Denham Coates-Evely, Department of Computer Science, Kings College,
// January 2004 and the references in there.
// - Often "optimal" algorithms are optimal in the number of assignments
// but Interface has only Swap as operation.
// Stable sorts data while keeping the original order of equal elements.
//
// It makes one call to data.Len to determine n, O(n*log(n)) calls to
// data.Less and O(n*log(n)*log(n)) calls to data.Swap.
func Stable(data Interface) {
if fs, ok := data.(*funcs); ok {
stable_func(fs.lessSwap, fs.length)
} else {
stable(data, data.Len())
}
}
func stable(data Interface, n int) {
blockSize := 20 // must be > 0
a, b := 0, blockSize
for b <= n {
insertionSort(data, a, b)
a = b
b += blockSize
}
insertionSort(data, a, n)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
symMerge(data, a, a+blockSize, b)
a = b
b += 2 * blockSize
}
if m := a + blockSize; m < n {
symMerge(data, a, m, n)
}
blockSize *= 2
}
}
// SymMerge merges the two sorted subsequences data[a:m] and data[m:b] using
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
// Computer Science, pages 714-723. Springer, 2004.
//
// Let M = m-a and N = b-n. Wolog M < N.
// The recursion depth is bound by ceil(log(N+M)).
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
//
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
// in the paper carries through for Swap operations, especially as the block
// swapping rotate uses only O(M+N) Swaps.
//
// symMerge assumes non-degenerate arguments: a < m && m < b.
// Having the caller check this condition eliminates many leaf recursion calls,
// which improves performance.
func symMerge(data Interface, a, m, b int) {
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[a] into data[m:b]
// if data[a:m] only contains one element.
if m-a == 1 {
// Use binary search to find the lowest index i
// such that data[i] >= data[a] for m <= i < b.
// Exit the search loop with i == b in case no such index exists.
i := m
j := b
for i < j {
h := i + (j-i)/2
if data.Less(h, a) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[a] reaches the position before i.
for k := a; k < i-1; k++ {
data.Swap(k, k+1)
}
return
}
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[m] into data[a:m]
// if data[m:b] only contains one element.
if b-m == 1 {
// Use binary search to find the lowest index i
// such that data[i] > data[m] for a <= i < m.
// Exit the search loop with i == m in case no such index exists.
i := a
j := m
for i < j {
h := i + (j-i)/2
if !data.Less(m, h) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[m] reaches the position i.
for k := m; k > i; k-- {
data.Swap(k, k-1)
}
return
}
mid := a + (b-a)/2
n := mid + m
var start, r int
if m > mid {
start = n - b
r = mid
} else {
start = a
r = m
}
p := n - 1
for start < r {
c := start + (r-start)/2
if !data.Less(p-c, c) {
start = c + 1
} else {
r = c
}
}
end := n - start
if start < m && m < end {
rotate(data, start, m, end)
}
if a < start && start < mid {
symMerge(data, a, start, mid)
}
if mid < end && end < b {
symMerge(data, mid, end, b)
}
}
// Rotate two consecutives blocks u = data[a:m] and v = data[m:b] in data:
// Data of the form 'x u v y' is changed to 'x v u y'.
// Rotate performs at most b-a many calls to data.Swap.
// Rotate assumes non-degenerate arguments: a < m && m < b.
func rotate(data Interface, a, m, b int) {
i := m - a
j := b - m
for i != j {
if i > j {
swapRange(data, m-i, m, j)
i -= j
} else {
swapRange(data, m-i, m+j-i, i)
j -= i
}
}
// i == j
swapRange(data, m-i, m, i)
}
/*
Complexity of Stable Sorting
Complexity of block swapping rotation
Each Swap puts one new element into its correct, final position.
Elements which reach their final position are no longer moved.
Thus block swapping rotation needs |u|+|v| calls to Swaps.
This is best possible as each element might need a move.
Pay attention when comparing to other optimal algorithms which
typically count the number of assignments instead of swaps:
E.g. the optimal algorithm of Dudzinski and Dydek for in-place
rotations uses O(u + v + gcd(u,v)) assignments which is
better than our O(3 * (u+v)) as gcd(u,v) <= u.
Stable sorting by SymMerge and BlockSwap rotations
SymMerg complexity for same size input M = N:
Calls to Less: O(M*log(N/M+1)) = O(N*log(2)) = O(N)
Calls to Swap: O((M+N)*log(M)) = O(2*N*log(N)) = O(N*log(N))
(The following argument does not fuzz over a missing -1 or
other stuff which does not impact the final result).
Let n = data.Len(). Assume n = 2^k.
Plain merge sort performs log(n) = k iterations.
On iteration i the algorithm merges 2^(k-i) blocks, each of size 2^i.
Thus iteration i of merge sort performs:
Calls to Less O(2^(k-i) * 2^i) = O(2^k) = O(2^log(n)) = O(n)
Calls to Swap O(2^(k-i) * 2^i * log(2^i)) = O(2^k * i) = O(n*i)
In total k = log(n) iterations are performed; so in total:
Calls to Less O(log(n) * n)
Calls to Swap O(n + 2*n + 3*n + ... + (k-1)*n + k*n)
= O((k/2) * k * n) = O(n * k^2) = O(n * log^2(n))
Above results should generalize to arbitrary n = 2^k + p
and should not be influenced by the initial insertion sort phase:
Insertion sort is O(n^2) on Swap and Less, thus O(bs^2) per block of
size bs at n/bs blocks: O(bs*n) Swaps and Less during insertion sort.
Merge sort iterations start at i = log(bs). With t = log(bs) constant:
Calls to Less O((log(n)-t) * n + bs*n) = O(log(n)*n + (bs-t)*n)
= O(n * log(n))
Calls to Swap O(n * log^2(n) - (t^2+t)/2*n) = O(n * log^2(n))
*/

695
vendor/go4.org/sort/sort_test.go generated vendored

@ -1,695 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"math"
"math/rand"
"reflect"
"strconv"
"testing"
"go4.org/reflectutil"
. "go4.org/sort"
)
var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
var float64s = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
var strings = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
func TestSlice(t *testing.T) {
s := []int{5, 4, 3, 2, 1}
want := []int{1, 2, 3, 4, 5}
Slice(s, func(i, j int) bool { return s[i] < s[j] })
if !reflect.DeepEqual(s, want) {
t.Errorf("sorted = %v; want %v", s, want)
}
}
func TestSortIntSlice(t *testing.T) {
data := ints
a := IntSlice(data[0:])
Sort(a)
if !IsSorted(a) {
t.Errorf("sorted %v", ints)
t.Errorf(" got %v", data)
}
}
func TestSortFloat64Slice(t *testing.T) {
data := float64s
a := Float64Slice(data[0:])
Sort(a)
if !IsSorted(a) {
t.Errorf("sorted %v", float64s)
t.Errorf(" got %v", data)
}
}
func TestSortStringSlice(t *testing.T) {
data := strings
a := StringSlice(data[0:])
Sort(a)
if !IsSorted(a) {
t.Errorf("sorted %v", strings)
t.Errorf(" got %v", data)
}
}
func TestInts(t *testing.T) {
data := ints
Ints(data[0:])
if !IntsAreSorted(data[0:]) {
t.Errorf("sorted %v", ints)
t.Errorf(" got %v", data)
}
}
func TestFloat64s(t *testing.T) {
data := float64s
Float64s(data[0:])
if !Float64sAreSorted(data[0:]) {
t.Errorf("sorted %v", float64s)
t.Errorf(" got %v", data)
}
}
func TestStrings(t *testing.T) {
data := strings
Strings(data[0:])
if !StringsAreSorted(data[0:]) {
t.Errorf("sorted %v", strings)
t.Errorf(" got %v", data)
}
}
func TestStringsWithSwapper(t *testing.T) {
data := strings
With(len(data), reflectutil.Swapper(data[:]), func(i, j int) bool {
return data[i] < data[j]
})
if !StringsAreSorted(data[:]) {
t.Errorf("sorted %v", strings)
t.Errorf(" got %v", data)
}
}
func TestSortLarge_Random(t *testing.T) {
n := 1000000
if testing.Short() {
n /= 100
}
data := make([]int, n)
for i := 0; i < len(data); i++ {
data[i] = rand.Intn(100)
}
if IntsAreSorted(data) {
t.Fatalf("terrible rand.rand")
}
Ints(data)
if !IntsAreSorted(data) {
t.Errorf("sort didn't sort - 1M ints")
}
}
func TestReverseSortIntSlice(t *testing.T) {
data := ints
data1 := ints
a := IntSlice(data[0:])
Sort(a)
r := IntSlice(data1[0:])
Sort(Reverse(r))
for i := 0; i < len(data); i++ {
if a[i] != r[len(data)-1-i] {
t.Errorf("reverse sort didn't sort")
}
if i > len(data)/2 {
break
}
}
}
type nonDeterministicTestingData struct {
r *rand.Rand
}
func (t *nonDeterministicTestingData) Len() int {
return 500
}
func (t *nonDeterministicTestingData) Less(i, j int) bool {
if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() {
panic("nondeterministic comparison out of bounds")
}
return t.r.Float32() < 0.5
}
func (t *nonDeterministicTestingData) Swap(i, j int) {
if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() {
panic("nondeterministic comparison out of bounds")
}
}
func TestNonDeterministicComparison(t *testing.T) {
// Ensure that sort.Sort does not panic when Less returns inconsistent results.
// See https://golang.org/issue/14377.
defer func() {
if r := recover(); r != nil {
t.Error(r)
}
}()
td := &nonDeterministicTestingData{
r: rand.New(rand.NewSource(0)),
}
for i := 0; i < 10; i++ {
Sort(td)
}
}
func BenchmarkSortString1K(b *testing.B) {
b.StopTimer()
unsorted := make([]string, 1<<10)
for i := range unsorted {
unsorted[i] = strconv.Itoa(i ^ 0x2cc)
}
data := make([]string, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
Strings(data)
b.StopTimer()
}
}
func BenchmarkSortString1K_With(b *testing.B) {
b.StopTimer()
unsorted := make([]string, 1<<10)
for i := range unsorted {
unsorted[i] = strconv.Itoa(i ^ 0x2cc)
}
data := make([]string, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
With(len(data),
func(i, j int) { data[i], data[j] = data[j], data[i] },
func(i, j int) bool {
return data[i] < data[j]
})
b.StopTimer()
}
}
func BenchmarkSortString1K_WithSwapper(b *testing.B) {
b.StopTimer()
unsorted := make([]string, 1<<10)
for i := range unsorted {
unsorted[i] = strconv.Itoa(i ^ 0x2cc)
}
data := make([]string, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
With(len(data), reflectutil.Swapper(data), func(i, j int) bool {
return data[i] < data[j]
})
b.StopTimer()
}
}
func BenchmarkStableString1K(b *testing.B) {
b.StopTimer()
unsorted := make([]string, 1<<10)
for i := 0; i < len(data); i++ {
unsorted[i] = strconv.Itoa(i ^ 0x2cc)
}
data := make([]string, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
Stable(StringSlice(data))
b.StopTimer()
}
}
func BenchmarkSortInt1K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
data := make([]int, 1<<10)
for i := 0; i < len(data); i++ {
data[i] = i ^ 0x2cc
}
b.StartTimer()
Ints(data)
b.StopTimer()
}
}
func BenchmarkStableInt1K(b *testing.B) {
b.StopTimer()
unsorted := make([]int, 1<<10)
for i := range unsorted {
unsorted[i] = i ^ 0x2cc
}
data := make([]int, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
Stable(IntSlice(data))
b.StopTimer()
}
}
func BenchmarkStableInt1K_With(b *testing.B) {
b.StopTimer()
unsorted := make([]int, 1<<10)
for i := range unsorted {
unsorted[i] = i ^ 0x2cc
}
data := make([]int, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
Stable(MakeInterface(
len(data),
func(i, j int) { data[i], data[j] = data[j], data[i] },
func(i, j int) bool { return data[i] < data[j] },
))
b.StopTimer()
}
}
func BenchmarkStableInt1K_WithSwapper(b *testing.B) {
b.StopTimer()
unsorted := make([]int, 1<<10)
for i := range unsorted {
unsorted[i] = i ^ 0x2cc
}
data := make([]int, len(unsorted))
for i := 0; i < b.N; i++ {
copy(data, unsorted)
b.StartTimer()
Stable(MakeInterface(len(data), reflectutil.Swapper(data), func(i, j int) bool {
return data[i] < data[j]
}))
b.StopTimer()
}
}
func BenchmarkSortInt64K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
data := make([]int, 1<<16)
for i := 0; i < len(data); i++ {
data[i] = i ^ 0xcccc
}
b.StartTimer()
Ints(data)
b.StopTimer()
}
}
func BenchmarkStableInt64K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
data := make([]int, 1<<16)
for i := 0; i < len(data); i++ {
data[i] = i ^ 0xcccc
}
b.StartTimer()
Stable(IntSlice(data))
b.StopTimer()
}
}
const (
_Sawtooth = iota
_Rand
_Stagger
_Plateau
_Shuffle
_NDist
)
const (
_Copy = iota
_Reverse
_ReverseFirstHalf
_ReverseSecondHalf
_Sorted
_Dither
_NMode
)
type testingData struct {
desc string
t *testing.T
data []int
maxswap int // number of swaps allowed
ncmp, nswap int
}
func (d *testingData) Len() int { return len(d.data) }
func (d *testingData) Less(i, j int) bool {
d.ncmp++
return d.data[i] < d.data[j]
}
func (d *testingData) Swap(i, j int) {
if d.nswap >= d.maxswap {
d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data))
d.t.FailNow()
}
d.nswap++
d.data[i], d.data[j] = d.data[j], d.data[i]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func lg(n int) int {
i := 0
for 1<<uint(i) < n {
i++
}
return i
}
func testBentleyMcIlroy(t *testing.T, sort func(Interface), maxswap func(int) int) {
sizes := []int{100, 1023, 1024, 1025}
if testing.Short() {
sizes = []int{100, 127, 128, 129}
}
dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
var tmp1, tmp2 [1025]int
for _, n := range sizes {
for m := 1; m < 2*n; m *= 2 {
for dist := 0; dist < _NDist; dist++ {
j := 0
k := 1
data := tmp1[0:n]
for i := 0; i < n; i++ {
switch dist {
case _Sawtooth:
data[i] = i % m
case _Rand:
data[i] = rand.Intn(m)
case _Stagger:
data[i] = (i*m + i) % n
case _Plateau:
data[i] = min(i, m)
case _Shuffle:
if rand.Intn(m) != 0 {
j += 2
data[i] = j
} else {
k += 2
data[i] = k
}
}
}
mdata := tmp2[0:n]
for mode := 0; mode < _NMode; mode++ {
switch mode {
case _Copy:
for i := 0; i < n; i++ {
mdata[i] = data[i]
}
case _Reverse:
for i := 0; i < n; i++ {
mdata[i] = data[n-i-1]
}
case _ReverseFirstHalf:
for i := 0; i < n/2; i++ {
mdata[i] = data[n/2-i-1]
}
for i := n / 2; i < n; i++ {
mdata[i] = data[i]
}
case _ReverseSecondHalf:
for i := 0; i < n/2; i++ {
mdata[i] = data[i]
}
for i := n / 2; i < n; i++ {
mdata[i] = data[n-(i-n/2)-1]
}
case _Sorted:
for i := 0; i < n; i++ {
mdata[i] = data[i]
}
// Ints is known to be correct
// because mode Sort runs after mode _Copy.
Ints(mdata)
case _Dither:
for i := 0; i < n; i++ {
mdata[i] = data[i] + i%5
}
}
desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode])
d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: maxswap(n)}
sort(d)
// Uncomment if you are trying to improve the number of compares/swaps.
//t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap)
// If we were testing C qsort, we'd have to make a copy
// of the slice and sort it ourselves and then compare
// x against it, to ensure that qsort was only permuting
// the data, not (for example) overwriting it with zeros.
//
// In go, we don't have to be so paranoid: since the only
// mutating method Sort can call is TestingData.swap,
// it suffices here just to check that the final slice is sorted.
if !IntsAreSorted(mdata) {
t.Errorf("%s: ints not sorted", desc)
t.Errorf("\t%v", mdata)
t.FailNow()
}
}
}
}
}
}
func TestSortBM(t *testing.T) {
testBentleyMcIlroy(t, Sort, func(n int) int { return n * lg(n) * 12 / 10 })
}
func TestHeapsortBM(t *testing.T) {
testBentleyMcIlroy(t, Heapsort, func(n int) int { return n * lg(n) * 12 / 10 })
}
func TestStableBM(t *testing.T) {
testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 })
}
// This is based on the "antiquicksort" implementation by M. Douglas McIlroy.
// See http://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info.
type adversaryTestingData struct {
data []int
keys map[int]int
candidate int
}
func (d *adversaryTestingData) Len() int { return len(d.data) }
func (d *adversaryTestingData) Less(i, j int) bool {
if _, present := d.keys[i]; !present {
if _, present := d.keys[j]; !present {
if i == d.candidate {
d.keys[i] = len(d.keys)
} else {
d.keys[j] = len(d.keys)
}
}
}
if _, present := d.keys[i]; !present {
d.candidate = i
return false
}
if _, present := d.keys[j]; !present {
d.candidate = j
return true
}
return d.keys[i] >= d.keys[j]
}
func (d *adversaryTestingData) Swap(i, j int) {
d.data[i], d.data[j] = d.data[j], d.data[i]
}
func TestAdversary(t *testing.T) {
const size = 100
data := make([]int, size)
for i := 0; i < size; i++ {
data[i] = i
}
d := &adversaryTestingData{data, make(map[int]int), 0}
Sort(d) // This should degenerate to heapsort.
}
func TestStableInts(t *testing.T) {
data := ints
Stable(IntSlice(data[0:]))
if !IntsAreSorted(data[0:]) {
t.Errorf("nsorted %v\n got %v", ints, data)
}
}
type intPairs []struct {
a, b int
}
// IntPairs compare on a only.
func (d intPairs) Len() int { return len(d) }
func (d intPairs) Less(i, j int) bool { return d[i].a < d[j].a }
func (d intPairs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
// Record initial order in B.
func (d intPairs) initB() {
for i := range d {
d[i].b = i
}
}
// InOrder checks if a-equal elements were not reordered.
func (d intPairs) inOrder() bool {
lastA, lastB := -1, 0
for i := 0; i < len(d); i++ {
if lastA != d[i].a {
lastA = d[i].a
lastB = d[i].b
continue
}
if d[i].b <= lastB {
return false
}
lastB = d[i].b
}
return true
}
func TestStability(t *testing.T) {
n, m := 100000, 1000
if testing.Short() {
n, m = 1000, 100
}
data := make(intPairs, n)
// random distribution
for i := 0; i < len(data); i++ {
data[i].a = rand.Intn(m)
}
if IsSorted(data) {
t.Fatalf("terrible rand.rand")
}
data.initB()
Stable(data)
if !IsSorted(data) {
t.Errorf("Stable didn't sort %d ints", n)
}
if !data.inOrder() {
t.Errorf("Stable wasn't stable on %d ints", n)
}
// already sorted
data.initB()
Stable(data)
if !IsSorted(data) {
t.Errorf("Stable shuffled sorted %d ints (order)", n)
}
if !data.inOrder() {
t.Errorf("Stable shuffled sorted %d ints (stability)", n)
}
// sorted reversed
for i := 0; i < len(data); i++ {
data[i].a = len(data) - i
}
data.initB()
Stable(data)
if !IsSorted(data) {
t.Errorf("Stable didn't sort %d ints", n)
}
if !data.inOrder() {
t.Errorf("Stable wasn't stable on %d ints", n)
}
}
var countOpsSizes = []int{1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6}
func countOps(t *testing.T, algo func(Interface), name string) {
sizes := countOpsSizes
if testing.Short() {
sizes = sizes[:5]
}
if !testing.Verbose() {
t.Skip("Counting skipped as non-verbose mode.")
}
for _, n := range sizes {
td := testingData{
desc: name,
t: t,
data: make([]int, n),
maxswap: 1<<31 - 1,
}
for i := 0; i < n; i++ {
td.data[i] = rand.Intn(n / 5)
}
algo(&td)
t.Logf("%s %8d elements: %11d Swap, %10d Less", name, n, td.nswap, td.ncmp)
}
}
func TestCountStableOps(t *testing.T) { countOps(t, Stable, "Stable") }
func TestCountSortOps(t *testing.T) { countOps(t, Sort, "Sort ") }
func bench(b *testing.B, size int, algo func(Interface), name string) {
b.StopTimer()
data := make(intPairs, size)
x := ^uint32(0)
for i := 0; i < b.N; i++ {
for n := size - 3; n <= size+3; n++ {
for i := 0; i < len(data); i++ {
x += x
x ^= 1
if int32(x) < 0 {
x ^= 0x88888eef
}
data[i].a = int(x % uint32(n/5))
}
data.initB()
b.StartTimer()
algo(data)
b.StopTimer()
if !IsSorted(data) {
b.Errorf("%s did not sort %d ints", name, n)
}
if name == "Stable" && !data.inOrder() {
b.Errorf("%s unstable on %d ints", name, n)
}
}
}
}
func BenchmarkSort1e2(b *testing.B) { bench(b, 1e2, Sort, "Sort") }
func BenchmarkStable1e2(b *testing.B) { bench(b, 1e2, Stable, "Stable") }
func BenchmarkSort1e4(b *testing.B) { bench(b, 1e4, Sort, "Sort") }
func BenchmarkStable1e4(b *testing.B) { bench(b, 1e4, Stable, "Stable") }
func BenchmarkSort1e6(b *testing.B) { bench(b, 1e6, Sort, "Sort") }
func BenchmarkStable1e6(b *testing.B) { bench(b, 1e6, Stable, "Stable") }

265
vendor/go4.org/sort/zfuncversion.go generated vendored

@ -1,265 +0,0 @@
// DO NOT EDIT; AUTO-GENERATED from sort.go using genzfunc.go
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort
// Auto-generated variant of sort.go:insertionSort
func insertionSort_func(data lessSwap, a, b int) {
for i := a + 1; i < b; i++ {
for j := i; j > a && data.Less(j, j-1); j-- {
data.Swap(j, j-1)
}
}
}
// Auto-generated variant of sort.go:siftDown
func siftDown_func(data lessSwap, lo, hi, first int) {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && data.Less(first+child, first+child+1) {
child++
}
if !data.Less(first+root, first+child) {
return
}
data.Swap(first+root, first+child)
root = child
}
}
// Auto-generated variant of sort.go:heapSort
func heapSort_func(data lessSwap, a, b int) {
first := a
lo := 0
hi := b - a
for i := (hi - 1) / 2; i >= 0; i-- {
siftDown_func(data, i, hi, first)
}
for i := hi - 1; i >= 0; i-- {
data.Swap(first, first+i)
siftDown_func(data, lo, i, first)
}
}
// Auto-generated variant of sort.go:medianOfThree
func medianOfThree_func(data lessSwap, m1, m0, m2 int) {
if data.Less(m1, m0) {
data.Swap(m1, m0)
}
if data.Less(m2, m1) {
data.Swap(m2, m1)
if data.Less(m1, m0) {
data.Swap(m1, m0)
}
}
}
// Auto-generated variant of sort.go:swapRange
func swapRange_func(data lessSwap, a, b, n int) {
for i := 0; i < n; i++ {
data.Swap(a+i, b+i)
}
}
// Auto-generated variant of sort.go:doPivot
func doPivot_func(data lessSwap, lo, hi int) (midlo, midhi int) {
m := lo + (hi-lo)/2
if hi-lo > 40 {
s := (hi - lo) / 8
medianOfThree_func(data, lo, lo+s, lo+2*s)
medianOfThree_func(data, m, m-s, m+s)
medianOfThree_func(data, hi-1, hi-1-s, hi-1-2*s)
}
medianOfThree_func(data, lo, m, hi-1)
pivot := lo
a, c := lo+1, hi-1
for ; a < c && data.Less(a, pivot); a++ {
}
b := a
for {
for ; b < c && !data.Less(pivot, b); b++ {
}
for ; b < c && data.Less(pivot, c-1); c-- {
}
if b >= c {
break
}
data.Swap(b, c-1)
b++
c--
}
protect := hi-c < 5
if !protect && hi-c < (hi-lo)/4 {
dups := 0
if !data.Less(pivot, hi-1) {
data.Swap(c, hi-1)
c++
dups++
}
if !data.Less(b-1, pivot) {
b--
dups++
}
if !data.Less(m, pivot) {
data.Swap(m, b-1)
b--
dups++
}
protect = dups > 1
}
if protect {
for {
for ; a < b && !data.Less(b-1, pivot); b-- {
}
for ; a < b && data.Less(a, pivot); a++ {
}
if a >= b {
break
}
data.Swap(a, b-1)
a++
b--
}
}
data.Swap(pivot, b-1)
return b - 1, c
}
// Auto-generated variant of sort.go:quickSort
func quickSort_func(data lessSwap, a, b, maxDepth int) {
for b-a > 12 {
if maxDepth == 0 {
heapSort_func(data, a, b)
return
}
maxDepth--
mlo, mhi := doPivot_func(data, a, b)
if mlo-a < b-mhi {
quickSort_func(data, a, mlo, maxDepth)
a = mhi
} else {
quickSort_func(data, mhi, b, maxDepth)
b = mlo
}
}
if b-a > 1 {
for i := a + 6; i < b; i++ {
if data.Less(i, i-6) {
data.Swap(i, i-6)
}
}
insertionSort_func(data, a, b)
}
}
// Auto-generated variant of sort.go:stable
func stable_func(data lessSwap, n int) {
blockSize := 20
a, b := 0, blockSize
for b <= n {
insertionSort_func(data, a, b)
a = b
b += blockSize
}
insertionSort_func(data, a, n)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
symMerge_func(data, a, a+blockSize, b)
a = b
b += 2 * blockSize
}
if m := a + blockSize; m < n {
symMerge_func(data, a, m, n)
}
blockSize *= 2
}
}
// Auto-generated variant of sort.go:symMerge
func symMerge_func(data lessSwap, a, m, b int) {
if m-a == 1 {
i := m
j := b
for i < j {
h := i + (j-i)/2
if data.Less(h, a) {
i = h + 1
} else {
j = h
}
}
for k := a; k < i-1; k++ {
data.Swap(k, k+1)
}
return
}
if b-m == 1 {
i := a
j := m
for i < j {
h := i + (j-i)/2
if !data.Less(m, h) {
i = h + 1
} else {
j = h
}
}
for k := m; k > i; k-- {
data.Swap(k, k-1)
}
return
}
mid := a + (b-a)/2
n := mid + m
var start, r int
if m > mid {
start = n - b
r = mid
} else {
start = a
r = m
}
p := n - 1
for start < r {
c := start + (r-start)/2
if !data.Less(p-c, c) {
start = c + 1
} else {
r = c
}
}
end := n - start
if start < m && m < end {
rotate_func(data, start, m, end)
}
if a < start && start < mid {
symMerge_func(data, a, start, mid)
}
if mid < end && end < b {
symMerge_func(data, mid, end, b)
}
}
// Auto-generated variant of sort.go:rotate
func rotate_func(data lessSwap, a, m, b int) {
i := m - a
j := b - m
for i != j {
if i > j {
swapRange_func(data, m-i, m, j)
i -= j
} else {
swapRange_func(data, m-i, m+j-i, i)
j -= i
}
}
swapRange_func(data, m-i, m, i)
}

39
vendor/go4.org/strutil/intern.go generated vendored

@ -1,39 +0,0 @@
/*
Copyright 2013 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package strutil
var internStr = map[string]string{}
// RegisterCommonString adds common strings to the interned string
// table. This should be called during init from the main
// goroutine, not later at runtime.
func RegisterCommonString(s ...string) {
for _, v := range s {
internStr[v] = v
}
}
// StringFromBytes returns string(v), minimizing copies for common values of v
// as previously registered with RegisterCommonString.
func StringFromBytes(v []byte) string {
// In Go 1.3, this string conversion in the map lookup does not allocate
// to make a new string. We depend on Go 1.3, so this is always free:
if s, ok := internStr[string(v)]; ok {
return s
}
return string(v)
}

117
vendor/go4.org/strutil/strconv.go generated vendored

@ -1,117 +0,0 @@
/*
Copyright 2013 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package strutil
import (
"errors"
"strconv"
)
// ParseUintBytes is like strconv.ParseUint, but using a []byte.
func ParseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64
if bitSize == 0 {
bitSize = int(strconv.IntSize)
}
s0 := s
switch {
case len(s) < 1:
err = strconv.ErrSyntax
goto Error
case 2 <= base && base <= 36:
// valid base; nothing to do
case base == 0:
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
base = 16
s = s[2:]
if len(s) < 1 {
err = strconv.ErrSyntax
goto Error
}
case s[0] == '0':
base = 8
default:
base = 10
}
default:
err = errors.New("invalid base " + strconv.Itoa(base))
goto Error
}
n = 0
cutoff = cutoff64(base)
maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ {
var v byte
d := s[i]
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
n = 0
err = strconv.ErrSyntax
goto Error
}
if int(v) >= base {
n = 0
err = strconv.ErrSyntax
goto Error
}
if n >= cutoff {
// n*base overflows
n = 1<<64 - 1
err = strconv.ErrRange
goto Error
}
n *= uint64(base)
n1 := n + uint64(v)
if n1 < n || n1 > maxVal {
// n+v overflows
n = 1<<64 - 1
err = strconv.ErrRange
goto Error
}
n = n1
}
return n, nil
Error:
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
}
// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
if base < 2 {
return 0
}
return (1<<64-1)/uint64(base) + 1
}

200
vendor/go4.org/strutil/strutil.go generated vendored

@ -1,200 +0,0 @@
/*
Copyright 2013 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package strutil contains string and byte processing functions.
package strutil // import "go4.org/strutil"
import (
"strings"
"unicode"
"unicode/utf8"
)
// Fork of Go's implementation in pkg/strings/strings.go:
// Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays.
func genSplit(dst []string, s, sep string, sepSave, n int) []string {
if n == 0 {
return nil
}
if sep == "" {
panic("sep is empty")
}
if n < 0 {
n = strings.Count(s, sep) + 1
}
c := sep[0]
start := 0
na := 0
for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
dst = append(dst, s[start:i+sepSave])
na++
start = i + len(sep)
i += len(sep) - 1
}
}
dst = append(dst, s[start:])
return dst
}
// AppendSplitN is like strings.SplitN but appends to and returns dst.
// Unlike strings.SplitN, an empty separator is not supported.
// The count n determines the number of substrings to return:
// n > 0: at most n substrings; the last substring will be the unsplit remainder.
// n == 0: the result is nil (zero substrings)
// n < 0: all substrings
func AppendSplitN(dst []string, s, sep string, n int) []string {
return genSplit(dst, s, sep, 0, n)
}
// equalFoldRune compares a and b runes whether they fold equally.
//
// The code comes from strings.EqualFold, but shortened to only one rune.
func equalFoldRune(sr, tr rune) bool {
if sr == tr {
return true
}
// Make sr < tr to simplify what follows.
if tr < sr {
sr, tr = tr, sr
}
// Fast check for ASCII.
if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
// ASCII, and sr is upper case. tr must be lower case.
if tr == sr+'a'-'A' {
return true
}
return false
}
// General case. SimpleFold(x) returns the next equivalent rune > x
// or wraps around to smaller values.
r := unicode.SimpleFold(sr)
for r != sr && r < tr {
r = unicode.SimpleFold(r)
}
if r == tr {
return true
}
return false
}
// HasPrefixFold is like strings.HasPrefix but uses Unicode case-folding.
func HasPrefixFold(s, prefix string) bool {
if prefix == "" {
return true
}
for _, pr := range prefix {
if s == "" {
return false
}
// step with s, too
sr, size := utf8.DecodeRuneInString(s)
if sr == utf8.RuneError {
return false
}
s = s[size:]
if !equalFoldRune(sr, pr) {
return false
}
}
return true
}
// HasSuffixFold is like strings.HasPrefix but uses Unicode case-folding.
func HasSuffixFold(s, suffix string) bool {
if suffix == "" {
return true
}
// count the runes and bytes in s, but only till rune count of suffix
bo, so := len(s), len(suffix)
for bo > 0 && so > 0 {
r, size := utf8.DecodeLastRuneInString(s[:bo])
if r == utf8.RuneError {
return false
}
bo -= size
sr, size := utf8.DecodeLastRuneInString(suffix[:so])
if sr == utf8.RuneError {
return false
}
so -= size
if !equalFoldRune(r, sr) {
return false
}
}
return so == 0
}
// ContainsFold is like strings.Contains but uses Unicode case-folding.
func ContainsFold(s, substr string) bool {
if substr == "" {
return true
}
if s == "" {
return false
}
firstRune := rune(substr[0])
if firstRune >= utf8.RuneSelf {
firstRune, _ = utf8.DecodeRuneInString(substr)
}
for i, rune := range s {
if equalFoldRune(rune, firstRune) && HasPrefixFold(s[i:], substr) {
return true
}
}
return false
}
// IsPlausibleJSON reports whether s likely contains a JSON object, without
// actually parsing it. It's meant to be a light heuristic.
func IsPlausibleJSON(s string) bool {
return startsWithOpenBrace(s) && endsWithCloseBrace(s)
}
func isASCIIWhite(b byte) bool { return b == ' ' || b == '\n' || b == '\r' || b == '\t' }
func startsWithOpenBrace(s string) bool {
for len(s) > 0 {
switch {
case s[0] == '{':
return true
case isASCIIWhite(s[0]):
s = s[1:]
default:
return false
}
}
return false
}
func endsWithCloseBrace(s string) bool {
for len(s) > 0 {
last := len(s) - 1
switch {
case s[last] == '}':
return true
case isASCIIWhite(s[last]):
s = s[:last]
default:
return false
}
}
return false
}

@ -1,230 +0,0 @@
/*
Copyright 2013 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package strutil
import (
"reflect"
"strings"
"testing"
)
func TestAppendSplitN(t *testing.T) {
var got []string
tests := []struct {
s, sep string
n int
}{
{"foo", "|", 1},
{"foo", "|", -1},
{"foo|bar", "|", 1},
{"foo|bar", "|", -1},
{"foo|bar|", "|", 2},
{"foo|bar|", "|", -1},
{"foo|bar|baz", "|", 1},
{"foo|bar|baz", "|", 2},
{"foo|bar|baz", "|", 3},
{"foo|bar|baz", "|", -1},
}
for _, tt := range tests {
want := strings.SplitN(tt.s, tt.sep, tt.n)
got = AppendSplitN(got[:0], tt.s, tt.sep, tt.n)
if !reflect.DeepEqual(want, got) {
t.Errorf("AppendSplitN(%q, %q, %d) = %q; want %q",
tt.s, tt.sep, tt.n, got, want)
}
}
}
func TestStringFromBytes(t *testing.T) {
for _, s := range []string{"foo", "permanode", "file", "zzzz"} {
got := StringFromBytes([]byte(s))
if got != s {
t.Errorf("StringFromBytes(%q) didn't round-trip; got %q instead", s, got)
}
}
}
func TestHasPrefixFold(t *testing.T) {
tests := []struct {
s, prefix string
result bool
}{
{"camli", "CAML", true},
{"CAMLI", "caml", true},
{"cam", "Cam", true},
{"camli", "car", false},
{"caml", "camli", false},
{"Hello, 世界 dasdsa", "HeLlO, 世界", true},
{"Hello, 世界", "HeLlO, 世界-", false},
{"kelvin", "\u212A" + "elvin", true}, // "\u212A" is the elvin temperature sign
{"Kelvin", "\u212A" + "elvin", true},
{"kelvin", "\u212A" + "el", true},
{"Kelvin", "\u212A" + "el", true},
{"\u212A" + "elvin", "Kelvin", true},
{"\u212A" + "elvin", "kelvin", true},
{"\u212A" + "elvin", "Kel", true},
{"\u212A" + "elvin", "kel", true},
}
for _, tt := range tests {
r := HasPrefixFold(tt.s, tt.prefix)
if r != tt.result {
t.Errorf("HasPrefixFold(%q, %q) returned %v", tt.s, tt.prefix, r)
}
}
}
func TestHasSuffixFold(t *testing.T) {
tests := []struct {
s, suffix string
result bool
}{
{"camli", "AMLI", true},
{"CAMLI", "amli", true},
{"mli", "MLI", true},
{"camli", "ali", false},
{"amli", "camli", false},
{"asas Hello, 世界", "HeLlO, 世界", true},
{"Hello, 世界", "HeLlO, 世界-", false},
{"kkkkelvin", "\u212A" + "elvin", true}, // "\u212A" is the elvin temperature sign
{"kelvin", "\u212A" + "elvin", true}, // "\u212A" is the elvin temperature sign
{"Kelvin", "\u212A" + "elvin", true},
{"\u212A" + "elvin", "Kelvin", true},
{"\u212A" + "elvin", "kelvin", true},
{"\u212A" + "elvin", "vin", true},
{"\u212A" + "elvin", "viN", true},
}
for _, tt := range tests {
r := HasSuffixFold(tt.s, tt.suffix)
if r != tt.result {
t.Errorf("HasSuffixFold(%q, %q) returned %v", tt.s, tt.suffix, r)
}
}
}
func TestContainsFold(t *testing.T) {
// TODO: more tests, more languages.
tests := []struct {
s, substr string
result bool
}{
{"camli", "CAML", true},
{"CAMLI", "caml", true},
{"cam", "Cam", true},
{"мир", "ми", true},
{"МИP", "ми", true},
{"КАМЛИЙСТОР", "камлийс", true},
{"КаМлИйСтОр", "КаМлИйС", true},
{"camli", "car", false},
{"caml", "camli", false},
{"camli", "AMLI", true},
{"CAMLI", "amli", true},
{"mli", "MLI", true},
{"мир", "ир", true},
{"МИP", "ми", true},
{"КАМЛИЙСТОР", "лийстор", true},
{"КаМлИйСтОр", "лИйСтОр", true},
{"мир", "р", true},
{"camli", "ali", false},
{"amli", "camli", false},
{"МИP", "и", true},
{"мир", "и", true},
{"КАМЛИЙСТОР", "лийс", true},
{"КаМлИйСтОр", "лИйС", true},
{"árvíztűrő tükörfúrógép", "árvíztŰrŐ", true},
{"I love ☕", "i love ☕", true},
{"k", "\u212A", true}, // "\u212A" is the elvin temperature sign
{"\u212A" + "elvin", "k", true},
{"kelvin", "\u212A" + "elvin", true},
{"Kelvin", "\u212A" + "elvin", true},
{"\u212A" + "elvin", "Kelvin", true},
{"\u212A" + "elvin", "kelvin", true},
{"273.15 kelvin", "\u212A" + "elvin", true},
{"273.15 Kelvin", "\u212A" + "elvin", true},
{"273.15 \u212A" + "elvin", "Kelvin", true},
{"273.15 \u212A" + "elvin", "kelvin", true},
}
for _, tt := range tests {
r := ContainsFold(tt.s, tt.substr)
if r != tt.result {
t.Errorf("ContainsFold(%q, %q) returned %v", tt.s, tt.substr, r)
}
}
}
func TestIsPlausibleJSON(t *testing.T) {
tests := []struct {
in string
want bool
}{
{"{}", true},
{" {}", true},
{"{} ", true},
{"\n\r\t {}\t \r \n", true},
{"\n\r\t {x\t \r \n", false},
{"{x", false},
{"x}", false},
{"x", false},
{"", false},
}
for _, tt := range tests {
got := IsPlausibleJSON(tt.in)
if got != tt.want {
t.Errorf("IsPlausibleJSON(%q) = %v; want %v", tt.in, got, tt.want)
}
}
}
func BenchmarkHasSuffixFoldToLower(tb *testing.B) {
a, b := "camlik", "AMLI\u212A"
for i := 0; i < tb.N; i++ {
if !strings.HasSuffix(strings.ToLower(a), strings.ToLower(b)) {
tb.Fatalf("%q should have the same suffix as %q", a, b)
}
}
}
func BenchmarkHasSuffixFold(tb *testing.B) {
a, b := "camlik", "AMLI\u212A"
for i := 0; i < tb.N; i++ {
if !HasSuffixFold(a, b) {
tb.Fatalf("%q should have the same suffix as %q", a, b)
}
}
}
func BenchmarkHasPrefixFoldToLower(tb *testing.B) {
a, b := "kamlistore", "\u212AAMLI"
for i := 0; i < tb.N; i++ {
if !strings.HasPrefix(strings.ToLower(a), strings.ToLower(b)) {
tb.Fatalf("%q should have the same suffix as %q", a, b)
}
}
}
func BenchmarkHasPrefixFold(tb *testing.B) {
a, b := "kamlistore", "\u212AAMLI"
for i := 0; i < tb.N; i++ {
if !HasPrefixFold(a, b) {
tb.Fatalf("%q should have the same suffix as %q", a, b)
}
}
}

41
vendor/go4.org/syncutil/gate.go generated vendored

@ -1,41 +0,0 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package syncutil
// A Gate limits concurrency.
type Gate struct {
c chan struct{}
}
// NewGate returns a new gate that will only permit max operations at once.
func NewGate(max int) *Gate {
return &Gate{make(chan struct{}, max)}
}
// Start starts an operation, blocking until the gate has room.
func (g *Gate) Start() {
g.c <- struct{}{}
}
// Done finishes an operation.
func (g *Gate) Done() {
select {
case <-g.c:
default:
panic("Done called more than Start")
}
}

64
vendor/go4.org/syncutil/group.go generated vendored

@ -1,64 +0,0 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package syncutil
import "sync"
// A Group is like a sync.WaitGroup and coordinates doing
// multiple things at once. Its zero value is ready to use.
type Group struct {
wg sync.WaitGroup
mu sync.Mutex // guards errs
errs []error
}
// Go runs fn in its own goroutine, but does not wait for it to complete.
// Call Err or Errs to wait for all the goroutines to complete.
func (g *Group) Go(fn func() error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
err := fn()
if err != nil {
g.mu.Lock()
defer g.mu.Unlock()
g.errs = append(g.errs, err)
}
}()
}
// Wait waits for all the previous calls to Go to complete.
func (g *Group) Wait() {
g.wg.Wait()
}
// Err waits for all previous calls to Go to complete and returns the
// first non-nil error, or nil.
func (g *Group) Err() error {
g.wg.Wait()
if len(g.errs) > 0 {
return g.errs[0]
}
return nil
}
// Errs waits for all previous calls to Go to complete and returns
// all non-nil errors.
func (g *Group) Errs() []error {
g.wg.Wait()
return g.errs
}

60
vendor/go4.org/syncutil/once.go generated vendored

@ -1,60 +0,0 @@
/*
Copyright 2014 The Perkeep Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package syncutil
import (
"sync"
"sync/atomic"
)
// A Once will perform a successful action exactly once.
//
// Unlike a sync.Once, this Once's func returns an error
// and is re-armed on failure.
type Once struct {
m sync.Mutex
done uint32
}
// Do calls the function f if and only if Do has not been invoked
// without error for this instance of Once. In other words, given
// var once Once
// if once.Do(f) is called multiple times, only the first call will
// invoke f, even if f has a different value in each invocation unless
// f returns an error. A new instance of Once is required for each
// function to execute.
//
// Do is intended for initialization that must be run exactly once. Since f
// is niladic, it may be necessary to use a function literal to capture the
// arguments to a function to be invoked by Do:
// err := config.once.Do(func() error { return config.init(filename) })
func (o *Once) Do(f func() error) error {
if atomic.LoadUint32(&o.done) == 1 {
return nil
}
// Slow-path.
o.m.Lock()
defer o.m.Unlock()
var err error
if o.done == 0 {
err = f()
if err == nil {
atomic.StoreUint32(&o.done, 1)
}
}
return err
}

@ -1,57 +0,0 @@
package syncutil
import (
"errors"
"testing"
)
func TestOnce(t *testing.T) {
timesRan := 0
f := func() error {
timesRan++
return nil
}
once := Once{}
grp := Group{}
for i := 0; i < 10; i++ {
grp.Go(func() error { return once.Do(f) })
}
if grp.Err() != nil {
t.Errorf("Expected no errors, got %v", grp.Err())
}
if timesRan != 1 {
t.Errorf("Expected to run one time, ran %d", timesRan)
}
}
// TestOnceErroring verifies we retry on every error, but stop after
// the first success.
func TestOnceErroring(t *testing.T) {
timesRan := 0
f := func() error {
timesRan++
if timesRan < 3 {
return errors.New("retry")
}
return nil
}
once := Once{}
grp := Group{}
for i := 0; i < 10; i++ {
grp.Go(func() error { return once.Do(f) })
}
if len(grp.Errs()) != 2 {
t.Errorf("Expected two errors, got %d", len(grp.Errs()))
}
if timesRan != 3 {
t.Errorf("Expected to run two times, ran %d", timesRan)
}
}

64
vendor/go4.org/syncutil/sem.go generated vendored

@ -1,64 +0,0 @@
package syncutil
import (
"fmt"
"log"
"sync"
)
type debugT bool
var debug = debugT(false)
func (d debugT) Printf(format string, args ...interface{}) {
if bool(d) {
log.Printf(format, args...)
}
}
// Sem implements a semaphore that can have multiple units acquired/released
// at a time.
type Sem struct {
c *sync.Cond // Protects size
max, free int64
}
// NewSem creates a semaphore with max units available for acquisition.
func NewSem(max int64) *Sem {
return &Sem{
c: sync.NewCond(new(sync.Mutex)),
free: max,
max: max,
}
}
// Acquire will deduct n units from the semaphore. If the deduction would
// result in the available units falling below zero, the call will block until
// another go routine returns units via a call to Release. If more units are
// requested than the semaphore is configured to hold, error will be non-nil.
func (s *Sem) Acquire(n int64) error {
if n > s.max {
return fmt.Errorf("sem: attempt to acquire more units than semaphore size %d > %d", n, s.max)
}
s.c.L.Lock()
defer s.c.L.Unlock()
for {
debug.Printf("Acquire check max %d free %d, n %d", s.max, s.free, n)
if s.free >= n {
s.free -= n
return nil
}
debug.Printf("Acquire Wait max %d free %d, n %d", s.max, s.free, n)
s.c.Wait()
}
}
// Release will return n units to the semaphore and notify any currently
// blocking Acquire calls.
func (s *Sem) Release(n int64) {
s.c.L.Lock()
defer s.c.L.Unlock()
debug.Printf("Release max %d free %d, n %d", s.max, s.free, n)
s.free += n
s.c.Broadcast()
}

@ -1,33 +0,0 @@
package syncutil_test
import (
"testing"
"go4.org/syncutil"
)
func TestSem(t *testing.T) {
s := syncutil.NewSem(5)
if err := s.Acquire(2); err != nil {
t.Fatal(err)
}
if err := s.Acquire(2); err != nil {
t.Fatal(err)
}
go func() {
s.Release(2)
s.Release(2)
}()
if err := s.Acquire(5); err != nil {
t.Fatal(err)
}
}
func TestSemErr(t *testing.T) {
s := syncutil.NewSem(5)
if err := s.Acquire(6); err == nil {
t.Fatal("Didn't get expected error for large acquire.")
}
}

@ -1,64 +0,0 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package singleflight provides a duplicate function call suppression
// mechanism.
package singleflight // import "go4.org/syncutil/singleflight"
import "sync"
// call is an in-flight or completed Do call
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err
}

@ -1,85 +0,0 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package singleflight
import (
"errors"
"fmt"
"sync"
"sync/atomic"
"testing"
"time"
)
func TestDo(t *testing.T) {
var g Group
v, err := g.Do("key", func() (interface{}, error) {
return "bar", nil
})
if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
t.Errorf("Do = %v; want %v", got, want)
}
if err != nil {
t.Errorf("Do error = %v", err)
}
}
func TestDoErr(t *testing.T) {
var g Group
someErr := errors.New("Some error")
v, err := g.Do("key", func() (interface{}, error) {
return nil, someErr
})
if err != someErr {
t.Errorf("Do error = %v; want someErr %v", err, someErr)
}
if v != nil {
t.Errorf("unexpected non-nil value %#v", v)
}
}
func TestDoDupSuppress(t *testing.T) {
var g Group
c := make(chan string)
var calls int32
fn := func() (interface{}, error) {
atomic.AddInt32(&calls, 1)
return <-c, nil
}
const n = 10
var wg sync.WaitGroup
for i := 0; i < n; i++ {
wg.Add(1)
go func() {
v, err := g.Do("key", fn)
if err != nil {
t.Errorf("Do error: %v", err)
}
if v.(string) != "bar" {
t.Errorf("got %q; want %q", v, "bar")
}
wg.Done()
}()
}
time.Sleep(100 * time.Millisecond) // let goroutines above block
c <- "bar"
wg.Wait()
if got := atomic.LoadInt32(&calls); got != 1 {
t.Errorf("number of calls = %d; want 1", got)
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save