mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-11-14 02:13:51 +00:00
deploy: 3a20d238d9
This commit is contained in:
parent
ca571eb691
commit
b706f63726
2
Makefile
2
Makefile
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
# install mkbook
|
# install mkbook
|
||||||
|
|
||||||
# http://www.imagemagick.org/
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
mdbook serve
|
mdbook serve
|
||||||
|
|
||||||
|
172
builder.go
172
builder.go
@ -1,172 +0,0 @@
|
|||||||
// Copyright 2015 ChaiShushan <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
// 打包 gopl-zh 为 zip 文件.
|
|
||||||
//
|
|
||||||
// 文件名格式: gopl-zh-20151001-2ae607.zip
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/zip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Git版本号
|
|
||||||
gitVersion := getGitCommitVersion()
|
|
||||||
|
|
||||||
// zip文件名
|
|
||||||
zipBaseName := fmt.Sprintf("gopl-zh-%s-%s", time.Now().Format("20060102"), gitVersion[:6])
|
|
||||||
|
|
||||||
// 导出Git
|
|
||||||
exportGitToZip("./_book/" + "gopl-zh-" + gitVersion + ".zip")
|
|
||||||
|
|
||||||
os.Remove(zipBaseName + ".zip")
|
|
||||||
file, err := os.Create(zipBaseName + ".zip")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("os.Create: ", err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
zipFile := zip.NewWriter(file)
|
|
||||||
defer zipFile.Close()
|
|
||||||
|
|
||||||
// create /gopl-zh-20151001-2ae607/
|
|
||||||
f, err := zipFile.Create(zipBaseName + "/")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, err = f.Write([]byte("")); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := "_book"
|
|
||||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("filepath.Walk: ", err)
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
relpath, err := filepath.Rel(dir, path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("filepath.Rel: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.ToSlash(relpath)
|
|
||||||
if isIngoreFile(filename) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("ioutil.ReadFile: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := zipFile.Create(zipBaseName + "/" + filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, err = f.Write(data); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s\n", filename)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Printf("Done\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取Git最新的版本号
|
|
||||||
//
|
|
||||||
// git log -1
|
|
||||||
// commit 0460c1b3bb8fbb7e2fc88961e69aa37f4041d6c1
|
|
||||||
// Merge: b2d582a e826457
|
|
||||||
// Author: chai2010 <chaishushan{AT}gmail.com>
|
|
||||||
// Date: Mon Feb 1 08:04:44 2016 +0800
|
|
||||||
//
|
|
||||||
// Merge pull request #249 from sunclx/patch-3
|
|
||||||
//
|
|
||||||
// fix typo
|
|
||||||
func getGitCommitVersion() (version string) {
|
|
||||||
cmdOut, err := exec.Command(`git`, `log`, `-1`).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
for _, line := range strings.Split(string(cmdOut), "\n") {
|
|
||||||
line := strings.TrimSpace(line)
|
|
||||||
if strings.HasPrefix(line, "commit") {
|
|
||||||
version = strings.TrimSpace(line[len("commit"):])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导出Git到Zip文件
|
|
||||||
func exportGitToZip(filename string) error {
|
|
||||||
if !strings.HasSuffix(filename, ".zip") {
|
|
||||||
filename += ".zip"
|
|
||||||
}
|
|
||||||
if _, err := exec.Command(`git`, `archive`, `--format`, `zip`, `--output`, filename, `master`).CombinedOutput(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cpFile(dst, src string) {
|
|
||||||
err := os.MkdirAll(filepath.Dir(dst), 0666)
|
|
||||||
if err != nil && !os.IsExist(err) {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
fsrc, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
defer fsrc.Close()
|
|
||||||
|
|
||||||
fdst, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
defer fdst.Close()
|
|
||||||
if _, err = io.Copy(fdst, fsrc); err != nil {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIngoreFile(path string) bool {
|
|
||||||
if strings.HasPrefix(path, ".git") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(path, ".gitignore") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(path, "vendor") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(path, "tools") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(path, "testdata") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(path, ".go") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
@ -437,11 +437,9 @@ gopl.io/ch7/xmlselect
|
|||||||
<pre><code>$ go list -f '{{join .Deps " "}}' strconv
|
<pre><code>$ go list -f '{{join .Deps " "}}' strconv
|
||||||
errors math runtime unicode/utf8 unsafe
|
errors math runtime unicode/utf8 unsafe
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>译注:上面的命令在Windows的命令行运行会遇到<code>template: main:1: unclosed action</code>的错误。产生这个错误的原因是因为命令行对命令中的<code>" "</code>参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:</p>
|
<p>译注:上面的命令在Windows的命令行运行会遇到<code>template: main:1: unclosed action</code>的错误。产生这个错误的原因是因为命令行对命令中的<code>" "</code>参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:</p>
|
||||||
<pre><code>$ go list -f "{{join .Deps \" \"}}" strconv
|
<pre><code>$ go list -f "{{join .Deps \" \"}}" strconv
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>下面的命令打印compress子目录下所有包的导入包列表:</p>
|
<p>下面的命令打印compress子目录下所有包的导入包列表:</p>
|
||||||
<pre><code>$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/...
|
<pre><code>$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/...
|
||||||
compress/bzip2 -> bufio io sort
|
compress/bzip2 -> bufio io sort
|
||||||
@ -450,11 +448,9 @@ compress/gzip -> bufio compress/flate errors fmt hash hash/crc32 io time
|
|||||||
compress/lzw -> bufio errors fmt io
|
compress/lzw -> bufio errors fmt io
|
||||||
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>译注:Windows下有同样有问题,要避免转义字符串的干扰:</p>
|
<p>译注:Windows下有同样有问题,要避免转义字符串的干扰:</p>
|
||||||
<pre><code>$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
|
<pre><code>$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p><code>go list</code>命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用<code>go help list</code>命令查看。</p>
|
<p><code>go list</code>命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用<code>go help list</code>命令查看。</p>
|
||||||
<p>在本章,我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。在下一章,我们将看到如何用<code>go test</code>命令去运行Go语言程序中的测试代码。</p>
|
<p>在本章,我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。在下一章,我们将看到如何用<code>go test</code>命令去运行Go语言程序中的测试代码。</p>
|
||||||
<p><strong>练习 10.4:</strong> 创建一个工具,根据命令行指定的参数,报告工作区所有依赖包指定的其它包集合。提示:你需要运行<code>go list</code>命令两次,一次用于初始化包,一次用于所有包。你可能需要用encoding/json(§4.5)包来分析输出的JSON格式的信息。</p>
|
<p><strong>练习 10.4:</strong> 创建一个工具,根据命令行指定的参数,报告工作区所有依赖包指定的其它包集合。提示:你需要运行<code>go list</code>命令两次,一次用于初始化包,一次用于所有包。你可能需要用encoding/json(§4.5)包来分析输出的JSON格式的信息。</p>
|
||||||
|
@ -579,18 +579,15 @@ func TestCheckQuotaNotifiesUser(t *testing.T) {
|
|||||||
<pre><code>$ go list -f={{.GoFiles}} fmt
|
<pre><code>$ go list -f={{.GoFiles}} fmt
|
||||||
[doc.go format.go print.go scan.go]
|
[doc.go format.go print.go scan.go]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>TestGoFiles表示的是fmt包内部测试代码,以_test.go为后缀文件名,不过只在测试时被构建:</p>
|
<p>TestGoFiles表示的是fmt包内部测试代码,以_test.go为后缀文件名,不过只在测试时被构建:</p>
|
||||||
<pre><code>$ go list -f={{.TestGoFiles}} fmt
|
<pre><code>$ go list -f={{.TestGoFiles}} fmt
|
||||||
[export_test.go]
|
[export_test.go]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>包的测试代码通常都在这些文件中,不过fmt包并非如此;稍后我们再解释export_test.go文件的作用。</p>
|
<p>包的测试代码通常都在这些文件中,不过fmt包并非如此;稍后我们再解释export_test.go文件的作用。</p>
|
||||||
<p>XTestGoFiles表示的是属于外部测试包的测试代码,也就是fmt_test包,因此它们必须先导入fmt包。同样,这些文件也只是在测试时被构建运行:</p>
|
<p>XTestGoFiles表示的是属于外部测试包的测试代码,也就是fmt_test包,因此它们必须先导入fmt包。同样,这些文件也只是在测试时被构建运行:</p>
|
||||||
<pre><code>$ go list -f={{.XTestGoFiles}} fmt
|
<pre><code>$ go list -f={{.XTestGoFiles}} fmt
|
||||||
[fmt_test.go scan_test.go stringer_test.go]
|
[fmt_test.go scan_test.go stringer_test.go]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>有时候外部测试包也需要访问被测试包内部的代码,例如在一个为了避免循环导入而被独立到外部测试包的白盒测试。在这种情况下,我们可以通过一些技巧解决:我们在包内的一个_test.go文件中导出一个内部的实现给外部测试包。因为这些代码只有在测试时才需要,因此一般会放在export_test.go文件中。</p>
|
<p>有时候外部测试包也需要访问被测试包内部的代码,例如在一个为了避免循环导入而被独立到外部测试包的白盒测试。在这种情况下,我们可以通过一些技巧解决:我们在包内的一个_test.go文件中导出一个内部的实现给外部测试包。因为这些代码只有在测试时才需要,因此一般会放在export_test.go文件中。</p>
|
||||||
<p>例如,fmt包的fmt.Scanf函数需要unicode.IsSpace函数提供的功能。但是为了避免太多的依赖,fmt包并没有导入包含巨大表格数据的unicode包;相反fmt包有一个叫isSpace内部的简易实现。</p>
|
<p>例如,fmt包的fmt.Scanf函数需要unicode.IsSpace函数提供的功能。但是为了避免太多的依赖,fmt包并没有导入包含巨大表格数据的unicode包;相反fmt包有一个叫isSpace内部的简易实现。</p>
|
||||||
<p>为了确保fmt.isSpace和unicode.IsSpace函数的行为保持一致,fmt包谨慎地包含了一个测试。一个在外部测试包内的白盒测试,是无法直接访问到isSpace内部函数的,因此fmt通过一个后门导出了isSpace函数。export_test.go文件就是专门用于外部测试包的后门。</p>
|
<p>为了确保fmt.isSpace和unicode.IsSpace函数的行为保持一致,fmt包谨慎地包含了一个测试。一个在外部测试包内的白盒测试,是无法直接访问到isSpace内部函数的,因此fmt通过一个后门导出了isSpace函数。export_test.go文件就是专门用于外部测试包的后门。</p>
|
||||||
|
@ -172,9 +172,7 @@ Title: {{.Title | printf "%.64s"}}
|
|||||||
Age: {{.CreatedAt | daysAgo}} days
|
Age: {{.CreatedAt | daysAgo}} days
|
||||||
{{end}}`
|
{{end}}`
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>这个模板先打印匹配到的issue总数,然后打印每个issue的编号、创建用户、标题还有存在的时间。对于每一个action,都有一个当前值的概念,对应点操作符,写作“.”。当前值“.”最初被初始化为调用模板时的参数,在当前例子中对应github.IssuesSearchResult类型的变量。模板中<code>{{.TotalCount}}</code>对应action将展开为结构体中TotalCount成员以默认的方式打印的值。模板中<code>{{range .Items}}</code>和<code>{{end}}</code>对应一个循环action,因此它们之间的内容可能会被展开多次,循环每次迭代的当前值对应当前的Items元素的值。</p>
|
<p>这个模板先打印匹配到的issue总数,然后打印每个issue的编号、创建用户、标题还有存在的时间。对于每一个action,都有一个当前值的概念,对应点操作符,写作“.”。当前值“.”最初被初始化为调用模板时的参数,在当前例子中对应github.IssuesSearchResult类型的变量。模板中<code>{{.TotalCount}}</code>对应action将展开为结构体中TotalCount成员以默认的方式打印的值。模板中<code>{{range .Items}}</code>和<code>{{end}}</code>对应一个循环action,因此它们之间的内容可能会被展开多次,循环每次迭代的当前值对应当前的Items元素的值。</p>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>在一个action中,<code>|</code>操作符表示将前一个表达式的结果作为后一个函数的输入,类似于UNIX中管道的概念。在Title这一行的action中,第二个操作是一个printf函数,是一个基于fmt.Sprintf实现的内置函数,所有模板都可以直接使用。对于Age部分,第二个动作是一个叫daysAgo的函数,通过time.Since函数将CreatedAt成员转换为过去的时间长度:</p>
|
<p>在一个action中,<code>|</code>操作符表示将前一个表达式的结果作为后一个函数的输入,类似于UNIX中管道的概念。在Title这一行的action中,第二个操作是一个printf函数,是一个基于fmt.Sprintf实现的内置函数,所有模板都可以直接使用。对于Age部分,第二个动作是一个叫daysAgo的函数,通过time.Since函数将CreatedAt成员转换为过去的时间长度:</p>
|
||||||
<pre><code class="language-Go">func daysAgo(t time.Time) int {
|
<pre><code class="language-Go">func daysAgo(t time.Time) int {
|
||||||
return int(time.Since(t).Hours() / 24)
|
return int(time.Since(t).Hours() / 24)
|
||||||
@ -247,7 +245,6 @@ var issueList = template.Must(template.New("issuelist").Parse(`
|
|||||||
</table>
|
</table>
|
||||||
`))
|
`))
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>下面的命令将在新的模板上执行一个稍微不同的查询:</p>
|
<p>下面的命令将在新的模板上执行一个稍微不同的查询:</p>
|
||||||
<pre><code>$ go build gopl.io/ch4/issueshtml
|
<pre><code>$ go build gopl.io/ch4/issueshtml
|
||||||
$ ./issueshtml repo:golang/go commenter:gopherbot json encoder >issues.html
|
$ ./issueshtml repo:golang/go commenter:gopherbot json encoder >issues.html
|
||||||
@ -275,7 +272,6 @@ $ ./issueshtml repo:golang/go commenter:gopherbot json encoder >issues.html
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>图4.6显示了出现在浏览器中的模板输出。我们看到A的黑体标记被转义失效了,但是B没有。</p>
|
<p>图4.6显示了出现在浏览器中的模板输出。我们看到A的黑体标记被转义失效了,但是B没有。</p>
|
||||||
<p><img src="../images/ch4-06.png" alt="" /></p>
|
<p><img src="../images/ch4-06.png" alt="" /></p>
|
||||||
<p>我们这里只讲述了模板系统中最基本的特性。一如既往,如果想了解更多的信息,请自己查看包文档:</p>
|
<p>我们这里只讲述了模板系统中最基本的特性。一如既往,如果想了解更多的信息,请自己查看包文档:</p>
|
||||||
|
BIN
cover.jpg
BIN
cover.jpg
Binary file not shown.
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 108 KiB |
BIN
cover_middle.jpg
BIN
cover_middle.jpg
Binary file not shown.
Before Width: | Height: | Size: 34 KiB |
BIN
cover_small.jpg
BIN
cover_small.jpg
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
@ -1,82 +0,0 @@
|
|||||||
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
// 修复Gitbook生成html的时间戳.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 输出目录
|
|
||||||
const dir = "_book"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// data-revision="Mon Feb 01 2016 10:18:48 GMT+0800 (中国标准时间)"
|
|
||||||
reDataRevision = regexp.MustCompile(`data\-revision\=\"[^"]+\"`)
|
|
||||||
goldenDataRevision = `data-revision="Mon Jan 2 15:04:05 -0700 MST 2006"`
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
total := 0
|
|
||||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("filepath.Walk: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
relpath, err := filepath.Rel(dir, path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("filepath.Rel: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasSuffix(relpath, ".html") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if changed := convertFile(path); changed {
|
|
||||||
fmt.Printf("%s\n", relpath)
|
|
||||||
total++
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
fmt.Printf("total %d\n", total)
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertFile(path string) (changed bool) {
|
|
||||||
abspath, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("convertFile: filepath.Abs:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
oldData, err := ioutil.ReadFile(abspath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("convertFile: ioutil.ReadFile:", err)
|
|
||||||
}
|
|
||||||
if !utf8.Valid(oldData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
newData := reDataRevision.ReplaceAll(oldData, []byte(goldenDataRevision))
|
|
||||||
if string(newData) == string(oldData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(abspath, newData, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("convertFile: ioutil.WriteFile:", err)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
@ -172,8 +172,8 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>译者:柴树杉,Github <a href="https://github.com/chai2010">@chai2010</a>,Twitter <a href="https://twitter.com/chaishushan">@chaishushan</a></li>
|
<li>译者:柴树杉,Github <a href="https://github.com/chai2010">@chai2010</a>,Twitter <a href="https://twitter.com/chaishushan">@chaishushan</a></li>
|
||||||
<li>译者:Xargin, <a href="https://github.com/cch123">https://github.com/cch123</a></li>
|
<li>译者:Xargin, <a href="https://github.com/cch123">https://github.com/cch123</a></li>
|
||||||
<li>译者:CrazySssst</li>
|
<li>译者:CrazySssst, <a href="https://github.com/CrazySssst">https://github.com/CrazySssst</a></li>
|
||||||
<li>译者:foreversmart <a href="mailto:njutree@gmail.com">njutree@gmail.com</a></li>
|
<li>译者:foreversmart,<a href="https://github.com/foreversmart">https://github.com/foreversmart</a> <a href="mailto:njutree@gmail.com">njutree@gmail.com</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
9
make.bat
9
make.bat
@ -1,9 +0,0 @@
|
|||||||
@echo off
|
|
||||||
cls
|
|
||||||
setlocal EnableDelayedExpansion
|
|
||||||
rem gitbook install
|
|
||||||
rem &^ 批处理运行gitbook会中断命令 所以用&链接,用^处理换行
|
|
||||||
go run update_version.go
|
|
||||||
gitbook build &^
|
|
||||||
go run fix-data-revision.go &^
|
|
||||||
go run builder.go
|
|
15
print.html
15
print.html
@ -170,8 +170,8 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>译者:柴树杉,Github <a href="https://github.com/chai2010">@chai2010</a>,Twitter <a href="https://twitter.com/chaishushan">@chaishushan</a></li>
|
<li>译者:柴树杉,Github <a href="https://github.com/chai2010">@chai2010</a>,Twitter <a href="https://twitter.com/chaishushan">@chaishushan</a></li>
|
||||||
<li>译者:Xargin, <a href="https://github.com/cch123">https://github.com/cch123</a></li>
|
<li>译者:Xargin, <a href="https://github.com/cch123">https://github.com/cch123</a></li>
|
||||||
<li>译者:CrazySssst</li>
|
<li>译者:CrazySssst, <a href="https://github.com/CrazySssst">https://github.com/CrazySssst</a></li>
|
||||||
<li>译者:foreversmart <a href="mailto:njutree@gmail.com">njutree@gmail.com</a></li>
|
<li>译者:foreversmart,<a href="https://github.com/foreversmart">https://github.com/foreversmart</a> <a href="mailto:njutree@gmail.com">njutree@gmail.com</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div style="break-before: page; page-break-before: always;"></div><h1 id="前言"><a class="header" href="#前言">前言</a></h1>
|
<div style="break-before: page; page-break-before: always;"></div><h1 id="前言"><a class="header" href="#前言">前言</a></h1>
|
||||||
<h2 id="go语言起源"><a class="header" href="#go语言起源">Go语言起源</a></h2>
|
<h2 id="go语言起源"><a class="header" href="#go语言起源">Go语言起源</a></h2>
|
||||||
@ -3361,9 +3361,7 @@ Title: {{.Title | printf "%.64s"}}
|
|||||||
Age: {{.CreatedAt | daysAgo}} days
|
Age: {{.CreatedAt | daysAgo}} days
|
||||||
{{end}}`
|
{{end}}`
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>这个模板先打印匹配到的issue总数,然后打印每个issue的编号、创建用户、标题还有存在的时间。对于每一个action,都有一个当前值的概念,对应点操作符,写作“.”。当前值“.”最初被初始化为调用模板时的参数,在当前例子中对应github.IssuesSearchResult类型的变量。模板中<code>{{.TotalCount}}</code>对应action将展开为结构体中TotalCount成员以默认的方式打印的值。模板中<code>{{range .Items}}</code>和<code>{{end}}</code>对应一个循环action,因此它们之间的内容可能会被展开多次,循环每次迭代的当前值对应当前的Items元素的值。</p>
|
<p>这个模板先打印匹配到的issue总数,然后打印每个issue的编号、创建用户、标题还有存在的时间。对于每一个action,都有一个当前值的概念,对应点操作符,写作“.”。当前值“.”最初被初始化为调用模板时的参数,在当前例子中对应github.IssuesSearchResult类型的变量。模板中<code>{{.TotalCount}}</code>对应action将展开为结构体中TotalCount成员以默认的方式打印的值。模板中<code>{{range .Items}}</code>和<code>{{end}}</code>对应一个循环action,因此它们之间的内容可能会被展开多次,循环每次迭代的当前值对应当前的Items元素的值。</p>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>在一个action中,<code>|</code>操作符表示将前一个表达式的结果作为后一个函数的输入,类似于UNIX中管道的概念。在Title这一行的action中,第二个操作是一个printf函数,是一个基于fmt.Sprintf实现的内置函数,所有模板都可以直接使用。对于Age部分,第二个动作是一个叫daysAgo的函数,通过time.Since函数将CreatedAt成员转换为过去的时间长度:</p>
|
<p>在一个action中,<code>|</code>操作符表示将前一个表达式的结果作为后一个函数的输入,类似于UNIX中管道的概念。在Title这一行的action中,第二个操作是一个printf函数,是一个基于fmt.Sprintf实现的内置函数,所有模板都可以直接使用。对于Age部分,第二个动作是一个叫daysAgo的函数,通过time.Since函数将CreatedAt成员转换为过去的时间长度:</p>
|
||||||
<pre><code class="language-Go">func daysAgo(t time.Time) int {
|
<pre><code class="language-Go">func daysAgo(t time.Time) int {
|
||||||
return int(time.Since(t).Hours() / 24)
|
return int(time.Since(t).Hours() / 24)
|
||||||
@ -3436,7 +3434,6 @@ var issueList = template.Must(template.New("issuelist").Parse(`
|
|||||||
</table>
|
</table>
|
||||||
`))
|
`))
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>下面的命令将在新的模板上执行一个稍微不同的查询:</p>
|
<p>下面的命令将在新的模板上执行一个稍微不同的查询:</p>
|
||||||
<pre><code>$ go build gopl.io/ch4/issueshtml
|
<pre><code>$ go build gopl.io/ch4/issueshtml
|
||||||
$ ./issueshtml repo:golang/go commenter:gopherbot json encoder >issues.html
|
$ ./issueshtml repo:golang/go commenter:gopherbot json encoder >issues.html
|
||||||
@ -3464,7 +3461,6 @@ $ ./issueshtml repo:golang/go commenter:gopherbot json encoder >issues.html
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>图4.6显示了出现在浏览器中的模板输出。我们看到A的黑体标记被转义失效了,但是B没有。</p>
|
<p>图4.6显示了出现在浏览器中的模板输出。我们看到A的黑体标记被转义失效了,但是B没有。</p>
|
||||||
<p><img src="ch4/../images/ch4-06.png" alt="" /></p>
|
<p><img src="ch4/../images/ch4-06.png" alt="" /></p>
|
||||||
<p>我们这里只讲述了模板系统中最基本的特性。一如既往,如果想了解更多的信息,请自己查看包文档:</p>
|
<p>我们这里只讲述了模板系统中最基本的特性。一如既往,如果想了解更多的信息,请自己查看包文档:</p>
|
||||||
@ -8528,11 +8524,9 @@ gopl.io/ch7/xmlselect
|
|||||||
<pre><code>$ go list -f '{{join .Deps " "}}' strconv
|
<pre><code>$ go list -f '{{join .Deps " "}}' strconv
|
||||||
errors math runtime unicode/utf8 unsafe
|
errors math runtime unicode/utf8 unsafe
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>译注:上面的命令在Windows的命令行运行会遇到<code>template: main:1: unclosed action</code>的错误。产生这个错误的原因是因为命令行对命令中的<code>" "</code>参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:</p>
|
<p>译注:上面的命令在Windows的命令行运行会遇到<code>template: main:1: unclosed action</code>的错误。产生这个错误的原因是因为命令行对命令中的<code>" "</code>参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:</p>
|
||||||
<pre><code>$ go list -f "{{join .Deps \" \"}}" strconv
|
<pre><code>$ go list -f "{{join .Deps \" \"}}" strconv
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>下面的命令打印compress子目录下所有包的导入包列表:</p>
|
<p>下面的命令打印compress子目录下所有包的导入包列表:</p>
|
||||||
<pre><code>$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/...
|
<pre><code>$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/...
|
||||||
compress/bzip2 -> bufio io sort
|
compress/bzip2 -> bufio io sort
|
||||||
@ -8541,11 +8535,9 @@ compress/gzip -> bufio compress/flate errors fmt hash hash/crc32 io time
|
|||||||
compress/lzw -> bufio errors fmt io
|
compress/lzw -> bufio errors fmt io
|
||||||
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>译注:Windows下有同样有问题,要避免转义字符串的干扰:</p>
|
<p>译注:Windows下有同样有问题,要避免转义字符串的干扰:</p>
|
||||||
<pre><code>$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
|
<pre><code>$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p><code>go list</code>命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用<code>go help list</code>命令查看。</p>
|
<p><code>go list</code>命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用<code>go help list</code>命令查看。</p>
|
||||||
<p>在本章,我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。在下一章,我们将看到如何用<code>go test</code>命令去运行Go语言程序中的测试代码。</p>
|
<p>在本章,我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。在下一章,我们将看到如何用<code>go test</code>命令去运行Go语言程序中的测试代码。</p>
|
||||||
<p><strong>练习 10.4:</strong> 创建一个工具,根据命令行指定的参数,报告工作区所有依赖包指定的其它包集合。提示:你需要运行<code>go list</code>命令两次,一次用于初始化包,一次用于所有包。你可能需要用encoding/json(§4.5)包来分析输出的JSON格式的信息。</p>
|
<p><strong>练习 10.4:</strong> 创建一个工具,根据命令行指定的参数,报告工作区所有依赖包指定的其它包集合。提示:你需要运行<code>go list</code>命令两次,一次用于初始化包,一次用于所有包。你可能需要用encoding/json(§4.5)包来分析输出的JSON格式的信息。</p>
|
||||||
@ -8979,18 +8971,15 @@ func TestCheckQuotaNotifiesUser(t *testing.T) {
|
|||||||
<pre><code>$ go list -f={{.GoFiles}} fmt
|
<pre><code>$ go list -f={{.GoFiles}} fmt
|
||||||
[doc.go format.go print.go scan.go]
|
[doc.go format.go print.go scan.go]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>TestGoFiles表示的是fmt包内部测试代码,以_test.go为后缀文件名,不过只在测试时被构建:</p>
|
<p>TestGoFiles表示的是fmt包内部测试代码,以_test.go为后缀文件名,不过只在测试时被构建:</p>
|
||||||
<pre><code>$ go list -f={{.TestGoFiles}} fmt
|
<pre><code>$ go list -f={{.TestGoFiles}} fmt
|
||||||
[export_test.go]
|
[export_test.go]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>包的测试代码通常都在这些文件中,不过fmt包并非如此;稍后我们再解释export_test.go文件的作用。</p>
|
<p>包的测试代码通常都在这些文件中,不过fmt包并非如此;稍后我们再解释export_test.go文件的作用。</p>
|
||||||
<p>XTestGoFiles表示的是属于外部测试包的测试代码,也就是fmt_test包,因此它们必须先导入fmt包。同样,这些文件也只是在测试时被构建运行:</p>
|
<p>XTestGoFiles表示的是属于外部测试包的测试代码,也就是fmt_test包,因此它们必须先导入fmt包。同样,这些文件也只是在测试时被构建运行:</p>
|
||||||
<pre><code>$ go list -f={{.XTestGoFiles}} fmt
|
<pre><code>$ go list -f={{.XTestGoFiles}} fmt
|
||||||
[fmt_test.go scan_test.go stringer_test.go]
|
[fmt_test.go scan_test.go stringer_test.go]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>{% endraw %}</p>
|
|
||||||
<p>有时候外部测试包也需要访问被测试包内部的代码,例如在一个为了避免循环导入而被独立到外部测试包的白盒测试。在这种情况下,我们可以通过一些技巧解决:我们在包内的一个_test.go文件中导出一个内部的实现给外部测试包。因为这些代码只有在测试时才需要,因此一般会放在export_test.go文件中。</p>
|
<p>有时候外部测试包也需要访问被测试包内部的代码,例如在一个为了避免循环导入而被独立到外部测试包的白盒测试。在这种情况下,我们可以通过一些技巧解决:我们在包内的一个_test.go文件中导出一个内部的实现给外部测试包。因为这些代码只有在测试时才需要,因此一般会放在export_test.go文件中。</p>
|
||||||
<p>例如,fmt包的fmt.Scanf函数需要unicode.IsSpace函数提供的功能。但是为了避免太多的依赖,fmt包并没有导入包含巨大表格数据的unicode包;相反fmt包有一个叫isSpace内部的简易实现。</p>
|
<p>例如,fmt包的fmt.Scanf函数需要unicode.IsSpace函数提供的功能。但是为了避免太多的依赖,fmt包并没有导入包含巨大表格数据的unicode包;相反fmt包有一个叫isSpace内部的简易实现。</p>
|
||||||
<p>为了确保fmt.isSpace和unicode.IsSpace函数的行为保持一致,fmt包谨慎地包含了一个测试。一个在外部测试包内的白盒测试,是无法直接访问到isSpace内部函数的,因此fmt通过一个后门导出了isSpace函数。export_test.go文件就是专门用于外部测试包的后门。</p>
|
<p>为了确保fmt.isSpace和unicode.IsSpace函数的行为保持一致,fmt包谨慎地包含了一个测试。一个在外部测试包内的白盒测试,是无法直接访问到isSpace内部函数的,因此fmt通过一个后门导出了isSpace函数。export_test.go文件就是专门用于外部测试包的后门。</p>
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
48933
tools/STPhrases.txt
48933
tools/STPhrases.txt
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
102
tools/cpdir.go
102
tools/cpdir.go
@ -1,102 +0,0 @@
|
|||||||
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
//
|
|
||||||
// Copy dir, support regexp.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// cpdir src dst
|
|
||||||
// cpdir src dst "\.go$"
|
|
||||||
// cpdir src dst "\.tiff?$"
|
|
||||||
// cpdir src dst "\.tiff?|jpg|jpeg$"
|
|
||||||
//
|
|
||||||
// Help:
|
|
||||||
// cpdir -h
|
|
||||||
//
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
const usage = `
|
|
||||||
Usage: cpdir src dst [filter]
|
|
||||||
cpdir -h
|
|
||||||
|
|
||||||
Example:
|
|
||||||
cpdir src dst
|
|
||||||
cpdir src dst "\.go$"
|
|
||||||
cpdir src dst "\.tiff?$"
|
|
||||||
cpdir src dst "\.tiff?|jpg|jpeg$"
|
|
||||||
|
|
||||||
Report bugs to <chaishushan{AT}gmail.com>.
|
|
||||||
`
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) < 3 {
|
|
||||||
fmt.Fprintln(os.Stderr, usage[1:len(usage)-1])
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
filter := ".*"
|
|
||||||
if len(os.Args) > 3 {
|
|
||||||
filter = os.Args[3]
|
|
||||||
}
|
|
||||||
total := cpDir(os.Args[2], os.Args[1], filter)
|
|
||||||
fmt.Printf("total %d\n", total)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cpDir(dst, src, filter string) (total int) {
|
|
||||||
entryList, err := ioutil.ReadDir(src)
|
|
||||||
if err != nil && !os.IsExist(err) {
|
|
||||||
log.Fatal("cpDir: ", err)
|
|
||||||
}
|
|
||||||
for _, entry := range entryList {
|
|
||||||
if entry.IsDir() {
|
|
||||||
cpDir(dst+"/"+entry.Name(), src+"/"+entry.Name(), filter)
|
|
||||||
} else {
|
|
||||||
mathed, err := regexp.MatchString(filter, entry.Name())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("regexp.MatchString: ", err)
|
|
||||||
}
|
|
||||||
if mathed {
|
|
||||||
srcFname := filepath.Clean(src + "/" + entry.Name())
|
|
||||||
dstFname := filepath.Clean(dst + "/" + entry.Name())
|
|
||||||
fmt.Printf("copy %s\n", srcFname)
|
|
||||||
|
|
||||||
cpFile(dstFname, srcFname)
|
|
||||||
total++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func cpFile(dst, src string) {
|
|
||||||
err := os.MkdirAll(filepath.Dir(dst), 0666)
|
|
||||||
if err != nil && !os.IsExist(err) {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
fsrc, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
defer fsrc.Close()
|
|
||||||
|
|
||||||
fdst, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
defer fdst.Close()
|
|
||||||
if _, err = io.Copy(fdst, fsrc); err != nil {
|
|
||||||
log.Fatal("cpFile: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
// Lookpath is a simple which.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) < 2 {
|
|
||||||
fmt.Printf(`Usage: lookpath COMMAND [...]
|
|
||||||
Write the full path of COMMAND(s) to standard output.
|
|
||||||
|
|
||||||
Report bugs to <chaishushan@gmail.com>.
|
|
||||||
`)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
for i := 1; i < len(os.Args); i++ {
|
|
||||||
path, err := exec.LookPath(os.Args[i])
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("lookpath: no %s in (%v)\n", os.Args[i], GetEnv("PATH"))
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
fmt.Println(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetEnv(key string) string {
|
|
||||||
key = strings.ToUpper(key) + "="
|
|
||||||
for _, env := range os.Environ() {
|
|
||||||
if strings.HasPrefix(strings.ToUpper(env), key) {
|
|
||||||
return env[len(key):]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
//
|
|
||||||
// List files, support file/header regexp.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// lsdir dir
|
|
||||||
// lsdir dir "\.go$"
|
|
||||||
// lsdir dir "\.go$" "chaishushan"
|
|
||||||
// lsdir dir "\.tiff?|jpg|jpeg$"
|
|
||||||
//
|
|
||||||
// Help:
|
|
||||||
// lsdir -h
|
|
||||||
//
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
const usage = `
|
|
||||||
Usage: lsdir dir [nameFilter [dataFilter]]
|
|
||||||
lsdir -h
|
|
||||||
|
|
||||||
Example:
|
|
||||||
lsdir dir
|
|
||||||
lsdir dir "\.go$"
|
|
||||||
lsdir dir "\.go$" "chaishushan"
|
|
||||||
lsdir dir "\.tiff?|jpg|jpeg$"
|
|
||||||
|
|
||||||
Report bugs to <chaishushan{AT}gmail.com>.
|
|
||||||
`
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) < 2 || os.Args[1] == "-h" {
|
|
||||||
fmt.Fprintln(os.Stderr, usage[1:len(usage)-1])
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
dir, nameFilter, dataFilter := os.Args[1], ".*", ""
|
|
||||||
if len(os.Args) > 2 {
|
|
||||||
nameFilter = os.Args[2]
|
|
||||||
}
|
|
||||||
if len(os.Args) > 3 {
|
|
||||||
dataFilter = os.Args[3]
|
|
||||||
}
|
|
||||||
|
|
||||||
total := 0
|
|
||||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("filepath.Walk: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
relpath, err := filepath.Rel(dir, path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("filepath.Rel: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mathed, err := regexp.MatchString(nameFilter, relpath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("regexp.MatchString: ", err)
|
|
||||||
}
|
|
||||||
if mathed {
|
|
||||||
if dataFilter != "" {
|
|
||||||
data, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("ioutil.ReadFile: %s\n", path)
|
|
||||||
log.Fatal("ioutil.ReadFile: ", err)
|
|
||||||
}
|
|
||||||
mathed, err := regexp.MatchString(dataFilter, string(data))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("regexp.MatchString: ", err)
|
|
||||||
}
|
|
||||||
if mathed {
|
|
||||||
fmt.Printf("%s\n", relpath)
|
|
||||||
total++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%s\n", relpath)
|
|
||||||
total++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
fmt.Printf("total %d\n", total)
|
|
||||||
}
|
|
121
tools/md5.go
121
tools/md5.go
@ -1,121 +0,0 @@
|
|||||||
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
//
|
|
||||||
// Cacl dir or file MD5Sum, support regexp.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// md5 file
|
|
||||||
// md5 dir "\.go$"
|
|
||||||
// md5 dir "\.tiff?$"
|
|
||||||
// md5 dir "\.tiff?|jpg|jpeg$"
|
|
||||||
//
|
|
||||||
// Help:
|
|
||||||
// cpdir -h
|
|
||||||
//
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
const usage = `
|
|
||||||
Usage: md5 [dir|file [filter]]
|
|
||||||
md5 -h
|
|
||||||
|
|
||||||
Example:
|
|
||||||
md5 file
|
|
||||||
md5 dir "\.go$"
|
|
||||||
md5 dir "\.go$"
|
|
||||||
md5 dir "\.tiff?$"
|
|
||||||
md5 dir "\.tiff?|jpg|jpeg$"
|
|
||||||
|
|
||||||
Report bugs to <chaishushan{AT}gmail.com>.
|
|
||||||
`
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) < 2 {
|
|
||||||
fmt.Fprintln(os.Stderr, usage[1:len(usage)-1])
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
filter := ".*"
|
|
||||||
if len(os.Args) > 2 {
|
|
||||||
filter = os.Args[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
if name := os.Args[1]; IsDir(name) {
|
|
||||||
m, err := MD5Dir(name, filter)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("%s: %v", name, err)
|
|
||||||
}
|
|
||||||
var paths []string
|
|
||||||
for path := range m {
|
|
||||||
paths = append(paths, path)
|
|
||||||
}
|
|
||||||
sort.Strings(paths)
|
|
||||||
for _, path := range paths {
|
|
||||||
fmt.Printf("%x *%s\n", m[path], path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sum, err := MD5File(name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("%s: %v", name, err)
|
|
||||||
}
|
|
||||||
fmt.Printf("%x *%s\n", sum, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsDir(name string) bool {
|
|
||||||
fi, err := os.Lstat(name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return fi.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func MD5Dir(root string, filter string) (map[string][md5.Size]byte, error) {
|
|
||||||
m := make(map[string][md5.Size]byte)
|
|
||||||
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
mathed, err := regexp.MatchString(filter, path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("regexp.MatchString: ", err)
|
|
||||||
}
|
|
||||||
if mathed {
|
|
||||||
data, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m[path] = md5.Sum(data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MD5File(filename string) (sum [md5.Size]byte, err error) {
|
|
||||||
data, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sum = md5.Sum(data)
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
qr "github.com/chai2010/image/qrencoder"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
gopl_zh_url = "https://github.com/golang-china/gopl-zh"
|
|
||||||
output = "gopl-zh-qrcode.png"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
c, err := qr.Encode(gopl_zh_url, qr.H)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(output, c.PNG(), 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("output:", output)
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
f, err := os.Open("./TSCharacters.txt")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("open failed:", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
br := bufio.NewReader(f)
|
|
||||||
|
|
||||||
var out bytes.Buffer
|
|
||||||
fmt.Fprintf(&out, `
|
|
||||||
// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Auto generated by go generate, DO NOT EDIT !!!
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
var _TSCharactersMap = map[rune]rune{
|
|
||||||
`[1:])
|
|
||||||
for i := 0; i < 1<<20; i++ {
|
|
||||||
data, isPrefix, err := br.ReadLine()
|
|
||||||
if err != nil || isPrefix {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !utf8.ValidString(string(data)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
line := strings.Replace(string(data), "\t", " ", -1)
|
|
||||||
ss := strings.Split(string(line), " ")
|
|
||||||
|
|
||||||
if len(ss) >= 2 {
|
|
||||||
tw := strings.TrimSpace(ss[0])
|
|
||||||
zh := strings.TrimSpace(ss[1])
|
|
||||||
fmt.Fprintf(&out, "\t'%s': '%s',\n", tw, zh)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&out, "}\n")
|
|
||||||
|
|
||||||
ioutil.WriteFile("z_TSCharacters.go", []byte(out.Bytes()), 0666)
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
// 更新版本信息
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
version, commitTime := getGitCommitVersion()
|
|
||||||
buildTime := time.Now()
|
|
||||||
|
|
||||||
data := makeVersionMarkdown(version, commitTime, buildTime)
|
|
||||||
|
|
||||||
err := ioutil.WriteFile("./version.md", []byte(data), 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ioutil.WriteFile: err = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("build version", version)
|
|
||||||
fmt.Println(commitTime.Format("2006-01-02 15:04:05"))
|
|
||||||
fmt.Println(buildTime.Format("2006-01-02 15:04:05"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成版本文件
|
|
||||||
func makeVersionMarkdown(version string, commitTime, buildTime time.Time) string {
|
|
||||||
return fmt.Sprintf(`
|
|
||||||
<!-- 版本号文件,用于被其它md文件包含 -->
|
|
||||||
|
|
||||||
### 版本信息
|
|
||||||
|
|
||||||
- 仓库版本:[%s](gopl-zh-%s.zip)
|
|
||||||
- 更新时间:%s
|
|
||||||
- 构建时间:%s
|
|
||||||
`,
|
|
||||||
version, version,
|
|
||||||
commitTime.Format("2006-01-02 15:04:05"),
|
|
||||||
buildTime.Format("2006-01-02 15:04:05"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取Git最新的版本号
|
|
||||||
//
|
|
||||||
// git log -1
|
|
||||||
// commit 0460c1b3bb8fbb7e2fc88961e69aa37f4041d6c1
|
|
||||||
// Merge: b2d582a e826457
|
|
||||||
// Author: chai2010 <chaishushan{AT}gmail.com>
|
|
||||||
// Date: Mon Feb 1 08:04:44 2016 +0800
|
|
||||||
//
|
|
||||||
// Merge pull request #249 from sunclx/patch-3
|
|
||||||
//
|
|
||||||
// fix typo
|
|
||||||
func getGitCommitVersion() (version string, date time.Time) {
|
|
||||||
cmdOut, err := exec.Command(`git`, `log`, `-1`).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "unknown", time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC) // 第一版发布时间
|
|
||||||
}
|
|
||||||
for _, line := range strings.Split(string(cmdOut), "\n") {
|
|
||||||
line := strings.TrimSpace(line)
|
|
||||||
if strings.HasPrefix(line, "commit") {
|
|
||||||
version = strings.TrimSpace(line[len("commit"):])
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(line, "Date") {
|
|
||||||
const longForm = "Mon Jan 2 15:04:05 2006 -0700"
|
|
||||||
date, _ = time.Parse(longForm, strings.TrimSpace(line[len("Date:"):]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if version == "" || date.IsZero() {
|
|
||||||
return "unknown", time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC) // 第一版发布时间
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
28
vendor/github.com/chai2010/image/qrencoder/hello.go
generated
vendored
28
vendor/github.com/chai2010/image/qrencoder/hello.go
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ingore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
qr "github.com/chai2010/image/qrencoder"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
c, err := qr.Encode("hello, world", qr.L)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile("zz_qrout.png", c.PNG(), 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("output: zz_qrout.png\n")
|
|
||||||
}
|
|
149
vendor/github.com/chai2010/image/qrencoder/internal/coding/gen.go
generated
vendored
149
vendor/github.com/chai2010/image/qrencoder/internal/coding/gen.go
generated
vendored
@ -1,149 +0,0 @@
|
|||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// tables from qrencode-3.1.1/qrspec.c
|
|
||||||
|
|
||||||
var capacity = [41]struct {
|
|
||||||
width int
|
|
||||||
words int
|
|
||||||
remainder int
|
|
||||||
ec [4]int
|
|
||||||
}{
|
|
||||||
{0, 0, 0, [4]int{0, 0, 0, 0}},
|
|
||||||
{21, 26, 0, [4]int{7, 10, 13, 17}}, // 1
|
|
||||||
{25, 44, 7, [4]int{10, 16, 22, 28}},
|
|
||||||
{29, 70, 7, [4]int{15, 26, 36, 44}},
|
|
||||||
{33, 100, 7, [4]int{20, 36, 52, 64}},
|
|
||||||
{37, 134, 7, [4]int{26, 48, 72, 88}}, // 5
|
|
||||||
{41, 172, 7, [4]int{36, 64, 96, 112}},
|
|
||||||
{45, 196, 0, [4]int{40, 72, 108, 130}},
|
|
||||||
{49, 242, 0, [4]int{48, 88, 132, 156}},
|
|
||||||
{53, 292, 0, [4]int{60, 110, 160, 192}},
|
|
||||||
{57, 346, 0, [4]int{72, 130, 192, 224}}, //10
|
|
||||||
{61, 404, 0, [4]int{80, 150, 224, 264}},
|
|
||||||
{65, 466, 0, [4]int{96, 176, 260, 308}},
|
|
||||||
{69, 532, 0, [4]int{104, 198, 288, 352}},
|
|
||||||
{73, 581, 3, [4]int{120, 216, 320, 384}},
|
|
||||||
{77, 655, 3, [4]int{132, 240, 360, 432}}, //15
|
|
||||||
{81, 733, 3, [4]int{144, 280, 408, 480}},
|
|
||||||
{85, 815, 3, [4]int{168, 308, 448, 532}},
|
|
||||||
{89, 901, 3, [4]int{180, 338, 504, 588}},
|
|
||||||
{93, 991, 3, [4]int{196, 364, 546, 650}},
|
|
||||||
{97, 1085, 3, [4]int{224, 416, 600, 700}}, //20
|
|
||||||
{101, 1156, 4, [4]int{224, 442, 644, 750}},
|
|
||||||
{105, 1258, 4, [4]int{252, 476, 690, 816}},
|
|
||||||
{109, 1364, 4, [4]int{270, 504, 750, 900}},
|
|
||||||
{113, 1474, 4, [4]int{300, 560, 810, 960}},
|
|
||||||
{117, 1588, 4, [4]int{312, 588, 870, 1050}}, //25
|
|
||||||
{121, 1706, 4, [4]int{336, 644, 952, 1110}},
|
|
||||||
{125, 1828, 4, [4]int{360, 700, 1020, 1200}},
|
|
||||||
{129, 1921, 3, [4]int{390, 728, 1050, 1260}},
|
|
||||||
{133, 2051, 3, [4]int{420, 784, 1140, 1350}},
|
|
||||||
{137, 2185, 3, [4]int{450, 812, 1200, 1440}}, //30
|
|
||||||
{141, 2323, 3, [4]int{480, 868, 1290, 1530}},
|
|
||||||
{145, 2465, 3, [4]int{510, 924, 1350, 1620}},
|
|
||||||
{149, 2611, 3, [4]int{540, 980, 1440, 1710}},
|
|
||||||
{153, 2761, 3, [4]int{570, 1036, 1530, 1800}},
|
|
||||||
{157, 2876, 0, [4]int{570, 1064, 1590, 1890}}, //35
|
|
||||||
{161, 3034, 0, [4]int{600, 1120, 1680, 1980}},
|
|
||||||
{165, 3196, 0, [4]int{630, 1204, 1770, 2100}},
|
|
||||||
{169, 3362, 0, [4]int{660, 1260, 1860, 2220}},
|
|
||||||
{173, 3532, 0, [4]int{720, 1316, 1950, 2310}},
|
|
||||||
{177, 3706, 0, [4]int{750, 1372, 2040, 2430}}, //40
|
|
||||||
}
|
|
||||||
|
|
||||||
var eccTable = [41][4][2]int{
|
|
||||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}},
|
|
||||||
{{1, 0}, {1, 0}, {1, 0}, {1, 0}}, // 1
|
|
||||||
{{1, 0}, {1, 0}, {1, 0}, {1, 0}},
|
|
||||||
{{1, 0}, {1, 0}, {2, 0}, {2, 0}},
|
|
||||||
{{1, 0}, {2, 0}, {2, 0}, {4, 0}},
|
|
||||||
{{1, 0}, {2, 0}, {2, 2}, {2, 2}}, // 5
|
|
||||||
{{2, 0}, {4, 0}, {4, 0}, {4, 0}},
|
|
||||||
{{2, 0}, {4, 0}, {2, 4}, {4, 1}},
|
|
||||||
{{2, 0}, {2, 2}, {4, 2}, {4, 2}},
|
|
||||||
{{2, 0}, {3, 2}, {4, 4}, {4, 4}},
|
|
||||||
{{2, 2}, {4, 1}, {6, 2}, {6, 2}}, //10
|
|
||||||
{{4, 0}, {1, 4}, {4, 4}, {3, 8}},
|
|
||||||
{{2, 2}, {6, 2}, {4, 6}, {7, 4}},
|
|
||||||
{{4, 0}, {8, 1}, {8, 4}, {12, 4}},
|
|
||||||
{{3, 1}, {4, 5}, {11, 5}, {11, 5}},
|
|
||||||
{{5, 1}, {5, 5}, {5, 7}, {11, 7}}, //15
|
|
||||||
{{5, 1}, {7, 3}, {15, 2}, {3, 13}},
|
|
||||||
{{1, 5}, {10, 1}, {1, 15}, {2, 17}},
|
|
||||||
{{5, 1}, {9, 4}, {17, 1}, {2, 19}},
|
|
||||||
{{3, 4}, {3, 11}, {17, 4}, {9, 16}},
|
|
||||||
{{3, 5}, {3, 13}, {15, 5}, {15, 10}}, //20
|
|
||||||
{{4, 4}, {17, 0}, {17, 6}, {19, 6}},
|
|
||||||
{{2, 7}, {17, 0}, {7, 16}, {34, 0}},
|
|
||||||
{{4, 5}, {4, 14}, {11, 14}, {16, 14}},
|
|
||||||
{{6, 4}, {6, 14}, {11, 16}, {30, 2}},
|
|
||||||
{{8, 4}, {8, 13}, {7, 22}, {22, 13}}, //25
|
|
||||||
{{10, 2}, {19, 4}, {28, 6}, {33, 4}},
|
|
||||||
{{8, 4}, {22, 3}, {8, 26}, {12, 28}},
|
|
||||||
{{3, 10}, {3, 23}, {4, 31}, {11, 31}},
|
|
||||||
{{7, 7}, {21, 7}, {1, 37}, {19, 26}},
|
|
||||||
{{5, 10}, {19, 10}, {15, 25}, {23, 25}}, //30
|
|
||||||
{{13, 3}, {2, 29}, {42, 1}, {23, 28}},
|
|
||||||
{{17, 0}, {10, 23}, {10, 35}, {19, 35}},
|
|
||||||
{{17, 1}, {14, 21}, {29, 19}, {11, 46}},
|
|
||||||
{{13, 6}, {14, 23}, {44, 7}, {59, 1}},
|
|
||||||
{{12, 7}, {12, 26}, {39, 14}, {22, 41}}, //35
|
|
||||||
{{6, 14}, {6, 34}, {46, 10}, {2, 64}},
|
|
||||||
{{17, 4}, {29, 14}, {49, 10}, {24, 46}},
|
|
||||||
{{4, 18}, {13, 32}, {48, 14}, {42, 32}},
|
|
||||||
{{20, 4}, {40, 7}, {43, 22}, {10, 67}},
|
|
||||||
{{19, 6}, {18, 31}, {34, 34}, {20, 61}}, //40
|
|
||||||
}
|
|
||||||
|
|
||||||
var align = [41][2]int{
|
|
||||||
{0, 0},
|
|
||||||
{0, 0}, {18, 0}, {22, 0}, {26, 0}, {30, 0}, // 1- 5
|
|
||||||
{34, 0}, {22, 38}, {24, 42}, {26, 46}, {28, 50}, // 6-10
|
|
||||||
{30, 54}, {32, 58}, {34, 62}, {26, 46}, {26, 48}, //11-15
|
|
||||||
{26, 50}, {30, 54}, {30, 56}, {30, 58}, {34, 62}, //16-20
|
|
||||||
{28, 50}, {26, 50}, {30, 54}, {28, 54}, {32, 58}, //21-25
|
|
||||||
{30, 58}, {34, 62}, {26, 50}, {30, 54}, {26, 52}, //26-30
|
|
||||||
{30, 56}, {34, 60}, {30, 58}, {34, 62}, {30, 54}, //31-35
|
|
||||||
{24, 50}, {28, 54}, {32, 58}, {26, 54}, {30, 58}, //35-40
|
|
||||||
}
|
|
||||||
|
|
||||||
var versionPattern = [41]int{
|
|
||||||
0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
|
|
||||||
0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
|
|
||||||
0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
|
|
||||||
0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
|
|
||||||
0x27541, 0x28c69,
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Printf("\t{},\n")
|
|
||||||
for i := 1; i <= 40; i++ {
|
|
||||||
apos := align[i][0] - 2
|
|
||||||
if apos < 0 {
|
|
||||||
apos = 100
|
|
||||||
}
|
|
||||||
astride := align[i][1] - align[i][0]
|
|
||||||
if astride < 1 {
|
|
||||||
astride = 100
|
|
||||||
}
|
|
||||||
fmt.Printf("\t{%v, %v, %v, %#x, [4]level{{%v, %v}, {%v, %v}, {%v, %v}, {%v, %v}}}, // %v\n",
|
|
||||||
apos, astride, capacity[i].words,
|
|
||||||
versionPattern[i],
|
|
||||||
eccTable[i][0][0]+eccTable[i][0][1],
|
|
||||||
float64(capacity[i].ec[0])/float64(eccTable[i][0][0]+eccTable[i][0][1]),
|
|
||||||
eccTable[i][1][0]+eccTable[i][1][1],
|
|
||||||
float64(capacity[i].ec[1])/float64(eccTable[i][1][0]+eccTable[i][1][1]),
|
|
||||||
eccTable[i][2][0]+eccTable[i][2][1],
|
|
||||||
float64(capacity[i].ec[2])/float64(eccTable[i][2][0]+eccTable[i][2][1]),
|
|
||||||
eccTable[i][3][0]+eccTable[i][3][1],
|
|
||||||
float64(capacity[i].ec[3])/float64(eccTable[i][3][0]+eccTable[i][3][1]),
|
|
||||||
i,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
815
vendor/github.com/chai2010/image/qrencoder/internal/coding/qr.go
generated
vendored
815
vendor/github.com/chai2010/image/qrencoder/internal/coding/qr.go
generated
vendored
@ -1,815 +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 coding implements low-level QR coding details.
|
|
||||||
package coding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/chai2010/image/qrencoder/internal/gf256"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Field is the field for QR error correction.
|
|
||||||
var Field = gf256.NewField(0x11d, 2)
|
|
||||||
|
|
||||||
// A Version represents a QR version.
|
|
||||||
// The version specifies the size of the QR code:
|
|
||||||
// a QR code with version v has 4v+17 pixels on a side.
|
|
||||||
// Versions number from 1 to 40: the larger the version,
|
|
||||||
// the more information the code can store.
|
|
||||||
type Version int
|
|
||||||
|
|
||||||
const MinVersion = 1
|
|
||||||
const MaxVersion = 40
|
|
||||||
|
|
||||||
func (v Version) String() string {
|
|
||||||
return strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Version) sizeClass() int {
|
|
||||||
if v <= 9 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if v <= 26 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataBytes returns the number of data bytes that can be
|
|
||||||
// stored in a QR code with the given version and level.
|
|
||||||
func (v Version) DataBytes(l Level) int {
|
|
||||||
vt := &vtab[v]
|
|
||||||
lev := &vt.level[l]
|
|
||||||
return vt.bytes - lev.nblock*lev.check
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoding implements a QR data encoding scheme.
|
|
||||||
// The implementations--Numeric, Alphanumeric, and String--specify
|
|
||||||
// the character set and the mapping from UTF-8 to code bits.
|
|
||||||
// The more restrictive the mode, the fewer code bits are needed.
|
|
||||||
type Encoding interface {
|
|
||||||
Check() error
|
|
||||||
Bits(v Version) int
|
|
||||||
Encode(b *Bits, v Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bits struct {
|
|
||||||
b []byte
|
|
||||||
nbit int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) Reset() {
|
|
||||||
b.b = b.b[:0]
|
|
||||||
b.nbit = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) Bits() int {
|
|
||||||
return b.nbit
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) Bytes() []byte {
|
|
||||||
if b.nbit%8 != 0 {
|
|
||||||
panic("fractional byte")
|
|
||||||
}
|
|
||||||
return b.b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) Append(p []byte) {
|
|
||||||
if b.nbit%8 != 0 {
|
|
||||||
panic("fractional byte")
|
|
||||||
}
|
|
||||||
b.b = append(b.b, p...)
|
|
||||||
b.nbit += 8 * len(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) Write(v uint, nbit int) {
|
|
||||||
for nbit > 0 {
|
|
||||||
n := nbit
|
|
||||||
if n > 8 {
|
|
||||||
n = 8
|
|
||||||
}
|
|
||||||
if b.nbit%8 == 0 {
|
|
||||||
b.b = append(b.b, 0)
|
|
||||||
} else {
|
|
||||||
m := -b.nbit & 7
|
|
||||||
if n > m {
|
|
||||||
n = m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.nbit += n
|
|
||||||
sh := uint(nbit - n)
|
|
||||||
b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7))
|
|
||||||
v -= v >> sh << sh
|
|
||||||
nbit -= n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Num is the encoding for numeric data.
|
|
||||||
// The only valid characters are the decimal digits 0 through 9.
|
|
||||||
type Num string
|
|
||||||
|
|
||||||
func (s Num) String() string {
|
|
||||||
return fmt.Sprintf("Num(%#q)", string(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Num) Check() error {
|
|
||||||
for _, c := range s {
|
|
||||||
if c < '0' || '9' < c {
|
|
||||||
return fmt.Errorf("non-numeric string %#q", string(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var numLen = [3]int{10, 12, 14}
|
|
||||||
|
|
||||||
func (s Num) Bits(v Version) int {
|
|
||||||
return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Num) Encode(b *Bits, v Version) {
|
|
||||||
b.Write(1, 4)
|
|
||||||
b.Write(uint(len(s)), numLen[v.sizeClass()])
|
|
||||||
var i int
|
|
||||||
for i = 0; i+3 <= len(s); i += 3 {
|
|
||||||
w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0')
|
|
||||||
b.Write(w, 10)
|
|
||||||
}
|
|
||||||
switch len(s) - i {
|
|
||||||
case 1:
|
|
||||||
w := uint(s[i] - '0')
|
|
||||||
b.Write(w, 4)
|
|
||||||
case 2:
|
|
||||||
w := uint(s[i]-'0')*10 + uint(s[i+1]-'0')
|
|
||||||
b.Write(w, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alpha is the encoding for alphanumeric data.
|
|
||||||
// The valid characters are 0-9A-Z$%*+-./: and space.
|
|
||||||
type Alpha string
|
|
||||||
|
|
||||||
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
|
|
||||||
|
|
||||||
func (s Alpha) String() string {
|
|
||||||
return fmt.Sprintf("Alpha(%#q)", string(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Alpha) Check() error {
|
|
||||||
for _, c := range s {
|
|
||||||
if strings.IndexRune(alphabet, c) < 0 {
|
|
||||||
return fmt.Errorf("non-alphanumeric string %#q", string(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var alphaLen = [3]int{9, 11, 13}
|
|
||||||
|
|
||||||
func (s Alpha) Bits(v Version) int {
|
|
||||||
return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Alpha) Encode(b *Bits, v Version) {
|
|
||||||
b.Write(2, 4)
|
|
||||||
b.Write(uint(len(s)), alphaLen[v.sizeClass()])
|
|
||||||
var i int
|
|
||||||
for i = 0; i+2 <= len(s); i += 2 {
|
|
||||||
w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 +
|
|
||||||
uint(strings.IndexRune(alphabet, rune(s[i+1])))
|
|
||||||
b.Write(w, 11)
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < len(s) {
|
|
||||||
w := uint(strings.IndexRune(alphabet, rune(s[i])))
|
|
||||||
b.Write(w, 6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String is the encoding for 8-bit data. All bytes are valid.
|
|
||||||
type String string
|
|
||||||
|
|
||||||
func (s String) String() string {
|
|
||||||
return fmt.Sprintf("String(%#q)", string(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s String) Check() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var stringLen = [3]int{8, 16, 16}
|
|
||||||
|
|
||||||
func (s String) Bits(v Version) int {
|
|
||||||
return 4 + stringLen[v.sizeClass()] + 8*len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s String) Encode(b *Bits, v Version) {
|
|
||||||
b.Write(4, 4)
|
|
||||||
b.Write(uint(len(s)), stringLen[v.sizeClass()])
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
b.Write(uint(s[i]), 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Pixel describes a single pixel in a QR code.
|
|
||||||
type Pixel uint32
|
|
||||||
|
|
||||||
const (
|
|
||||||
Black Pixel = 1 << iota
|
|
||||||
Invert
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p Pixel) Offset() uint {
|
|
||||||
return uint(p >> 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OffsetPixel(o uint) Pixel {
|
|
||||||
return Pixel(o << 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r PixelRole) Pixel() Pixel {
|
|
||||||
return Pixel(r << 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pixel) Role() PixelRole {
|
|
||||||
return PixelRole(p>>2) & 15
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pixel) String() string {
|
|
||||||
s := p.Role().String()
|
|
||||||
if p&Black != 0 {
|
|
||||||
s += "+black"
|
|
||||||
}
|
|
||||||
if p&Invert != 0 {
|
|
||||||
s += "+invert"
|
|
||||||
}
|
|
||||||
s += "+" + strconv.FormatUint(uint64(p.Offset()), 10)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PixelRole describes the role of a QR pixel.
|
|
||||||
type PixelRole uint32
|
|
||||||
|
|
||||||
const (
|
|
||||||
_ PixelRole = iota
|
|
||||||
Position // position squares (large)
|
|
||||||
Alignment // alignment squares (small)
|
|
||||||
Timing // timing strip between position squares
|
|
||||||
Format // format metadata
|
|
||||||
PVersion // version pattern
|
|
||||||
Unused // unused pixel
|
|
||||||
Data // data bit
|
|
||||||
Check // error correction check bit
|
|
||||||
Extra
|
|
||||||
)
|
|
||||||
|
|
||||||
var roles = []string{
|
|
||||||
"",
|
|
||||||
"position",
|
|
||||||
"alignment",
|
|
||||||
"timing",
|
|
||||||
"format",
|
|
||||||
"pversion",
|
|
||||||
"unused",
|
|
||||||
"data",
|
|
||||||
"check",
|
|
||||||
"extra",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r PixelRole) String() string {
|
|
||||||
if Position <= r && r <= Check {
|
|
||||||
return roles[r]
|
|
||||||
}
|
|
||||||
return strconv.Itoa(int(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Level represents a QR error correction level.
|
|
||||||
// From least to most tolerant of errors, they are L, M, Q, H.
|
|
||||||
type Level int
|
|
||||||
|
|
||||||
const (
|
|
||||||
L Level = iota
|
|
||||||
M
|
|
||||||
Q
|
|
||||||
H
|
|
||||||
)
|
|
||||||
|
|
||||||
func (l Level) String() string {
|
|
||||||
if L <= l && l <= H {
|
|
||||||
return "LMQH"[l : l+1]
|
|
||||||
}
|
|
||||||
return strconv.Itoa(int(l))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Code is a square pixel grid.
|
|
||||||
type Code struct {
|
|
||||||
Bitmap []byte // 1 is black, 0 is white
|
|
||||||
Size int // number of pixels on a side
|
|
||||||
Stride int // number of bytes per row
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Code) Black(x, y int) bool {
|
|
||||||
return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
|
|
||||||
c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Mask describes a mask that is applied to the QR
|
|
||||||
// code to avoid QR artifacts being interpreted as
|
|
||||||
// alignment and timing patterns (such as the squares
|
|
||||||
// in the corners). Valid masks are integers from 0 to 7.
|
|
||||||
type Mask int
|
|
||||||
|
|
||||||
// http://www.swetake.com/qr/qr5_en.html
|
|
||||||
var mfunc = []func(int, int) bool{
|
|
||||||
func(i, j int) bool { return (i+j)%2 == 0 },
|
|
||||||
func(i, j int) bool { return i%2 == 0 },
|
|
||||||
func(i, j int) bool { return j%3 == 0 },
|
|
||||||
func(i, j int) bool { return (i+j)%3 == 0 },
|
|
||||||
func(i, j int) bool { return (i/2+j/3)%2 == 0 },
|
|
||||||
func(i, j int) bool { return i*j%2+i*j%3 == 0 },
|
|
||||||
func(i, j int) bool { return (i*j%2+i*j%3)%2 == 0 },
|
|
||||||
func(i, j int) bool { return (i*j%3+(i+j)%2)%2 == 0 },
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m Mask) Invert(y, x int) bool {
|
|
||||||
if m < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return mfunc[m](y, x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Plan describes how to construct a QR code
|
|
||||||
// with a specific version, level, and mask.
|
|
||||||
type Plan struct {
|
|
||||||
Version Version
|
|
||||||
Level Level
|
|
||||||
Mask Mask
|
|
||||||
|
|
||||||
DataBytes int // number of data bytes
|
|
||||||
CheckBytes int // number of error correcting (checksum) bytes
|
|
||||||
Blocks int // number of data blocks
|
|
||||||
|
|
||||||
Pixel [][]Pixel // pixel map
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPlan returns a Plan for a QR code with the given
|
|
||||||
// version, level, and mask.
|
|
||||||
func NewPlan(version Version, level Level, mask Mask) (*Plan, error) {
|
|
||||||
p, err := vplan(version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := fplan(level, mask, p); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := lplan(version, level, p); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := mplan(mask, p); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) Pad(n int) {
|
|
||||||
if n < 0 {
|
|
||||||
panic("qr: invalid pad size")
|
|
||||||
}
|
|
||||||
if n <= 4 {
|
|
||||||
b.Write(0, n)
|
|
||||||
} else {
|
|
||||||
b.Write(0, 4)
|
|
||||||
n -= 4
|
|
||||||
n -= -b.Bits() & 7
|
|
||||||
b.Write(0, -b.Bits()&7)
|
|
||||||
pad := n / 8
|
|
||||||
for i := 0; i < pad; i += 2 {
|
|
||||||
b.Write(0xec, 8)
|
|
||||||
if i+1 >= pad {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
b.Write(0x11, 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bits) AddCheckBytes(v Version, l Level) {
|
|
||||||
nd := v.DataBytes(l)
|
|
||||||
if b.nbit < nd*8 {
|
|
||||||
b.Pad(nd*8 - b.nbit)
|
|
||||||
}
|
|
||||||
if b.nbit != nd*8 {
|
|
||||||
panic("qr: too much data")
|
|
||||||
}
|
|
||||||
|
|
||||||
dat := b.Bytes()
|
|
||||||
vt := &vtab[v]
|
|
||||||
lev := &vt.level[l]
|
|
||||||
db := nd / lev.nblock
|
|
||||||
extra := nd % lev.nblock
|
|
||||||
chk := make([]byte, lev.check)
|
|
||||||
rs := gf256.NewRSEncoder(Field, lev.check)
|
|
||||||
for i := 0; i < lev.nblock; i++ {
|
|
||||||
if i == lev.nblock-extra {
|
|
||||||
db++
|
|
||||||
}
|
|
||||||
rs.ECC(dat[:db], chk)
|
|
||||||
b.Append(chk)
|
|
||||||
dat = dat[db:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.Bytes()) != vt.bytes {
|
|
||||||
panic("qr: internal error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Plan) Encode(text ...Encoding) (*Code, error) {
|
|
||||||
var b Bits
|
|
||||||
for _, t := range text {
|
|
||||||
if err := t.Check(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t.Encode(&b, p.Version)
|
|
||||||
}
|
|
||||||
if b.Bits() > p.DataBytes*8 {
|
|
||||||
return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8)
|
|
||||||
}
|
|
||||||
b.AddCheckBytes(p.Version, p.Level)
|
|
||||||
bytes := b.Bytes()
|
|
||||||
|
|
||||||
// Now we have the checksum bytes and the data bytes.
|
|
||||||
// Construct the actual code.
|
|
||||||
c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7}
|
|
||||||
c.Bitmap = make([]byte, c.Stride*c.Size)
|
|
||||||
crow := c.Bitmap
|
|
||||||
for _, row := range p.Pixel {
|
|
||||||
for x, pix := range row {
|
|
||||||
switch pix.Role() {
|
|
||||||
case Data, Check:
|
|
||||||
o := pix.Offset()
|
|
||||||
if bytes[o/8]&(1<<uint(7-o&7)) != 0 {
|
|
||||||
pix ^= Black
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pix&Black != 0 {
|
|
||||||
crow[x/8] |= 1 << uint(7-x&7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crow = crow[c.Stride:]
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A version describes metadata associated with a version.
|
|
||||||
type version struct {
|
|
||||||
apos int
|
|
||||||
astride int
|
|
||||||
bytes int
|
|
||||||
pattern int
|
|
||||||
level [4]level
|
|
||||||
}
|
|
||||||
|
|
||||||
type level struct {
|
|
||||||
nblock int
|
|
||||||
check int
|
|
||||||
}
|
|
||||||
|
|
||||||
var vtab = []version{
|
|
||||||
{},
|
|
||||||
{100, 100, 26, 0x0, [4]level{{1, 7}, {1, 10}, {1, 13}, {1, 17}}}, // 1
|
|
||||||
{16, 100, 44, 0x0, [4]level{{1, 10}, {1, 16}, {1, 22}, {1, 28}}}, // 2
|
|
||||||
{20, 100, 70, 0x0, [4]level{{1, 15}, {1, 26}, {2, 18}, {2, 22}}}, // 3
|
|
||||||
{24, 100, 100, 0x0, [4]level{{1, 20}, {2, 18}, {2, 26}, {4, 16}}}, // 4
|
|
||||||
{28, 100, 134, 0x0, [4]level{{1, 26}, {2, 24}, {4, 18}, {4, 22}}}, // 5
|
|
||||||
{32, 100, 172, 0x0, [4]level{{2, 18}, {4, 16}, {4, 24}, {4, 28}}}, // 6
|
|
||||||
{20, 16, 196, 0x7c94, [4]level{{2, 20}, {4, 18}, {6, 18}, {5, 26}}}, // 7
|
|
||||||
{22, 18, 242, 0x85bc, [4]level{{2, 24}, {4, 22}, {6, 22}, {6, 26}}}, // 8
|
|
||||||
{24, 20, 292, 0x9a99, [4]level{{2, 30}, {5, 22}, {8, 20}, {8, 24}}}, // 9
|
|
||||||
{26, 22, 346, 0xa4d3, [4]level{{4, 18}, {5, 26}, {8, 24}, {8, 28}}}, // 10
|
|
||||||
{28, 24, 404, 0xbbf6, [4]level{{4, 20}, {5, 30}, {8, 28}, {11, 24}}}, // 11
|
|
||||||
{30, 26, 466, 0xc762, [4]level{{4, 24}, {8, 22}, {10, 26}, {11, 28}}}, // 12
|
|
||||||
{32, 28, 532, 0xd847, [4]level{{4, 26}, {9, 22}, {12, 24}, {16, 22}}}, // 13
|
|
||||||
{24, 20, 581, 0xe60d, [4]level{{4, 30}, {9, 24}, {16, 20}, {16, 24}}}, // 14
|
|
||||||
{24, 22, 655, 0xf928, [4]level{{6, 22}, {10, 24}, {12, 30}, {18, 24}}}, // 15
|
|
||||||
{24, 24, 733, 0x10b78, [4]level{{6, 24}, {10, 28}, {17, 24}, {16, 30}}}, // 16
|
|
||||||
{28, 24, 815, 0x1145d, [4]level{{6, 28}, {11, 28}, {16, 28}, {19, 28}}}, // 17
|
|
||||||
{28, 26, 901, 0x12a17, [4]level{{6, 30}, {13, 26}, {18, 28}, {21, 28}}}, // 18
|
|
||||||
{28, 28, 991, 0x13532, [4]level{{7, 28}, {14, 26}, {21, 26}, {25, 26}}}, // 19
|
|
||||||
{32, 28, 1085, 0x149a6, [4]level{{8, 28}, {16, 26}, {20, 30}, {25, 28}}}, // 20
|
|
||||||
{26, 22, 1156, 0x15683, [4]level{{8, 28}, {17, 26}, {23, 28}, {25, 30}}}, // 21
|
|
||||||
{24, 24, 1258, 0x168c9, [4]level{{9, 28}, {17, 28}, {23, 30}, {34, 24}}}, // 22
|
|
||||||
{28, 24, 1364, 0x177ec, [4]level{{9, 30}, {18, 28}, {25, 30}, {30, 30}}}, // 23
|
|
||||||
{26, 26, 1474, 0x18ec4, [4]level{{10, 30}, {20, 28}, {27, 30}, {32, 30}}}, // 24
|
|
||||||
{30, 26, 1588, 0x191e1, [4]level{{12, 26}, {21, 28}, {29, 30}, {35, 30}}}, // 25
|
|
||||||
{28, 28, 1706, 0x1afab, [4]level{{12, 28}, {23, 28}, {34, 28}, {37, 30}}}, // 26
|
|
||||||
{32, 28, 1828, 0x1b08e, [4]level{{12, 30}, {25, 28}, {34, 30}, {40, 30}}}, // 27
|
|
||||||
{24, 24, 1921, 0x1cc1a, [4]level{{13, 30}, {26, 28}, {35, 30}, {42, 30}}}, // 28
|
|
||||||
{28, 24, 2051, 0x1d33f, [4]level{{14, 30}, {28, 28}, {38, 30}, {45, 30}}}, // 29
|
|
||||||
{24, 26, 2185, 0x1ed75, [4]level{{15, 30}, {29, 28}, {40, 30}, {48, 30}}}, // 30
|
|
||||||
{28, 26, 2323, 0x1f250, [4]level{{16, 30}, {31, 28}, {43, 30}, {51, 30}}}, // 31
|
|
||||||
{32, 26, 2465, 0x209d5, [4]level{{17, 30}, {33, 28}, {45, 30}, {54, 30}}}, // 32
|
|
||||||
{28, 28, 2611, 0x216f0, [4]level{{18, 30}, {35, 28}, {48, 30}, {57, 30}}}, // 33
|
|
||||||
{32, 28, 2761, 0x228ba, [4]level{{19, 30}, {37, 28}, {51, 30}, {60, 30}}}, // 34
|
|
||||||
{28, 24, 2876, 0x2379f, [4]level{{19, 30}, {38, 28}, {53, 30}, {63, 30}}}, // 35
|
|
||||||
{22, 26, 3034, 0x24b0b, [4]level{{20, 30}, {40, 28}, {56, 30}, {66, 30}}}, // 36
|
|
||||||
{26, 26, 3196, 0x2542e, [4]level{{21, 30}, {43, 28}, {59, 30}, {70, 30}}}, // 37
|
|
||||||
{30, 26, 3362, 0x26a64, [4]level{{22, 30}, {45, 28}, {62, 30}, {74, 30}}}, // 38
|
|
||||||
{24, 28, 3532, 0x27541, [4]level{{24, 30}, {47, 28}, {65, 30}, {77, 30}}}, // 39
|
|
||||||
{28, 28, 3706, 0x28c69, [4]level{{25, 30}, {49, 28}, {68, 30}, {81, 30}}}, // 40
|
|
||||||
}
|
|
||||||
|
|
||||||
func grid(siz int) [][]Pixel {
|
|
||||||
m := make([][]Pixel, siz)
|
|
||||||
pix := make([]Pixel, siz*siz)
|
|
||||||
for i := range m {
|
|
||||||
m[i], pix = pix[:siz], pix[siz:]
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// vplan creates a Plan for the given version.
|
|
||||||
func vplan(v Version) (*Plan, error) {
|
|
||||||
p := &Plan{Version: v}
|
|
||||||
if v < 1 || v > 40 {
|
|
||||||
return nil, fmt.Errorf("invalid QR version %d", int(v))
|
|
||||||
}
|
|
||||||
siz := 17 + int(v)*4
|
|
||||||
m := grid(siz)
|
|
||||||
p.Pixel = m
|
|
||||||
|
|
||||||
// Timing markers (overwritten by boxes).
|
|
||||||
const ti = 6 // timing is in row/column 6 (counting from 0)
|
|
||||||
for i := range m {
|
|
||||||
p := Timing.Pixel()
|
|
||||||
if i&1 == 0 {
|
|
||||||
p |= Black
|
|
||||||
}
|
|
||||||
m[i][ti] = p
|
|
||||||
m[ti][i] = p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position boxes.
|
|
||||||
posBox(m, 0, 0)
|
|
||||||
posBox(m, siz-7, 0)
|
|
||||||
posBox(m, 0, siz-7)
|
|
||||||
|
|
||||||
// Alignment boxes.
|
|
||||||
info := &vtab[v]
|
|
||||||
for x := 4; x+5 < siz; {
|
|
||||||
for y := 4; y+5 < siz; {
|
|
||||||
// don't overwrite timing markers
|
|
||||||
if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) {
|
|
||||||
} else {
|
|
||||||
alignBox(m, x, y)
|
|
||||||
}
|
|
||||||
if y == 4 {
|
|
||||||
y = info.apos
|
|
||||||
} else {
|
|
||||||
y += info.astride
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if x == 4 {
|
|
||||||
x = info.apos
|
|
||||||
} else {
|
|
||||||
x += info.astride
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version pattern.
|
|
||||||
pat := vtab[v].pattern
|
|
||||||
if pat != 0 {
|
|
||||||
v := pat
|
|
||||||
for x := 0; x < 6; x++ {
|
|
||||||
for y := 0; y < 3; y++ {
|
|
||||||
p := PVersion.Pixel()
|
|
||||||
if v&1 != 0 {
|
|
||||||
p |= Black
|
|
||||||
}
|
|
||||||
m[siz-11+y][x] = p
|
|
||||||
m[x][siz-11+y] = p
|
|
||||||
v >>= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// One lonely black pixel
|
|
||||||
m[siz-8][8] = Unused.Pixel() | Black
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fplan adds the format pixels
|
|
||||||
func fplan(l Level, m Mask, p *Plan) error {
|
|
||||||
// Format pixels.
|
|
||||||
fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10
|
|
||||||
fb |= uint32(m) << 10 // mask
|
|
||||||
const formatPoly = 0x537
|
|
||||||
rem := fb
|
|
||||||
for i := 14; i >= 10; i-- {
|
|
||||||
if rem&(1<<uint(i)) != 0 {
|
|
||||||
rem ^= formatPoly << uint(i-10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fb |= rem
|
|
||||||
invert := uint32(0x5412)
|
|
||||||
siz := len(p.Pixel)
|
|
||||||
for i := uint(0); i < 15; i++ {
|
|
||||||
pix := Format.Pixel() + OffsetPixel(i)
|
|
||||||
if (fb>>i)&1 == 1 {
|
|
||||||
pix |= Black
|
|
||||||
}
|
|
||||||
if (invert>>i)&1 == 1 {
|
|
||||||
pix ^= Invert | Black
|
|
||||||
}
|
|
||||||
// top left
|
|
||||||
switch {
|
|
||||||
case i < 6:
|
|
||||||
p.Pixel[i][8] = pix
|
|
||||||
case i < 8:
|
|
||||||
p.Pixel[i+1][8] = pix
|
|
||||||
case i < 9:
|
|
||||||
p.Pixel[8][7] = pix
|
|
||||||
default:
|
|
||||||
p.Pixel[8][14-i] = pix
|
|
||||||
}
|
|
||||||
// bottom right
|
|
||||||
switch {
|
|
||||||
case i < 8:
|
|
||||||
p.Pixel[8][siz-1-int(i)] = pix
|
|
||||||
default:
|
|
||||||
p.Pixel[siz-1-int(14-i)][8] = pix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lplan edits a version-only Plan to add information
|
|
||||||
// about the error correction levels.
|
|
||||||
func lplan(v Version, l Level, p *Plan) error {
|
|
||||||
p.Level = l
|
|
||||||
|
|
||||||
nblock := vtab[v].level[l].nblock
|
|
||||||
ne := vtab[v].level[l].check
|
|
||||||
nde := (vtab[v].bytes - ne*nblock) / nblock
|
|
||||||
extra := (vtab[v].bytes - ne*nblock) % nblock
|
|
||||||
dataBits := (nde*nblock + extra) * 8
|
|
||||||
checkBits := ne * nblock * 8
|
|
||||||
|
|
||||||
p.DataBytes = vtab[v].bytes - ne*nblock
|
|
||||||
p.CheckBytes = ne * nblock
|
|
||||||
p.Blocks = nblock
|
|
||||||
|
|
||||||
// Make data + checksum pixels.
|
|
||||||
data := make([]Pixel, dataBits)
|
|
||||||
for i := range data {
|
|
||||||
data[i] = Data.Pixel() | OffsetPixel(uint(i))
|
|
||||||
}
|
|
||||||
check := make([]Pixel, checkBits)
|
|
||||||
for i := range check {
|
|
||||||
check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split into blocks.
|
|
||||||
dataList := make([][]Pixel, nblock)
|
|
||||||
checkList := make([][]Pixel, nblock)
|
|
||||||
for i := 0; i < nblock; i++ {
|
|
||||||
// The last few blocks have an extra data byte (8 pixels).
|
|
||||||
nd := nde
|
|
||||||
if i >= nblock-extra {
|
|
||||||
nd++
|
|
||||||
}
|
|
||||||
dataList[i], data = data[0:nd*8], data[nd*8:]
|
|
||||||
checkList[i], check = check[0:ne*8], check[ne*8:]
|
|
||||||
}
|
|
||||||
if len(data) != 0 || len(check) != 0 {
|
|
||||||
panic("data/check math")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build up bit sequence, taking first byte of each block,
|
|
||||||
// then second byte, and so on. Then checksums.
|
|
||||||
bits := make([]Pixel, dataBits+checkBits)
|
|
||||||
dst := bits
|
|
||||||
for i := 0; i < nde+1; i++ {
|
|
||||||
for _, b := range dataList {
|
|
||||||
if i*8 < len(b) {
|
|
||||||
copy(dst, b[i*8:(i+1)*8])
|
|
||||||
dst = dst[8:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < ne; i++ {
|
|
||||||
for _, b := range checkList {
|
|
||||||
if i*8 < len(b) {
|
|
||||||
copy(dst, b[i*8:(i+1)*8])
|
|
||||||
dst = dst[8:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(dst) != 0 {
|
|
||||||
panic("dst math")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sweep up pair of columns,
|
|
||||||
// then down, assigning to right then left pixel.
|
|
||||||
// Repeat.
|
|
||||||
// See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm
|
|
||||||
siz := len(p.Pixel)
|
|
||||||
rem := make([]Pixel, 7)
|
|
||||||
for i := range rem {
|
|
||||||
rem[i] = Extra.Pixel()
|
|
||||||
}
|
|
||||||
src := append(bits, rem...)
|
|
||||||
for x := siz; x > 0; {
|
|
||||||
for y := siz - 1; y >= 0; y-- {
|
|
||||||
if p.Pixel[y][x-1].Role() == 0 {
|
|
||||||
p.Pixel[y][x-1], src = src[0], src[1:]
|
|
||||||
}
|
|
||||||
if p.Pixel[y][x-2].Role() == 0 {
|
|
||||||
p.Pixel[y][x-2], src = src[0], src[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x -= 2
|
|
||||||
if x == 7 { // vertical timing strip
|
|
||||||
x--
|
|
||||||
}
|
|
||||||
for y := 0; y < siz; y++ {
|
|
||||||
if p.Pixel[y][x-1].Role() == 0 {
|
|
||||||
p.Pixel[y][x-1], src = src[0], src[1:]
|
|
||||||
}
|
|
||||||
if p.Pixel[y][x-2].Role() == 0 {
|
|
||||||
p.Pixel[y][x-2], src = src[0], src[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x -= 2
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// mplan edits a version+level-only Plan to add the mask.
|
|
||||||
func mplan(m Mask, p *Plan) error {
|
|
||||||
p.Mask = m
|
|
||||||
for y, row := range p.Pixel {
|
|
||||||
for x, pix := range row {
|
|
||||||
if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) {
|
|
||||||
row[x] ^= Black | Invert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// posBox draws a position (large) box at upper left x, y.
|
|
||||||
func posBox(m [][]Pixel, x, y int) {
|
|
||||||
pos := Position.Pixel()
|
|
||||||
// box
|
|
||||||
for dy := 0; dy < 7; dy++ {
|
|
||||||
for dx := 0; dx < 7; dx++ {
|
|
||||||
p := pos
|
|
||||||
if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 {
|
|
||||||
p |= Black
|
|
||||||
}
|
|
||||||
m[y+dy][x+dx] = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// white border
|
|
||||||
for dy := -1; dy < 8; dy++ {
|
|
||||||
if 0 <= y+dy && y+dy < len(m) {
|
|
||||||
if x > 0 {
|
|
||||||
m[y+dy][x-1] = pos
|
|
||||||
}
|
|
||||||
if x+7 < len(m) {
|
|
||||||
m[y+dy][x+7] = pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for dx := -1; dx < 8; dx++ {
|
|
||||||
if 0 <= x+dx && x+dx < len(m) {
|
|
||||||
if y > 0 {
|
|
||||||
m[y-1][x+dx] = pos
|
|
||||||
}
|
|
||||||
if y+7 < len(m) {
|
|
||||||
m[y+7][x+dx] = pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// alignBox draw an alignment (small) box at upper left x, y.
|
|
||||||
func alignBox(m [][]Pixel, x, y int) {
|
|
||||||
// box
|
|
||||||
align := Alignment.Pixel()
|
|
||||||
for dy := 0; dy < 5; dy++ {
|
|
||||||
for dx := 0; dx < 5; dx++ {
|
|
||||||
p := align
|
|
||||||
if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 {
|
|
||||||
p |= Black
|
|
||||||
}
|
|
||||||
m[y+dy][x+dx] = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
85
vendor/github.com/chai2010/image/qrencoder/internal/gf256/blog_test.go
generated
vendored
85
vendor/github.com/chai2010/image/qrencoder/internal/gf256/blog_test.go
generated
vendored
@ -1,85 +0,0 @@
|
|||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains a straightforward implementation of
|
|
||||||
// Reed-Solomon encoding, along with a benchmark.
|
|
||||||
// It goes with http://research.swtch.com/field.
|
|
||||||
//
|
|
||||||
// For an optimized implementation, see gf256.go.
|
|
||||||
|
|
||||||
package gf256
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlogECC writes to check the error correcting code bytes
|
|
||||||
// for data using the given Reed-Solomon parameters.
|
|
||||||
func BlogECC(rs *RSEncoder, m []byte, check []byte) {
|
|
||||||
if len(check) < rs.c {
|
|
||||||
panic("gf256: invalid check byte length")
|
|
||||||
}
|
|
||||||
if rs.c == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The check bytes are the remainder after dividing
|
|
||||||
// data padded with c zeros by the generator polynomial.
|
|
||||||
|
|
||||||
// p = data padded with c zeros.
|
|
||||||
var p []byte
|
|
||||||
n := len(m) + rs.c
|
|
||||||
if len(rs.p) >= n {
|
|
||||||
p = rs.p
|
|
||||||
} else {
|
|
||||||
p = make([]byte, n)
|
|
||||||
}
|
|
||||||
copy(p, m)
|
|
||||||
for i := len(m); i < len(p); i++ {
|
|
||||||
p[i] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
gen := rs.gen
|
|
||||||
|
|
||||||
// Divide p by gen, leaving the remainder in p[len(data):].
|
|
||||||
// p[0] is the most significant term in p, and
|
|
||||||
// gen[0] is the most significant term in the generator.
|
|
||||||
for i := 0; i < len(m); i++ {
|
|
||||||
k := f.Mul(p[i], f.Inv(gen[0])) // k = pi / g0
|
|
||||||
// p -= k·g
|
|
||||||
for j, g := range gen {
|
|
||||||
p[i+j] = f.Add(p[i+j], f.Mul(k, g))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(check, p[len(m):])
|
|
||||||
rs.p = p
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkBlogECC(b *testing.B) {
|
|
||||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
|
||||||
check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
|
|
||||||
out := make([]byte, len(check))
|
|
||||||
rs := NewRSEncoder(f, len(check))
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
BlogECC(rs, data, out)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
if !bytes.Equal(out, check) {
|
|
||||||
fmt.Printf("have %#v want %#v\n", out, check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlogECC(t *testing.T) {
|
|
||||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
|
||||||
check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
|
|
||||||
out := make([]byte, len(check))
|
|
||||||
rs := NewRSEncoder(f, len(check))
|
|
||||||
BlogECC(rs, data, out)
|
|
||||||
if !bytes.Equal(out, check) {
|
|
||||||
t.Errorf("have %x want %x", out, check)
|
|
||||||
}
|
|
||||||
}
|
|
241
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256.go
generated
vendored
241
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256.go
generated
vendored
@ -1,241 +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 gf256 implements arithmetic over the Galois Field GF(256).
|
|
||||||
package gf256
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
// A Field represents an instance of GF(256) defined by a specific polynomial.
|
|
||||||
type Field struct {
|
|
||||||
log [256]byte // log[0] is unused
|
|
||||||
exp [510]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewField returns a new field corresponding to the polynomial poly
|
|
||||||
// and generator α. The Reed-Solomon encoding in QR codes uses
|
|
||||||
// polynomial 0x11d with generator 2.
|
|
||||||
//
|
|
||||||
// The choice of generator α only affects the Exp and Log operations.
|
|
||||||
func NewField(poly, α int) *Field {
|
|
||||||
if poly < 0x100 || poly >= 0x200 || reducible(poly) {
|
|
||||||
panic("gf256: invalid polynomial: " + strconv.Itoa(poly))
|
|
||||||
}
|
|
||||||
|
|
||||||
var f Field
|
|
||||||
x := 1
|
|
||||||
for i := 0; i < 255; i++ {
|
|
||||||
if x == 1 && i != 0 {
|
|
||||||
panic("gf256: invalid generator " + strconv.Itoa(α) +
|
|
||||||
" for polynomial " + strconv.Itoa(poly))
|
|
||||||
}
|
|
||||||
f.exp[i] = byte(x)
|
|
||||||
f.exp[i+255] = byte(x)
|
|
||||||
f.log[x] = byte(i)
|
|
||||||
x = mul(x, α, poly)
|
|
||||||
}
|
|
||||||
f.log[0] = 255
|
|
||||||
for i := 0; i < 255; i++ {
|
|
||||||
if f.log[f.exp[i]] != byte(i) {
|
|
||||||
panic("bad log")
|
|
||||||
}
|
|
||||||
if f.log[f.exp[i+255]] != byte(i) {
|
|
||||||
panic("bad log")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 1; i < 256; i++ {
|
|
||||||
if f.exp[f.log[i]] != byte(i) {
|
|
||||||
panic("bad log")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &f
|
|
||||||
}
|
|
||||||
|
|
||||||
// nbit returns the number of significant in p.
|
|
||||||
func nbit(p int) uint {
|
|
||||||
n := uint(0)
|
|
||||||
for ; p > 0; p >>= 1 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// polyDiv divides the polynomial p by q and returns the remainder.
|
|
||||||
func polyDiv(p, q int) int {
|
|
||||||
np := nbit(p)
|
|
||||||
nq := nbit(q)
|
|
||||||
for ; np >= nq; np-- {
|
|
||||||
if p&(1<<(np-1)) != 0 {
|
|
||||||
p ^= q << (np - nq)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// mul returns the product x*y mod poly, a GF(256) multiplication.
|
|
||||||
func mul(x, y, poly int) int {
|
|
||||||
z := 0
|
|
||||||
for x > 0 {
|
|
||||||
if x&1 != 0 {
|
|
||||||
z ^= y
|
|
||||||
}
|
|
||||||
x >>= 1
|
|
||||||
y <<= 1
|
|
||||||
if y&0x100 != 0 {
|
|
||||||
y ^= poly
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// reducible reports whether p is reducible.
|
|
||||||
func reducible(p int) bool {
|
|
||||||
// Multiplying n-bit * n-bit produces (2n-1)-bit,
|
|
||||||
// so if p is reducible, one of its factors must be
|
|
||||||
// of np/2+1 bits or fewer.
|
|
||||||
np := nbit(p)
|
|
||||||
for q := 2; q < 1<<(np/2+1); q++ {
|
|
||||||
if polyDiv(p, q) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add returns the sum of x and y in the field.
|
|
||||||
func (f *Field) Add(x, y byte) byte {
|
|
||||||
return x ^ y
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exp returns the base-α exponential of e in the field.
|
|
||||||
// If e < 0, Exp returns 0.
|
|
||||||
func (f *Field) Exp(e int) byte {
|
|
||||||
if e < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return f.exp[e%255]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log returns the base-α logarithm of x in the field.
|
|
||||||
// If x == 0, Log returns -1.
|
|
||||||
func (f *Field) Log(x byte) int {
|
|
||||||
if x == 0 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return int(f.log[x])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inv returns the multiplicative inverse of x in the field.
|
|
||||||
// If x == 0, Inv returns 0.
|
|
||||||
func (f *Field) Inv(x byte) byte {
|
|
||||||
if x == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return f.exp[255-f.log[x]]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mul returns the product of x and y in the field.
|
|
||||||
func (f *Field) Mul(x, y byte) byte {
|
|
||||||
if x == 0 || y == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return f.exp[int(f.log[x])+int(f.log[y])]
|
|
||||||
}
|
|
||||||
|
|
||||||
// An RSEncoder implements Reed-Solomon encoding
|
|
||||||
// over a given field using a given number of error correction bytes.
|
|
||||||
type RSEncoder struct {
|
|
||||||
f *Field
|
|
||||||
c int
|
|
||||||
gen []byte
|
|
||||||
lgen []byte
|
|
||||||
p []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Field) gen(e int) (gen, lgen []byte) {
|
|
||||||
// p = 1
|
|
||||||
p := make([]byte, e+1)
|
|
||||||
p[e] = 1
|
|
||||||
|
|
||||||
for i := 0; i < e; i++ {
|
|
||||||
// p *= (x + Exp(i))
|
|
||||||
// p[j] = p[j]*Exp(i) + p[j+1].
|
|
||||||
c := f.Exp(i)
|
|
||||||
for j := 0; j < e; j++ {
|
|
||||||
p[j] = f.Mul(p[j], c) ^ p[j+1]
|
|
||||||
}
|
|
||||||
p[e] = f.Mul(p[e], c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lp = log p.
|
|
||||||
lp := make([]byte, e+1)
|
|
||||||
for i, c := range p {
|
|
||||||
if c == 0 {
|
|
||||||
lp[i] = 255
|
|
||||||
} else {
|
|
||||||
lp[i] = byte(f.Log(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, lp
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRSEncoder returns a new Reed-Solomon encoder
|
|
||||||
// over the given field and number of error correction bytes.
|
|
||||||
func NewRSEncoder(f *Field, c int) *RSEncoder {
|
|
||||||
gen, lgen := f.gen(c)
|
|
||||||
return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ECC writes to check the error correcting code bytes
|
|
||||||
// for data using the given Reed-Solomon parameters.
|
|
||||||
func (rs *RSEncoder) ECC(data []byte, check []byte) {
|
|
||||||
if len(check) < rs.c {
|
|
||||||
panic("gf256: invalid check byte length")
|
|
||||||
}
|
|
||||||
if rs.c == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The check bytes are the remainder after dividing
|
|
||||||
// data padded with c zeros by the generator polynomial.
|
|
||||||
|
|
||||||
// p = data padded with c zeros.
|
|
||||||
var p []byte
|
|
||||||
n := len(data) + rs.c
|
|
||||||
if len(rs.p) >= n {
|
|
||||||
p = rs.p
|
|
||||||
} else {
|
|
||||||
p = make([]byte, n)
|
|
||||||
}
|
|
||||||
copy(p, data)
|
|
||||||
for i := len(data); i < len(p); i++ {
|
|
||||||
p[i] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Divide p by gen, leaving the remainder in p[len(data):].
|
|
||||||
// p[0] is the most significant term in p, and
|
|
||||||
// gen[0] is the most significant term in the generator,
|
|
||||||
// which is always 1.
|
|
||||||
// To avoid repeated work, we store various values as
|
|
||||||
// lv, not v, where lv = log[v].
|
|
||||||
f := rs.f
|
|
||||||
lgen := rs.lgen[1:]
|
|
||||||
for i := 0; i < len(data); i++ {
|
|
||||||
c := p[i]
|
|
||||||
if c == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
q := p[i+1:]
|
|
||||||
exp := f.exp[f.log[c]:]
|
|
||||||
for j, lg := range lgen {
|
|
||||||
if lg != 255 { // lgen uses 255 for log 0
|
|
||||||
q[j] ^= exp[lg]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
copy(check, p[len(data):])
|
|
||||||
rs.p = p
|
|
||||||
}
|
|
194
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256_test.go
generated
vendored
194
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256_test.go
generated
vendored
@ -1,194 +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 gf256
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var f = NewField(0x11d, 2) // x^8 + x^4 + x^3 + x^2 + 1
|
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
|
||||||
if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 {
|
|
||||||
panic("bad Exp")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECC(t *testing.T) {
|
|
||||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
|
||||||
check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
|
|
||||||
out := make([]byte, len(check))
|
|
||||||
rs := NewRSEncoder(f, len(check))
|
|
||||||
rs.ECC(data, out)
|
|
||||||
if !bytes.Equal(out, check) {
|
|
||||||
t.Errorf("have %x want %x", out, check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLinear(t *testing.T) {
|
|
||||||
d1 := []byte{0x00, 0x00}
|
|
||||||
c1 := []byte{0x00, 0x00}
|
|
||||||
out := make([]byte, len(c1))
|
|
||||||
rs := NewRSEncoder(f, len(c1))
|
|
||||||
if rs.ECC(d1, out); !bytes.Equal(out, c1) {
|
|
||||||
t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out)
|
|
||||||
}
|
|
||||||
d2 := []byte{0x00, 0x01}
|
|
||||||
c2 := make([]byte, 2)
|
|
||||||
rs.ECC(d2, c2)
|
|
||||||
d3 := []byte{0x00, 0x02}
|
|
||||||
c3 := make([]byte, 2)
|
|
||||||
rs.ECC(d3, c3)
|
|
||||||
cx := make([]byte, 2)
|
|
||||||
for i := range cx {
|
|
||||||
cx[i] = c2[i] ^ c3[i]
|
|
||||||
}
|
|
||||||
d4 := []byte{0x00, 0x03}
|
|
||||||
c4 := make([]byte, 2)
|
|
||||||
rs.ECC(d4, c4)
|
|
||||||
if !bytes.Equal(cx, c4) {
|
|
||||||
t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x",
|
|
||||||
d2, c2, d3, c3, cx, d4, c4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGaussJordan(t *testing.T) {
|
|
||||||
rs := NewRSEncoder(f, 2)
|
|
||||||
m := make([][]byte, 16)
|
|
||||||
for i := range m {
|
|
||||||
m[i] = make([]byte, 4)
|
|
||||||
m[i][i/8] = 1 << uint(i%8)
|
|
||||||
rs.ECC(m[i][:2], m[i][2:])
|
|
||||||
}
|
|
||||||
if false {
|
|
||||||
fmt.Printf("---\n")
|
|
||||||
for _, row := range m {
|
|
||||||
fmt.Printf("%x\n", row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b := []uint{0, 1, 2, 3, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27}
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
bi := b[i]
|
|
||||||
if m[i][bi/8]&(1<<(7-bi%8)) == 0 {
|
|
||||||
for j := i + 1; ; j++ {
|
|
||||||
if j >= len(m) {
|
|
||||||
t.Errorf("lost track for %d", bi)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
|
|
||||||
m[i], m[j] = m[j], m[i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for j := i + 1; j < len(m); j++ {
|
|
||||||
if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
|
|
||||||
for k := range m[j] {
|
|
||||||
m[j][k] ^= m[i][k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if false {
|
|
||||||
fmt.Printf("---\n")
|
|
||||||
for _, row := range m {
|
|
||||||
fmt.Printf("%x\n", row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 15; i >= 0; i-- {
|
|
||||||
bi := b[i]
|
|
||||||
for j := i - 1; j >= 0; j-- {
|
|
||||||
if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
|
|
||||||
for k := range m[j] {
|
|
||||||
m[j][k] ^= m[i][k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if false {
|
|
||||||
fmt.Printf("---\n")
|
|
||||||
for _, row := range m {
|
|
||||||
fmt.Printf("%x", row)
|
|
||||||
out := make([]byte, 2)
|
|
||||||
if rs.ECC(row[:2], out); !bytes.Equal(out, row[2:]) {
|
|
||||||
fmt.Printf(" - want %x", out)
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkECC(b *testing.B) {
|
|
||||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
|
||||||
check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
|
|
||||||
out := make([]byte, len(check))
|
|
||||||
rs := NewRSEncoder(f, len(check))
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
rs.ECC(data, out)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
if !bytes.Equal(out, check) {
|
|
||||||
fmt.Printf("have %#v want %#v\n", out, check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGen(t *testing.T) {
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
_, lg := f.gen(i)
|
|
||||||
if lg[0] != 0 {
|
|
||||||
t.Errorf("#%d: %x", i, lg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReducible(t *testing.T) {
|
|
||||||
var count = []int{1, 2, 3, 6, 9, 18, 30, 56, 99, 186} // oeis.org/A1037
|
|
||||||
for i, want := range count {
|
|
||||||
n := 0
|
|
||||||
for p := 1 << uint(i+2); p < 1<<uint(i+3); p++ {
|
|
||||||
if !reducible(p) {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n != want {
|
|
||||||
t.Errorf("#reducible(%d-bit) = %d, want %d", i+2, n, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExhaustive(t *testing.T) {
|
|
||||||
for poly := 0x100; poly < 0x200; poly++ {
|
|
||||||
if reducible(poly) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
α := 2
|
|
||||||
for !generates(α, poly) {
|
|
||||||
α++
|
|
||||||
}
|
|
||||||
f := NewField(poly, α)
|
|
||||||
for p := 0; p < 256; p++ {
|
|
||||||
for q := 0; q < 256; q++ {
|
|
||||||
fm := int(f.Mul(byte(p), byte(q)))
|
|
||||||
pm := mul(p, q, poly)
|
|
||||||
if fm != pm {
|
|
||||||
t.Errorf("NewField(%#x).Mul(%#x, %#x) = %#x, want %#x", poly, p, q, fm, pm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generates(α, poly int) bool {
|
|
||||||
x := α
|
|
||||||
for i := 0; i < 254; i++ {
|
|
||||||
if x == 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
x = mul(x, α, poly)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
400
vendor/github.com/chai2010/image/qrencoder/png.go
generated
vendored
400
vendor/github.com/chai2010/image/qrencoder/png.go
generated
vendored
@ -1,400 +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 qrcode
|
|
||||||
|
|
||||||
// PNG writer for QR codes.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"hash/crc32"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PNG returns a PNG image displaying the code.
|
|
||||||
//
|
|
||||||
// PNG uses a custom encoder tailored to QR codes.
|
|
||||||
// Its compressed size is about 2x away from optimal,
|
|
||||||
// but it runs about 20x faster than calling png.Encode
|
|
||||||
// on c.Image().
|
|
||||||
func (c *Code) PNG() []byte {
|
|
||||||
var p pngWriter
|
|
||||||
return p.encode(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
type pngWriter struct {
|
|
||||||
tmp [16]byte
|
|
||||||
wctmp [4]byte
|
|
||||||
buf bytes.Buffer
|
|
||||||
zlib bitWriter
|
|
||||||
crc hash.Hash32
|
|
||||||
}
|
|
||||||
|
|
||||||
var pngHeader = []byte("\x89PNG\r\n\x1a\n")
|
|
||||||
|
|
||||||
func (w *pngWriter) encode(c *Code) []byte {
|
|
||||||
scale := c.Scale
|
|
||||||
siz := c.Size
|
|
||||||
|
|
||||||
w.buf.Reset()
|
|
||||||
|
|
||||||
// Header
|
|
||||||
w.buf.Write(pngHeader)
|
|
||||||
|
|
||||||
// Header block
|
|
||||||
binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale))
|
|
||||||
binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale))
|
|
||||||
w.tmp[8] = 1 // 1-bit
|
|
||||||
w.tmp[9] = 0 // gray
|
|
||||||
w.tmp[10] = 0
|
|
||||||
w.tmp[11] = 0
|
|
||||||
w.tmp[12] = 0
|
|
||||||
w.writeChunk("IHDR", w.tmp[:13])
|
|
||||||
|
|
||||||
// Comment
|
|
||||||
w.writeChunk("tEXt", comment)
|
|
||||||
|
|
||||||
// Data
|
|
||||||
w.zlib.writeCode(c)
|
|
||||||
w.writeChunk("IDAT", w.zlib.bytes.Bytes())
|
|
||||||
|
|
||||||
// End
|
|
||||||
w.writeChunk("IEND", nil)
|
|
||||||
|
|
||||||
return w.buf.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
var comment = []byte("Software\x00QR-PNG <chaishushan{AT}gmail.com>")
|
|
||||||
|
|
||||||
func (w *pngWriter) writeChunk(name string, data []byte) {
|
|
||||||
if w.crc == nil {
|
|
||||||
w.crc = crc32.NewIEEE()
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data)))
|
|
||||||
w.buf.Write(w.wctmp[0:4])
|
|
||||||
w.crc.Reset()
|
|
||||||
copy(w.wctmp[0:4], name)
|
|
||||||
w.buf.Write(w.wctmp[0:4])
|
|
||||||
w.crc.Write(w.wctmp[0:4])
|
|
||||||
w.buf.Write(data)
|
|
||||||
w.crc.Write(data)
|
|
||||||
crc := w.crc.Sum32()
|
|
||||||
binary.BigEndian.PutUint32(w.wctmp[0:4], crc)
|
|
||||||
w.buf.Write(w.wctmp[0:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) writeCode(c *Code) {
|
|
||||||
const ftNone = 0
|
|
||||||
|
|
||||||
b.adler32.Reset()
|
|
||||||
b.bytes.Reset()
|
|
||||||
b.nbit = 0
|
|
||||||
|
|
||||||
scale := c.Scale
|
|
||||||
siz := c.Size
|
|
||||||
|
|
||||||
// zlib header
|
|
||||||
b.tmp[0] = 0x78
|
|
||||||
b.tmp[1] = 0
|
|
||||||
b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31)
|
|
||||||
b.bytes.Write(b.tmp[0:2])
|
|
||||||
|
|
||||||
// Start flate block.
|
|
||||||
b.writeBits(1, 1, false) // final block
|
|
||||||
b.writeBits(1, 2, false) // compressed, fixed Huffman tables
|
|
||||||
|
|
||||||
// White border.
|
|
||||||
// First row.
|
|
||||||
b.byte(ftNone)
|
|
||||||
n := (scale*(siz+8) + 7) / 8
|
|
||||||
b.byte(255)
|
|
||||||
b.repeat(n-1, 1)
|
|
||||||
// 4*scale rows total.
|
|
||||||
b.repeat((4*scale-1)*(1+n), 1+n)
|
|
||||||
|
|
||||||
for i := 0; i < 4*scale; i++ {
|
|
||||||
b.adler32.WriteNByte(ftNone, 1)
|
|
||||||
b.adler32.WriteNByte(255, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
row := make([]byte, 1+n)
|
|
||||||
for y := 0; y < siz; y++ {
|
|
||||||
row[0] = ftNone
|
|
||||||
j := 1
|
|
||||||
var z uint8
|
|
||||||
nz := 0
|
|
||||||
for x := -4; x < siz+4; x++ {
|
|
||||||
// Raw data.
|
|
||||||
for i := 0; i < scale; i++ {
|
|
||||||
z <<= 1
|
|
||||||
if !c.Black(x, y) {
|
|
||||||
z |= 1
|
|
||||||
}
|
|
||||||
if nz++; nz == 8 {
|
|
||||||
row[j] = z
|
|
||||||
j++
|
|
||||||
nz = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if j < len(row) {
|
|
||||||
row[j] = z
|
|
||||||
}
|
|
||||||
for _, z := range row {
|
|
||||||
b.byte(z)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale-1 copies.
|
|
||||||
b.repeat((scale-1)*(1+n), 1+n)
|
|
||||||
|
|
||||||
b.adler32.WriteN(row, scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// White border.
|
|
||||||
// First row.
|
|
||||||
b.byte(ftNone)
|
|
||||||
b.byte(255)
|
|
||||||
b.repeat(n-1, 1)
|
|
||||||
// 4*scale rows total.
|
|
||||||
b.repeat((4*scale-1)*(1+n), 1+n)
|
|
||||||
|
|
||||||
for i := 0; i < 4*scale; i++ {
|
|
||||||
b.adler32.WriteNByte(ftNone, 1)
|
|
||||||
b.adler32.WriteNByte(255, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of block.
|
|
||||||
b.hcode(256)
|
|
||||||
b.flushBits()
|
|
||||||
|
|
||||||
// adler32
|
|
||||||
binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32())
|
|
||||||
b.bytes.Write(b.tmp[0:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
// A bitWriter is a write buffer for bit-oriented data like deflate.
|
|
||||||
type bitWriter struct {
|
|
||||||
bytes bytes.Buffer
|
|
||||||
bit uint32
|
|
||||||
nbit uint
|
|
||||||
|
|
||||||
tmp [4]byte
|
|
||||||
adler32 adigest
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) {
|
|
||||||
// reverse, for huffman codes
|
|
||||||
if rev {
|
|
||||||
br := uint32(0)
|
|
||||||
for i := uint(0); i < nbit; i++ {
|
|
||||||
br |= ((bit >> i) & 1) << (nbit - 1 - i)
|
|
||||||
}
|
|
||||||
bit = br
|
|
||||||
}
|
|
||||||
b.bit |= bit << b.nbit
|
|
||||||
b.nbit += nbit
|
|
||||||
for b.nbit >= 8 {
|
|
||||||
b.bytes.WriteByte(byte(b.bit))
|
|
||||||
b.bit >>= 8
|
|
||||||
b.nbit -= 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) flushBits() {
|
|
||||||
if b.nbit > 0 {
|
|
||||||
b.bytes.WriteByte(byte(b.bit))
|
|
||||||
b.nbit = 0
|
|
||||||
b.bit = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) hcode(v int) {
|
|
||||||
/*
|
|
||||||
Lit Value Bits Codes
|
|
||||||
--------- ---- -----
|
|
||||||
0 - 143 8 00110000 through
|
|
||||||
10111111
|
|
||||||
144 - 255 9 110010000 through
|
|
||||||
111111111
|
|
||||||
256 - 279 7 0000000 through
|
|
||||||
0010111
|
|
||||||
280 - 287 8 11000000 through
|
|
||||||
11000111
|
|
||||||
*/
|
|
||||||
switch {
|
|
||||||
case v <= 143:
|
|
||||||
b.writeBits(uint32(v)+0x30, 8, true)
|
|
||||||
case v <= 255:
|
|
||||||
b.writeBits(uint32(v-144)+0x190, 9, true)
|
|
||||||
case v <= 279:
|
|
||||||
b.writeBits(uint32(v-256)+0, 7, true)
|
|
||||||
case v <= 287:
|
|
||||||
b.writeBits(uint32(v-280)+0xc0, 8, true)
|
|
||||||
default:
|
|
||||||
panic("invalid hcode")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) byte(x byte) {
|
|
||||||
b.hcode(int(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) codex(c int, val int, nx uint) {
|
|
||||||
b.hcode(c + val>>nx)
|
|
||||||
b.writeBits(uint32(val)&(1<<nx-1), nx, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) repeat(n, d int) {
|
|
||||||
for ; n >= 258+3; n -= 258 {
|
|
||||||
b.repeat1(258, d)
|
|
||||||
}
|
|
||||||
if n > 258 {
|
|
||||||
// 258 < n < 258+3
|
|
||||||
b.repeat1(10, d)
|
|
||||||
b.repeat1(n-10, d)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n < 3 {
|
|
||||||
panic("invalid flate repeat")
|
|
||||||
}
|
|
||||||
b.repeat1(n, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) repeat1(n, d int) {
|
|
||||||
/*
|
|
||||||
Extra Extra Extra
|
|
||||||
Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
|
|
||||||
---- ---- ------ ---- ---- ------- ---- ---- -------
|
|
||||||
257 0 3 267 1 15,16 277 4 67-82
|
|
||||||
258 0 4 268 1 17,18 278 4 83-98
|
|
||||||
259 0 5 269 2 19-22 279 4 99-114
|
|
||||||
260 0 6 270 2 23-26 280 4 115-130
|
|
||||||
261 0 7 271 2 27-30 281 5 131-162
|
|
||||||
262 0 8 272 2 31-34 282 5 163-194
|
|
||||||
263 0 9 273 3 35-42 283 5 195-226
|
|
||||||
264 0 10 274 3 43-50 284 5 227-257
|
|
||||||
265 1 11,12 275 3 51-58 285 0 258
|
|
||||||
266 1 13,14 276 3 59-66
|
|
||||||
*/
|
|
||||||
switch {
|
|
||||||
case n <= 10:
|
|
||||||
b.codex(257, n-3, 0)
|
|
||||||
case n <= 18:
|
|
||||||
b.codex(265, n-11, 1)
|
|
||||||
case n <= 34:
|
|
||||||
b.codex(269, n-19, 2)
|
|
||||||
case n <= 66:
|
|
||||||
b.codex(273, n-35, 3)
|
|
||||||
case n <= 130:
|
|
||||||
b.codex(277, n-67, 4)
|
|
||||||
case n <= 257:
|
|
||||||
b.codex(281, n-131, 5)
|
|
||||||
case n == 258:
|
|
||||||
b.hcode(285)
|
|
||||||
default:
|
|
||||||
panic("invalid repeat length")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Extra Extra Extra
|
|
||||||
Code Bits Dist Code Bits Dist Code Bits Distance
|
|
||||||
---- ---- ---- ---- ---- ------ ---- ---- --------
|
|
||||||
0 0 1 10 4 33-48 20 9 1025-1536
|
|
||||||
1 0 2 11 4 49-64 21 9 1537-2048
|
|
||||||
2 0 3 12 5 65-96 22 10 2049-3072
|
|
||||||
3 0 4 13 5 97-128 23 10 3073-4096
|
|
||||||
4 1 5,6 14 6 129-192 24 11 4097-6144
|
|
||||||
5 1 7,8 15 6 193-256 25 11 6145-8192
|
|
||||||
6 2 9-12 16 7 257-384 26 12 8193-12288
|
|
||||||
7 2 13-16 17 7 385-512 27 12 12289-16384
|
|
||||||
8 3 17-24 18 8 513-768 28 13 16385-24576
|
|
||||||
9 3 25-32 19 8 769-1024 29 13 24577-32768
|
|
||||||
*/
|
|
||||||
if d <= 4 {
|
|
||||||
b.writeBits(uint32(d-1), 5, true)
|
|
||||||
} else if d <= 32768 {
|
|
||||||
nbit := uint(16)
|
|
||||||
for d <= 1<<(nbit-1) {
|
|
||||||
nbit--
|
|
||||||
}
|
|
||||||
v := uint32(d - 1)
|
|
||||||
v &^= 1 << (nbit - 1) // top bit is implicit
|
|
||||||
code := uint32(2*nbit - 2) // second bit is low bit of code
|
|
||||||
code |= v >> (nbit - 2)
|
|
||||||
v &^= 1 << (nbit - 2)
|
|
||||||
b.writeBits(code, 5, true)
|
|
||||||
// rest of bits follow
|
|
||||||
b.writeBits(uint32(v), nbit-2, false)
|
|
||||||
} else {
|
|
||||||
panic("invalid repeat distance")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bitWriter) run(v byte, n int) {
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.byte(v)
|
|
||||||
if n-1 < 3 {
|
|
||||||
for i := 0; i < n-1; i++ {
|
|
||||||
b.byte(v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.repeat(n-1, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type adigest struct {
|
|
||||||
a, b uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *adigest) Reset() { d.a, d.b = 1, 0 }
|
|
||||||
|
|
||||||
const amod = 65521
|
|
||||||
|
|
||||||
func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) {
|
|
||||||
// TODO(rsc): 6g doesn't do magic multiplies for b %= amod,
|
|
||||||
// only for b = b%amod.
|
|
||||||
|
|
||||||
// invariant: a, b < amod
|
|
||||||
if pi == 0 {
|
|
||||||
b += uint32(n%amod) * a
|
|
||||||
b = b % amod
|
|
||||||
return a, b
|
|
||||||
}
|
|
||||||
|
|
||||||
// n times:
|
|
||||||
// a += pi
|
|
||||||
// b += a
|
|
||||||
// is same as
|
|
||||||
// b += n*a + n*(n+1)/2*pi
|
|
||||||
// a += n*pi
|
|
||||||
m := uint32(n)
|
|
||||||
b += (m % amod) * a
|
|
||||||
b = b % amod
|
|
||||||
b += (m * (m + 1) / 2) % amod * uint32(pi)
|
|
||||||
b = b % amod
|
|
||||||
a += (m % amod) * uint32(pi)
|
|
||||||
a = a % amod
|
|
||||||
return a, b
|
|
||||||
}
|
|
||||||
|
|
||||||
func afinish(a, b uint32) uint32 {
|
|
||||||
return b<<16 | a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *adigest) WriteN(p []byte, n int) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
for _, pi := range p {
|
|
||||||
d.a, d.b = aupdate(d.a, d.b, pi, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *adigest) WriteNByte(pi byte, n int) {
|
|
||||||
d.a, d.b = aupdate(d.a, d.b, pi, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) }
|
|
69
vendor/github.com/chai2010/image/qrencoder/png_test.go
generated
vendored
69
vendor/github.com/chai2010/image/qrencoder/png_test.go
generated
vendored
@ -1,69 +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 qrcode
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"image/png"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPNG(t *testing.T) {
|
|
||||||
c, err := Encode("hello, world", L)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pngdat := c.PNG()
|
|
||||||
m, err := png.Decode(bytes.NewBuffer(pngdat))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
gm := m.(*image.Gray)
|
|
||||||
|
|
||||||
scale := c.Scale
|
|
||||||
siz := c.Size
|
|
||||||
nbad := 0
|
|
||||||
for y := 0; y < scale*(8+siz); y++ {
|
|
||||||
for x := 0; x < scale*(8+siz); x++ {
|
|
||||||
v := byte(255)
|
|
||||||
if c.Black(x/scale-4, y/scale-4) {
|
|
||||||
v = 0
|
|
||||||
}
|
|
||||||
if gv := gm.At(x, y).(color.Gray).Y; gv != v {
|
|
||||||
t.Errorf("%d,%d = %d, want %d", x, y, gv, v)
|
|
||||||
if nbad++; nbad >= 20 {
|
|
||||||
t.Fatalf("too many bad pixels")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPNG(b *testing.B) {
|
|
||||||
c, err := Encode("0123456789012345678901234567890123456789", L)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var bytes []byte
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
bytes = c.PNG()
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(bytes)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkImagePNG(b *testing.B) {
|
|
||||||
c, err := Encode("0123456789012345678901234567890123456789", L)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
buf.Reset()
|
|
||||||
png.Encode(&buf, c.Image())
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(buf.Len()))
|
|
||||||
}
|
|
114
vendor/github.com/chai2010/image/qrencoder/qr.go
generated
vendored
114
vendor/github.com/chai2010/image/qrencoder/qr.go
generated
vendored
@ -1,114 +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 qrencoder implements a encoder for QR code.
|
|
||||||
package qrcode
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
|
|
||||||
"github.com/chai2010/image/qrencoder/internal/coding"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Level denotes a QR error correction level.
|
|
||||||
// From least to most tolerant of errors, they are L, M, Q, H.
|
|
||||||
type Level int
|
|
||||||
|
|
||||||
const (
|
|
||||||
L Level = iota // 20% redundant
|
|
||||||
M // 38% redundant
|
|
||||||
Q // 55% redundant
|
|
||||||
H // 65% redundant
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encode returns an encoding of text at the given error correction level.
|
|
||||||
func Encode(text string, level Level) (*Code, error) {
|
|
||||||
// Pick data encoding, smallest first.
|
|
||||||
// We could split the string and use different encodings
|
|
||||||
// but that seems like overkill for now.
|
|
||||||
var enc coding.Encoding
|
|
||||||
switch {
|
|
||||||
case coding.Num(text).Check() == nil:
|
|
||||||
enc = coding.Num(text)
|
|
||||||
case coding.Alpha(text).Check() == nil:
|
|
||||||
enc = coding.Alpha(text)
|
|
||||||
default:
|
|
||||||
enc = coding.String(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pick size.
|
|
||||||
l := coding.Level(level)
|
|
||||||
var v coding.Version
|
|
||||||
for v = coding.MinVersion; ; v++ {
|
|
||||||
if v > coding.MaxVersion {
|
|
||||||
return nil, errors.New("text too long to encode as QR")
|
|
||||||
}
|
|
||||||
if enc.Bits(v) <= v.DataBytes(l)*8 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build and execute plan.
|
|
||||||
p, err := coding.NewPlan(v, l, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cc, err := p.Encode(enc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Pick appropriate mask.
|
|
||||||
|
|
||||||
return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Code is a square pixel grid.
|
|
||||||
// It implements image.Image and direct PNG encoding.
|
|
||||||
type Code struct {
|
|
||||||
Bitmap []byte // 1 is black, 0 is white
|
|
||||||
Size int // number of pixels on a side
|
|
||||||
Stride int // number of bytes per row
|
|
||||||
Scale int // number of image pixels per QR pixel
|
|
||||||
}
|
|
||||||
|
|
||||||
// Black returns true if the pixel at (x,y) is black.
|
|
||||||
func (c *Code) Black(x, y int) bool {
|
|
||||||
return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
|
|
||||||
c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image returns an Image displaying the code.
|
|
||||||
func (c *Code) Image() image.Image {
|
|
||||||
return &codeImage{c}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// codeImage implements image.Image
|
|
||||||
type codeImage struct {
|
|
||||||
*Code
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
whiteColor color.Color = color.Gray{0xFF}
|
|
||||||
blackColor color.Color = color.Gray{0x00}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *codeImage) Bounds() image.Rectangle {
|
|
||||||
d := (c.Size + 8) * c.Scale
|
|
||||||
return image.Rect(0, 0, d, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *codeImage) At(x, y int) color.Color {
|
|
||||||
if c.Black(x, y) {
|
|
||||||
return blackColor
|
|
||||||
}
|
|
||||||
return whiteColor
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *codeImage) ColorModel() color.Model {
|
|
||||||
return color.GrayModel
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user