Showing preview only (1,029K chars total). Download the full file or copy to clipboard to get everything.
Repository: imroc/ontts
Branch: master
Commit: af26ab273796
Files: 168
Total size: 952.7 KB
Directory structure:
gitextract_fcvvxwx4/
├── .gitattributes
├── README.md
├── glide.yaml
├── main.go
├── server/
│ └── server.go
├── speed_test.go
├── vendor/
│ └── github.com/
│ ├── garyburd/
│ │ └── redigo/
│ │ ├── .travis.yml
│ │ ├── LICENSE
│ │ ├── README.markdown
│ │ ├── internal/
│ │ │ ├── commandinfo.go
│ │ │ ├── commandinfo_test.go
│ │ │ └── redistest/
│ │ │ └── testdb.go
│ │ ├── redis/
│ │ │ ├── conn.go
│ │ │ ├── conn_test.go
│ │ │ ├── doc.go
│ │ │ ├── log.go
│ │ │ ├── pool.go
│ │ │ ├── pool_test.go
│ │ │ ├── pubsub.go
│ │ │ ├── pubsub_test.go
│ │ │ ├── redis.go
│ │ │ ├── reply.go
│ │ │ ├── reply_test.go
│ │ │ ├── scan.go
│ │ │ ├── scan_test.go
│ │ │ ├── script.go
│ │ │ ├── script_test.go
│ │ │ ├── test_test.go
│ │ │ └── zpop_example_test.go
│ │ └── redisx/
│ │ ├── connmux.go
│ │ ├── connmux_test.go
│ │ └── doc.go
│ └── imroc/
│ └── log/
│ ├── LICENSE
│ ├── README.md
│ └── log.go
└── xf/
├── README.md
├── doc/
│ ├── iFlytek MSC Reference Manual/
│ │ ├── all__0_8js.html
│ │ ├── all__1_8js.html
│ │ ├── dir_25539194184bab781b1c7ecd67774cd8.html
│ │ ├── dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html
│ │ ├── doxygen.css
│ │ ├── dynsections.js
│ │ ├── dynsections_8js.html
│ │ ├── files.html
│ │ ├── files__0_8js.html
│ │ ├── files__1_8js.html
│ │ ├── functions__0_8js.html
│ │ ├── functions__1_8js.html
│ │ ├── globals.html
│ │ ├── globals_func.html
│ │ ├── globals_vars.html
│ │ ├── index.html
│ │ ├── jquery.js
│ │ ├── jquery_8js.html
│ │ ├── msp__cmn_8h.html
│ │ ├── msp__cmn_8h_source.html
│ │ ├── qisr_8h.html
│ │ ├── qisr_8h_source.html
│ │ ├── qmfv_8h.html
│ │ ├── qmfv_8h_source.html
│ │ ├── qtts_8h.html
│ │ ├── qtts_8h_source.html
│ │ ├── search/
│ │ │ ├── all_0.html
│ │ │ ├── all_0.js
│ │ │ ├── all_1.html
│ │ │ ├── all_1.js
│ │ │ ├── all_10.html
│ │ │ ├── all_10.js
│ │ │ ├── all_11.html
│ │ │ ├── all_11.js
│ │ │ ├── all_2.html
│ │ │ ├── all_2.js
│ │ │ ├── all_3.html
│ │ │ ├── all_3.js
│ │ │ ├── all_4.html
│ │ │ ├── all_4.js
│ │ │ ├── all_5.html
│ │ │ ├── all_5.js
│ │ │ ├── all_6.html
│ │ │ ├── all_6.js
│ │ │ ├── all_7.html
│ │ │ ├── all_7.js
│ │ │ ├── all_8.html
│ │ │ ├── all_8.js
│ │ │ ├── all_9.html
│ │ │ ├── all_9.js
│ │ │ ├── all_a.html
│ │ │ ├── all_a.js
│ │ │ ├── all_b.html
│ │ │ ├── all_b.js
│ │ │ ├── all_c.html
│ │ │ ├── all_c.js
│ │ │ ├── all_d.html
│ │ │ ├── all_d.js
│ │ │ ├── all_e.html
│ │ │ ├── all_e.js
│ │ │ ├── all_f.html
│ │ │ ├── all_f.js
│ │ │ ├── files_0.html
│ │ │ ├── files_0.js
│ │ │ ├── files_1.html
│ │ │ ├── files_1.js
│ │ │ ├── files_2.html
│ │ │ ├── files_2.js
│ │ │ ├── files_3.html
│ │ │ ├── files_3.js
│ │ │ ├── files_4.html
│ │ │ ├── files_4.js
│ │ │ ├── files_5.html
│ │ │ ├── files_5.js
│ │ │ ├── files_6.html
│ │ │ ├── files_6.js
│ │ │ ├── functions_0.html
│ │ │ ├── functions_0.js
│ │ │ ├── functions_1.html
│ │ │ ├── functions_1.js
│ │ │ ├── functions_2.html
│ │ │ ├── functions_2.js
│ │ │ ├── functions_3.html
│ │ │ ├── functions_3.js
│ │ │ ├── functions_4.html
│ │ │ ├── functions_4.js
│ │ │ ├── functions_5.html
│ │ │ ├── functions_5.js
│ │ │ ├── functions_6.html
│ │ │ ├── functions_6.js
│ │ │ ├── functions_7.html
│ │ │ ├── functions_7.js
│ │ │ ├── functions_8.html
│ │ │ ├── functions_8.js
│ │ │ ├── functions_9.html
│ │ │ ├── functions_9.js
│ │ │ ├── functions_a.html
│ │ │ ├── functions_a.js
│ │ │ ├── nomatches.html
│ │ │ ├── search.css
│ │ │ ├── search.js
│ │ │ ├── searchdata.js
│ │ │ ├── variables_0.html
│ │ │ ├── variables_0.js
│ │ │ ├── variables_1.html
│ │ │ ├── variables_1.js
│ │ │ ├── variables_2.html
│ │ │ ├── variables_2.js
│ │ │ ├── variables_3.html
│ │ │ ├── variables_3.js
│ │ │ ├── variables_4.html
│ │ │ ├── variables_4.js
│ │ │ ├── variables_5.html
│ │ │ ├── variables_5.js
│ │ │ ├── variables_6.html
│ │ │ ├── variables_6.js
│ │ │ ├── variables_7.html
│ │ │ └── variables_7.js
│ │ ├── search_8js.html
│ │ ├── searchdata_8js.html
│ │ └── tabs.css
│ └── tts_sample/
│ ├── 32bit_make.sh
│ ├── 64bit_make.sh
│ ├── Makefile
│ └── tts_sample.c
├── include/
│ ├── .msp_cmn.h.swp
│ ├── convert.h
│ ├── msp_cmn.h
│ ├── msp_errors.h
│ ├── msp_types.h
│ └── qtts.h
└── xf.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.js linguist-language=Go
*.css linguist-language=Go
*.html linguist-language=Go
*.c linguist-language=Go
*.h linguist-language=Go
================================================
FILE: README.md
================================================
ontts
==============
ontts 是语音在线合成服务
## 编译
##### GOPATH
项目源文件需要放在$GOPATH下
##### glide
通过glide管理依赖,若没有安装glide,需先安装
Ubuntu下glide安装方法:
``` sh
sudo add-apt-repository ppa:masterminds/glide && sudo apt-get update
sudo apt-get install glide
```
在源文件目录下执行以下命令下载依赖
``` sh
glide install
```
##### 编译
``` sh
go build
```
##### 运行
需要将libmsc.so加入环境变量
``` sh
mv xf/lib/libmsc.so /usr/local/lib/
vi ~/.bashrc
```
export LD_LIBRARY_PATH=/usr/local/lib
## 使用示例
##### 单次合成:
``` sh
./ontts -t "云喇叭快递,快递小管家,您的快递到了,请于下午6点前到学校后门申通快递取件" -o test.wav
```
##### 启动合成后台服务:
``` sh
./ontts -r ":6379" -d /tmp/out
```
## 命令参数
<pre>
讯飞语音参数选项:
-tp <param> TTS合成参数[有默认值]
-lp <param> 登录参数[有默认值]
单次合成模式选项:
-t <text> 待合成的文本
-o <file> 音频输出路径
合成服务模式选项:
-d <dir> 音频保存的目录
-s <digit> 合成速度级别(1-10),数值越小速度越快,越耗CPU[默认为1]
-r <addr> redis连接地址
日志选项:
-l <file> 日志输出路径[默认./ontts.log]
-ll <level> 日志输出级别(debug,info,warn,error)
其他:
-h 查看帮助
</pre>
## 目录
<pre>
── ontts
├── glide.yaml (glide依赖配置)
├── main.go (程序入口)
├── README.md
├── server (TTS合成主体逻辑的package)
│ └── server.go
├── speed_test.go (速度测试)
└── xf (讯飞SDK的Go封装)
├── doc (讯飞语音linux SDK相关参考)
├── include (cgo需要用到的头文件)
├── lib (动态链接库 SDK)
│ └── libmsc.so
├── README.md
└── xf.go
</pre>
================================================
FILE: glide.yaml
================================================
package: ontts
import:
- package: github.com/imroc/log
================================================
FILE: main.go
================================================
package main
import (
"flag"
"fmt"
"ontts/server"
"os"
"strings"
"github.com/imroc/log"
)
var usageStr = `
Usage: ontts [options]
讯飞语音参数选项:
-tp <param> TTS合成参数[有默认值]
-lp <param> 登录参数[有默认值]
单次合成模式选项:
-t <text> 待合成的文本
-o <file> 音频输出路径
合成服务模式选项:
-d <dir> 音频保存的目录
-b <dir> 音频备份的目录
-s <digit> 合成速度级别(1-10),数值越小速度越快,越耗CPU[默认为1]
-r <addr> redis连接地址
-rp <pass> redis密码
日志选项:
-l <file> 日志输出路径[默认./ontts.log]
-ll <level> 日志输出级别(debug,info,warn,error)
其他:
-h 查看帮助
`
func main() {
opts := &server.Options{}
var txt string
var out string
var help bool
var logFile string
var logLevel string
flag.StringVar(&txt, "t", "", "单次合成的文本")
flag.StringVar(&out, "o", "", "单次合成的输出路径")
flag.StringVar(&logFile, "l", "ontts.log", "日志输出路径")
flag.StringVar(&logLevel, "ll", "debug", "日志输出级别")
flag.BoolVar(&help, "h", false, "Help")
flag.StringVar(&opts.TTSParams, "tp", "voice_name = xiaoqi, text_encoding = UTF8, sample_rate = 8000, speed = 50, volume = 50, pitch = 50, rdn = 2", "TTS合成参数")
flag.StringVar(&opts.LoginParams, "lp", "appid = 5718a335, work_dir = .", "登录参数")
flag.StringVar(&opts.RedisAddr, "r", ":6379", "redis连接地址")
flag.StringVar(&opts.RedisPass, "rp", "", "redis连接密码")
flag.StringVar(&opts.OutDir, "d", "", "音频输出目录")
flag.StringVar(&opts.BackupDir, "b", "", "音频保存目录")
flag.IntVar(&opts.Speed, "s", 1, "合成速度")
flag.Parse()
if help {
fmt.Printf("%s\n", usageStr)
return
}
err := configureLog(logFile, logLevel)
if err != nil {
log.Error("日志配置失败:%v", err)
return
}
s := server.New(opts)
if txt != "" { // 单次合成
if out == "" {
out = txt + ".wav"
}
log.Debug("合成文本:%q,输出:%s", txt, out)
if err = s.Once(txt, out); err != nil {
log.Error("%v", err)
return
}
}
s.Start()
}
func configureLog(logFile, logLevel string) error {
level := log.DEBUG
switch strings.ToLower(logLevel) {
case "debug":
level = log.DEBUG
case "info":
level = log.INFO
case "warn":
level = log.WARN
case "error":
level = log.ERROR
}
file, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return err
}
log.Set(level, file, log.Lshortfile|log.LstdFlags)
return nil
}
================================================
FILE: server/server.go
================================================
package server
import (
"encoding/json"
"fmt"
"io"
"ontts/xf"
"os"
"strings"
"time"
"github.com/garyburd/redigo/redis"
"github.com/imroc/log"
)
type Server struct {
opts *Options
}
type Options struct {
OutDir string //音频输出目录
BackupDir string // 备份目录,文件输出成功之后,再将文件复制到备份目录
Level int //音频生成速度级别,越快越耗CPU,级别1~10,数字越小速度越快
TTSParams string
LoginParams string
RedisAddr string
RedisPass string
Speed int
}
type Speech struct {
Id string `json:"id"`
Txt string `json:"txt"`
}
func New(opts *Options) *Server {
return &Server{
opts: opts,
}
}
func (s *Server) Start() {
var c redis.Conn
var err error
if s.opts.RedisPass == "" {
c, err = redis.Dial("tcp", s.opts.RedisAddr)
} else {
c, err = redis.Dial("tcp", s.opts.RedisAddr, redis.DialPassword(s.opts.RedisPass))
}
if err != nil {
log.Error("failed to connect redis:%v")
return
}
defer c.Close()
psc := redis.PubSubConn{Conn: c}
err = psc.Subscribe("tts")
if err != nil {
log.Error("failed to subscribe:%v", err)
return
}
sub, ok := psc.Receive().(redis.Subscription)
if !ok {
log.Error("first message is not subscription")
return
}
if sub.Count == 0 {
log.Error("redis subscription count is 0")
return
}
err = setXF(s.opts.Speed, s.opts.TTSParams, s.opts.LoginParams)
if err != nil {
log.Error("failed to set xunfei params:%v", err)
return
}
if s.opts.OutDir != "" && s.opts.OutDir[len(s.opts.OutDir)-1] != os.PathSeparator {
s.opts.OutDir += string(os.PathSeparator)
}
if s.opts.BackupDir != "" && s.opts.BackupDir[len(s.opts.BackupDir)-1] != os.PathSeparator {
s.opts.BackupDir += string(os.PathSeparator)
}
var speech Speech
for {
switch n := psc.Receive().(type) {
case redis.Message:
err := json.Unmarshal(n.Data, &speech)
if err != nil {
log.Error("error unmarshal:%v", err)
continue
}
if len(strings.Fields(speech.Txt)) == 0 { // 忽略空白字符串,会导致语音合成参数错误
continue
}
tryN := 0
ttsFilename := s.opts.OutDir + speech.Id + ".wav"
TTS:
err = xf.TextToSpeech(speech.Txt, ttsFilename)
if err != nil {
tryN++
log.Error("error convert:%v,tts ID:%s,TXT:%s", err, speech.Id, speech.Txt)
if tryN > 5 { // 多次重试失败,忽略此条语音的合成
continue
}
time.Sleep(5 * time.Second)
goto TTS
}
log.Debug("合成ID:%s,TXT:%s", speech.Id, speech.Txt)
if s.opts.BackupDir != "" {
src, _err := os.Open(ttsFilename)
if _err != nil {
log.Error("failed to open file %s:%v", ttsFilename, _err)
}
filename := s.opts.BackupDir + speech.Id + ".wav"
dst, _err := os.Create(filename)
if _err != nil {
log.Error("failed to create file %s:%v", filename, _err)
} else {
_, _err = io.Copy(dst, src)
if _err != nil {
log.Error("failed to copy file %s->%s:%v", ttsFilename, filename, _err)
}
}
}
case error:
log.Error("error redis message:%v", n)
time.Sleep(10 * time.Second)
default:
log.Warn("unknown message:%v", n)
}
}
}
func (s *Server) Once(txt string, desPath string) error {
log.Debug("tts:%s,login:%s", s.opts.TTSParams, s.opts.LoginParams)
xf.SetTTSParams(s.opts.TTSParams)
err := xf.Login(s.opts.LoginParams)
if err != nil {
return err
}
//不SetSleep,默认为0,单次合成以高性能模式
log.Debug("txt:%s,des_path:%s", txt, desPath)
err = xf.TextToSpeech(txt, desPath)
if err != nil {
return err
}
return nil
}
func setXF(speedLevel int, ttsParams, loginParams string) error {
if speedLevel < 1 || speedLevel > 10 {
return fmt.Errorf("wrong speed level:%d,it should between 1 and 10", speedLevel)
}
sleepTime := 15000 * (speedLevel - 1)
xf.SetSleep(sleepTime)
xf.SetTTSParams(ttsParams)
err := xf.Login(loginParams)
if err != nil {
return err
}
return nil
}
================================================
FILE: speed_test.go
================================================
package main
import (
"fmt"
"math/rand"
"ontts/xf"
"os"
"testing"
"time"
"github.com/imroc/log"
)
func TestSpeed(t *testing.T) {
file, _ := os.OpenFile("test.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
log.Set(log.DEBUG, file, log.LstdFlags)
xf.SetTTSParams("voice_name = xiaoqi, text_encoding = UTF8, sample_rate = 8000, speed = 50, volume = 50, pitch = 50, rdn = 2")
xf.SetSleep(0)
err := xf.Login("appid = 5718a335, work_dir = .")
if err != nil {
log.Error("err:%v", err)
return
}
now := time.Now()
var txt string
for i := 1; i < 1000; i++ {
txt = getRandomString(r.Intn(21) + 40)
err = xf.TextToSpeech(txt, fmt.Sprintf("/home/roc/wav/%d.wav", i))
if err != nil {
log.Error("err:%v", err)
return
}
log.Debug("已生成第%d个:%s,共用%f秒", i, txt, time.Since(now).Seconds())
}
log.Info("执行完毕,总消耗:%f秒", time.Since(now).Seconds())
xf.Logout()
}
var start rune = 0x4e00
var stop rune = 0x9fa5
var n int32 = int32(stop - start)
var r *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
func getRandomString(l int) string {
s := make([]rune, l)
for i := 0; i < l; i++ {
s[i] = rune(r.Int31n(n) + start)
}
return string(s)
}
================================================
FILE: vendor/github.com/garyburd/redigo/.travis.yml
================================================
language: go
sudo: false
services:
- redis-server
go:
- 1.4
- 1.5
- 1.6
- 1.7
- tip
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d .)
- go vet $(go list ./... | grep -v /vendor/)
- go test -v -race ./...
================================================
FILE: vendor/github.com/garyburd/redigo/LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
================================================
FILE: vendor/github.com/garyburd/redigo/README.markdown
================================================
Redigo
======
[](https://travis-ci.org/garyburd/redigo)
[](https://godoc.org/github.com/garyburd/redigo/redis)
Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database.
Features
-------
* A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands.
* [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions.
* [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe).
* [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool).
* [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA.
* [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies.
Documentation
-------------
- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis)
- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ)
Installation
------------
Install Redigo using the "go get" command:
go get github.com/garyburd/redigo/redis
The Go distribution is Redigo's only dependency.
Related Projects
----------------
- [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo.
- [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation.
- [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo
- [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo
Contributing
------------
Send email to Gary Burd (address in GitHub profile) before doing any work on Redigo.
License
-------
Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
================================================
FILE: vendor/github.com/garyburd/redigo/internal/commandinfo.go
================================================
// Copyright 2014 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package internal // import "github.com/garyburd/redigo/internal"
import (
"strings"
)
const (
WatchState = 1 << iota
MultiState
SubscribeState
MonitorState
)
type CommandInfo struct {
Set, Clear int
}
var commandInfos = map[string]CommandInfo{
"WATCH": {Set: WatchState},
"UNWATCH": {Clear: WatchState},
"MULTI": {Set: MultiState},
"EXEC": {Clear: WatchState | MultiState},
"DISCARD": {Clear: WatchState | MultiState},
"PSUBSCRIBE": {Set: SubscribeState},
"SUBSCRIBE": {Set: SubscribeState},
"MONITOR": {Set: MonitorState},
}
func init() {
for n, ci := range commandInfos {
commandInfos[strings.ToLower(n)] = ci
}
}
func LookupCommandInfo(commandName string) CommandInfo {
if ci, ok := commandInfos[commandName]; ok {
return ci
}
return commandInfos[strings.ToUpper(commandName)]
}
================================================
FILE: vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
================================================
package internal
import "testing"
func TestLookupCommandInfo(t *testing.T) {
for _, n := range []string{"watch", "WATCH", "wAtch"} {
if LookupCommandInfo(n) == (CommandInfo{}) {
t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n)
}
}
}
func benchmarkLookupCommandInfo(b *testing.B, names ...string) {
for i := 0; i < b.N; i++ {
for _, c := range names {
LookupCommandInfo(c)
}
}
}
func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) {
benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR")
}
func BenchmarkLookupCommandInfoMixedCase(b *testing.B) {
benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR")
}
================================================
FILE: vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
================================================
// Copyright 2014 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// Package redistest contains utilities for writing Redigo tests.
package redistest
import (
"errors"
"time"
"github.com/garyburd/redigo/redis"
)
type testConn struct {
redis.Conn
}
func (t testConn) Close() error {
_, err := t.Conn.Do("SELECT", "9")
if err != nil {
return nil
}
_, err = t.Conn.Do("FLUSHDB")
if err != nil {
return err
}
return t.Conn.Close()
}
// Dial dials the local Redis server and selects database 9. To prevent
// stomping on real data, DialTestDB fails if database 9 contains data. The
// returned connection flushes database 9 on close.
func Dial() (redis.Conn, error) {
c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
if err != nil {
return nil, err
}
_, err = c.Do("SELECT", "9")
if err != nil {
c.Close()
return nil, err
}
n, err := redis.Int(c.Do("DBSIZE"))
if err != nil {
c.Close()
return nil, err
}
if n != 0 {
c.Close()
return nil, errors.New("database #9 is not empty, test can not continue")
}
return testConn{c}, nil
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/conn.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"net"
"net/url"
"regexp"
"strconv"
"sync"
"time"
)
// conn is the low-level implementation of Conn
type conn struct {
// Shared
mu sync.Mutex
pending int
err error
conn net.Conn
// Read
readTimeout time.Duration
br *bufio.Reader
// Write
writeTimeout time.Duration
bw *bufio.Writer
// Scratch space for formatting argument length.
// '*' or '$', length, "\r\n"
lenScratch [32]byte
// Scratch space for formatting integers and floats.
numScratch [40]byte
}
// DialTimeout acts like Dial but takes timeouts for establishing the
// connection to the server, writing a command and reading a reply.
//
// Deprecated: Use Dial with options instead.
func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
return Dial(network, address,
DialConnectTimeout(connectTimeout),
DialReadTimeout(readTimeout),
DialWriteTimeout(writeTimeout))
}
// DialOption specifies an option for dialing a Redis server.
type DialOption struct {
f func(*dialOptions)
}
type dialOptions struct {
readTimeout time.Duration
writeTimeout time.Duration
dial func(network, addr string) (net.Conn, error)
db int
password string
}
// DialReadTimeout specifies the timeout for reading a single command reply.
func DialReadTimeout(d time.Duration) DialOption {
return DialOption{func(do *dialOptions) {
do.readTimeout = d
}}
}
// DialWriteTimeout specifies the timeout for writing a single command.
func DialWriteTimeout(d time.Duration) DialOption {
return DialOption{func(do *dialOptions) {
do.writeTimeout = d
}}
}
// DialConnectTimeout specifies the timeout for connecting to the Redis server.
func DialConnectTimeout(d time.Duration) DialOption {
return DialOption{func(do *dialOptions) {
dialer := net.Dialer{Timeout: d}
do.dial = dialer.Dial
}}
}
// DialNetDial specifies a custom dial function for creating TCP
// connections. If this option is left out, then net.Dial is
// used. DialNetDial overrides DialConnectTimeout.
func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
return DialOption{func(do *dialOptions) {
do.dial = dial
}}
}
// DialDatabase specifies the database to select when dialing a connection.
func DialDatabase(db int) DialOption {
return DialOption{func(do *dialOptions) {
do.db = db
}}
}
// DialPassword specifies the password to use when connecting to
// the Redis server.
func DialPassword(password string) DialOption {
return DialOption{func(do *dialOptions) {
do.password = password
}}
}
// Dial connects to the Redis server at the given network and
// address using the specified options.
func Dial(network, address string, options ...DialOption) (Conn, error) {
do := dialOptions{
dial: net.Dial,
}
for _, option := range options {
option.f(&do)
}
netConn, err := do.dial(network, address)
if err != nil {
return nil, err
}
c := &conn{
conn: netConn,
bw: bufio.NewWriter(netConn),
br: bufio.NewReader(netConn),
readTimeout: do.readTimeout,
writeTimeout: do.writeTimeout,
}
if do.password != "" {
if _, err := c.Do("AUTH", do.password); err != nil {
netConn.Close()
return nil, err
}
}
if do.db != 0 {
if _, err := c.Do("SELECT", do.db); err != nil {
netConn.Close()
return nil, err
}
}
return c, nil
}
var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
// DialURL connects to a Redis server at the given URL using the Redis
// URI scheme. URLs should follow the draft IANA specification for the
// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
func DialURL(rawurl string, options ...DialOption) (Conn, error) {
u, err := url.Parse(rawurl)
if err != nil {
return nil, err
}
if u.Scheme != "redis" {
return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
}
// As per the IANA draft spec, the host defaults to localhost and
// the port defaults to 6379.
host, port, err := net.SplitHostPort(u.Host)
if err != nil {
// assume port is missing
host = u.Host
port = "6379"
}
if host == "" {
host = "localhost"
}
address := net.JoinHostPort(host, port)
if u.User != nil {
password, isSet := u.User.Password()
if isSet {
options = append(options, DialPassword(password))
}
}
match := pathDBRegexp.FindStringSubmatch(u.Path)
if len(match) == 2 {
db := 0
if len(match[1]) > 0 {
db, err = strconv.Atoi(match[1])
if err != nil {
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
}
}
if db != 0 {
options = append(options, DialDatabase(db))
}
} else if u.Path != "" {
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
}
return Dial("tcp", address, options...)
}
// NewConn returns a new Redigo connection for the given net connection.
func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
return &conn{
conn: netConn,
bw: bufio.NewWriter(netConn),
br: bufio.NewReader(netConn),
readTimeout: readTimeout,
writeTimeout: writeTimeout,
}
}
func (c *conn) Close() error {
c.mu.Lock()
err := c.err
if c.err == nil {
c.err = errors.New("redigo: closed")
err = c.conn.Close()
}
c.mu.Unlock()
return err
}
func (c *conn) fatal(err error) error {
c.mu.Lock()
if c.err == nil {
c.err = err
// Close connection to force errors on subsequent calls and to unblock
// other reader or writer.
c.conn.Close()
}
c.mu.Unlock()
return err
}
func (c *conn) Err() error {
c.mu.Lock()
err := c.err
c.mu.Unlock()
return err
}
func (c *conn) writeLen(prefix byte, n int) error {
c.lenScratch[len(c.lenScratch)-1] = '\n'
c.lenScratch[len(c.lenScratch)-2] = '\r'
i := len(c.lenScratch) - 3
for {
c.lenScratch[i] = byte('0' + n%10)
i -= 1
n = n / 10
if n == 0 {
break
}
}
c.lenScratch[i] = prefix
_, err := c.bw.Write(c.lenScratch[i:])
return err
}
func (c *conn) writeString(s string) error {
c.writeLen('$', len(s))
c.bw.WriteString(s)
_, err := c.bw.WriteString("\r\n")
return err
}
func (c *conn) writeBytes(p []byte) error {
c.writeLen('$', len(p))
c.bw.Write(p)
_, err := c.bw.WriteString("\r\n")
return err
}
func (c *conn) writeInt64(n int64) error {
return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
}
func (c *conn) writeFloat64(n float64) error {
return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
}
func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
c.writeLen('*', 1+len(args))
err = c.writeString(cmd)
for _, arg := range args {
if err != nil {
break
}
switch arg := arg.(type) {
case string:
err = c.writeString(arg)
case []byte:
err = c.writeBytes(arg)
case int:
err = c.writeInt64(int64(arg))
case int64:
err = c.writeInt64(arg)
case float64:
err = c.writeFloat64(arg)
case bool:
if arg {
err = c.writeString("1")
} else {
err = c.writeString("0")
}
case nil:
err = c.writeString("")
default:
var buf bytes.Buffer
fmt.Fprint(&buf, arg)
err = c.writeBytes(buf.Bytes())
}
}
return err
}
type protocolError string
func (pe protocolError) Error() string {
return fmt.Sprintf("redigo: %s (possible server error or unsupported concurrent read by application)", string(pe))
}
func (c *conn) readLine() ([]byte, error) {
p, err := c.br.ReadSlice('\n')
if err == bufio.ErrBufferFull {
return nil, protocolError("long response line")
}
if err != nil {
return nil, err
}
i := len(p) - 2
if i < 0 || p[i] != '\r' {
return nil, protocolError("bad response line terminator")
}
return p[:i], nil
}
// parseLen parses bulk string and array lengths.
func parseLen(p []byte) (int, error) {
if len(p) == 0 {
return -1, protocolError("malformed length")
}
if p[0] == '-' && len(p) == 2 && p[1] == '1' {
// handle $-1 and $-1 null replies.
return -1, nil
}
var n int
for _, b := range p {
n *= 10
if b < '0' || b > '9' {
return -1, protocolError("illegal bytes in length")
}
n += int(b - '0')
}
return n, nil
}
// parseInt parses an integer reply.
func parseInt(p []byte) (interface{}, error) {
if len(p) == 0 {
return 0, protocolError("malformed integer")
}
var negate bool
if p[0] == '-' {
negate = true
p = p[1:]
if len(p) == 0 {
return 0, protocolError("malformed integer")
}
}
var n int64
for _, b := range p {
n *= 10
if b < '0' || b > '9' {
return 0, protocolError("illegal bytes in length")
}
n += int64(b - '0')
}
if negate {
n = -n
}
return n, nil
}
var (
okReply interface{} = "OK"
pongReply interface{} = "PONG"
)
func (c *conn) readReply() (interface{}, error) {
line, err := c.readLine()
if err != nil {
return nil, err
}
if len(line) == 0 {
return nil, protocolError("short response line")
}
switch line[0] {
case '+':
switch {
case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
// Avoid allocation for frequent "+OK" response.
return okReply, nil
case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
// Avoid allocation in PING command benchmarks :)
return pongReply, nil
default:
return string(line[1:]), nil
}
case '-':
return Error(string(line[1:])), nil
case ':':
return parseInt(line[1:])
case '$':
n, err := parseLen(line[1:])
if n < 0 || err != nil {
return nil, err
}
p := make([]byte, n)
_, err = io.ReadFull(c.br, p)
if err != nil {
return nil, err
}
if line, err := c.readLine(); err != nil {
return nil, err
} else if len(line) != 0 {
return nil, protocolError("bad bulk string format")
}
return p, nil
case '*':
n, err := parseLen(line[1:])
if n < 0 || err != nil {
return nil, err
}
r := make([]interface{}, n)
for i := range r {
r[i], err = c.readReply()
if err != nil {
return nil, err
}
}
return r, nil
}
return nil, protocolError("unexpected response line")
}
func (c *conn) Send(cmd string, args ...interface{}) error {
c.mu.Lock()
c.pending += 1
c.mu.Unlock()
if c.writeTimeout != 0 {
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
}
if err := c.writeCommand(cmd, args); err != nil {
return c.fatal(err)
}
return nil
}
func (c *conn) Flush() error {
if c.writeTimeout != 0 {
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
}
if err := c.bw.Flush(); err != nil {
return c.fatal(err)
}
return nil
}
func (c *conn) Receive() (reply interface{}, err error) {
if c.readTimeout != 0 {
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
}
if reply, err = c.readReply(); err != nil {
return nil, c.fatal(err)
}
// When using pub/sub, the number of receives can be greater than the
// number of sends. To enable normal use of the connection after
// unsubscribing from all channels, we do not decrement pending to a
// negative value.
//
// The pending field is decremented after the reply is read to handle the
// case where Receive is called before Send.
c.mu.Lock()
if c.pending > 0 {
c.pending -= 1
}
c.mu.Unlock()
if err, ok := reply.(Error); ok {
return nil, err
}
return
}
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
c.mu.Lock()
pending := c.pending
c.pending = 0
c.mu.Unlock()
if cmd == "" && pending == 0 {
return nil, nil
}
if c.writeTimeout != 0 {
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
}
if cmd != "" {
if err := c.writeCommand(cmd, args); err != nil {
return nil, c.fatal(err)
}
}
if err := c.bw.Flush(); err != nil {
return nil, c.fatal(err)
}
if c.readTimeout != 0 {
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
}
if cmd == "" {
reply := make([]interface{}, pending)
for i := range reply {
r, e := c.readReply()
if e != nil {
return nil, c.fatal(e)
}
reply[i] = r
}
return reply, nil
}
var err error
var reply interface{}
for i := 0; i <= pending; i++ {
var e error
if reply, e = c.readReply(); e != nil {
return nil, c.fatal(e)
}
if e, ok := reply.(Error); ok && err == nil {
err = e
}
}
return reply, err
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/conn_test.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"bytes"
"io"
"math"
"net"
"os"
"reflect"
"strings"
"testing"
"time"
"github.com/garyburd/redigo/redis"
)
type testConn struct {
io.Reader
io.Writer
}
func (*testConn) Close() error { return nil }
func (*testConn) LocalAddr() net.Addr { return nil }
func (*testConn) RemoteAddr() net.Addr { return nil }
func (*testConn) SetDeadline(t time.Time) error { return nil }
func (*testConn) SetReadDeadline(t time.Time) error { return nil }
func (*testConn) SetWriteDeadline(t time.Time) error { return nil }
func dialTestConn(r io.Reader, w io.Writer) redis.DialOption {
return redis.DialNetDial(func(net, addr string) (net.Conn, error) {
return &testConn{Reader: r, Writer: w}, nil
})
}
var writeTests = []struct {
args []interface{}
expected string
}{
{
[]interface{}{"SET", "key", "value"},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
},
{
[]interface{}{"SET", "key", "value"},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
},
{
[]interface{}{"SET", "key", byte(100)},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
},
{
[]interface{}{"SET", "key", 100},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
},
{
[]interface{}{"SET", "key", int64(math.MinInt64)},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n",
},
{
[]interface{}{"SET", "key", float64(1349673917.939762)},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n",
},
{
[]interface{}{"SET", "key", ""},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
},
{
[]interface{}{"SET", "key", nil},
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
},
{
[]interface{}{"ECHO", true, false},
"*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
},
}
func TestWrite(t *testing.T) {
for _, tt := range writeTests {
var buf bytes.Buffer
c, _ := redis.Dial("", "", dialTestConn(nil, &buf))
err := c.Send(tt.args[0].(string), tt.args[1:]...)
if err != nil {
t.Errorf("Send(%v) returned error %v", tt.args, err)
continue
}
c.Flush()
actual := buf.String()
if actual != tt.expected {
t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected)
}
}
}
var errorSentinel = &struct{}{}
var readTests = []struct {
reply string
expected interface{}
}{
{
"+OK\r\n",
"OK",
},
{
"+PONG\r\n",
"PONG",
},
{
"@OK\r\n",
errorSentinel,
},
{
"$6\r\nfoobar\r\n",
[]byte("foobar"),
},
{
"$-1\r\n",
nil,
},
{
":1\r\n",
int64(1),
},
{
":-2\r\n",
int64(-2),
},
{
"*0\r\n",
[]interface{}{},
},
{
"*-1\r\n",
nil,
},
{
"*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n",
[]interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")},
},
{
"*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n",
[]interface{}{[]byte("foo"), nil, []byte("bar")},
},
{
// "x" is not a valid length
"$x\r\nfoobar\r\n",
errorSentinel,
},
{
// -2 is not a valid length
"$-2\r\n",
errorSentinel,
},
{
// "x" is not a valid integer
":x\r\n",
errorSentinel,
},
{
// missing \r\n following value
"$6\r\nfoobar",
errorSentinel,
},
{
// short value
"$6\r\nxx",
errorSentinel,
},
{
// long value
"$6\r\nfoobarx\r\n",
errorSentinel,
},
}
func TestRead(t *testing.T) {
for _, tt := range readTests {
c, _ := redis.Dial("", "", dialTestConn(strings.NewReader(tt.reply), nil))
actual, err := c.Receive()
if tt.expected == errorSentinel {
if err == nil {
t.Errorf("Receive(%q) did not return expected error", tt.reply)
}
} else {
if err != nil {
t.Errorf("Receive(%q) returned error %v", tt.reply, err)
continue
}
if !reflect.DeepEqual(actual, tt.expected) {
t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected)
}
}
}
}
var testCommands = []struct {
args []interface{}
expected interface{}
}{
{
[]interface{}{"PING"},
"PONG",
},
{
[]interface{}{"SET", "foo", "bar"},
"OK",
},
{
[]interface{}{"GET", "foo"},
[]byte("bar"),
},
{
[]interface{}{"GET", "nokey"},
nil,
},
{
[]interface{}{"MGET", "nokey", "foo"},
[]interface{}{nil, []byte("bar")},
},
{
[]interface{}{"INCR", "mycounter"},
int64(1),
},
{
[]interface{}{"LPUSH", "mylist", "foo"},
int64(1),
},
{
[]interface{}{"LPUSH", "mylist", "bar"},
int64(2),
},
{
[]interface{}{"LRANGE", "mylist", 0, -1},
[]interface{}{[]byte("bar"), []byte("foo")},
},
{
[]interface{}{"MULTI"},
"OK",
},
{
[]interface{}{"LRANGE", "mylist", 0, -1},
"QUEUED",
},
{
[]interface{}{"PING"},
"QUEUED",
},
{
[]interface{}{"EXEC"},
[]interface{}{
[]interface{}{[]byte("bar"), []byte("foo")},
"PONG",
},
},
}
func TestDoCommands(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
for _, cmd := range testCommands {
actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...)
if err != nil {
t.Errorf("Do(%v) returned error %v", cmd.args, err)
continue
}
if !reflect.DeepEqual(actual, cmd.expected) {
t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected)
}
}
}
func TestPipelineCommands(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
for _, cmd := range testCommands {
if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
t.Fatalf("Send(%v) returned error %v", cmd.args, err)
}
}
if err := c.Flush(); err != nil {
t.Errorf("Flush() returned error %v", err)
}
for _, cmd := range testCommands {
actual, err := c.Receive()
if err != nil {
t.Fatalf("Receive(%v) returned error %v", cmd.args, err)
}
if !reflect.DeepEqual(actual, cmd.expected) {
t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
}
}
}
func TestBlankCommmand(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
for _, cmd := range testCommands {
if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
t.Fatalf("Send(%v) returned error %v", cmd.args, err)
}
}
reply, err := redis.Values(c.Do(""))
if err != nil {
t.Fatalf("Do() returned error %v", err)
}
if len(reply) != len(testCommands) {
t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands))
}
for i, cmd := range testCommands {
actual := reply[i]
if !reflect.DeepEqual(actual, cmd.expected) {
t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
}
}
}
func TestRecvBeforeSend(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
done := make(chan struct{})
go func() {
c.Receive()
close(done)
}()
time.Sleep(time.Millisecond)
c.Send("PING")
c.Flush()
<-done
_, err = c.Do("")
if err != nil {
t.Fatalf("error=%v", err)
}
}
func TestError(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
c.Do("SET", "key", "val")
_, err = c.Do("HSET", "key", "fld", "val")
if err == nil {
t.Errorf("Expected err for HSET on string key.")
}
if c.Err() != nil {
t.Errorf("Conn has Err()=%v, expect nil", c.Err())
}
_, err = c.Do("SET", "key", "val")
if err != nil {
t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err)
}
}
func TestReadTimeout(t *testing.T) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("net.Listen returned %v", err)
}
defer l.Close()
go func() {
for {
c, err := l.Accept()
if err != nil {
return
}
go func() {
time.Sleep(time.Second)
c.Write([]byte("+OK\r\n"))
c.Close()
}()
}
}()
// Do
c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
if err != nil {
t.Fatalf("redis.Dial returned %v", err)
}
defer c1.Close()
_, err = c1.Do("PING")
if err == nil {
t.Fatalf("c1.Do() returned nil, expect error")
}
if c1.Err() == nil {
t.Fatalf("c1.Err() = nil, expect error")
}
// Send/Flush/Receive
c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
if err != nil {
t.Fatalf("redis.Dial returned %v", err)
}
defer c2.Close()
c2.Send("PING")
c2.Flush()
_, err = c2.Receive()
if err == nil {
t.Fatalf("c2.Receive() returned nil, expect error")
}
if c2.Err() == nil {
t.Fatalf("c2.Err() = nil, expect error")
}
}
var dialErrors = []struct {
rawurl string
expectedError string
}{
{
"localhost",
"invalid redis URL scheme",
},
// The error message for invalid hosts is diffferent in different
// versions of Go, so just check that there is an error message.
{
"redis://weird url",
"",
},
{
"redis://foo:bar:baz",
"",
},
{
"http://www.google.com",
"invalid redis URL scheme: http",
},
{
"redis://localhost:6379/abc123",
"invalid database: abc123",
},
}
func TestDialURLErrors(t *testing.T) {
for _, d := range dialErrors {
_, err := redis.DialURL(d.rawurl)
if err == nil || !strings.Contains(err.Error(), d.expectedError) {
t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
}
}
}
func TestDialURLPort(t *testing.T) {
checkPort := func(network, address string) (net.Conn, error) {
if address != "localhost:6379" {
t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
}
return nil, nil
}
_, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
if err != nil {
t.Error("dial error:", err)
}
}
func TestDialURLHost(t *testing.T) {
checkHost := func(network, address string) (net.Conn, error) {
if address != "localhost:6379" {
t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
}
return nil, nil
}
_, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
if err != nil {
t.Error("dial error:", err)
}
}
func TestDialURLPassword(t *testing.T) {
var buf bytes.Buffer
_, err := redis.DialURL("redis://x:abc123@localhost", dialTestConn(strings.NewReader("+OK\r\n"), &buf))
if err != nil {
t.Error("dial error:", err)
}
expected := "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"
actual := buf.String()
if actual != expected {
t.Errorf("commands = %q, want %q", actual, expected)
}
}
func TestDialURLDatabase(t *testing.T) {
var buf3 bytes.Buffer
_, err3 := redis.DialURL("redis://localhost/3", dialTestConn(strings.NewReader("+OK\r\n"), &buf3))
if err3 != nil {
t.Error("dial error:", err3)
}
expected3 := "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"
actual3 := buf3.String()
if actual3 != expected3 {
t.Errorf("commands = %q, want %q", actual3, expected3)
}
// empty DB means 0
var buf0 bytes.Buffer
_, err0 := redis.DialURL("redis://localhost/", dialTestConn(strings.NewReader("+OK\r\n"), &buf0))
if err0 != nil {
t.Error("dial error:", err0)
}
expected0 := ""
actual0 := buf0.String()
if actual0 != expected0 {
t.Errorf("commands = %q, want %q", actual0, expected0)
}
}
// Connect to local instance of Redis running on the default port.
func ExampleDial() {
c, err := redis.Dial("tcp", ":6379")
if err != nil {
// handle error
}
defer c.Close()
}
// Connect to remote instance of Redis using a URL.
func ExampleDialURL() {
c, err := redis.DialURL(os.Getenv("REDIS_URL"))
if err != nil {
// handle connection error
}
defer c.Close()
}
// TextExecError tests handling of errors in a transaction. See
// http://redis.io/topics/transactions for information on how Redis handles
// errors in a transaction.
func TestExecError(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
// Execute commands that fail before EXEC is called.
c.Do("DEL", "k0")
c.Do("ZADD", "k0", 0, 0)
c.Send("MULTI")
c.Send("NOTACOMMAND", "k0", 0, 0)
c.Send("ZINCRBY", "k0", 0, 0)
v, err := c.Do("EXEC")
if err == nil {
t.Fatalf("EXEC returned values %v, expected error", v)
}
// Execute commands that fail after EXEC is called. The first command
// returns an error.
c.Do("DEL", "k1")
c.Do("ZADD", "k1", 0, 0)
c.Send("MULTI")
c.Send("HSET", "k1", 0, 0)
c.Send("ZINCRBY", "k1", 0, 0)
v, err = c.Do("EXEC")
if err != nil {
t.Fatalf("EXEC returned error %v", err)
}
vs, err := redis.Values(v, nil)
if err != nil {
t.Fatalf("Values(v) returned error %v", err)
}
if len(vs) != 2 {
t.Fatalf("len(vs) == %d, want 2", len(vs))
}
if _, ok := vs[0].(error); !ok {
t.Fatalf("first result is type %T, expected error", vs[0])
}
if _, ok := vs[1].([]byte); !ok {
t.Fatalf("second result is type %T, expected []byte", vs[1])
}
// Execute commands that fail after EXEC is called. The second command
// returns an error.
c.Do("ZADD", "k2", 0, 0)
c.Send("MULTI")
c.Send("ZINCRBY", "k2", 0, 0)
c.Send("HSET", "k2", 0, 0)
v, err = c.Do("EXEC")
if err != nil {
t.Fatalf("EXEC returned error %v", err)
}
vs, err = redis.Values(v, nil)
if err != nil {
t.Fatalf("Values(v) returned error %v", err)
}
if len(vs) != 2 {
t.Fatalf("len(vs) == %d, want 2", len(vs))
}
if _, ok := vs[0].([]byte); !ok {
t.Fatalf("first result is type %T, expected []byte", vs[0])
}
if _, ok := vs[1].(error); !ok {
t.Fatalf("second result is type %T, expected error", vs[2])
}
}
func BenchmarkDoEmpty(b *testing.B) {
b.StopTimer()
c, err := redis.DialDefaultServer()
if err != nil {
b.Fatal(err)
}
defer c.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
if _, err := c.Do(""); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkDoPing(b *testing.B) {
b.StopTimer()
c, err := redis.DialDefaultServer()
if err != nil {
b.Fatal(err)
}
defer c.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
}
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/doc.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// Package redis is a client for the Redis database.
//
// The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more
// documentation about this package.
//
// Connections
//
// The Conn interface is the primary interface for working with Redis.
// Applications create connections by calling the Dial, DialWithTimeout or
// NewConn functions. In the future, functions will be added for creating
// sharded and other types of connections.
//
// The application must call the connection Close method when the application
// is done with the connection.
//
// Executing Commands
//
// The Conn interface has a generic method for executing Redis commands:
//
// Do(commandName string, args ...interface{}) (reply interface{}, err error)
//
// The Redis command reference (http://redis.io/commands) lists the available
// commands. An example of using the Redis APPEND command is:
//
// n, err := conn.Do("APPEND", "key", "value")
//
// The Do method converts command arguments to binary strings for transmission
// to the server as follows:
//
// Go Type Conversion
// []byte Sent as is
// string Sent as is
// int, int64 strconv.FormatInt(v)
// float64 strconv.FormatFloat(v, 'g', -1, 64)
// bool true -> "1", false -> "0"
// nil ""
// all other types fmt.Print(v)
//
// Redis command reply types are represented using the following Go types:
//
// Redis type Go type
// error redis.Error
// integer int64
// simple string string
// bulk string []byte or nil if value not present.
// array []interface{} or nil if value not present.
//
// Use type assertions or the reply helper functions to convert from
// interface{} to the specific Go type for the command result.
//
// Pipelining
//
// Connections support pipelining using the Send, Flush and Receive methods.
//
// Send(commandName string, args ...interface{}) error
// Flush() error
// Receive() (reply interface{}, err error)
//
// Send writes the command to the connection's output buffer. Flush flushes the
// connection's output buffer to the server. Receive reads a single reply from
// the server. The following example shows a simple pipeline.
//
// c.Send("SET", "foo", "bar")
// c.Send("GET", "foo")
// c.Flush()
// c.Receive() // reply from SET
// v, err = c.Receive() // reply from GET
//
// The Do method combines the functionality of the Send, Flush and Receive
// methods. The Do method starts by writing the command and flushing the output
// buffer. Next, the Do method receives all pending replies including the reply
// for the command just sent by Do. If any of the received replies is an error,
// then Do returns the error. If there are no errors, then Do returns the last
// reply. If the command argument to the Do method is "", then the Do method
// will flush the output buffer and receive pending replies without sending a
// command.
//
// Use the Send and Do methods to implement pipelined transactions.
//
// c.Send("MULTI")
// c.Send("INCR", "foo")
// c.Send("INCR", "bar")
// r, err := c.Do("EXEC")
// fmt.Println(r) // prints [1, 1]
//
// Concurrency
//
// Connections support one concurrent caller to the Receive method and one
// concurrent caller to the Send and Flush methods. No other concurrency is
// supported including concurrent calls to the Do method.
//
// For full concurrent access to Redis, use the thread-safe Pool to get, use
// and release a connection from within a goroutine. Connections returned from
// a Pool have the concurrency restrictions described in the previous
// paragraph.
//
// Publish and Subscribe
//
// Use the Send, Flush and Receive methods to implement Pub/Sub subscribers.
//
// c.Send("SUBSCRIBE", "example")
// c.Flush()
// for {
// reply, err := c.Receive()
// if err != nil {
// return err
// }
// // process pushed message
// }
//
// The PubSubConn type wraps a Conn with convenience methods for implementing
// subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods
// send and flush a subscription management command. The receive method
// converts a pushed message to convenient types for use in a type switch.
//
// psc := redis.PubSubConn{c}
// psc.Subscribe("example")
// for {
// switch v := psc.Receive().(type) {
// case redis.Message:
// fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
// case redis.Subscription:
// fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
// case error:
// return v
// }
// }
//
// Reply Helpers
//
// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
// to a value of a specific type. To allow convenient wrapping of calls to the
// connection Do and Receive methods, the functions take a second argument of
// type error. If the error is non-nil, then the helper function returns the
// error. If the error is nil, the function converts the reply to the specified
// type:
//
// exists, err := redis.Bool(c.Do("EXISTS", "foo"))
// if err != nil {
// // handle error return from c.Do or type conversion error.
// }
//
// The Scan function converts elements of a array reply to Go types:
//
// var value1 int
// var value2 string
// reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
// if err != nil {
// // handle error
// }
// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
// // handle error
// }
package redis // import "github.com/garyburd/redigo/redis"
================================================
FILE: vendor/github.com/garyburd/redigo/redis/log.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"bytes"
"fmt"
"log"
)
// NewLoggingConn returns a logging wrapper around a connection.
func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
if prefix != "" {
prefix = prefix + "."
}
return &loggingConn{conn, logger, prefix}
}
type loggingConn struct {
Conn
logger *log.Logger
prefix string
}
func (c *loggingConn) Close() error {
err := c.Conn.Close()
var buf bytes.Buffer
fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err)
c.logger.Output(2, buf.String())
return err
}
func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
const chop = 32
switch v := v.(type) {
case []byte:
if len(v) > chop {
fmt.Fprintf(buf, "%q...", v[:chop])
} else {
fmt.Fprintf(buf, "%q", v)
}
case string:
if len(v) > chop {
fmt.Fprintf(buf, "%q...", v[:chop])
} else {
fmt.Fprintf(buf, "%q", v)
}
case []interface{}:
if len(v) == 0 {
buf.WriteString("[]")
} else {
sep := "["
fin := "]"
if len(v) > chop {
v = v[:chop]
fin = "...]"
}
for _, vv := range v {
buf.WriteString(sep)
c.printValue(buf, vv)
sep = ", "
}
buf.WriteString(fin)
}
default:
fmt.Fprint(buf, v)
}
}
func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s%s(", c.prefix, method)
if method != "Receive" {
buf.WriteString(commandName)
for _, arg := range args {
buf.WriteString(", ")
c.printValue(&buf, arg)
}
}
buf.WriteString(") -> (")
if method != "Send" {
c.printValue(&buf, reply)
buf.WriteString(", ")
}
fmt.Fprintf(&buf, "%v)", err)
c.logger.Output(3, buf.String())
}
func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) {
reply, err := c.Conn.Do(commandName, args...)
c.print("Do", commandName, args, reply, err)
return reply, err
}
func (c *loggingConn) Send(commandName string, args ...interface{}) error {
err := c.Conn.Send(commandName, args...)
c.print("Send", commandName, args, nil, err)
return err
}
func (c *loggingConn) Receive() (interface{}, error) {
reply, err := c.Conn.Receive()
c.print("Receive", "", nil, reply, err)
return reply, err
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/pool.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"bytes"
"container/list"
"crypto/rand"
"crypto/sha1"
"errors"
"io"
"strconv"
"sync"
"time"
"github.com/garyburd/redigo/internal"
)
var nowFunc = time.Now // for testing
// ErrPoolExhausted is returned from a pool connection method (Do, Send,
// Receive, Flush, Err) when the maximum number of database connections in the
// pool has been reached.
var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")
var (
errPoolClosed = errors.New("redigo: connection pool closed")
errConnClosed = errors.New("redigo: connection closed")
)
// Pool maintains a pool of connections. The application calls the Get method
// to get a connection from the pool and the connection's Close method to
// return the connection's resources to the pool.
//
// The following example shows how to use a pool in a web application. The
// application creates a pool at application startup and makes it available to
// request handlers using a global variable. The pool configuration used here
// is an example, not a recommendation.
//
// func newPool(server, password string) *redis.Pool {
// return &redis.Pool{
// MaxIdle: 3,
// IdleTimeout: 240 * time.Second,
// Dial: func () (redis.Conn, error) {
// c, err := redis.Dial("tcp", server)
// if err != nil {
// return nil, err
// }
// if _, err := c.Do("AUTH", password); err != nil {
// c.Close()
// return nil, err
// }
// return c, err
// },
// TestOnBorrow: func(c redis.Conn, t time.Time) error {
// if time.Since(t) < time.Minute {
// return nil
// }
// _, err := c.Do("PING")
// return err
// },
// }
// }
//
// var (
// pool *redis.Pool
// redisServer = flag.String("redisServer", ":6379", "")
// redisPassword = flag.String("redisPassword", "", "")
// )
//
// func main() {
// flag.Parse()
// pool = newPool(*redisServer, *redisPassword)
// ...
// }
//
// A request handler gets a connection from the pool and closes the connection
// when the handler is done:
//
// func serveHome(w http.ResponseWriter, r *http.Request) {
// conn := pool.Get()
// defer conn.Close()
// ....
// }
//
type Pool struct {
// Dial is an application supplied function for creating and configuring a
// connection.
//
// The connection returned from Dial must not be in a special state
// (subscribed to pubsub channel, transaction started, ...).
Dial func() (Conn, error)
// TestOnBorrow is an optional application supplied function for checking
// the health of an idle connection before the connection is used again by
// the application. Argument t is the time that the connection was returned
// to the pool. If the function returns an error, then the connection is
// closed.
TestOnBorrow func(c Conn, t time.Time) error
// Maximum number of idle connections in the pool.
MaxIdle int
// Maximum number of connections allocated by the pool at a given time.
// When zero, there is no limit on the number of connections in the pool.
MaxActive int
// Close connections after remaining idle for this duration. If the value
// is zero, then idle connections are not closed. Applications should set
// the timeout to a value less than the server's timeout.
IdleTimeout time.Duration
// If Wait is true and the pool is at the MaxActive limit, then Get() waits
// for a connection to be returned to the pool before returning.
Wait bool
// mu protects fields defined below.
mu sync.Mutex
cond *sync.Cond
closed bool
active int
// Stack of idleConn with most recently used at the front.
idle list.List
}
type idleConn struct {
c Conn
t time.Time
}
// NewPool creates a new pool.
//
// Deprecated: Initialize the Pool directory as shown in the example.
func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
return &Pool{Dial: newFn, MaxIdle: maxIdle}
}
// Get gets a connection. The application must close the returned connection.
// This method always returns a valid connection so that applications can defer
// error handling to the first use of the connection. If there is an error
// getting an underlying connection, then the connection Err, Do, Send, Flush
// and Receive methods return that error.
func (p *Pool) Get() Conn {
c, err := p.get()
if err != nil {
return errorConnection{err}
}
return &pooledConnection{p: p, c: c}
}
// ActiveCount returns the number of active connections in the pool.
func (p *Pool) ActiveCount() int {
p.mu.Lock()
active := p.active
p.mu.Unlock()
return active
}
// Close releases the resources used by the pool.
func (p *Pool) Close() error {
p.mu.Lock()
idle := p.idle
p.idle.Init()
p.closed = true
p.active -= idle.Len()
if p.cond != nil {
p.cond.Broadcast()
}
p.mu.Unlock()
for e := idle.Front(); e != nil; e = e.Next() {
e.Value.(idleConn).c.Close()
}
return nil
}
// release decrements the active count and signals waiters. The caller must
// hold p.mu during the call.
func (p *Pool) release() {
p.active -= 1
if p.cond != nil {
p.cond.Signal()
}
}
// get prunes stale connections and returns a connection from the idle list or
// creates a new connection.
func (p *Pool) get() (Conn, error) {
p.mu.Lock()
// Prune stale connections.
if timeout := p.IdleTimeout; timeout > 0 {
for i, n := 0, p.idle.Len(); i < n; i++ {
e := p.idle.Back()
if e == nil {
break
}
ic := e.Value.(idleConn)
if ic.t.Add(timeout).After(nowFunc()) {
break
}
p.idle.Remove(e)
p.release()
p.mu.Unlock()
ic.c.Close()
p.mu.Lock()
}
}
for {
// Get idle connection.
for i, n := 0, p.idle.Len(); i < n; i++ {
e := p.idle.Front()
if e == nil {
break
}
ic := e.Value.(idleConn)
p.idle.Remove(e)
test := p.TestOnBorrow
p.mu.Unlock()
if test == nil || test(ic.c, ic.t) == nil {
return ic.c, nil
}
ic.c.Close()
p.mu.Lock()
p.release()
}
// Check for pool closed before dialing a new connection.
if p.closed {
p.mu.Unlock()
return nil, errors.New("redigo: get on closed pool")
}
// Dial new connection if under limit.
if p.MaxActive == 0 || p.active < p.MaxActive {
dial := p.Dial
p.active += 1
p.mu.Unlock()
c, err := dial()
if err != nil {
p.mu.Lock()
p.release()
p.mu.Unlock()
c = nil
}
return c, err
}
if !p.Wait {
p.mu.Unlock()
return nil, ErrPoolExhausted
}
if p.cond == nil {
p.cond = sync.NewCond(&p.mu)
}
p.cond.Wait()
}
}
func (p *Pool) put(c Conn, forceClose bool) error {
err := c.Err()
p.mu.Lock()
if !p.closed && err == nil && !forceClose {
p.idle.PushFront(idleConn{t: nowFunc(), c: c})
if p.idle.Len() > p.MaxIdle {
c = p.idle.Remove(p.idle.Back()).(idleConn).c
} else {
c = nil
}
}
if c == nil {
if p.cond != nil {
p.cond.Signal()
}
p.mu.Unlock()
return nil
}
p.release()
p.mu.Unlock()
return c.Close()
}
type pooledConnection struct {
p *Pool
c Conn
state int
}
var (
sentinel []byte
sentinelOnce sync.Once
)
func initSentinel() {
p := make([]byte, 64)
if _, err := rand.Read(p); err == nil {
sentinel = p
} else {
h := sha1.New()
io.WriteString(h, "Oops, rand failed. Use time instead.")
io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10))
sentinel = h.Sum(nil)
}
}
func (pc *pooledConnection) Close() error {
c := pc.c
if _, ok := c.(errorConnection); ok {
return nil
}
pc.c = errorConnection{errConnClosed}
if pc.state&internal.MultiState != 0 {
c.Send("DISCARD")
pc.state &^= (internal.MultiState | internal.WatchState)
} else if pc.state&internal.WatchState != 0 {
c.Send("UNWATCH")
pc.state &^= internal.WatchState
}
if pc.state&internal.SubscribeState != 0 {
c.Send("UNSUBSCRIBE")
c.Send("PUNSUBSCRIBE")
// To detect the end of the message stream, ask the server to echo
// a sentinel value and read until we see that value.
sentinelOnce.Do(initSentinel)
c.Send("ECHO", sentinel)
c.Flush()
for {
p, err := c.Receive()
if err != nil {
break
}
if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) {
pc.state &^= internal.SubscribeState
break
}
}
}
c.Do("")
pc.p.put(c, pc.state != 0)
return nil
}
func (pc *pooledConnection) Err() error {
return pc.c.Err()
}
func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
ci := internal.LookupCommandInfo(commandName)
pc.state = (pc.state | ci.Set) &^ ci.Clear
return pc.c.Do(commandName, args...)
}
func (pc *pooledConnection) Send(commandName string, args ...interface{}) error {
ci := internal.LookupCommandInfo(commandName)
pc.state = (pc.state | ci.Set) &^ ci.Clear
return pc.c.Send(commandName, args...)
}
func (pc *pooledConnection) Flush() error {
return pc.c.Flush()
}
func (pc *pooledConnection) Receive() (reply interface{}, err error) {
return pc.c.Receive()
}
type errorConnection struct{ err error }
func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
func (ec errorConnection) Send(string, ...interface{}) error { return ec.err }
func (ec errorConnection) Err() error { return ec.err }
func (ec errorConnection) Close() error { return ec.err }
func (ec errorConnection) Flush() error { return ec.err }
func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err }
================================================
FILE: vendor/github.com/garyburd/redigo/redis/pool_test.go
================================================
// Copyright 2011 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"errors"
"io"
"reflect"
"sync"
"testing"
"time"
"github.com/garyburd/redigo/redis"
)
type poolTestConn struct {
d *poolDialer
err error
redis.Conn
}
func (c *poolTestConn) Close() error {
c.d.mu.Lock()
c.d.open -= 1
c.d.mu.Unlock()
return c.Conn.Close()
}
func (c *poolTestConn) Err() error { return c.err }
func (c *poolTestConn) Do(commandName string, args ...interface{}) (interface{}, error) {
if commandName == "ERR" {
c.err = args[0].(error)
commandName = "PING"
}
if commandName != "" {
c.d.commands = append(c.d.commands, commandName)
}
return c.Conn.Do(commandName, args...)
}
func (c *poolTestConn) Send(commandName string, args ...interface{}) error {
c.d.commands = append(c.d.commands, commandName)
return c.Conn.Send(commandName, args...)
}
type poolDialer struct {
mu sync.Mutex
t *testing.T
dialed int
open int
commands []string
dialErr error
}
func (d *poolDialer) dial() (redis.Conn, error) {
d.mu.Lock()
d.dialed += 1
dialErr := d.dialErr
d.mu.Unlock()
if dialErr != nil {
return nil, d.dialErr
}
c, err := redis.DialDefaultServer()
if err != nil {
return nil, err
}
d.mu.Lock()
d.open += 1
d.mu.Unlock()
return &poolTestConn{d: d, Conn: c}, nil
}
func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) {
d.mu.Lock()
if d.dialed != dialed {
d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed)
}
if d.open != open {
d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
}
if active := p.ActiveCount(); active != open {
d.t.Errorf("%s: active=%d, want %d", message, active, open)
}
d.mu.Unlock()
}
func TestPoolReuse(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
Dial: d.dial,
}
for i := 0; i < 10; i++ {
c1 := p.Get()
c1.Do("PING")
c2 := p.Get()
c2.Do("PING")
c1.Close()
c2.Close()
}
d.check("before close", p, 2, 2)
p.Close()
d.check("after close", p, 2, 0)
}
func TestPoolMaxIdle(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
Dial: d.dial,
}
defer p.Close()
for i := 0; i < 10; i++ {
c1 := p.Get()
c1.Do("PING")
c2 := p.Get()
c2.Do("PING")
c3 := p.Get()
c3.Do("PING")
c1.Close()
c2.Close()
c3.Close()
}
d.check("before close", p, 12, 2)
p.Close()
d.check("after close", p, 12, 0)
}
func TestPoolError(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
Dial: d.dial,
}
defer p.Close()
c := p.Get()
c.Do("ERR", io.EOF)
if c.Err() == nil {
t.Errorf("expected c.Err() != nil")
}
c.Close()
c = p.Get()
c.Do("ERR", io.EOF)
c.Close()
d.check(".", p, 2, 0)
}
func TestPoolClose(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
Dial: d.dial,
}
defer p.Close()
c1 := p.Get()
c1.Do("PING")
c2 := p.Get()
c2.Do("PING")
c3 := p.Get()
c3.Do("PING")
c1.Close()
if _, err := c1.Do("PING"); err == nil {
t.Errorf("expected error after connection closed")
}
c2.Close()
c2.Close()
p.Close()
d.check("after pool close", p, 3, 1)
if _, err := c1.Do("PING"); err == nil {
t.Errorf("expected error after connection and pool closed")
}
c3.Close()
d.check("after conn close", p, 3, 0)
c1 = p.Get()
if _, err := c1.Do("PING"); err == nil {
t.Errorf("expected error after pool closed")
}
}
func TestPoolTimeout(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
IdleTimeout: 300 * time.Second,
Dial: d.dial,
}
defer p.Close()
now := time.Now()
redis.SetNowFunc(func() time.Time { return now })
defer redis.SetNowFunc(time.Now)
c := p.Get()
c.Do("PING")
c.Close()
d.check("1", p, 1, 1)
now = now.Add(p.IdleTimeout)
c = p.Get()
c.Do("PING")
c.Close()
d.check("2", p, 2, 1)
}
func TestPoolConcurrenSendReceive(t *testing.T) {
p := &redis.Pool{
Dial: redis.DialDefaultServer,
}
defer p.Close()
c := p.Get()
done := make(chan error, 1)
go func() {
_, err := c.Receive()
done <- err
}()
c.Send("PING")
c.Flush()
err := <-done
if err != nil {
t.Fatalf("Receive() returned error %v", err)
}
_, err = c.Do("")
if err != nil {
t.Fatalf("Do() returned error %v", err)
}
c.Close()
}
func TestPoolBorrowCheck(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
Dial: d.dial,
TestOnBorrow: func(redis.Conn, time.Time) error { return redis.Error("BLAH") },
}
defer p.Close()
for i := 0; i < 10; i++ {
c := p.Get()
c.Do("PING")
c.Close()
}
d.check("1", p, 10, 1)
}
func TestPoolMaxActive(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
MaxActive: 2,
Dial: d.dial,
}
defer p.Close()
c1 := p.Get()
c1.Do("PING")
c2 := p.Get()
c2.Do("PING")
d.check("1", p, 2, 2)
c3 := p.Get()
if _, err := c3.Do("PING"); err != redis.ErrPoolExhausted {
t.Errorf("expected pool exhausted")
}
c3.Close()
d.check("2", p, 2, 2)
c2.Close()
d.check("3", p, 2, 2)
c3 = p.Get()
if _, err := c3.Do("PING"); err != nil {
t.Errorf("expected good channel, err=%v", err)
}
c3.Close()
d.check("4", p, 2, 2)
}
func TestPoolMonitorCleanup(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
MaxActive: 2,
Dial: d.dial,
}
defer p.Close()
c := p.Get()
c.Send("MONITOR")
c.Close()
d.check("", p, 1, 0)
}
func TestPoolPubSubCleanup(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
MaxActive: 2,
Dial: d.dial,
}
defer p.Close()
c := p.Get()
c.Send("SUBSCRIBE", "x")
c.Close()
want := []string{"SUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
c = p.Get()
c.Send("PSUBSCRIBE", "x*")
c.Close()
want = []string{"PSUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
}
func TestPoolTransactionCleanup(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 2,
MaxActive: 2,
Dial: d.dial,
}
defer p.Close()
c := p.Get()
c.Do("WATCH", "key")
c.Do("PING")
c.Close()
want := []string{"WATCH", "PING", "UNWATCH"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
c = p.Get()
c.Do("WATCH", "key")
c.Do("UNWATCH")
c.Do("PING")
c.Close()
want = []string{"WATCH", "UNWATCH", "PING"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
c = p.Get()
c.Do("WATCH", "key")
c.Do("MULTI")
c.Do("PING")
c.Close()
want = []string{"WATCH", "MULTI", "PING", "DISCARD"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
c = p.Get()
c.Do("WATCH", "key")
c.Do("MULTI")
c.Do("DISCARD")
c.Do("PING")
c.Close()
want = []string{"WATCH", "MULTI", "DISCARD", "PING"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
c = p.Get()
c.Do("WATCH", "key")
c.Do("MULTI")
c.Do("EXEC")
c.Do("PING")
c.Close()
want = []string{"WATCH", "MULTI", "EXEC", "PING"}
if !reflect.DeepEqual(d.commands, want) {
t.Errorf("got commands %v, want %v", d.commands, want)
}
d.commands = nil
}
func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error {
errs := make(chan error, 10)
for i := 0; i < cap(errs); i++ {
go func() {
c := p.Get()
_, err := c.Do(cmd, args...)
errs <- err
c.Close()
}()
}
// Wait for goroutines to block.
time.Sleep(time.Second / 4)
return errs
}
func TestWaitPool(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 1,
MaxActive: 1,
Dial: d.dial,
Wait: true,
}
defer p.Close()
c := p.Get()
errs := startGoroutines(p, "PING")
d.check("before close", p, 1, 1)
c.Close()
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
select {
case err := <-errs:
if err != nil {
t.Fatal(err)
}
case <-timeout:
t.Fatalf("timeout waiting for blocked goroutine %d", i)
}
}
d.check("done", p, 1, 1)
}
func TestWaitPoolClose(t *testing.T) {
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 1,
MaxActive: 1,
Dial: d.dial,
Wait: true,
}
defer p.Close()
c := p.Get()
if _, err := c.Do("PING"); err != nil {
t.Fatal(err)
}
errs := startGoroutines(p, "PING")
d.check("before close", p, 1, 1)
p.Close()
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
select {
case err := <-errs:
switch err {
case nil:
t.Fatal("blocked goroutine did not get error")
case redis.ErrPoolExhausted:
t.Fatal("blocked goroutine got pool exhausted error")
}
case <-timeout:
t.Fatal("timeout waiting for blocked goroutine")
}
}
c.Close()
d.check("done", p, 1, 0)
}
func TestWaitPoolCommandError(t *testing.T) {
testErr := errors.New("test")
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 1,
MaxActive: 1,
Dial: d.dial,
Wait: true,
}
defer p.Close()
c := p.Get()
errs := startGoroutines(p, "ERR", testErr)
d.check("before close", p, 1, 1)
c.Close()
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
select {
case err := <-errs:
if err != nil {
t.Fatal(err)
}
case <-timeout:
t.Fatalf("timeout waiting for blocked goroutine %d", i)
}
}
d.check("done", p, cap(errs), 0)
}
func TestWaitPoolDialError(t *testing.T) {
testErr := errors.New("test")
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: 1,
MaxActive: 1,
Dial: d.dial,
Wait: true,
}
defer p.Close()
c := p.Get()
errs := startGoroutines(p, "ERR", testErr)
d.check("before close", p, 1, 1)
d.dialErr = errors.New("dial")
c.Close()
nilCount := 0
errCount := 0
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
select {
case err := <-errs:
switch err {
case nil:
nilCount++
case d.dialErr:
errCount++
default:
t.Fatalf("expected dial error or nil, got %v", err)
}
case <-timeout:
t.Fatalf("timeout waiting for blocked goroutine %d", i)
}
}
if nilCount != 1 {
t.Errorf("expected one nil error, got %d", nilCount)
}
if errCount != cap(errs)-1 {
t.Errorf("expected %d dial errors, got %d", cap(errs)-1, errCount)
}
d.check("done", p, cap(errs), 0)
}
// Borrowing requires us to iterate over the idle connections, unlock the pool,
// and perform a blocking operation to check the connection still works. If
// TestOnBorrow fails, we must reacquire the lock and continue iteration. This
// test ensures that iteration will work correctly if multiple threads are
// iterating simultaneously.
func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
const count = 100
// First we'll Create a pool where the pilfering of idle connections fails.
d := poolDialer{t: t}
p := &redis.Pool{
MaxIdle: count,
MaxActive: count,
Dial: d.dial,
TestOnBorrow: func(c redis.Conn, t time.Time) error {
return errors.New("No way back into the real world.")
},
}
defer p.Close()
// Fill the pool with idle connections.
conns := make([]redis.Conn, count)
for i := range conns {
conns[i] = p.Get()
}
for i := range conns {
conns[i].Close()
}
// Spawn a bunch of goroutines to thrash the pool.
var wg sync.WaitGroup
wg.Add(count)
for i := 0; i < count; i++ {
go func() {
c := p.Get()
if c.Err() != nil {
t.Errorf("pool get failed: %v", c.Err())
}
c.Close()
wg.Done()
}()
}
wg.Wait()
if d.dialed != count*2 {
t.Errorf("Expected %d dials, got %d", count*2, d.dialed)
}
}
func BenchmarkPoolGet(b *testing.B) {
b.StopTimer()
p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
c := p.Get()
if err := c.Err(); err != nil {
b.Fatal(err)
}
c.Close()
defer p.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
c = p.Get()
c.Close()
}
}
func BenchmarkPoolGetErr(b *testing.B) {
b.StopTimer()
p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
c := p.Get()
if err := c.Err(); err != nil {
b.Fatal(err)
}
c.Close()
defer p.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
c = p.Get()
if err := c.Err(); err != nil {
b.Fatal(err)
}
c.Close()
}
}
func BenchmarkPoolGetPing(b *testing.B) {
b.StopTimer()
p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
c := p.Get()
if err := c.Err(); err != nil {
b.Fatal(err)
}
c.Close()
defer p.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
c = p.Get()
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
c.Close()
}
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/pubsub.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import "errors"
// Subscription represents a subscribe or unsubscribe notification.
type Subscription struct {
// Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
Kind string
// The channel that was changed.
Channel string
// The current number of subscriptions for connection.
Count int
}
// Message represents a message notification.
type Message struct {
// The originating channel.
Channel string
// The message data.
Data []byte
}
// PMessage represents a pmessage notification.
type PMessage struct {
// The matched pattern.
Pattern string
// The originating channel.
Channel string
// The message data.
Data []byte
}
// Pong represents a pubsub pong notification.
type Pong struct {
Data string
}
// PubSubConn wraps a Conn with convenience methods for subscribers.
type PubSubConn struct {
Conn Conn
}
// Close closes the connection.
func (c PubSubConn) Close() error {
return c.Conn.Close()
}
// Subscribe subscribes the connection to the specified channels.
func (c PubSubConn) Subscribe(channel ...interface{}) error {
c.Conn.Send("SUBSCRIBE", channel...)
return c.Conn.Flush()
}
// PSubscribe subscribes the connection to the given patterns.
func (c PubSubConn) PSubscribe(channel ...interface{}) error {
c.Conn.Send("PSUBSCRIBE", channel...)
return c.Conn.Flush()
}
// Unsubscribe unsubscribes the connection from the given channels, or from all
// of them if none is given.
func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
c.Conn.Send("UNSUBSCRIBE", channel...)
return c.Conn.Flush()
}
// PUnsubscribe unsubscribes the connection from the given patterns, or from all
// of them if none is given.
func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
c.Conn.Send("PUNSUBSCRIBE", channel...)
return c.Conn.Flush()
}
// Ping sends a PING to the server with the specified data.
func (c PubSubConn) Ping(data string) error {
c.Conn.Send("PING", data)
return c.Conn.Flush()
}
// Receive returns a pushed message as a Subscription, Message, PMessage, Pong
// or error. The return value is intended to be used directly in a type switch
// as illustrated in the PubSubConn example.
func (c PubSubConn) Receive() interface{} {
reply, err := Values(c.Conn.Receive())
if err != nil {
return err
}
var kind string
reply, err = Scan(reply, &kind)
if err != nil {
return err
}
switch kind {
case "message":
var m Message
if _, err := Scan(reply, &m.Channel, &m.Data); err != nil {
return err
}
return m
case "pmessage":
var pm PMessage
if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil {
return err
}
return pm
case "subscribe", "psubscribe", "unsubscribe", "punsubscribe":
s := Subscription{Kind: kind}
if _, err := Scan(reply, &s.Channel, &s.Count); err != nil {
return err
}
return s
case "pong":
var p Pong
if _, err := Scan(reply, &p.Data); err != nil {
return err
}
return p
}
return errors.New("redigo: unknown pubsub notification")
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/pubsub_test.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"fmt"
"reflect"
"sync"
"testing"
"github.com/garyburd/redigo/redis"
)
func publish(channel, value interface{}) {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Do("PUBLISH", channel, value)
}
// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine.
func ExamplePubSubConn() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
var wg sync.WaitGroup
wg.Add(2)
psc := redis.PubSubConn{Conn: c}
// This goroutine receives and prints pushed notifications from the server.
// The goroutine exits when the connection is unsubscribed from all
// channels or there is an error.
go func() {
defer wg.Done()
for {
switch n := psc.Receive().(type) {
case redis.Message:
fmt.Printf("Message: %s %s\n", n.Channel, n.Data)
case redis.PMessage:
fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data)
case redis.Subscription:
fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count)
if n.Count == 0 {
return
}
case error:
fmt.Printf("error: %v\n", n)
return
}
}
}()
// This goroutine manages subscriptions for the connection.
go func() {
defer wg.Done()
psc.Subscribe("example")
psc.PSubscribe("p*")
// The following function calls publish a message using another
// connection to the Redis server.
publish("example", "hello")
publish("example", "world")
publish("pexample", "foo")
publish("pexample", "bar")
// Unsubscribe from all connections. This will cause the receiving
// goroutine to exit.
psc.Unsubscribe()
psc.PUnsubscribe()
}()
wg.Wait()
// Output:
// Subscription: subscribe example 1
// Subscription: psubscribe p* 2
// Message: example hello
// Message: example world
// PMessage: p* pexample foo
// PMessage: p* pexample bar
// Subscription: unsubscribe example 1
// Subscription: punsubscribe p* 0
}
func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) {
actual := c.Receive()
if !reflect.DeepEqual(actual, expected) {
t.Errorf("%s = %v, want %v", message, actual, expected)
}
}
func TestPushed(t *testing.T) {
pc, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer pc.Close()
sc, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer sc.Close()
c := redis.PubSubConn{Conn: sc}
c.Subscribe("c1")
expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1})
c.Subscribe("c2")
expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2})
c.PSubscribe("p1")
expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3})
c.PSubscribe("p2")
expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4})
c.PUnsubscribe()
expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3})
expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2})
pc.Do("PUBLISH", "c1", "hello")
expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")})
c.Ping("hello")
expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"})
c.Conn.Send("PING")
c.Conn.Flush()
expectPushed(t, c, `Send("PING")`, redis.Pong{})
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/redis.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
// Error represents an error returned in a command reply.
type Error string
func (err Error) Error() string { return string(err) }
// Conn represents a connection to a Redis server.
type Conn interface {
// Close closes the connection.
Close() error
// Err returns a non-nil value if the connection is broken. The returned
// value is either the first non-nil value returned from the underlying
// network connection or a protocol parsing error. Applications should
// close broken connections.
Err() error
// Do sends a command to the server and returns the received reply.
Do(commandName string, args ...interface{}) (reply interface{}, err error)
// Send writes the command to the client's output buffer.
Send(commandName string, args ...interface{}) error
// Flush flushes the output buffer to the Redis server.
Flush() error
// Receive receives a single reply from the Redis server
Receive() (reply interface{}, err error)
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/reply.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"errors"
"fmt"
"strconv"
)
// ErrNil indicates that a reply value is nil.
var ErrNil = errors.New("redigo: nil returned")
// Int is a helper that converts a command reply to an integer. If err is not
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
// reply to an int as follows:
//
// Reply type Result
// integer int(reply), nil
// bulk string parsed reply, nil
// nil 0, ErrNil
// other 0, error
func Int(reply interface{}, err error) (int, error) {
if err != nil {
return 0, err
}
switch reply := reply.(type) {
case int64:
x := int(reply)
if int64(x) != reply {
return 0, strconv.ErrRange
}
return x, nil
case []byte:
n, err := strconv.ParseInt(string(reply), 10, 0)
return int(n), err
case nil:
return 0, ErrNil
case Error:
return 0, reply
}
return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply)
}
// Int64 is a helper that converts a command reply to 64 bit integer. If err is
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
// reply to an int64 as follows:
//
// Reply type Result
// integer reply, nil
// bulk string parsed reply, nil
// nil 0, ErrNil
// other 0, error
func Int64(reply interface{}, err error) (int64, error) {
if err != nil {
return 0, err
}
switch reply := reply.(type) {
case int64:
return reply, nil
case []byte:
n, err := strconv.ParseInt(string(reply), 10, 64)
return n, err
case nil:
return 0, ErrNil
case Error:
return 0, reply
}
return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
}
var errNegativeInt = errors.New("redigo: unexpected value for Uint64")
// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
// reply to an int64 as follows:
//
// Reply type Result
// integer reply, nil
// bulk string parsed reply, nil
// nil 0, ErrNil
// other 0, error
func Uint64(reply interface{}, err error) (uint64, error) {
if err != nil {
return 0, err
}
switch reply := reply.(type) {
case int64:
if reply < 0 {
return 0, errNegativeInt
}
return uint64(reply), nil
case []byte:
n, err := strconv.ParseUint(string(reply), 10, 64)
return n, err
case nil:
return 0, ErrNil
case Error:
return 0, reply
}
return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply)
}
// Float64 is a helper that converts a command reply to 64 bit float. If err is
// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
// the reply to an int as follows:
//
// Reply type Result
// bulk string parsed reply, nil
// nil 0, ErrNil
// other 0, error
func Float64(reply interface{}, err error) (float64, error) {
if err != nil {
return 0, err
}
switch reply := reply.(type) {
case []byte:
n, err := strconv.ParseFloat(string(reply), 64)
return n, err
case nil:
return 0, ErrNil
case Error:
return 0, reply
}
return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
}
// String is a helper that converts a command reply to a string. If err is not
// equal to nil, then String returns "", err. Otherwise String converts the
// reply to a string as follows:
//
// Reply type Result
// bulk string string(reply), nil
// simple string reply, nil
// nil "", ErrNil
// other "", error
func String(reply interface{}, err error) (string, error) {
if err != nil {
return "", err
}
switch reply := reply.(type) {
case []byte:
return string(reply), nil
case string:
return reply, nil
case nil:
return "", ErrNil
case Error:
return "", reply
}
return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
}
// Bytes is a helper that converts a command reply to a slice of bytes. If err
// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
// the reply to a slice of bytes as follows:
//
// Reply type Result
// bulk string reply, nil
// simple string []byte(reply), nil
// nil nil, ErrNil
// other nil, error
func Bytes(reply interface{}, err error) ([]byte, error) {
if err != nil {
return nil, err
}
switch reply := reply.(type) {
case []byte:
return reply, nil
case string:
return []byte(reply), nil
case nil:
return nil, ErrNil
case Error:
return nil, reply
}
return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply)
}
// Bool is a helper that converts a command reply to a boolean. If err is not
// equal to nil, then Bool returns false, err. Otherwise Bool converts the
// reply to boolean as follows:
//
// Reply type Result
// integer value != 0, nil
// bulk string strconv.ParseBool(reply)
// nil false, ErrNil
// other false, error
func Bool(reply interface{}, err error) (bool, error) {
if err != nil {
return false, err
}
switch reply := reply.(type) {
case int64:
return reply != 0, nil
case []byte:
return strconv.ParseBool(string(reply))
case nil:
return false, ErrNil
case Error:
return false, reply
}
return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply)
}
// MultiBulk is a helper that converts an array command reply to a []interface{}.
//
// Deprecated: Use Values instead.
func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) }
// Values is a helper that converts an array command reply to a []interface{}.
// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
// converts the reply as follows:
//
// Reply type Result
// array reply, nil
// nil nil, ErrNil
// other nil, error
func Values(reply interface{}, err error) ([]interface{}, error) {
if err != nil {
return nil, err
}
switch reply := reply.(type) {
case []interface{}:
return reply, nil
case nil:
return nil, ErrNil
case Error:
return nil, reply
}
return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
}
// Strings is a helper that converts an array command reply to a []string. If
// err is not equal to nil, then Strings returns nil, err. Nil array items are
// converted to "" in the output slice. Strings returns an error if an array
// item is not a bulk string or nil.
func Strings(reply interface{}, err error) ([]string, error) {
if err != nil {
return nil, err
}
switch reply := reply.(type) {
case []interface{}:
result := make([]string, len(reply))
for i := range reply {
if reply[i] == nil {
continue
}
p, ok := reply[i].([]byte)
if !ok {
return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i])
}
result[i] = string(p)
}
return result, nil
case nil:
return nil, ErrNil
case Error:
return nil, reply
}
return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply)
}
// ByteSlices is a helper that converts an array command reply to a [][]byte.
// If err is not equal to nil, then ByteSlices returns nil, err. Nil array
// items are stay nil. ByteSlices returns an error if an array item is not a
// bulk string or nil.
func ByteSlices(reply interface{}, err error) ([][]byte, error) {
if err != nil {
return nil, err
}
switch reply := reply.(type) {
case []interface{}:
result := make([][]byte, len(reply))
for i := range reply {
if reply[i] == nil {
continue
}
p, ok := reply[i].([]byte)
if !ok {
return nil, fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", reply[i])
}
result[i] = p
}
return result, nil
case nil:
return nil, ErrNil
case Error:
return nil, reply
}
return nil, fmt.Errorf("redigo: unexpected type for ByteSlices, got type %T", reply)
}
// Ints is a helper that converts an array command reply to a []int. If
// err is not equal to nil, then Ints returns nil, err.
func Ints(reply interface{}, err error) ([]int, error) {
var ints []int
values, err := Values(reply, err)
if err != nil {
return ints, err
}
if err := ScanSlice(values, &ints); err != nil {
return ints, err
}
return ints, nil
}
// StringMap is a helper that converts an array of strings (alternating key, value)
// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
// Requires an even number of values in result.
func StringMap(result interface{}, err error) (map[string]string, error) {
values, err := Values(result, err)
if err != nil {
return nil, err
}
if len(values)%2 != 0 {
return nil, errors.New("redigo: StringMap expects even number of values result")
}
m := make(map[string]string, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, okKey := values[i].([]byte)
value, okValue := values[i+1].([]byte)
if !okKey || !okValue {
return nil, errors.New("redigo: ScanMap key not a bulk string value")
}
m[string(key)] = string(value)
}
return m, nil
}
// IntMap is a helper that converts an array of strings (alternating key, value)
// into a map[string]int. The HGETALL commands return replies in this format.
// Requires an even number of values in result.
func IntMap(result interface{}, err error) (map[string]int, error) {
values, err := Values(result, err)
if err != nil {
return nil, err
}
if len(values)%2 != 0 {
return nil, errors.New("redigo: IntMap expects even number of values result")
}
m := make(map[string]int, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].([]byte)
if !ok {
return nil, errors.New("redigo: ScanMap key not a bulk string value")
}
value, err := Int(values[i+1], nil)
if err != nil {
return nil, err
}
m[string(key)] = value
}
return m, nil
}
// Int64Map is a helper that converts an array of strings (alternating key, value)
// into a map[string]int64. The HGETALL commands return replies in this format.
// Requires an even number of values in result.
func Int64Map(result interface{}, err error) (map[string]int64, error) {
values, err := Values(result, err)
if err != nil {
return nil, err
}
if len(values)%2 != 0 {
return nil, errors.New("redigo: Int64Map expects even number of values result")
}
m := make(map[string]int64, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].([]byte)
if !ok {
return nil, errors.New("redigo: ScanMap key not a bulk string value")
}
value, err := Int64(values[i+1], nil)
if err != nil {
return nil, err
}
m[string(key)] = value
}
return m, nil
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/reply_test.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"fmt"
"reflect"
"testing"
"github.com/garyburd/redigo/redis"
)
type valueError struct {
v interface{}
err error
}
func ve(v interface{}, err error) valueError {
return valueError{v, err}
}
var replyTests = []struct {
name interface{}
actual valueError
expected valueError
}{
{
"ints([v1, v2])",
ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)),
ve([]int{4, 5}, nil),
},
{
"ints(nil)",
ve(redis.Ints(nil, nil)),
ve([]int(nil), redis.ErrNil),
},
{
"strings([v1, v2])",
ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
ve([]string{"v1", "v2"}, nil),
},
{
"strings(nil)",
ve(redis.Strings(nil, nil)),
ve([]string(nil), redis.ErrNil),
},
{
"byteslices([v1, v2])",
ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
ve([][]byte{[]byte("v1"), []byte("v2")}, nil),
},
{
"byteslices(nil)",
ve(redis.ByteSlices(nil, nil)),
ve([][]byte(nil), redis.ErrNil),
},
{
"values([v1, v2])",
ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
ve([]interface{}{[]byte("v1"), []byte("v2")}, nil),
},
{
"values(nil)",
ve(redis.Values(nil, nil)),
ve([]interface{}(nil), redis.ErrNil),
},
{
"float64(1.0)",
ve(redis.Float64([]byte("1.0"), nil)),
ve(float64(1.0), nil),
},
{
"float64(nil)",
ve(redis.Float64(nil, nil)),
ve(float64(0.0), redis.ErrNil),
},
{
"uint64(1)",
ve(redis.Uint64(int64(1), nil)),
ve(uint64(1), nil),
},
{
"uint64(-1)",
ve(redis.Uint64(int64(-1), nil)),
ve(uint64(0), redis.ErrNegativeInt),
},
}
func TestReply(t *testing.T) {
for _, rt := range replyTests {
if rt.actual.err != rt.expected.err {
t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err)
continue
}
if !reflect.DeepEqual(rt.actual.v, rt.expected.v) {
t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v)
}
}
}
// dial wraps DialDefaultServer() with a more suitable function name for examples.
func dial() (redis.Conn, error) {
return redis.DialDefaultServer()
}
func ExampleBool() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Do("SET", "foo", 1)
exists, _ := redis.Bool(c.Do("EXISTS", "foo"))
fmt.Printf("%#v\n", exists)
// Output:
// true
}
func ExampleInt() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Do("SET", "k1", 1)
n, _ := redis.Int(c.Do("GET", "k1"))
fmt.Printf("%#v\n", n)
n, _ = redis.Int(c.Do("INCR", "k1"))
fmt.Printf("%#v\n", n)
// Output:
// 1
// 2
}
func ExampleInts() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Do("SADD", "set_with_integers", 4, 5, 6)
ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers"))
fmt.Printf("%#v\n", ints)
// Output:
// []int{4, 5, 6}
}
func ExampleString() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Do("SET", "hello", "world")
s, err := redis.String(c.Do("GET", "hello"))
fmt.Printf("%#v\n", s)
// Output:
// "world"
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/scan.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
)
func ensureLen(d reflect.Value, n int) {
if n > d.Cap() {
d.Set(reflect.MakeSlice(d.Type(), n, n))
} else {
d.SetLen(n)
}
}
func cannotConvert(d reflect.Value, s interface{}) error {
var sname string
switch s.(type) {
case string:
sname = "Redis simple string"
case Error:
sname = "Redis error"
case int64:
sname = "Redis integer"
case []byte:
sname = "Redis bulk string"
case []interface{}:
sname = "Redis array"
default:
sname = reflect.TypeOf(s).String()
}
return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
}
func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
switch d.Type().Kind() {
case reflect.Float32, reflect.Float64:
var x float64
x, err = strconv.ParseFloat(string(s), d.Type().Bits())
d.SetFloat(x)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var x int64
x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
d.SetInt(x)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
var x uint64
x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
d.SetUint(x)
case reflect.Bool:
var x bool
x, err = strconv.ParseBool(string(s))
d.SetBool(x)
case reflect.String:
d.SetString(string(s))
case reflect.Slice:
if d.Type().Elem().Kind() != reflect.Uint8 {
err = cannotConvert(d, s)
} else {
d.SetBytes(s)
}
default:
err = cannotConvert(d, s)
}
return
}
func convertAssignInt(d reflect.Value, s int64) (err error) {
switch d.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
d.SetInt(s)
if d.Int() != s {
err = strconv.ErrRange
d.SetInt(0)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if s < 0 {
err = strconv.ErrRange
} else {
x := uint64(s)
d.SetUint(x)
if d.Uint() != x {
err = strconv.ErrRange
d.SetUint(0)
}
}
case reflect.Bool:
d.SetBool(s != 0)
default:
err = cannotConvert(d, s)
}
return
}
func convertAssignValue(d reflect.Value, s interface{}) (err error) {
switch s := s.(type) {
case []byte:
err = convertAssignBulkString(d, s)
case int64:
err = convertAssignInt(d, s)
default:
err = cannotConvert(d, s)
}
return err
}
func convertAssignArray(d reflect.Value, s []interface{}) error {
if d.Type().Kind() != reflect.Slice {
return cannotConvert(d, s)
}
ensureLen(d, len(s))
for i := 0; i < len(s); i++ {
if err := convertAssignValue(d.Index(i), s[i]); err != nil {
return err
}
}
return nil
}
func convertAssign(d interface{}, s interface{}) (err error) {
// Handle the most common destination types using type switches and
// fall back to reflection for all other types.
switch s := s.(type) {
case nil:
// ingore
case []byte:
switch d := d.(type) {
case *string:
*d = string(s)
case *int:
*d, err = strconv.Atoi(string(s))
case *bool:
*d, err = strconv.ParseBool(string(s))
case *[]byte:
*d = s
case *interface{}:
*d = s
case nil:
// skip value
default:
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
err = cannotConvert(d, s)
} else {
err = convertAssignBulkString(d.Elem(), s)
}
}
case int64:
switch d := d.(type) {
case *int:
x := int(s)
if int64(x) != s {
err = strconv.ErrRange
x = 0
}
*d = x
case *bool:
*d = s != 0
case *interface{}:
*d = s
case nil:
// skip value
default:
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
err = cannotConvert(d, s)
} else {
err = convertAssignInt(d.Elem(), s)
}
}
case string:
switch d := d.(type) {
case *string:
*d = string(s)
default:
err = cannotConvert(reflect.ValueOf(d), s)
}
case []interface{}:
switch d := d.(type) {
case *[]interface{}:
*d = s
case *interface{}:
*d = s
case nil:
// skip value
default:
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
err = cannotConvert(d, s)
} else {
err = convertAssignArray(d.Elem(), s)
}
}
case Error:
err = s
default:
err = cannotConvert(reflect.ValueOf(d), s)
}
return
}
// Scan copies from src to the values pointed at by dest.
//
// The values pointed at by dest must be an integer, float, boolean, string,
// []byte, interface{} or slices of these types. Scan uses the standard strconv
// package to convert bulk strings to numeric and boolean types.
//
// If a dest value is nil, then the corresponding src value is skipped.
//
// If a src element is nil, then the corresponding dest value is not modified.
//
// To enable easy use of Scan in a loop, Scan returns the slice of src
// following the copied values.
func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
if len(src) < len(dest) {
return nil, errors.New("redigo.Scan: array short")
}
var err error
for i, d := range dest {
err = convertAssign(d, src[i])
if err != nil {
err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
break
}
}
return src[len(dest):], err
}
type fieldSpec struct {
name string
index []int
omitEmpty bool
}
type structSpec struct {
m map[string]*fieldSpec
l []*fieldSpec
}
func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
return ss.m[string(name)]
}
func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch {
case f.PkgPath != "" && !f.Anonymous:
// Ignore unexported fields.
case f.Anonymous:
// TODO: Handle pointers. Requires change to decoder and
// protection against infinite recursion.
if f.Type.Kind() == reflect.Struct {
compileStructSpec(f.Type, depth, append(index, i), ss)
}
default:
fs := &fieldSpec{name: f.Name}
tag := f.Tag.Get("redis")
p := strings.Split(tag, ",")
if len(p) > 0 {
if p[0] == "-" {
continue
}
if len(p[0]) > 0 {
fs.name = p[0]
}
for _, s := range p[1:] {
switch s {
case "omitempty":
fs.omitEmpty = true
default:
panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
}
}
}
d, found := depth[fs.name]
if !found {
d = 1 << 30
}
switch {
case len(index) == d:
// At same depth, remove from result.
delete(ss.m, fs.name)
j := 0
for i := 0; i < len(ss.l); i++ {
if fs.name != ss.l[i].name {
ss.l[j] = ss.l[i]
j += 1
}
}
ss.l = ss.l[:j]
case len(index) < d:
fs.index = make([]int, len(index)+1)
copy(fs.index, index)
fs.index[len(index)] = i
depth[fs.name] = len(index)
ss.m[fs.name] = fs
ss.l = append(ss.l, fs)
}
}
}
}
var (
structSpecMutex sync.RWMutex
structSpecCache = make(map[reflect.Type]*structSpec)
defaultFieldSpec = &fieldSpec{}
)
func structSpecForType(t reflect.Type) *structSpec {
structSpecMutex.RLock()
ss, found := structSpecCache[t]
structSpecMutex.RUnlock()
if found {
return ss
}
structSpecMutex.Lock()
defer structSpecMutex.Unlock()
ss, found = structSpecCache[t]
if found {
return ss
}
ss = &structSpec{m: make(map[string]*fieldSpec)}
compileStructSpec(t, make(map[string]int), nil, ss)
structSpecCache[t] = ss
return ss
}
var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
// ScanStruct scans alternating names and values from src to a struct. The
// HGETALL and CONFIG GET commands return replies in this format.
//
// ScanStruct uses exported field names to match values in the response. Use
// 'redis' field tag to override the name:
//
// Field int `redis:"myName"`
//
// Fields with the tag redis:"-" are ignored.
//
// Integer, float, boolean, string and []byte fields are supported. Scan uses the
// standard strconv package to convert bulk string values to numeric and
// boolean types.
//
// If a src element is nil, then the corresponding field is not modified.
func ScanStruct(src []interface{}, dest interface{}) error {
d := reflect.ValueOf(dest)
if d.Kind() != reflect.Ptr || d.IsNil() {
return errScanStructValue
}
d = d.Elem()
if d.Kind() != reflect.Struct {
return errScanStructValue
}
ss := structSpecForType(d.Type())
if len(src)%2 != 0 {
return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
}
for i := 0; i < len(src); i += 2 {
s := src[i+1]
if s == nil {
continue
}
name, ok := src[i].([]byte)
if !ok {
return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
}
fs := ss.fieldSpec(name)
if fs == nil {
continue
}
if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
}
}
return nil
}
var (
errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
)
// ScanSlice scans src to the slice pointed to by dest. The elements the dest
// slice must be integer, float, boolean, string, struct or pointer to struct
// values.
//
// Struct fields must be integer, float, boolean or string values. All struct
// fields are used unless a subset is specified using fieldNames.
func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
d := reflect.ValueOf(dest)
if d.Kind() != reflect.Ptr || d.IsNil() {
return errScanSliceValue
}
d = d.Elem()
if d.Kind() != reflect.Slice {
return errScanSliceValue
}
isPtr := false
t := d.Type().Elem()
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
isPtr = true
t = t.Elem()
}
if t.Kind() != reflect.Struct {
ensureLen(d, len(src))
for i, s := range src {
if s == nil {
continue
}
if err := convertAssignValue(d.Index(i), s); err != nil {
return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
}
}
return nil
}
ss := structSpecForType(t)
fss := ss.l
if len(fieldNames) > 0 {
fss = make([]*fieldSpec, len(fieldNames))
for i, name := range fieldNames {
fss[i] = ss.m[name]
if fss[i] == nil {
return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
}
}
}
if len(fss) == 0 {
return errors.New("redigo.ScanSlice: no struct fields")
}
n := len(src) / len(fss)
if n*len(fss) != len(src) {
return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
}
ensureLen(d, n)
for i := 0; i < n; i++ {
d := d.Index(i)
if isPtr {
if d.IsNil() {
d.Set(reflect.New(t))
}
d = d.Elem()
}
for j, fs := range fss {
s := src[i*len(fss)+j]
if s == nil {
continue
}
if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
}
}
}
return nil
}
// Args is a helper for constructing command arguments from structured values.
type Args []interface{}
// Add returns the result of appending value to args.
func (args Args) Add(value ...interface{}) Args {
return append(args, value...)
}
// AddFlat returns the result of appending the flattened value of v to args.
//
// Maps are flattened by appending the alternating keys and map values to args.
//
// Slices are flattened by appending the slice elements to args.
//
// Structs are flattened by appending the alternating names and values of
// exported fields to args. If v is a nil struct pointer, then nothing is
// appended. The 'redis' field tag overrides struct field names. See ScanStruct
// for more information on the use of the 'redis' field tag.
//
// Other types are appended to args as is.
func (args Args) AddFlat(v interface{}) Args {
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Struct:
args = flattenStruct(args, rv)
case reflect.Slice:
for i := 0; i < rv.Len(); i++ {
args = append(args, rv.Index(i).Interface())
}
case reflect.Map:
for _, k := range rv.MapKeys() {
args = append(args, k.Interface(), rv.MapIndex(k).Interface())
}
case reflect.Ptr:
if rv.Type().Elem().Kind() == reflect.Struct {
if !rv.IsNil() {
args = flattenStruct(args, rv.Elem())
}
} else {
args = append(args, v)
}
default:
args = append(args, v)
}
return args
}
func flattenStruct(args Args, v reflect.Value) Args {
ss := structSpecForType(v.Type())
for _, fs := range ss.l {
fv := v.FieldByIndex(fs.index)
if fs.omitEmpty {
var empty = false
switch fv.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
empty = fv.Len() == 0
case reflect.Bool:
empty = !fv.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
empty = fv.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
empty = fv.Uint() == 0
case reflect.Float32, reflect.Float64:
empty = fv.Float() == 0
case reflect.Interface, reflect.Ptr:
empty = fv.IsNil()
}
if empty {
continue
}
}
args = append(args, fs.name, fv.Interface())
}
return args
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/scan_test.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"fmt"
"math"
"reflect"
"testing"
"github.com/garyburd/redigo/redis"
)
var scanConversionTests = []struct {
src interface{}
dest interface{}
}{
{[]byte("-inf"), math.Inf(-1)},
{[]byte("+inf"), math.Inf(1)},
{[]byte("0"), float64(0)},
{[]byte("3.14159"), float64(3.14159)},
{[]byte("3.14"), float32(3.14)},
{[]byte("-100"), int(-100)},
{[]byte("101"), int(101)},
{int64(102), int(102)},
{[]byte("103"), uint(103)},
{int64(104), uint(104)},
{[]byte("105"), int8(105)},
{int64(106), int8(106)},
{[]byte("107"), uint8(107)},
{int64(108), uint8(108)},
{[]byte("0"), false},
{int64(0), false},
{[]byte("f"), false},
{[]byte("1"), true},
{int64(1), true},
{[]byte("t"), true},
{"hello", "hello"},
{[]byte("hello"), "hello"},
{[]byte("world"), []byte("world")},
{[]interface{}{[]byte("foo")}, []interface{}{[]byte("foo")}},
{[]interface{}{[]byte("foo")}, []string{"foo"}},
{[]interface{}{[]byte("hello"), []byte("world")}, []string{"hello", "world"}},
{[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
{[]interface{}{[]byte("1")}, []int{1}},
{[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
{[]interface{}{[]byte("1"), []byte("2")}, []float64{1, 2}},
{[]interface{}{[]byte("1")}, []byte{1}},
{[]interface{}{[]byte("1")}, []bool{true}},
}
func TestScanConversion(t *testing.T) {
for _, tt := range scanConversionTests {
values := []interface{}{tt.src}
dest := reflect.New(reflect.TypeOf(tt.dest))
values, err := redis.Scan(values, dest.Interface())
if err != nil {
t.Errorf("Scan(%v) returned error %v", tt, err)
continue
}
if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
}
}
}
var scanConversionErrorTests = []struct {
src interface{}
dest interface{}
}{
{[]byte("1234"), byte(0)},
{int64(1234), byte(0)},
{[]byte("-1"), byte(0)},
{int64(-1), byte(0)},
{[]byte("junk"), false},
{redis.Error("blah"), false},
}
func TestScanConversionError(t *testing.T) {
for _, tt := range scanConversionErrorTests {
values := []interface{}{tt.src}
dest := reflect.New(reflect.TypeOf(tt.dest))
values, err := redis.Scan(values, dest.Interface())
if err == nil {
t.Errorf("Scan(%v) did not return error", tt)
}
}
}
func ExampleScan() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
c.Send("HMSET", "album:3", "title", "Beat")
c.Send("LPUSH", "albums", "1")
c.Send("LPUSH", "albums", "2")
c.Send("LPUSH", "albums", "3")
values, err := redis.Values(c.Do("SORT", "albums",
"BY", "album:*->rating",
"GET", "album:*->title",
"GET", "album:*->rating"))
if err != nil {
fmt.Println(err)
return
}
for len(values) > 0 {
var title string
rating := -1 // initialize to illegal value to detect nil.
values, err = redis.Scan(values, &title, &rating)
if err != nil {
fmt.Println(err)
return
}
if rating == -1 {
fmt.Println(title, "not-rated")
} else {
fmt.Println(title, rating)
}
}
// Output:
// Beat not-rated
// Earthbound 1
// Red 5
}
type s0 struct {
X int
Y int `redis:"y"`
Bt bool
}
type s1 struct {
X int `redis:"-"`
I int `redis:"i"`
U uint `redis:"u"`
S string `redis:"s"`
P []byte `redis:"p"`
B bool `redis:"b"`
Bt bool
Bf bool
s0
}
var scanStructTests = []struct {
title string
reply []string
value interface{}
}{
{"basic",
[]string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"},
&s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}},
},
}
func TestScanStruct(t *testing.T) {
for _, tt := range scanStructTests {
var reply []interface{}
for _, v := range tt.reply {
reply = append(reply, []byte(v))
}
value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())
if err := redis.ScanStruct(reply, value.Interface()); err != nil {
t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
}
if !reflect.DeepEqual(value.Interface(), tt.value) {
t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
}
}
}
func TestBadScanStructArgs(t *testing.T) {
x := []interface{}{"A", "b"}
test := func(v interface{}) {
if err := redis.ScanStruct(x, v); err == nil {
t.Errorf("Expect error for ScanStruct(%T, %T)", x, v)
}
}
test(nil)
var v0 *struct{}
test(v0)
var v1 int
test(&v1)
x = x[:1]
v2 := struct{ A string }{}
test(&v2)
}
var scanSliceTests = []struct {
src []interface{}
fieldNames []string
ok bool
dest interface{}
}{
{
[]interface{}{[]byte("1"), nil, []byte("-1")},
nil,
true,
[]int{1, 0, -1},
},
{
[]interface{}{[]byte("1"), nil, []byte("2")},
nil,
true,
[]uint{1, 0, 2},
},
{
[]interface{}{[]byte("-1")},
nil,
false,
[]uint{1},
},
{
[]interface{}{[]byte("hello"), nil, []byte("world")},
nil,
true,
[][]byte{[]byte("hello"), nil, []byte("world")},
},
{
[]interface{}{[]byte("hello"), nil, []byte("world")},
nil,
true,
[]string{"hello", "", "world"},
},
{
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
nil,
true,
[]struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
},
{
[]interface{}{[]byte("a1"), []byte("b1")},
nil,
false,
[]struct{ A, B, C string }{{"a1", "b1", ""}},
},
{
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
nil,
true,
[]*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
},
{
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
[]string{"A", "B"},
true,
[]struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}},
},
{
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
nil,
false,
[]struct{}{},
},
}
func TestScanSlice(t *testing.T) {
for _, tt := range scanSliceTests {
typ := reflect.ValueOf(tt.dest).Type()
dest := reflect.New(typ)
err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...)
if tt.ok != (err == nil) {
t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err)
continue
}
if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) {
t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest)
}
}
}
func ExampleScanSlice() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
c.Send("HMSET", "album:3", "title", "Beat", "rating", 4)
c.Send("LPUSH", "albums", "1")
c.Send("LPUSH", "albums", "2")
c.Send("LPUSH", "albums", "3")
values, err := redis.Values(c.Do("SORT", "albums",
"BY", "album:*->rating",
"GET", "album:*->title",
"GET", "album:*->rating"))
if err != nil {
fmt.Println(err)
return
}
var albums []struct {
Title string
Rating int
}
if err := redis.ScanSlice(values, &albums); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%v\n", albums)
// Output:
// [{Earthbound 1} {Beat 4} {Red 5}]
}
var argsTests = []struct {
title string
actual redis.Args
expected redis.Args
}{
{"struct ptr",
redis.Args{}.AddFlat(&struct {
I int `redis:"i"`
U uint `redis:"u"`
S string `redis:"s"`
P []byte `redis:"p"`
M map[string]string `redis:"m"`
Bt bool
Bf bool
}{
-1234, 5678, "hello", []byte("world"), map[string]string{"hello": "world"}, true, false,
}),
redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "m", map[string]string{"hello": "world"}, "Bt", true, "Bf", false},
},
{"struct",
redis.Args{}.AddFlat(struct{ I int }{123}),
redis.Args{"I", 123},
},
{"slice",
redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
redis.Args{1, "a", "b", "c", 2},
},
{"struct omitempty",
redis.Args{}.AddFlat(&struct {
I int `redis:"i,omitempty"`
U uint `redis:"u,omitempty"`
S string `redis:"s,omitempty"`
P []byte `redis:"p,omitempty"`
M map[string]string `redis:"m,omitempty"`
Bt bool `redis:"Bt,omitempty"`
Bf bool `redis:"Bf,omitempty"`
}{
0, 0, "", []byte{}, map[string]string{}, true, false,
}),
redis.Args{"Bt", true},
},
}
func TestArgs(t *testing.T) {
for _, tt := range argsTests {
if !reflect.DeepEqual(tt.actual, tt.expected) {
t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
}
}
}
func ExampleArgs() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
var p1, p2 struct {
Title string `redis:"title"`
Author string `redis:"author"`
Body string `redis:"body"`
}
p1.Title = "Example"
p1.Author = "Gary"
p1.Body = "Hello"
if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
fmt.Println(err)
return
}
m := map[string]string{
"title": "Example2",
"author": "Steve",
"body": "Map",
}
if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
fmt.Println(err)
return
}
for _, id := range []string{"id1", "id2"} {
v, err := redis.Values(c.Do("HGETALL", id))
if err != nil {
fmt.Println(err)
return
}
if err := redis.ScanStruct(v, &p2); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", p2)
}
// Output:
// {Title:Example Author:Gary Body:Hello}
// {Title:Example2 Author:Steve Body:Map}
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/script.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"crypto/sha1"
"encoding/hex"
"io"
"strings"
)
// Script encapsulates the source, hash and key count for a Lua script. See
// http://redis.io/commands/eval for information on scripts in Redis.
type Script struct {
keyCount int
src string
hash string
}
// NewScript returns a new script object. If keyCount is greater than or equal
// to zero, then the count is automatically inserted in the EVAL command
// argument list. If keyCount is less than zero, then the application supplies
// the count as the first value in the keysAndArgs argument to the Do, Send and
// SendHash methods.
func NewScript(keyCount int, src string) *Script {
h := sha1.New()
io.WriteString(h, src)
return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))}
}
func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
var args []interface{}
if s.keyCount < 0 {
args = make([]interface{}, 1+len(keysAndArgs))
args[0] = spec
copy(args[1:], keysAndArgs)
} else {
args = make([]interface{}, 2+len(keysAndArgs))
args[0] = spec
args[1] = s.keyCount
copy(args[2:], keysAndArgs)
}
return args
}
// Do evaluates the script. Under the covers, Do optimistically evaluates the
// script using the EVALSHA command. If the command fails because the script is
// not loaded, then Do evaluates the script using the EVAL command (thus
// causing the script to load).
func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) {
v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...)
if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...)
}
return v, err
}
// SendHash evaluates the script without waiting for the reply. The script is
// evaluated with the EVALSHA command. The application must ensure that the
// script is loaded by a previous call to Send, Do or Load methods.
func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...)
}
// Send evaluates the script without waiting for the reply.
func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
return c.Send("EVAL", s.args(s.src, keysAndArgs)...)
}
// Load loads the script without evaluating it.
func (s *Script) Load(c Conn) error {
_, err := c.Do("SCRIPT", "LOAD", s.src)
return err
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/script_test.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/garyburd/redigo/redis"
)
var (
// These variables are declared at package level to remove distracting
// details from the examples.
c redis.Conn
reply interface{}
err error
)
func ExampleScript() {
// Initialize a package-level variable with a script.
var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`)
// In a function, use the script Do method to evaluate the script. The Do
// method optimistically uses the EVALSHA command. If the script is not
// loaded, then the Do method falls back to the EVAL command.
reply, err = getScript.Do(c, "foo")
}
func TestScript(t *testing.T) {
c, err := redis.DialDefaultServer()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
defer c.Close()
// To test fall back in Do, we make script unique by adding comment with current time.
script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano())
s := redis.NewScript(2, script)
reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")}
v, err := s.Do(c, "key1", "key2", "arg1", "arg2")
if err != nil {
t.Errorf("s.Do(c, ...) returned %v", err)
}
if !reflect.DeepEqual(v, reply) {
t.Errorf("s.Do(c, ..); = %v, want %v", v, reply)
}
err = s.Load(c)
if err != nil {
t.Errorf("s.Load(c) returned %v", err)
}
err = s.SendHash(c, "key1", "key2", "arg1", "arg2")
if err != nil {
t.Errorf("s.SendHash(c, ...) returned %v", err)
}
err = c.Flush()
if err != nil {
t.Errorf("c.Flush() returned %v", err)
}
v, err = c.Receive()
if !reflect.DeepEqual(v, reply) {
t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply)
}
err = s.Send(c, "key1", "key2", "arg1", "arg2")
if err != nil {
t.Errorf("s.Send(c, ...) returned %v", err)
}
err = c.Flush()
if err != nil {
t.Errorf("c.Flush() returned %v", err)
}
v, err = c.Receive()
if !reflect.DeepEqual(v, reply) {
t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply)
}
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/test_test.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis
import (
"bufio"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"testing"
"time"
)
func SetNowFunc(f func() time.Time) {
nowFunc = f
}
var (
ErrNegativeInt = errNegativeInt
serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
serverLog = ioutil.Discard
defaultServerMu sync.Mutex
defaultServer *Server
defaultServerErr error
)
type Server struct {
name string
cmd *exec.Cmd
done chan struct{}
}
func NewServer(name string, args ...string) (*Server, error) {
s := &Server{
name: name,
cmd: exec.Command(*serverPath, args...),
done: make(chan struct{}),
}
r, err := s.cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = s.cmd.Start()
if err != nil {
return nil, err
}
ready := make(chan error, 1)
go s.watch(r, ready)
select {
case err = <-ready:
case <-time.After(time.Second * 10):
err = errors.New("timeout waiting for server to start")
}
if err != nil {
s.Stop()
return nil, err
}
return s, nil
}
func (s *Server) watch(r io.Reader, ready chan error) {
fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name)
var listening bool
var text string
scn := bufio.NewScanner(r)
for scn.Scan() {
text = scn.Text()
fmt.Fprintf(serverLog, "%s\n", text)
if !listening {
if strings.Contains(text, "The server is now ready to accept connections on port") {
listening = true
ready <- nil
}
}
}
if !listening {
ready <- fmt.Errorf("server exited: %s", text)
}
s.cmd.Wait()
fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name)
close(s.done)
}
func (s *Server) Stop() {
s.cmd.Process.Signal(os.Interrupt)
<-s.done
}
// stopDefaultServer stops the server created by DialDefaultServer.
func stopDefaultServer() {
defaultServerMu.Lock()
defer defaultServerMu.Unlock()
if defaultServer != nil {
defaultServer.Stop()
defaultServer = nil
}
}
// startDefaultServer starts the default server if not already running.
func startDefaultServer() error {
defaultServerMu.Lock()
defer defaultServerMu.Unlock()
if defaultServer != nil || defaultServerErr != nil {
return defaultServerErr
}
defaultServer, defaultServerErr = NewServer(
"default",
"--port", strconv.Itoa(*serverBasePort),
"--save", "",
"--appendonly", "no")
return defaultServerErr
}
// DialDefaultServer starts the test server if not already started and dials a
// connection to the server.
func DialDefaultServer() (Conn, error) {
if err := startDefaultServer(); err != nil {
return nil, err
}
c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
if err != nil {
return nil, err
}
c.Do("FLUSHDB")
return c, nil
}
func TestMain(m *testing.M) {
os.Exit(func() int {
flag.Parse()
var f *os.File
if *serverLogName != "" {
var err error
f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err)
return 1
}
defer f.Close()
serverLog = f
}
defer stopDefaultServer()
return m.Run()
}())
}
================================================
FILE: vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
================================================
// Copyright 2013 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redis_test
import (
"fmt"
"github.com/garyburd/redigo/redis"
)
// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands.
func zpop(c redis.Conn, key string) (result string, err error) {
defer func() {
// Return connection to normal state on error.
if err != nil {
c.Do("DISCARD")
}
}()
// Loop until transaction is successful.
for {
if _, err := c.Do("WATCH", key); err != nil {
return "", err
}
members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0))
if err != nil {
return "", err
}
if len(members) != 1 {
return "", redis.ErrNil
}
c.Send("MULTI")
c.Send("ZREM", key, members[0])
queued, err := c.Do("EXEC")
if err != nil {
return "", err
}
if queued != nil {
result = members[0]
break
}
}
return result, nil
}
// zpopScript pops a value from a ZSET.
var zpopScript = redis.NewScript(1, `
local r = redis.call('ZRANGE', KEYS[1], 0, 0)
if r ~= nil then
r = r[1]
redis.call('ZREM', KEYS[1], r)
end
return r
`)
// This example implements ZPOP as described at
// http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting.
func Example_zpop() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
// Add test data using a pipeline.
for i, member := range []string{"red", "blue", "green"} {
c.Send("ZADD", "zset", i, member)
}
if _, err := c.Do(""); err != nil {
fmt.Println(err)
return
}
// Pop using WATCH/MULTI/EXEC
v, err := zpop(c, "zset")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(v)
// Pop using a script.
v, err = redis.String(zpopScript.Do(c, "zset"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(v)
// Output:
// red
// blue
}
================================================
FILE: vendor/github.com/garyburd/redigo/redisx/connmux.go
================================================
// Copyright 2014 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redisx
import (
"errors"
"sync"
"github.com/garyburd/redigo/internal"
"github.com/garyburd/redigo/redis"
)
// ConnMux multiplexes one or more connections to a single underlying
// connection. The ConnMux connections do not support concurrency, commands
// that associate server side state with the connection or commands that put
// the connection in a special mode.
type ConnMux struct {
c redis.Conn
sendMu sync.Mutex
sendID uint
recvMu sync.Mutex
recvID uint
recvWait map[uint]chan struct{}
}
func NewConnMux(c redis.Conn) *ConnMux {
return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})}
}
// Get gets a connection. The application must close the returned connection.
func (p *ConnMux) Get() redis.Conn {
c := &muxConn{p: p}
c.ids = c.buf[:0]
return c
}
// Close closes the underlying connection.
func (p *ConnMux) Close() error {
return p.c.Close()
}
type muxConn struct {
p *ConnMux
ids []uint
buf [8]uint
}
func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error {
if internal.LookupCommandInfo(cmd).Set != 0 {
return errors.New("command not supported by mux pool")
}
p := c.p
p.sendMu.Lock()
id := p.sendID
c.ids = append(c.ids, id)
p.sendID++
err := p.c.Send(cmd, args...)
if flush {
err = p.c.Flush()
}
p.sendMu.Unlock()
return err
}
func (c *muxConn) Send(cmd string, args ...interface{}) error {
return c.send(false, cmd, args...)
}
func (c *muxConn) Flush() error {
p := c.p
p.sendMu.Lock()
err := p.c.Flush()
p.sendMu.Unlock()
return err
}
func (c *muxConn) Receive() (interface{}, error) {
if len(c.ids) == 0 {
return nil, errors.New("mux pool underflow")
}
id := c.ids[0]
c.ids = c.ids[1:]
if len(c.ids) == 0 {
c.ids = c.buf[:0]
}
p := c.p
p.recvMu.Lock()
if p.recvID != id {
ch := make(chan struct{})
p.recvWait[id] = ch
p.recvMu.Unlock()
<-ch
p.recvMu.Lock()
if p.recvID != id {
panic("out of sync")
}
}
v, err := p.c.Receive()
id++
p.recvID = id
ch, ok := p.recvWait[id]
if ok {
delete(p.recvWait, id)
}
p.recvMu.Unlock()
if ok {
ch <- struct{}{}
}
return v, err
}
func (c *muxConn) Close() error {
var err error
if len(c.ids) == 0 {
return nil
}
c.Flush()
for _ = range c.ids {
_, err = c.Receive()
}
return err
}
func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) {
if err := c.send(true, cmd, args...); err != nil {
return nil, err
}
return c.Receive()
}
func (c *muxConn) Err() error {
return c.p.c.Err()
}
================================================
FILE: vendor/github.com/garyburd/redigo/redisx/connmux_test.go
================================================
// Copyright 2014 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package redisx_test
import (
"net/textproto"
"sync"
"testing"
"github.com/garyburd/redigo/internal/redistest"
"github.com/garyburd/redigo/redis"
"github.com/garyburd/redigo/redisx"
)
func TestConnMux(t *testing.T) {
c, err := redistest.Dial()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
m := redisx.NewConnMux(c)
defer m.Close()
c1 := m.Get()
c2 := m.Get()
c1.Send("ECHO", "hello")
c2.Send("ECHO", "world")
c1.Flush()
c2.Flush()
s, err := redis.String(c1.Receive())
if err != nil {
t.Fatal(err)
}
if s != "hello" {
t.Fatalf("echo returned %q, want %q", s, "hello")
}
s, err = redis.String(c2.Receive())
if err != nil {
t.Fatal(err)
}
if s != "world" {
t.Fatalf("echo returned %q, want %q", s, "world")
}
c1.Close()
c2.Close()
}
func TestConnMuxClose(t *testing.T) {
c, err := redistest.Dial()
if err != nil {
t.Fatalf("error connection to database, %v", err)
}
m := redisx.NewConnMux(c)
defer m.Close()
c1 := m.Get()
c2 := m.Get()
if err := c1.Send("ECHO", "hello"); err != nil {
t.Fatal(err)
}
if err := c1.Close(); err != nil {
t.Fatal(err)
}
if err := c2.Send("ECHO", "world"); err != nil {
t.Fatal(err)
}
if err := c2.Flush(); err != nil {
t.Fatal(err)
}
s, err := redis.String(c2.Receive())
if err != nil {
t.Fatal(err)
}
if s != "world" {
t.Fatalf("echo returned %q, want %q", s, "world")
}
c2.Close()
}
func BenchmarkConn(b *testing.B) {
b.StopTimer()
c, err := redistest.Dial()
if err != nil {
b.Fatalf("error connection to database, %v", err)
}
defer c.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkConnMux(b *testing.B) {
b.StopTimer()
c, err := redistest.Dial()
if err != nil {
b.Fatalf("error connection to database, %v", err)
}
m := redisx.NewConnMux(c)
defer m.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
c := m.Get()
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
c.Close()
}
}
func BenchmarkPool(b *testing.B) {
b.StopTimer()
p := redis.Pool{Dial: redistest.Dial, MaxIdle: 1}
defer p.Close()
// Fill the pool.
c := p.Get()
if err := c.Err(); err != nil {
b.Fatal(err)
}
c.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
c := p.Get()
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
c.Close()
}
}
const numConcurrent = 10
func BenchmarkConnMuxConcurrent(b *testing.B) {
b.StopTimer()
c, err := redistest.Dial()
if err != nil {
b.Fatalf("error connection to database, %v", err)
}
defer c.Close()
m := redisx.NewConnMux(c)
var wg sync.WaitGroup
wg.Add(numConcurrent)
b.StartTimer()
for i := 0; i < numConcurrent; i++ {
go func() {
defer wg.Done()
for i := 0; i < b.N; i++ {
c := m.Get()
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
c.Close()
}
}()
}
wg.Wait()
}
func BenchmarkPoolConcurrent(b *testing.B) {
b.StopTimer()
p := redis.Pool{Dial: redistest.Dial, MaxIdle: numConcurrent}
defer p.Close()
// Fill the pool.
conns := make([]redis.Conn, numConcurrent)
for i := range conns {
c := p.Get()
if err := c.Err(); err != nil {
b.Fatal(err)
}
conns[i] = c
}
for _, c := range conns {
c.Close()
}
var wg sync.WaitGroup
wg.Add(numConcurrent)
b.StartTimer()
for i := 0; i < numConcurrent; i++ {
go func() {
defer wg.Done()
for i := 0; i < b.N; i++ {
c := p.Get()
if _, err := c.Do("PING"); err != nil {
b.Fatal(err)
}
c.Close()
}
}()
}
wg.Wait()
}
func BenchmarkPipelineConcurrency(b *testing.B) {
b.StopTimer()
c, err := redistest.Dial()
if err != nil {
b.Fatalf("error connection to database, %v", err)
}
defer c.Close()
var wg sync.WaitGroup
wg.Add(numConcurrent)
var pipeline textproto.Pipeline
b.StartTimer()
for i := 0; i < numConcurrent; i++ {
go func() {
defer wg.Done()
for i := 0; i < b.N; i++ {
id := pipeline.Next()
pipeline.StartRequest(id)
c.Send("PING")
c.Flush()
pipeline.EndRequest(id)
pipeline.StartResponse(id)
_, err := c.Receive()
if err != nil {
b.Fatal(err)
}
pipeline.EndResponse(id)
}
}()
}
wg.Wait()
}
================================================
FILE: vendor/github.com/garyburd/redigo/redisx/doc.go
================================================
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// Package redisx contains experimental features for Redigo. Features in this
// package may be modified or deleted at any time.
package redisx // import "github.com/garyburd/redigo/redisx"
================================================
FILE: vendor/github.com/imroc/log/LICENSE
================================================
MIT License
Copyright (c) 2016 roc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: vendor/github.com/imroc/log/README.md
================================================
log
==============
log is a logging framework of Go. see [API](https://godoc.org/github.com/imroc/log).
## Features
* light weight
* easy to use
* level logging support
* flexible
## Quick Start
##### Installation
``` sh
go get github.com/imroc/log
```
##### Simple Usage
``` go
import (
"github.com/imroc/log"
)
func main() {
// DEBUG < INFO < WARN < ERROR < FATAL
log.SetLevel(log.WARN) // level DEBUG and INFO will not output
log.Debug("%s message", "debug")
log.Info("%s message", "info")
log.Warn("%s message", "warn")
log.Error("%s message", "error")
log.Fatal("%s message", "fatal")
}
```
output:
```
2016/10/04 14:38:38 [WARN] warn message
2016/10/04 14:38:38 [EROR] error message
2016/10/04 14:38:38 [FATL] fatal message
```
##### More Control
``` go
file, _ := os.OpenFile("test.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
log.Set(log.INFO, file, log.Llongfile|log.LstdFlags)
log.Debug("%s message", "debug")
log.Info("%s message", "info")
log.Warn("%s message", "warn")
log.Error("%s message", "error")
log.Fatal("%s message", "fatal")
```
output to test.log:
```
2016/10/04 15:04:00 [INFO] /home/cpwl/go/dev/src/test/test.go:13: info message
2016/10/04 15:04:00 [WARN] /home/cpwl/go/dev/src/test/test.go:14: warn message
2016/10/04 15:04:00 [EROR] /home/cpwl/go/dev/src/test/test.go:15: error message
2016/10/04 15:04:00 [FATL] /home/cpwl/go/dev/src/test/test.go:16: fatal message
```
## LICENSE
log is is distributed under the terms of the MIT License.
================================================
FILE: vendor/github.com/imroc/log/log.go
================================================
package log
import (
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
)
// These flags define which text to prefix to each log entry generated by the Logger.
const (
// Bits or'ed together to control what's printed.
// There is no control over the order they appear (the order listed
// here) or the format they present (as described in the comments).
// The prefix is followed by a colon only when Llongfile or Lshortfile
// is specified.
// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
// log levels
const (
DEBUG int = iota
INFO
WARN
ERROR
FATAL
)
// Log level strings
var (
levelStrings = [...]string{"[DEBG]", "[INFO]", "[WARN]", "[EROR]", "[FATL]"}
)
// A Logger represents an active logging object that generates lines of
// output to an io.Writer. Each logging operation makes a single call to
// the Writer's Write method. A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
level int // log level
mu sync.Mutex // ensures atomic writes; protects the following fields
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
}
// Set sets the wirter,log level and flags.
func Set(level int, out io.Writer, flag int) {
if out == nil || level < DEBUG || level > FATAL {
panic("error logger arguments")
}
std = Logger{out: out, level: level, flag: flag}
}
var std = Logger{out: os.Stderr, level: DEBUG, flag: LstdFlags}
// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
func itoa(buf *[]byte, i int, wid int) {
// Assemble decimal in reverse order.
var b [20]byte
bp := len(b) - 1
for i >= 10 || wid > 1 {
wid--
q := i / 10
b[bp] = byte('0' + i - q*10)
bp--
i = q
}
// i < 10
b[bp] = byte('0' + i)
*buf = append(*buf, b[bp:]...)
}
func (l *Logger) formatHeader(buf *[]byte, t time.Time, level string, file string, line int) {
if l.flag&LUTC != 0 {
t = t.UTC()
}
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
if l.flag&Ldate != 0 {
year, month, day := t.Date()
itoa(buf, year, 4)
*buf = append(*buf, '/')
itoa(buf, int(month), 2)
*buf = append(*buf, '/')
itoa(buf, day, 2)
*buf = append(*buf, ' ')
}
if l.flag&(Ltime|Lmicroseconds) != 0 {
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
*buf = append(*buf, ':')
itoa(buf, min, 2)
*buf = append(*buf, ':')
itoa(buf, sec, 2)
if l.flag&Lmicroseconds != 0 {
*buf = append(*buf, '.')
itoa(buf, t.Nanosecond()/1e3, 6)
}
*buf = append(*buf, ' ')
}
}
*buf = append(*buf, level...)
*buf = append(*buf, ' ')
if l.flag&(Lshortfile|Llongfile) != 0 {
if l.flag&Lshortfile != 0 {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
}
*buf = append(*buf, file...)
*buf = append(*buf, ':')
itoa(buf, line, -1)
*buf = append(*buf, ": "...)
}
}
// Output outputs the string of lv level to the writer.
func (l *Logger) Output(lv int, s string) error {
now := time.Now() // get this early.
var file string
var line int
l.mu.Lock()
defer l.mu.Unlock()
if l.flag&(Lshortfile|Llongfile) != 0 {
// release lock while getting caller info - it's expensive.
l.mu.Unlock()
var ok bool
_, file, line, ok = runtime.Caller(2)
if !ok {
file = "???"
line = 0
}
l.mu.Lock()
}
l.buf = l.buf[:0]
l.formatHeader(&l.buf, now, levelStrings[lv], file, line)
l.buf = append(l.buf, s...)
if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
}
_, err := l.out.Write(l.buf)
return err
}
// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
if w == nil {
panic("output can not be nil")
}
std.mu.Lock()
defer std.mu.Unlock()
std.out = w
}
//SetLevel sets the log level for the standard logger.
func SetLevel(level int) {
if level < DEBUG || level > FATAL {
panic("wrong log level")
}
std.mu.Lock()
defer std.mu.Unlock()
std.level = level
}
// SetFlags sets the output flags for the standard logger.
func SetFlags(flag int) {
std.flag = flag
}
// Debug output the debug info if currrent level is not less than DEBUG.
func Debug(format string, a ...interface{}) {
if DEBUG < std.level {
return
}
std.Output(DEBUG, fmt.Sprintf(format, a...))
}
// Info output the debug info if currrent level is not less than INFO.
func Info(format string, a ...interface{}) {
if INFO < std.level {
return
}
std.Output(INFO, fmt.Sprintf(format, a...))
}
// Warn output the debug info if currrent level is not less than WARN.
func Warn(format string, a ...interface{}) {
if WARN < std.level {
return
}
std.Output(WARN, fmt.Sprintf(format, a...))
}
// Error output the debug info if currrent level is not less than ERROR.
func Error(format string, a ...interface{}) {
if ERROR < std.level {
return
}
std.Output(ERROR, fmt.Sprintf(format, a...))
}
// Fatal output the debug info if currrent level is not less than Fatal.
func Fatal(format string, a ...interface{}) {
if FATAL < std.level {
return
}
std.Output(FATAL, fmt.Sprintf(format, a...))
}
================================================
FILE: xf/README.md
================================================
xf
==============
xf 是科大讯飞语音离线合成SDK的go封装.
##注意
使用该包的程序依赖 lib/libmsc.so 动态链接库文件,将其放入某路径并指定环境变量 LD_LIBRARY_PATH ,如:export LD_LIBRARY_PATH = /usr/local/lib
================================================
FILE: xf/doc/iFlytek MSC Reference Manual/all__0_8js.html
================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.9"/>
<title>MSC for Windows&Linux API: iFlytek MSC Reference Manual/search/all_0.js 文件参考</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td style="padding-left: 0.5em;">
<div id="projectname">MSC for Windows&Linux API
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- 制作者 Doxygen 1.8.9 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'搜索');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>首页</span></a></li>
<li class="current"><a href="files.html"><span>文件</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="搜索" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>文件列表</span></a></li>
<li><a href="globals.html"><span>文件成员</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div id="nav-path" class="navpath">
<ul>
<li class="navelem"><a class="el" href="dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html">iFlytek MSC Reference Manual</a></li><li class="navelem"><a class="el" href="dir_25539194184bab781b1c7ecd67774cd8.html">search</a></li> </ul>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#var-members">变量</a> </div>
<div class="headertitle">
<div class="title">all_0.js 文件参考</div> </div>
</div><!--header-->
<div class="contents">
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="var-members"></a>
变量</h2></td></tr>
<tr class="memitem:ad01a7523f103d6242ef9b0451861231e"><td class="memItemLeft" align="right" valign="top">var </td><td class="memItemRight" valign="bottom"><a class="el" href="all__0_8js.html#ad01a7523f103d6242ef9b0451861231e">searchData</a></td></tr>
<tr class="separator:ad01a7523f103d6242ef9b0451861231e"><td class="memSeparator" colspan="2"> </td></tr>
</table>
<h2 class="groupheader">变量说明</h2>
<a class="anchor" id="ad01a7523f103d6242ef9b0451861231e"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">var searchData</td>
</tr>
</table>
</div><div class="memdoc">
<b>初始值:</b><div class="fragment"><div class="line">=</div>
<div class="line">[</div>
<div class="line"> [<span class="stringliteral">'msp_5fcmn_2eh'</span>,[<span class="stringliteral">'msp_cmn.h'</span>,[<span class="stringliteral">'../msp__cmn_8h.html'</span>,1,<span class="stringliteral">''</span>]]],</div>
<div class="line"> [<span class="stringliteral">'mspgetparam'</span>,[<span class="stringliteral">'MSPGetParam'</span>,[<span class="stringliteral">'../msp__cmn_8h.html#a4d3fa0aad5e761cb2a2afe30ae2a9714'</span>,1,<span class="stringliteral">'msp_cmn.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'mspgetversion'</span>,[<span class="stringliteral">'MSPGetVersion'</span>,[<span class="stringliteral">'../msp__cmn_8h.html#a632008aeddf5eba09555920ce38686a4'</span>,1,<span class="stringliteral">'msp_cmn.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'msplogin'</span>,[<span class="stringliteral">'MSPLogin'</span>,[<span class="stringliteral">'../msp__cmn_8h.html#a137acfe684fe46cbe5baf19f7d4a7fcc'</span>,1,<span class="stringliteral">'msp_cmn.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'msplogout'</span>,[<span class="stringliteral">'MSPLogout'</span>,[<span class="stringliteral">'../msp__cmn_8h.html#a1e0f72cd113b4578afdf3d16ab34463e'</span>,1,<span class="stringliteral">'msp_cmn.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'mspsearch'</span>,[<span class="stringliteral">'MSPSearch'</span>,[<span class="stringliteral">'../msp__cmn_8h.html#ae7be2dd2c6ee318524621b952998c14d'</span>,1,<span class="stringliteral">'msp_cmn.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'mspuploaddata'</span>,[<span class="stringliteral">'MSPUploadData'</span>,[<span class="stringliteral">'../msp__cmn_8h.html#ada276fa6db4a66342d951820020e4e8f'</span>,1,<span class="stringliteral">'msp_cmn.h'</span>]]]</div>
<div class="line">]</div>
</div><!-- fragment -->
</div>
</div>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
生成于 2016年 八月 28日 星期日 20:43:39 , 为 MSC for Windows&Linux API使用  <a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.9
</small></address>
</body>
</html>
================================================
FILE: xf/doc/iFlytek MSC Reference Manual/all__1_8js.html
================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.9"/>
<title>MSC for Windows&Linux API: iFlytek MSC Reference Manual/search/all_1.js 文件参考</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td style="padding-left: 0.5em;">
<div id="projectname">MSC for Windows&Linux API
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- 制作者 Doxygen 1.8.9 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'搜索');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>首页</span></a></li>
<li class="current"><a href="files.html"><span>文件</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="搜索" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>文件列表</span></a></li>
<li><a href="globals.html"><span>文件成员</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div id="nav-path" class="navpath">
<ul>
<li class="navelem"><a class="el" href="dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html">iFlytek MSC Reference Manual</a></li><li class="navelem"><a class="el" href="dir_25539194184bab781b1c7ecd67774cd8.html">search</a></li> </ul>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#var-members">变量</a> </div>
<div class="headertitle">
<div class="title">all_1.js 文件参考</div> </div>
</div><!--header-->
<div class="contents">
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="var-members"></a>
变量</h2></td></tr>
<tr class="memitem:ad01a7523f103d6242ef9b0451861231e"><td class="memItemLeft" align="right" valign="top">var </td><td class="memItemRight" valign="bottom"><a class="el" href="all__1_8js.html#ad01a7523f103d6242ef9b0451861231e">searchData</a></td></tr>
<tr class="separator:ad01a7523f103d6242ef9b0451861231e"><td class="memSeparator" colspan="2"> </td></tr>
</table>
<h2 class="groupheader">变量说明</h2>
<a class="anchor" id="ad01a7523f103d6242ef9b0451861231e"></a>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">var searchData</td>
</tr>
</table>
</div><div class="memdoc">
<b>初始值:</b><div class="fragment"><div class="line">=</div>
<div class="line">[</div>
<div class="line"> [<span class="stringliteral">'qisr_2eh'</span>,[<span class="stringliteral">'qisr.h'</span>,[<span class="stringliteral">'../qisr_8h.html'</span>,1,<span class="stringliteral">''</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qisraudiowrite'</span>,[<span class="stringliteral">'QISRAudioWrite'</span>,[<span class="stringliteral">'../qisr_8h.html#a47fd2588fe834fa2d51fef1961d7aef4'</span>,1,<span class="stringliteral">'qisr.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qisrgetparam'</span>,[<span class="stringliteral">'QISRGetParam'</span>,[<span class="stringliteral">'../qisr_8h.html#a2081e3cad9a8155c15790a2476be7044'</span>,1,<span class="stringliteral">'qisr.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qisrgetresult'</span>,[<span class="stringliteral">'QISRGetResult'</span>,[<span class="stringliteral">'../qisr_8h.html#a2e7880db4792266a4d1439238c0b2c1b'</span>,1,<span class="stringliteral">'qisr.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qisrsessionbegin'</span>,[<span class="stringliteral">'QISRSessionBegin'</span>,[<span class="stringliteral">'../qisr_8h.html#aaec4a5779275e07c4f7405ed8d739416'</span>,1,<span class="stringliteral">'qisr.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qisrsessionend'</span>,[<span class="stringliteral">'QISRSessionEnd'</span>,[<span class="stringliteral">'../qisr_8h.html#ab50c4114e032100c4093ddd51329fecc'</span>,1,<span class="stringliteral">'qisr.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qmfv_2eh'</span>,[<span class="stringliteral">'qmfv.h'</span>,[<span class="stringliteral">'../qmfv_8h.html'</span>,1,<span class="stringliteral">''</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qmfvdatawrite'</span>,[<span class="stringliteral">'QMFVDataWrite'</span>,[<span class="stringliteral">'../qmfv_8h.html#a081a01b2add2dfac3010ec7cd8ba5eac'</span>,1,<span class="stringliteral">'qmfv.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qmfvgetparam'</span>,[<span class="stringliteral">'QMFVGetParam'</span>,[<span class="stringliteral">'../qmfv_8h.html#a7174d943c2f1691f6a8c717429d43f8d'</span>,1,<span class="stringliteral">'qmfv.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qmfvgetresult'</span>,[<span class="stringliteral">'QMFVGetResult'</span>,[<span class="stringliteral">'../qmfv_8h.html#aa87716ad6b28326982626c0c6a6ffcbd'</span>,1,<span class="stringliteral">'qmfv.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qmfvsessionbegin'</span>,[<span class="stringliteral">'QMFVSessionBegin'</span>,[<span class="stringliteral">'../qmfv_8h.html#adbe23b402d4c50b4c06f47ca26253be5'</span>,1,<span class="stringliteral">'qmfv.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qmfvsessionend'</span>,[<span class="stringliteral">'QMFVSessionEnd'</span>,[<span class="stringliteral">'../qmfv_8h.html#ad4ea72dd39285348cf26c627d67c6b65'</span>,1,<span class="stringliteral">'qmfv.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qtts_2eh'</span>,[<span class="stringliteral">'qtts.h'</span>,[<span class="stringliteral">'../qtts_8h.html'</span>,1,<span class="stringliteral">''</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qttsaudioget'</span>,[<span class="stringliteral">'QTTSAudioGet'</span>,[<span class="stringliteral">'../qtts_8h.html#a4e4f6bed4b9e4ea553aa00ccf539c22a'</span>,1,<span class="stringliteral">'qtts.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qttsgetparam'</span>,[<span class="stringliteral">'QTTSGetParam'</span>,[<span class="stringliteral">'../qtts_8h.html#a0812612ff738a828490e4e3db59767e8'</span>,1,<span class="stringliteral">'qtts.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qttssessionbegin'</span>,[<span class="stringliteral">'QTTSSessionBegin'</span>,[<span class="stringliteral">'../qtts_8h.html#a3fba4ad9599445073335851cc9479542'</span>,1,<span class="stringliteral">'qtts.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qttssessionend'</span>,[<span class="stringliteral">'QTTSSessionEnd'</span>,[<span class="stringliteral">'../qtts_8h.html#a75d5047a2a889dbd890d116a6d0b550a'</span>,1,<span class="stringliteral">'qtts.h'</span>]]],</div>
<div class="line"> [<span class="stringliteral">'qttstextput'</span>,[<span class="stringliteral">'QTTSTextPut'</span>,[<span class="stringliteral">'../qtts_8h.html#a5b7d146d6a35341d4d73efd720ae987b'</span>,1,<span class="stringliteral">'qtts.h'</span>]]]</div>
<div class="line">]</div>
</div><!-- fragment -->
</div>
</div>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
生成于 2016年 八月 28日 星期日 20:43:39 , 为 MSC for Windows&Linux API使用  <a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.9
</small></address>
</body>
</html>
================================================
FILE: xf/doc/iFlytek MSC Reference Manual/dir_25539194184bab781b1c7ecd67774cd8.html
================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.9"/>
<title>MSC for Windows&Linux API: iFlytek MSC Reference Manual/search 目录参考</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td style="padding-left: 0.5em;">
<div id="projectname">MSC for Windows&Linux API
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- 制作者 Doxygen 1.8.9 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'搜索');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>首页</span></a></li>
<li><a href="files.html"><span>文件</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="搜索" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div id="nav-path" class="navpath">
<ul>
<li class="navelem"><a class="el" href="dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html">iFlytek MSC Reference Manual</a></li><li class="navelem"><a class="el" href="dir_25539194184bab781b1c7ecd67774cd8.html">search</a></li> </ul>
</div>
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">search 目录参考</div> </div>
</div><!--header-->
<div class="contents">
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="files"></a>
文件</h2></td></tr>
<tr class="memitem:all__0_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="all__0_8js.html">all_0.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:all__1_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="all__1_8js.html">all_1.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:files__0_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="files__0_8js.html">files_0.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:files__1_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="files__1_8js.html">files_1.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:functions__0_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="functions__0_8js.html">functions_0.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:functions__1_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="functions__1_8js.html">functions_1.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:search_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="search_8js.html">search.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:searchdata_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="searchdata_8js.html">searchdata.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
</table>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
生成于 2016年 八月 28日 星期日 20:43:39 , 为 MSC for Windows&Linux API使用  <a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.9
</small></address>
</body>
</html>
================================================
FILE: xf/doc/iFlytek MSC Reference Manual/dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html
================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.9"/>
<title>MSC for Windows&Linux API: iFlytek MSC Reference Manual 目录参考</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td style="padding-left: 0.5em;">
<div id="projectname">MSC for Windows&Linux API
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- 制作者 Doxygen 1.8.9 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'搜索');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>首页</span></a></li>
<li><a href="files.html"><span>文件</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="搜索" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div id="nav-path" class="navpath">
<ul>
<li class="navelem"><a class="el" href="dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html">iFlytek MSC Reference Manual</a></li> </ul>
</div>
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">iFlytek MSC Reference Manual 目录参考</div> </div>
</div><!--header-->
<div class="contents">
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="subdirs"></a>
目录</h2></td></tr>
<tr class="memitem:dir_25539194184bab781b1c7ecd67774cd8"><td class="memItemLeft" align="right" valign="top">目录  </td><td class="memItemRight" valign="bottom"><a class="el" href="dir_25539194184bab781b1c7ecd67774cd8.html">search</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
</table><table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="files"></a>
文件</h2></td></tr>
<tr class="memitem:dynsections_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="dynsections_8js.html">dynsections.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
<tr class="memitem:jquery_8js"><td class="memItemLeft" align="right" valign="top">文件  </td><td class="memItemRight" valign="bottom"><a class="el" href="jquery_8js.html">jquery.js</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2"> </td></tr>
</table>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
生成于 2016年 八月 28日 星期日 20:43:39 , 为 MSC for Windows&Linux API使用  <a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.9
</small></address>
</body>
</html>
================================================
FILE: xf/doc/iFlytek MSC Reference Manual/doxygen.css
================================================
/* The standard CSS for doxygen 1.8.9 */
body, table, div, p, dl {
font: 400 14px/22px Roboto,sans-serif;
}
/* @group Heading Levels */
h1.groupheader {
font-size: 150%;
}
.title {
font: 400 14px/28px Roboto,sans-serif;
font-size: 150%;
font-weight: bold;
margin: 10px 2px;
}
h2.groupheader {
border-bottom: 1px solid #879ECB;
color: #354C7B;
font-size: 150%;
font-weight: normal;
margin-top: 1.75em;
padding-top: 8px;
padding-bottom: 4px;
width: 100%;
}
h3.groupheader {
font-size: 100%;
}
h1, h2, h3, h4, h5, h6 {
-webkit-transition: text-shadow 0.5s linear;
-moz-transition: text-shadow 0.5s linear;
-ms-transition: text-shadow 0.5s linear;
-o-transition: text-shadow 0.5s linear;
transition: text-shadow 0.5s linear;
margin-right: 15px;
}
h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
text-shadow: 0 0 15px cyan;
}
dt {
font-weight: bold;
}
div.multicol {
-moz-column-gap: 1em;
-webkit-column-gap: 1em;
-moz-column-count: 3;
-webkit-column-count: 3;
}
p.startli, p.startdd {
margin-top: 2px;
}
p.starttd {
margin-top: 0px;
}
p.endli {
margin-bottom: 0px;
}
p.enddd {
margin-bottom: 4px;
}
p.endtd {
margin-bottom: 2px;
}
/* @end */
caption {
font-weight: bold;
}
span.legend {
font-size: 70%;
text-align: center;
}
h3.version {
font-size: 90%;
text-align: center;
}
div.qindex, div.navtab{
background-color: #EBEFF6;
border: 1px solid #A3B4D7;
text-align: center;
}
div.qindex, div.navpath {
width: 100%;
line-height: 140%;
}
div.navtab {
margin-right: 15px;
}
/* @group Link Styling */
a {
color: #3D578C;
font-weight: normal;
text-decoration: none;
}
.contents a:visited {
color: #4665A2;
}
a:hover {
text-decoration: underline;
}
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #9CAFD4;
color: #ffffff;
border: 1px double #869DCA;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.el {
font-weight: bold;
}
a.elRef {
}
a.code, a.code:visited, a.line, a.line:visited {
color: #4665A2;
}
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
color: #4665A2;
}
/* @end */
dl.el {
margin-left: -1cm;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
font-family: monospace, fixed;
font-size: 105%;
}
div.fragment {
padding: 4px 6px;
margin: 4px 8px 4px 2px;
background-color: #FBFCFD;
border: 1px solid #C4CFE5;
}
div.line {
font-fami
gitextract_fcvvxwx4/
├── .gitattributes
├── README.md
├── glide.yaml
├── main.go
├── server/
│ └── server.go
├── speed_test.go
├── vendor/
│ └── github.com/
│ ├── garyburd/
│ │ └── redigo/
│ │ ├── .travis.yml
│ │ ├── LICENSE
│ │ ├── README.markdown
│ │ ├── internal/
│ │ │ ├── commandinfo.go
│ │ │ ├── commandinfo_test.go
│ │ │ └── redistest/
│ │ │ └── testdb.go
│ │ ├── redis/
│ │ │ ├── conn.go
│ │ │ ├── conn_test.go
│ │ │ ├── doc.go
│ │ │ ├── log.go
│ │ │ ├── pool.go
│ │ │ ├── pool_test.go
│ │ │ ├── pubsub.go
│ │ │ ├── pubsub_test.go
│ │ │ ├── redis.go
│ │ │ ├── reply.go
│ │ │ ├── reply_test.go
│ │ │ ├── scan.go
│ │ │ ├── scan_test.go
│ │ │ ├── script.go
│ │ │ ├── script_test.go
│ │ │ ├── test_test.go
│ │ │ └── zpop_example_test.go
│ │ └── redisx/
│ │ ├── connmux.go
│ │ ├── connmux_test.go
│ │ └── doc.go
│ └── imroc/
│ └── log/
│ ├── LICENSE
│ ├── README.md
│ └── log.go
└── xf/
├── README.md
├── doc/
│ ├── iFlytek MSC Reference Manual/
│ │ ├── all__0_8js.html
│ │ ├── all__1_8js.html
│ │ ├── dir_25539194184bab781b1c7ecd67774cd8.html
│ │ ├── dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html
│ │ ├── doxygen.css
│ │ ├── dynsections.js
│ │ ├── dynsections_8js.html
│ │ ├── files.html
│ │ ├── files__0_8js.html
│ │ ├── files__1_8js.html
│ │ ├── functions__0_8js.html
│ │ ├── functions__1_8js.html
│ │ ├── globals.html
│ │ ├── globals_func.html
│ │ ├── globals_vars.html
│ │ ├── index.html
│ │ ├── jquery.js
│ │ ├── jquery_8js.html
│ │ ├── msp__cmn_8h.html
│ │ ├── msp__cmn_8h_source.html
│ │ ├── qisr_8h.html
│ │ ├── qisr_8h_source.html
│ │ ├── qmfv_8h.html
│ │ ├── qmfv_8h_source.html
│ │ ├── qtts_8h.html
│ │ ├── qtts_8h_source.html
│ │ ├── search/
│ │ │ ├── all_0.html
│ │ │ ├── all_0.js
│ │ │ ├── all_1.html
│ │ │ ├── all_1.js
│ │ │ ├── all_10.html
│ │ │ ├── all_10.js
│ │ │ ├── all_11.html
│ │ │ ├── all_11.js
│ │ │ ├── all_2.html
│ │ │ ├── all_2.js
│ │ │ ├── all_3.html
│ │ │ ├── all_3.js
│ │ │ ├── all_4.html
│ │ │ ├── all_4.js
│ │ │ ├── all_5.html
│ │ │ ├── all_5.js
│ │ │ ├── all_6.html
│ │ │ ├── all_6.js
│ │ │ ├── all_7.html
│ │ │ ├── all_7.js
│ │ │ ├── all_8.html
│ │ │ ├── all_8.js
│ │ │ ├── all_9.html
│ │ │ ├── all_9.js
│ │ │ ├── all_a.html
│ │ │ ├── all_a.js
│ │ │ ├── all_b.html
│ │ │ ├── all_b.js
│ │ │ ├── all_c.html
│ │ │ ├── all_c.js
│ │ │ ├── all_d.html
│ │ │ ├── all_d.js
│ │ │ ├── all_e.html
│ │ │ ├── all_e.js
│ │ │ ├── all_f.html
│ │ │ ├── all_f.js
│ │ │ ├── files_0.html
│ │ │ ├── files_0.js
│ │ │ ├── files_1.html
│ │ │ ├── files_1.js
│ │ │ ├── files_2.html
│ │ │ ├── files_2.js
│ │ │ ├── files_3.html
│ │ │ ├── files_3.js
│ │ │ ├── files_4.html
│ │ │ ├── files_4.js
│ │ │ ├── files_5.html
│ │ │ ├── files_5.js
│ │ │ ├── files_6.html
│ │ │ ├── files_6.js
│ │ │ ├── functions_0.html
│ │ │ ├── functions_0.js
│ │ │ ├── functions_1.html
│ │ │ ├── functions_1.js
│ │ │ ├── functions_2.html
│ │ │ ├── functions_2.js
│ │ │ ├── functions_3.html
│ │ │ ├── functions_3.js
│ │ │ ├── functions_4.html
│ │ │ ├── functions_4.js
│ │ │ ├── functions_5.html
│ │ │ ├── functions_5.js
│ │ │ ├── functions_6.html
│ │ │ ├── functions_6.js
│ │ │ ├── functions_7.html
│ │ │ ├── functions_7.js
│ │ │ ├── functions_8.html
│ │ │ ├── functions_8.js
│ │ │ ├── functions_9.html
│ │ │ ├── functions_9.js
│ │ │ ├── functions_a.html
│ │ │ ├── functions_a.js
│ │ │ ├── nomatches.html
│ │ │ ├── search.css
│ │ │ ├── search.js
│ │ │ ├── searchdata.js
│ │ │ ├── variables_0.html
│ │ │ ├── variables_0.js
│ │ │ ├── variables_1.html
│ │ │ ├── variables_1.js
│ │ │ ├── variables_2.html
│ │ │ ├── variables_2.js
│ │ │ ├── variables_3.html
│ │ │ ├── variables_3.js
│ │ │ ├── variables_4.html
│ │ │ ├── variables_4.js
│ │ │ ├── variables_5.html
│ │ │ ├── variables_5.js
│ │ │ ├── variables_6.html
│ │ │ ├── variables_6.js
│ │ │ ├── variables_7.html
│ │ │ └── variables_7.js
│ │ ├── search_8js.html
│ │ ├── searchdata_8js.html
│ │ └── tabs.css
│ └── tts_sample/
│ ├── 32bit_make.sh
│ ├── 64bit_make.sh
│ ├── Makefile
│ └── tts_sample.c
├── include/
│ ├── .msp_cmn.h.swp
│ ├── convert.h
│ ├── msp_cmn.h
│ ├── msp_errors.h
│ ├── msp_types.h
│ └── qtts.h
└── xf.go
SYMBOL INDEX (367 symbols across 32 files)
FILE: main.go
function main (line 39) | func main() {
function configureLog (line 92) | func configureLog(logFile, logLevel string) error {
FILE: server/server.go
type Server (line 16) | type Server struct
method Start (line 42) | func (s *Server) Start() {
method Once (line 139) | func (s *Server) Once(txt string, desPath string) error {
type Options (line 20) | type Options struct
type Speech (line 31) | type Speech struct
function New (line 36) | func New(opts *Options) *Server {
function setXF (line 155) | func setXF(speedLevel int, ttsParams, loginParams string) error {
FILE: speed_test.go
function TestSpeed (line 14) | func TestSpeed(t *testing.T) {
function getRandomString (line 51) | func getRandomString(l int) string {
FILE: vendor/github.com/garyburd/redigo/internal/commandinfo.go
constant WatchState (line 22) | WatchState = 1 << iota
constant MultiState (line 23) | MultiState
constant SubscribeState (line 24) | SubscribeState
constant MonitorState (line 25) | MonitorState
type CommandInfo (line 28) | type CommandInfo struct
function init (line 43) | func init() {
function LookupCommandInfo (line 49) | func LookupCommandInfo(commandName string) CommandInfo {
FILE: vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
function TestLookupCommandInfo (line 5) | func TestLookupCommandInfo(t *testing.T) {
function benchmarkLookupCommandInfo (line 13) | func benchmarkLookupCommandInfo(b *testing.B, names ...string) {
function BenchmarkLookupCommandInfoCorrectCase (line 21) | func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) {
function BenchmarkLookupCommandInfoMixedCase (line 25) | func BenchmarkLookupCommandInfoMixedCase(b *testing.B) {
FILE: vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
type testConn (line 25) | type testConn struct
method Close (line 29) | func (t testConn) Close() error {
function Dial (line 44) | func Dial() (redis.Conn, error) {
FILE: vendor/github.com/garyburd/redigo/redis/conn.go
type conn (line 32) | type conn struct
method Close (line 230) | func (c *conn) Close() error {
method fatal (line 241) | func (c *conn) fatal(err error) error {
method Err (line 253) | func (c *conn) Err() error {
method writeLen (line 260) | func (c *conn) writeLen(prefix byte, n int) error {
method writeString (line 277) | func (c *conn) writeString(s string) error {
method writeBytes (line 284) | func (c *conn) writeBytes(p []byte) error {
method writeInt64 (line 291) | func (c *conn) writeInt64(n int64) error {
method writeFloat64 (line 295) | func (c *conn) writeFloat64(n float64) error {
method writeCommand (line 299) | func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
method readLine (line 340) | func (c *conn) readLine() ([]byte, error) {
method readReply (line 413) | func (c *conn) readReply() (interface{}, error) {
method Send (line 470) | func (c *conn) Send(cmd string, args ...interface{}) error {
method Flush (line 483) | func (c *conn) Flush() error {
method Receive (line 493) | func (c *conn) Receive() (reply interface{}, err error) {
method Do (line 518) | func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
function DialTimeout (line 60) | func DialTimeout(network, address string, connectTimeout, readTimeout, w...
type DialOption (line 68) | type DialOption struct
type dialOptions (line 72) | type dialOptions struct
function DialReadTimeout (line 81) | func DialReadTimeout(d time.Duration) DialOption {
function DialWriteTimeout (line 88) | func DialWriteTimeout(d time.Duration) DialOption {
function DialConnectTimeout (line 95) | func DialConnectTimeout(d time.Duration) DialOption {
function DialNetDial (line 105) | func DialNetDial(dial func(network, addr string) (net.Conn, error)) Dial...
function DialDatabase (line 112) | func DialDatabase(db int) DialOption {
function DialPassword (line 120) | func DialPassword(password string) DialOption {
function Dial (line 128) | func Dial(network, address string, options ...DialOption) (Conn, error) {
function DialURL (line 170) | func DialURL(rawurl string, options ...DialOption) (Conn, error) {
function NewConn (line 220) | func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) ...
type protocolError (line 334) | type protocolError
method Error (line 336) | func (pe protocolError) Error() string {
function parseLen (line 356) | func parseLen(p []byte) (int, error) {
function parseInt (line 379) | func parseInt(p []byte) (interface{}, error) {
FILE: vendor/github.com/garyburd/redigo/redis/conn_test.go
type testConn (line 31) | type testConn struct
method Close (line 36) | func (*testConn) Close() error { return nil }
method LocalAddr (line 37) | func (*testConn) LocalAddr() net.Addr { return nil }
method RemoteAddr (line 38) | func (*testConn) RemoteAddr() net.Addr { return nil }
method SetDeadline (line 39) | func (*testConn) SetDeadline(t time.Time) error { return nil }
method SetReadDeadline (line 40) | func (*testConn) SetReadDeadline(t time.Time) error { return nil }
method SetWriteDeadline (line 41) | func (*testConn) SetWriteDeadline(t time.Time) error { return nil }
function dialTestConn (line 43) | func dialTestConn(r io.Reader, w io.Writer) redis.DialOption {
function TestWrite (line 91) | func TestWrite(t *testing.T) {
function TestRead (line 191) | func TestRead(t *testing.T) {
function TestDoCommands (line 272) | func TestDoCommands(t *testing.T) {
function TestPipelineCommands (line 291) | func TestPipelineCommands(t *testing.T) {
function TestBlankCommmand (line 317) | func TestBlankCommmand(t *testing.T) {
function TestRecvBeforeSend (line 344) | func TestRecvBeforeSend(t *testing.T) {
function TestError (line 365) | func TestError(t *testing.T) {
function TestReadTimeout (line 386) | func TestReadTimeout(t *testing.T) {
function TestDialURLErrors (line 470) | func TestDialURLErrors(t *testing.T) {
function TestDialURLPort (line 479) | func TestDialURLPort(t *testing.T) {
function TestDialURLHost (line 492) | func TestDialURLHost(t *testing.T) {
function TestDialURLPassword (line 505) | func TestDialURLPassword(t *testing.T) {
function TestDialURLDatabase (line 518) | func TestDialURLDatabase(t *testing.T) {
function ExampleDial (line 543) | func ExampleDial() {
function ExampleDialURL (line 552) | func ExampleDialURL() {
function TestExecError (line 563) | func TestExecError(t *testing.T) {
function BenchmarkDoEmpty (line 642) | func BenchmarkDoEmpty(b *testing.B) {
function BenchmarkDoPing (line 657) | func BenchmarkDoPing(b *testing.B) {
FILE: vendor/github.com/garyburd/redigo/redis/log.go
function NewLoggingConn (line 24) | func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
type loggingConn (line 31) | type loggingConn struct
method Close (line 37) | func (c *loggingConn) Close() error {
method printValue (line 45) | func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
method print (line 82) | func (c *loggingConn) print(method, commandName string, args []interfa...
method Do (line 101) | func (c *loggingConn) Do(commandName string, args ...interface{}) (int...
method Send (line 107) | func (c *loggingConn) Send(commandName string, args ...interface{}) er...
method Receive (line 113) | func (c *loggingConn) Receive() (interface{}, error) {
FILE: vendor/github.com/garyburd/redigo/redis/pool.go
type Pool (line 98) | type Pool struct
method Get (line 157) | func (p *Pool) Get() Conn {
method ActiveCount (line 166) | func (p *Pool) ActiveCount() int {
method Close (line 174) | func (p *Pool) Close() error {
method release (line 192) | func (p *Pool) release() {
method get (line 201) | func (p *Pool) get() (Conn, error) {
method put (line 280) | func (p *Pool) put(c Conn, forceClose bool) error {
type idleConn (line 140) | type idleConn struct
function NewPool (line 148) | func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
type pooledConnection (line 305) | type pooledConnection struct
method Close (line 328) | func (pc *pooledConnection) Close() error {
method Err (line 366) | func (pc *pooledConnection) Err() error {
method Do (line 370) | func (pc *pooledConnection) Do(commandName string, args ...interface{}...
method Send (line 376) | func (pc *pooledConnection) Send(commandName string, args ...interface...
method Flush (line 382) | func (pc *pooledConnection) Flush() error {
method Receive (line 386) | func (pc *pooledConnection) Receive() (reply interface{}, err error) {
function initSentinel (line 316) | func initSentinel() {
type errorConnection (line 390) | type errorConnection struct
method Do (line 392) | func (ec errorConnection) Do(string, ...interface{}) (interface{}, err...
method Send (line 393) | func (ec errorConnection) Send(string, ...interface{}) error ...
method Err (line 394) | func (ec errorConnection) Err() error ...
method Close (line 395) | func (ec errorConnection) Close() error ...
method Flush (line 396) | func (ec errorConnection) Flush() error ...
method Receive (line 397) | func (ec errorConnection) Receive() (interface{}, error) ...
FILE: vendor/github.com/garyburd/redigo/redis/pool_test.go
type poolTestConn (line 28) | type poolTestConn struct
method Close (line 34) | func (c *poolTestConn) Close() error {
method Err (line 41) | func (c *poolTestConn) Err() error { return c.err }
method Do (line 43) | func (c *poolTestConn) Do(commandName string, args ...interface{}) (in...
method Send (line 54) | func (c *poolTestConn) Send(commandName string, args ...interface{}) e...
type poolDialer (line 59) | type poolDialer struct
method dial (line 68) | func (d *poolDialer) dial() (redis.Conn, error) {
method check (line 86) | func (d *poolDialer) check(message string, p *redis.Pool, dialed, open...
function TestPoolReuse (line 100) | func TestPoolReuse(t *testing.T) {
function TestPoolMaxIdle (line 121) | func TestPoolMaxIdle(t *testing.T) {
function TestPoolError (line 145) | func TestPoolError(t *testing.T) {
function TestPoolClose (line 167) | func TestPoolClose(t *testing.T) {
function TestPoolTimeout (line 208) | func TestPoolTimeout(t *testing.T) {
function TestPoolConcurrenSendReceive (line 236) | func TestPoolConcurrenSendReceive(t *testing.T) {
function TestPoolBorrowCheck (line 261) | func TestPoolBorrowCheck(t *testing.T) {
function TestPoolMaxActive (line 278) | func TestPoolMaxActive(t *testing.T) {
function TestPoolMonitorCleanup (line 313) | func TestPoolMonitorCleanup(t *testing.T) {
function TestPoolPubSubCleanup (line 329) | func TestPoolPubSubCleanup(t *testing.T) {
function TestPoolTransactionCleanup (line 359) | func TestPoolTransactionCleanup(t *testing.T) {
function startGoroutines (line 430) | func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) cha...
function TestWaitPool (line 447) | func TestWaitPool(t *testing.T) {
function TestWaitPoolClose (line 475) | func TestWaitPoolClose(t *testing.T) {
function TestWaitPoolCommandError (line 510) | func TestWaitPoolCommandError(t *testing.T) {
function TestWaitPoolDialError (line 539) | func TestWaitPoolDialError(t *testing.T) {
function TestLocking_TestOnBorrowFails_PoolDoesntCrash (line 589) | func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
function BenchmarkPoolGet (line 632) | func BenchmarkPoolGet(b *testing.B) {
function BenchmarkPoolGetErr (line 648) | func BenchmarkPoolGetErr(b *testing.B) {
function BenchmarkPoolGetPing (line 667) | func BenchmarkPoolGetPing(b *testing.B) {
FILE: vendor/github.com/garyburd/redigo/redis/pubsub.go
type Subscription (line 20) | type Subscription struct
type Message (line 33) | type Message struct
type PMessage (line 43) | type PMessage struct
type Pong (line 56) | type Pong struct
type PubSubConn (line 61) | type PubSubConn struct
method Close (line 66) | func (c PubSubConn) Close() error {
method Subscribe (line 71) | func (c PubSubConn) Subscribe(channel ...interface{}) error {
method PSubscribe (line 77) | func (c PubSubConn) PSubscribe(channel ...interface{}) error {
method Unsubscribe (line 84) | func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
method PUnsubscribe (line 91) | func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
method Ping (line 97) | func (c PubSubConn) Ping(data string) error {
method Receive (line 105) | func (c PubSubConn) Receive() interface{} {
FILE: vendor/github.com/garyburd/redigo/redis/pubsub_test.go
function publish (line 26) | func publish(channel, value interface{}) {
function ExamplePubSubConn (line 37) | func ExamplePubSubConn() {
function expectPushed (line 105) | func expectPushed(t *testing.T, c redis.PubSubConn, message string, expe...
function TestPushed (line 112) | func TestPushed(t *testing.T) {
FILE: vendor/github.com/garyburd/redigo/redis/redis.go
type Error (line 18) | type Error
method Error (line 20) | func (err Error) Error() string { return string(err) }
type Conn (line 23) | type Conn interface
FILE: vendor/github.com/garyburd/redigo/redis/reply.go
function Int (line 35) | func Int(reply interface{}, err error) (int, error) {
function Int64 (line 66) | func Int64(reply interface{}, err error) (int64, error) {
function Uint64 (line 95) | func Uint64(reply interface{}, err error) (uint64, error) {
function Float64 (line 124) | func Float64(reply interface{}, err error) (float64, error) {
function String (line 149) | func String(reply interface{}, err error) (string, error) {
function Bytes (line 175) | func Bytes(reply interface{}, err error) ([]byte, error) {
function Bool (line 201) | func Bool(reply interface{}, err error) (bool, error) {
function MultiBulk (line 221) | func MultiBulk(reply interface{}, err error) ([]interface{}, error) { re...
function Values (line 231) | func Values(reply interface{}, err error) ([]interface{}, error) {
function Strings (line 250) | func Strings(reply interface{}, err error) ([]string, error) {
function ByteSlices (line 280) | func ByteSlices(reply interface{}, err error) ([][]byte, error) {
function Ints (line 308) | func Ints(reply interface{}, err error) ([]int, error) {
function StringMap (line 323) | func StringMap(result interface{}, err error) (map[string]string, error) {
function IntMap (line 346) | func IntMap(result interface{}, err error) (map[string]int, error) {
function Int64Map (line 372) | func Int64Map(result interface{}, err error) (map[string]int64, error) {
FILE: vendor/github.com/garyburd/redigo/redis/reply_test.go
type valueError (line 25) | type valueError struct
function ve (line 30) | func ve(v interface{}, err error) valueError {
function TestReply (line 101) | func TestReply(t *testing.T) {
function dial (line 114) | func dial() (redis.Conn, error) {
function ExampleBool (line 118) | func ExampleBool() {
function ExampleInt (line 133) | func ExampleInt() {
function ExampleInts (line 151) | func ExampleInts() {
function ExampleString (line 166) | func ExampleString() {
FILE: vendor/github.com/garyburd/redigo/redis/scan.go
function ensureLen (line 26) | func ensureLen(d reflect.Value, n int) {
function cannotConvert (line 34) | func cannotConvert(d reflect.Value, s interface{}) error {
function convertAssignBulkString (line 53) | func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
function convertAssignInt (line 85) | func convertAssignInt(d reflect.Value, s int64) (err error) {
function convertAssignValue (line 112) | func convertAssignValue(d reflect.Value, s interface{}) (err error) {
function convertAssignArray (line 124) | func convertAssignArray(d reflect.Value, s []interface{}) error {
function convertAssign (line 137) | func convertAssign(d interface{}, s interface{}) (err error) {
function Scan (line 228) | func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
type fieldSpec (line 243) | type fieldSpec struct
type structSpec (line 249) | type structSpec struct
method fieldSpec (line 254) | func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
function compileStructSpec (line 258) | func compileStructSpec(t reflect.Type, depth map[string]int, index []int...
function structSpecForType (line 324) | func structSpecForType(t reflect.Type) *structSpec {
function ScanStruct (line 363) | func ScanStruct(src []interface{}, dest interface{}) error {
function ScanSlice (line 408) | func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string...
type Args (line 482) | type Args
method Add (line 485) | func (args Args) Add(value ...interface{}) Args {
method AddFlat (line 501) | func (args Args) AddFlat(v interface{}) Args {
function flattenStruct (line 528) | func flattenStruct(args Args, v reflect.Value) Args {
FILE: vendor/github.com/garyburd/redigo/redis/scan_test.go
function TestScanConversion (line 64) | func TestScanConversion(t *testing.T) {
function TestScanConversionError (line 91) | func TestScanConversionError(t *testing.T) {
function ExampleScan (line 102) | func ExampleScan() {
type s0 (line 145) | type s0 struct
type s1 (line 151) | type s1 struct
function TestScanStruct (line 174) | func TestScanStruct(t *testing.T) {
function TestBadScanStructArgs (line 194) | func TestBadScanStructArgs(t *testing.T) {
function TestScanSlice (line 283) | func TestScanSlice(t *testing.T) {
function ExampleScanSlice (line 300) | func ExampleScanSlice() {
function TestArgs (line 379) | func TestArgs(t *testing.T) {
function ExampleArgs (line 387) | func ExampleArgs() {
FILE: vendor/github.com/garyburd/redigo/redis/script.go
type Script (line 26) | type Script struct
method args (line 43) | func (s *Script) args(spec string, keysAndArgs []interface{}) []interf...
method Do (line 62) | func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, ...
method SendHash (line 73) | func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
method Send (line 78) | func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
method Load (line 83) | func (s *Script) Load(c Conn) error {
function NewScript (line 37) | func NewScript(keyCount int, src string) *Script {
FILE: vendor/github.com/garyburd/redigo/redis/script_test.go
function ExampleScript (line 34) | func ExampleScript() {
function TestScript (line 44) | func TestScript(t *testing.T) {
FILE: vendor/github.com/garyburd/redigo/redis/test_test.go
function SetNowFunc (line 33) | func SetNowFunc(f func() time.Time) {
type Server (line 50) | type Server struct
method watch (line 90) | func (s *Server) watch(r io.Reader, ready chan error) {
method Stop (line 113) | func (s *Server) Stop() {
function NewServer (line 56) | func NewServer(name string, args ...string) (*Server, error) {
function stopDefaultServer (line 119) | func stopDefaultServer() {
function startDefaultServer (line 129) | func startDefaultServer() error {
function DialDefaultServer (line 145) | func DialDefaultServer() (Conn, error) {
function TestMain (line 157) | func TestMain(m *testing.M) {
FILE: vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
function zpop (line 23) | func zpop(c redis.Conn, key string) (result string, err error) {
function Example_zpop (line 74) | func Example_zpop() {
FILE: vendor/github.com/garyburd/redigo/redisx/connmux.go
type ConnMux (line 29) | type ConnMux struct
method Get (line 45) | func (p *ConnMux) Get() redis.Conn {
method Close (line 52) | func (p *ConnMux) Close() error {
function NewConnMux (line 40) | func NewConnMux(c redis.Conn) *ConnMux {
type muxConn (line 56) | type muxConn struct
method send (line 62) | func (c *muxConn) send(flush bool, cmd string, args ...interface{}) er...
method Send (line 79) | func (c *muxConn) Send(cmd string, args ...interface{}) error {
method Flush (line 83) | func (c *muxConn) Flush() error {
method Receive (line 91) | func (c *muxConn) Receive() (interface{}, error) {
method Close (line 131) | func (c *muxConn) Close() error {
method Do (line 143) | func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, er...
method Err (line 150) | func (c *muxConn) Err() error {
FILE: vendor/github.com/garyburd/redigo/redisx/connmux_test.go
function TestConnMux (line 27) | func TestConnMux(t *testing.T) {
function TestConnMuxClose (line 59) | func TestConnMuxClose(t *testing.T) {
function BenchmarkConn (line 94) | func BenchmarkConn(b *testing.B) {
function BenchmarkConnMux (line 110) | func BenchmarkConnMux(b *testing.B) {
function BenchmarkPool (line 130) | func BenchmarkPool(b *testing.B) {
constant numConcurrent (line 154) | numConcurrent = 10
function BenchmarkConnMuxConcurrent (line 156) | func BenchmarkConnMuxConcurrent(b *testing.B) {
function BenchmarkPoolConcurrent (line 186) | func BenchmarkPoolConcurrent(b *testing.B) {
function BenchmarkPipelineConcurrency (line 225) | func BenchmarkPipelineConcurrency(b *testing.B) {
FILE: vendor/github.com/imroc/log/log.go
constant Ldate (line 23) | Ldate = 1 << iota
constant Ltime (line 24) | Ltime
constant Lmicroseconds (line 25) | Lmicroseconds
constant Llongfile (line 26) | Llongfile
constant Lshortfile (line 27) | Lshortfile
constant LUTC (line 28) | LUTC
constant LstdFlags (line 29) | LstdFlags = Ldate | Ltime
constant DEBUG (line 34) | DEBUG int = iota
constant INFO (line 35) | INFO
constant WARN (line 36) | WARN
constant ERROR (line 37) | ERROR
constant FATAL (line 38) | FATAL
type Logger (line 50) | type Logger struct
method formatHeader (line 85) | func (l *Logger) formatHeader(buf *[]byte, t time.Time, level string, ...
method Output (line 136) | func (l *Logger) Output(lv int, s string) error {
function Set (line 59) | func Set(level int, out io.Writer, flag int) {
function itoa (line 69) | func itoa(buf *[]byte, i int, wid int) {
function SetOutput (line 164) | func SetOutput(w io.Writer) {
function SetLevel (line 174) | func SetLevel(level int) {
function SetFlags (line 185) | func SetFlags(flag int) {
function Debug (line 190) | func Debug(format string, a ...interface{}) {
function Info (line 198) | func Info(format string, a ...interface{}) {
function Warn (line 206) | func Warn(format string, a ...interface{}) {
function Error (line 214) | func Error(format string, a ...interface{}) {
function Fatal (line 222) | func Fatal(format string, a ...interface{}) {
FILE: xf/doc/iFlytek MSC Reference Manual/dynsections.js
function toggleVisibility (line 1) | function toggleVisibility(linkObj)
function updateStripes (line 22) | function updateStripes()
function toggleLevel (line 28) | function toggleLevel(level)
function toggleFolder (line 49) | function toggleFolder(id)
function toggleInherit (line 84) | function toggleInherit(id)
FILE: xf/doc/iFlytek MSC Reference Manual/jquery.js
function b0 (line 16) | function b0(b3,b4){return new b0.fn.init(b3,b4)}
function bw (line 16) | function bw(){if(bF.isReady){return}try{av.documentElement.doScroll("lef...
function X (line 16) | function X(e){var bv=a2[e]={},bw,bx;e=e.split(/\s+/);for(bw=0,bx=e.lengt...
function bD (line 16) | function bD(bF){return function(bG){bx[bF]=arguments.length>1?aJ.call(ar...
function bz (line 16) | function bz(bF){return function(bG){bB[bF]=arguments.length>1?aJ.call(ar...
function a5 (line 16) | function a5(bx,bw,by){if(by===L&&bx.nodeType===1){var bv="data-"+bw.repl...
function S (line 16) | function S(bv){for(var e in bv){if(e==="data"&&b.isEmptyObject(bv[e])){c...
function bi (line 16) | function bi(by,bx,bA){var bw=bx+"defer",bv=bx+"queue",e=bx+"mark",bz=b._...
function bE (line 16) | function bE(){if(!(--bB)){e.resolveWith(bv,[bv])}}
function bk (line 16) | function bk(){return false}
function i (line 16) | function i(){return true}
function bv (line 23) | function bv(bR,bW,bV,bZ,bX,bY){for(var bT=0,bS=bZ.length;bT<bS;bT++){var...
function bN (line 23) | function bN(bR,bW,bV,bZ,bX,bY){for(var bT=0,bS=bZ.length;bT<bS;bT++){var...
function C (line 23) | function C(e){return !e||!e.parentNode||e.parentNode.nodeType===11}
function aG (line 23) | function aG(bx,bw,e){bw=bw||0;if(b.isFunction(bw)){return b.grep(bx,func...
function a (line 23) | function a(e){var bw=aR.split("|"),bv=e.createDocumentFragment();if(bv.c...
function ba (line 23) | function ba(e,bv){return b.nodeName(e,"table")?(e.getElementsByTagName("...
function t (line 23) | function t(bB,bv){if(bv.nodeType!==1||!b.hasData(bB)){return}var by,bx,e...
function ai (line 23) | function ai(bv,e){var bw;if(e.nodeType!==1){return}if(e.clearAttributes)...
function bg (line 23) | function bg(e){if(typeof e.getElementsByTagName!=="undefined"){return e....
function az (line 23) | function az(e){if(e.type==="checkbox"||e.type==="radio"){e.defaultChecke...
function E (line 23) | function E(e){var bv=(e.nodeName||"").toLowerCase();if(bv==="input"){az(...
function al (line 23) | function al(e){var bv=av.createElement("div");ac.appendChild(bv);bv.inne...
function bo (line 23) | function bo(e,bv){if(bv.src){b.ajax({url:bv.src,async:false,dataType:"sc...
function p (line 23) | function p(by,bw,bv){var bA=bw==="width"?by.offsetWidth:by.offsetHeight,...
function f (line 23) | function f(e){return function(by,bA){if(typeof by!=="string"){bA=by;by="...
function aW (line 23) | function aW(bv,bE,bz,bD,bB,bx){bB=bB||bE.dataTypes[0];bx=bx||{};bx[bB]=t...
function am (line 23) | function am(bw,bx){var bv,e,by=b.ajaxSettings.flatOptions||{};for(bv in ...
function bF (line 23) | function bF(bZ,bU,b0,bW){if(bA===2){return}bA=2;if(bE){clearTimeout(bE)}...
function v (line 23) | function v(bw,by,bv,bx){if(b.isArray(by)){b.each(by,function(bA,bz){if(b...
function bj (line 23) | function bj(bD,bC,bz){var bv=bD.contents,bB=bD.dataTypes,bw=bD.responseF...
function G (line 23) | function G(bH,bz){if(bH.dataFilter){bz=bH.dataFilter(bz,bH.dataType)}var...
function aL (line 23) | function aL(){try{return new bb.XMLHttpRequest()}catch(bv){}}
function aj (line 23) | function aj(){try{return new bb.ActiveXObject("Microsoft.XMLHTTP")}catch...
function bv (line 23) | function bv(){if(e.queue===false){b._mark(this)}var bE=b.extend({},e),bK...
function bB (line 23) | function bB(bE,bF,bD){var bC=bF[bD];b.removeData(bE,bD,true);bC.stop(e)}
function bh (line 23) | function bh(){setTimeout(at,0);return(a4=b.now())}
function at (line 23) | function at(){a4=L}
function a0 (line 23) | function a0(bv,e){var bw={};b.each(aH.concat.apply([],aH.slice(0,e)),fun...
function bv (line 23) | function bv(bA){return e.step(bA)}
function x (line 23) | function x(bx){if(!Q[bx]){var e=av.body,bv=b("<"+bx+">").appendTo(e),bw=...
function aK (line 23) | function aK(e){return b.isWindow(e)?e:e.nodeType===9?e.defaultView||e.pa...
function j (line 32) | function j(m,l,i,n){a.each(f,function(){l-=parseFloat(a.curCSS(m,"paddin...
function c (line 32) | function c(g,e){var j=g.nodeName.toLowerCase();if("area"===j){var i=g.pa...
function b (line 32) | function b(e){return !a(e).parents().andSelf().filter(function(){return ...
function a (line 61) | function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}
function n (line 61) | function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if...
function h (line 61) | function h(n){j.animate(g,e,d.easing,n&&function(){n.call(this,f,d)})}
function b (line 61) | function b(d){return typeof d=="object"?d:{top:d,left:d}}
function b (line 68) | function b(){var F=this;F.top="auto";F.left="auto";F.right="auto";F.bott...
function t (line 68) | function t(K,N,F){var J=null;function L(P,Q){M();if(!K.data(e)){if(!P){c...
function j (line 68) | function j(){function G(M,L,J,O,P){var K=L.split("-")[0],N=new b(),I;if(...
function x (line 68) | function x(Q){var P=new j(),O=k("#"+Q.popupId);if(O.length===0){O=k("<di...
function q (line 68) | function q(F){return window.SVGElement&&F[0] instanceof SVGElement}
function h (line 68) | function h(){if(!c.mouseTrackingActive){c.mouseTrackingActive=true;k(fun...
function i (line 68) | function i(F){c.currentX=F.pageX;c.currentY=F.pageY}
function v (line 68) | function v(F){var H=F.offset(),J=F[0].getBoundingClientRect(),I=J.right-...
function B (line 68) | function B(I){var G=I.data(y),F=I.data(o),K=I.data(l),H,J;if(G){if(k.isF...
function m (line 68) | function m(M,L,K){var G=c.scrollTop,J=c.scrollLeft,I=G+c.windowHeight,F=...
function a (line 68) | function a(G){var F=0;while(G){G&=G-1;F++}return F}
FILE: xf/doc/iFlytek MSC Reference Manual/search/search.js
function convertToId (line 1) | function convertToId(search)
function getXPos (line 24) | function getXPos(item)
function getYPos (line 38) | function getYPos(item)
function SearchBox (line 59) | function SearchBox(name, resultsPath, inFrame, label)
function SearchResults (line 404) | function SearchResults(name)
function setKeyActions (line 709) | function setKeyActions(elem,action)
function setClassAttr (line 716) | function setClassAttr(elem,attr)
function createResults (line 722) | function createResults()
function init_search (line 777) | function init_search()
FILE: xf/doc/tts_sample/tts_sample.c
type wave_pcm_hdr (line 17) | typedef struct _wave_pcm_hdr
function text_to_speech (line 54) | int text_to_speech(const char* src_text, const char* des_path, const cha...
function main (line 136) | int main(int argc, char* argv[])
FILE: xf/include/convert.h
type wave_pcm_hdr (line 12) | typedef struct _wave_pcm_hdr
function text_to_speech (line 51) | int text_to_speech(const char* src_text, const char* des_path, const cha...
FILE: xf/include/qtts.h
type wchar_t (line 62) | typedef const wchar_t* (MSPAPI *Proc_QTTSSessionBeginW)(const wchar_t* p...
type wchar_t (line 118) | typedef const wchar_t* (MSPAPI *Proc_QTTSAudioInfoW)(const wchar_t* sess...
FILE: xf/xf.go
function SetTTSParams (line 31) | func SetTTSParams(params string) {
function SetSleep (line 35) | func SetSleep(t int) {
function Login (line 39) | func Login(loginParams string) error {
function Logout (line 50) | func Logout() error {
function TextToSpeech (line 58) | func TextToSpeech(text, outPath string) error {
Condensed preview — 168 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,065K chars).
[
{
"path": ".gitattributes",
"chars": 131,
"preview": "*.js linguist-language=Go\n*.css linguist-language=Go\n*.html linguist-language=Go\n*.c linguist-language=Go\n*.h linguist-l"
},
{
"path": "README.md",
"chars": 1492,
"preview": "ontts\n==============\nontts 是语音在线合成服务\n\n## 编译\n\n##### GOPATH\n项目源文件需要放在$GOPATH下\n\n##### glide\n通过glide管理依赖,若没有安装glide,需先安装\n\nUb"
},
{
"path": "glide.yaml",
"chars": 55,
"preview": "package: ontts\nimport:\n- package: github.com/imroc/log\n"
},
{
"path": "main.go",
"chars": 2430,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"ontts/server\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/imroc/log\"\n)\n\nvar usageStr = `\nUsag"
},
{
"path": "server/server.go",
"chars": 3758,
"preview": "package server\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"ontts/xf\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/garyburd/redig"
},
{
"path": "speed_test.go",
"chars": 1182,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"ontts/xf\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/imroc/log\"\n)\n\nfunc TestSpe"
},
{
"path": "vendor/github.com/garyburd/redigo/.travis.yml",
"chars": 240,
"preview": "language: go\nsudo: false\nservices:\n - redis-server\n\ngo:\n - 1.4\n - 1.5\n - 1.6\n - 1.7\n - tip\n\nscript:\n - go get -t "
},
{
"path": "vendor/github.com/garyburd/redigo/LICENSE",
"chars": 10142,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "vendor/github.com/garyburd/redigo/README.markdown",
"chars": 2053,
"preview": "Redigo\n======\n\n[](https://travis-ci.org/garyburd"
},
{
"path": "vendor/github.com/garyburd/redigo/internal/commandinfo.go",
"chars": 1424,
"preview": "// Copyright 2014 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/internal/commandinfo_test.go",
"chars": 692,
"preview": "package internal\n\nimport \"testing\"\n\nfunc TestLookupCommandInfo(t *testing.T) {\n\tfor _, n := range []string{\"watch\", \"WAT"
},
{
"path": "vendor/github.com/garyburd/redigo/internal/redistest/testdb.go",
"chars": 1622,
"preview": "// Copyright 2014 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/conn.go",
"chars": 12810,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/conn_test.go",
"chars": 14640,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/doc.go",
"chars": 6268,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/log.go",
"chars": 2823,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/pool.go",
"chars": 10312,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/pool_test.go",
"chars": 13368,
"preview": "// Copyright 2011 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/pubsub.go",
"chars": 3623,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/pubsub_test.go",
"chars": 4144,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/redis.go",
"chars": 1554,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/reply.go",
"chars": 11311,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/reply_test.go",
"chars": 3693,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/scan.go",
"chars": 13779,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/scan_test.go",
"chars": 10338,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/script.go",
"chars": 2967,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/script_test.go",
"chars": 2664,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/test_test.go",
"chars": 3974,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redis/zpop_example_test.go",
"chars": 2354,
"preview": "// Copyright 2013 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redisx/connmux.go",
"chars": 3103,
"preview": "// Copyright 2014 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redisx/connmux_test.go",
"chars": 4813,
"preview": "// Copyright 2014 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/garyburd/redigo/redisx/doc.go",
"chars": 777,
"preview": "// Copyright 2012 Gary Burd\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"): you may\n// not use thi"
},
{
"path": "vendor/github.com/imroc/log/LICENSE",
"chars": 1060,
"preview": "MIT License\n\nCopyright (c) 2016 roc\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof thi"
},
{
"path": "vendor/github.com/imroc/log/README.md",
"chars": 1494,
"preview": "log\n==============\nlog is a logging framework of Go. see [API](https://godoc.org/github.com/imroc/log).\n\n## Features\n * "
},
{
"path": "vendor/github.com/imroc/log/log.go",
"chars": 5974,
"preview": "package log\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\n// These flags define which text to prefix to eac"
},
{
"path": "xf/README.md",
"chars": 155,
"preview": "xf\n==============\nxf 是科大讯飞语音离线合成SDK的go封装.\n\n##注意\n使用该包的程序依赖 lib/libmsc.so 动态链接库文件,将其放入某路径并指定环境变量 LD_LIBRARY_PATH ,如:export"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/all__0_8js.html",
"chars": 6968,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/all__1_8js.html",
"chars": 10154,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/dir_25539194184bab781b1c7ecd67774cd8.html",
"chars": 6054,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/dir_9a65024ac0a05d9abfc2bb6c7ff8f818.html",
"chars": 4743,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/doxygen.css",
"chars": 25493,
"preview": "/* The standard CSS for doxygen 1.8.9 */\n\nbody, table, div, p, dl {\n\tfont: 400 14px/22px Roboto,sans-serif;\n}\n\n/* @group"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/dynsections.js",
"chars": 3140,
"preview": "function toggleVisibility(linkObj)\n{\n var base = $(linkObj).attr('id');\n var summary = $('#'+base+'-summary');\n var cont"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/dynsections_8js.html",
"chars": 8034,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/files.html",
"chars": 8217,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/files__0_8js.html",
"chars": 5162,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/files__1_8js.html",
"chars": 5646,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/functions__0_8js.html",
"chars": 6726,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/functions__1_8js.html",
"chars": 9428,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/globals.html",
"chars": 12468,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/globals_func.html",
"chars": 9518,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/globals_vars.html",
"chars": 6309,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/index.html",
"chars": 3387,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/jquery.js",
"chars": 146338,
"preview": "/*!\n * jQuery JavaScript Library v1.7.1\n * http://jquery.com/\n *\n * Copyright 2011, John Resig\n * Dual licensed under th"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/jquery_8js.html",
"chars": 233731,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/msp__cmn_8h.html",
"chars": 22036,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/msp__cmn_8h_source.html",
"chars": 8519,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/qisr_8h.html",
"chars": 24077,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/qisr_8h_source.html",
"chars": 9223,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/qmfv_8h.html",
"chars": 21370,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/qmfv_8h_source.html",
"chars": 8372,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/qtts_8h.html",
"chars": 21638,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/qtts_8h_source.html",
"chars": 8930,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_0.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_0.js",
"chars": 670,
"preview": "var searchData=\n[\n ['ad',['aD',['../jquery_8js.html#ad223f5fba68c41c1236671ac5c5b0fcb',1,'jquery.js']]],\n ['all_5f0_2e"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_1.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_1.js",
"chars": 722,
"preview": "var searchData=\n[\n ['b',['b',['../jquery_8js.html#aa4026ad5544b958e54ce5e106fa1c805',1,'b(): jquery.js'],['../jque"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_10.html",
"chars": 996,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_10.js",
"chars": 140,
"preview": "var searchData=\n[\n ['updatestripes',['updateStripes',['../dynsections_8js.html#a8f7493ad859d4fbf2523917511ee7177',1,'dy"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_11.html",
"chars": 996,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_11.js",
"chars": 106,
"preview": "var searchData=\n[\n ['z',['Z',['../jquery_8js.html#adc18d83abfd9f87d396e8fd6b6ac0fe1',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_2.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_2.js",
"chars": 508,
"preview": "var searchData=\n[\n ['c',['c',['../jquery_8js.html#abce695e0af988ece0826d9ad59b8160d',1,'jquery.js']]],\n ['converttoid'"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_3.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_3.js",
"chars": 96,
"preview": "var searchData=\n[\n ['dynsections_2ejs',['dynsections.js',['../dynsections_8js.html',1,'']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_4.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_4.js",
"chars": 208,
"preview": "var searchData=\n[\n ['each',['each',['../jquery_8js.html#a871ff39db627c54c710a3e9909b8234c',1,'jquery.js']]],\n ['extend"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_5.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_5.js",
"chars": 312,
"preview": "var searchData=\n[\n ['files_5f0_2ejs',['files_0.js',['../files__0_8js.html',1,'']]],\n ['files_5f1_2ejs',['files_1.js',["
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_6.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_6.js",
"chars": 216,
"preview": "var searchData=\n[\n ['getxpos',['getXPos',['../search_8js.html#a76d24aea0009f892f8ccc31d941c0a2b',1,'search.js']]],\n ['"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_7.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_7.js",
"chars": 1003,
"preview": "var searchData=\n[\n ['if',['if',['../jquery_8js.html#a9db6d45a025ad692282fe23e69eeba43',1,'if(!b.support.opacity): "
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_8.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_8.js",
"chars": 81,
"preview": "var searchData=\n[\n ['jquery_2ejs',['jquery.js',['../jquery_8js.html',1,'']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_9.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_9.js",
"chars": 106,
"preview": "var searchData=\n[\n ['k',['k',['../jquery_8js.html#ab26645c014aa005ecedef329ecf58c99',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_a.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_a.js",
"chars": 106,
"preview": "var searchData=\n[\n ['l',['L',['../jquery_8js.html#a38ee4c0b5f4fe2a18d0c783af540d253',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_b.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_b.js",
"chars": 720,
"preview": "var searchData=\n[\n ['msp_5fcmn_2eh',['msp_cmn.h',['../msp__cmn_8h.html',1,'']]],\n ['mspgetparam',['MSPGetParam',['../m"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_c.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_c.js",
"chars": 106,
"preview": "var searchData=\n[\n ['p',['p',['../jquery_8js.html#a2335e57f79b6acfb6de59c235dc8a83e',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_d.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_d.js",
"chars": 1750,
"preview": "var searchData=\n[\n ['qisr_2eh',['qisr.h',['../qisr_8h.html',1,'']]],\n ['qisraudiowrite',['QISRAudioWrite',['../qisr_8h"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_e.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_e.js",
"chars": 1183,
"preview": "var searchData=\n[\n ['search_2ejs',['search.js',['../search_8js.html',1,'']]],\n ['searchbox',['SearchBox',['../search_8"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_f.html",
"chars": 995,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/all_f.js",
"chars": 500,
"preview": "var searchData=\n[\n ['togglefolder',['toggleFolder',['../dynsections_8js.html#af244da4527af2d845dca04f5656376cd',1,'dyns"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_0.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_0.js",
"chars": 142,
"preview": "var searchData=\n[\n ['all_5f0_2ejs',['all_0.js',['../all__0_8js.html',1,'']]],\n ['all_5f1_2ejs',['all_1.js',['../all__1"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_1.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_1.js",
"chars": 96,
"preview": "var searchData=\n[\n ['dynsections_2ejs',['dynsections.js',['../dynsections_8js.html',1,'']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_2.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_2.js",
"chars": 312,
"preview": "var searchData=\n[\n ['files_5f0_2ejs',['files_0.js',['../files__0_8js.html',1,'']]],\n ['files_5f1_2ejs',['files_1.js',["
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_3.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_3.js",
"chars": 81,
"preview": "var searchData=\n[\n ['jquery_2ejs',['jquery.js',['../jquery_8js.html',1,'']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_4.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_4.js",
"chars": 84,
"preview": "var searchData=\n[\n ['msp_5fcmn_2eh',['msp_cmn.h',['../msp__cmn_8h.html',1,'']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_5.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_5.js",
"chars": 176,
"preview": "var searchData=\n[\n ['qisr_2eh',['qisr.h',['../qisr_8h.html',1,'']]],\n ['qmfv_2eh',['qmfv.h',['../qmfv_8h.html',1,'']]]"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_6.html",
"chars": 997,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/files_6.js",
"chars": 154,
"preview": "var searchData=\n[\n ['search_2ejs',['search.js',['../search_8js.html',1,'']]],\n ['searchdata_2ejs',['searchdata.js',['."
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_0.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_0.js",
"chars": 106,
"preview": "var searchData=\n[\n ['b',['b',['../jquery_8js.html#a2fa551895933fae935a0a6b87282241d',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_1.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_1.js",
"chars": 236,
"preview": "var searchData=\n[\n ['converttoid',['convertToId',['../search_8js.html#a196a29bd5a5ee7cd5b485e0753a49e57',1,'search.js']"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_2.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_2.js",
"chars": 208,
"preview": "var searchData=\n[\n ['each',['each',['../jquery_8js.html#a871ff39db627c54c710a3e9909b8234c',1,'jquery.js']]],\n ['extend"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_3.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_3.js",
"chars": 216,
"preview": "var searchData=\n[\n ['getxpos',['getXPos',['../search_8js.html#a76d24aea0009f892f8ccc31d941c0a2b',1,'search.js']]],\n ['"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_4.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_4.js",
"chars": 609,
"preview": "var searchData=\n[\n ['if',['if',['../jquery_8js.html#a9db6d45a025ad692282fe23e69eeba43',1,'if(!b.support.opacity): "
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_5.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_5.js",
"chars": 656,
"preview": "var searchData=\n[\n ['mspgetparam',['MSPGetParam',['../msp__cmn_8h.html#a4d3fa0aad5e761cb2a2afe30ae2a9714',1,'msp_cmn.h'"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_6.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_6.js",
"chars": 106,
"preview": "var searchData=\n[\n ['p',['p',['../jquery_8js.html#a2335e57f79b6acfb6de59c235dc8a83e',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_7.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_7.js",
"chars": 1594,
"preview": "var searchData=\n[\n ['qisraudiowrite',['QISRAudioWrite',['../qisr_8h.html#a47fd2588fe834fa2d51fef1961d7aef4',1,'qisr.h']"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_8.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_8.js",
"chars": 450,
"preview": "var searchData=\n[\n ['searchbox',['SearchBox',['../search_8js.html#a52066106482f8136aa9e0ec859e8188f',1,'search.js']]],\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_9.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_9.js",
"chars": 500,
"preview": "var searchData=\n[\n ['togglefolder',['toggleFolder',['../dynsections_8js.html#af244da4527af2d845dca04f5656376cd',1,'dyns"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_a.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/functions_a.js",
"chars": 140,
"preview": "var searchData=\n[\n ['updatestripes',['updateStripes',['../dynsections_8js.html#a8f7493ad859d4fbf2523917511ee7177',1,'dy"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/nomatches.html",
"chars": 454,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/search.css",
"chars": 4461,
"preview": "/*---------------- Search Box */\n\n#FSearchBox {\n float: left;\n}\n\n#MSearchBox {\n white-space : nowrap;\n position"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/search.js",
"chars": 22125,
"preview": "function convertToId(search)\n{\n var result = '';\n for (i=0;i<search.length;i++)\n {\n var c = search.charAt(i);\n "
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/searchdata.js",
"chars": 281,
"preview": "var indexSectionsWithContent =\n{\n 0: \"abcdefgijklmpqstuz\",\n 1: \"adfjmqs\",\n 2: \"bcegimpqstu\",\n 3: \"abciklsz\"\n};\n\nvar "
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_0.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_0.js",
"chars": 548,
"preview": "var searchData=\n[\n ['ad',['aD',['../jquery_8js.html#ad223f5fba68c41c1236671ac5c5b0fcb',1,'jquery.js']]],\n ['am',['aM',"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_1.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_1.js",
"chars": 370,
"preview": "var searchData=\n[\n ['b',['b',['../jquery_8js.html#aa4026ad5544b958e54ce5e106fa1c805',1,'jquery.js']]],\n ['bb',['bb',['"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_2.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_2.js",
"chars": 292,
"preview": "var searchData=\n[\n ['c',['c',['../jquery_8js.html#abce695e0af988ece0826d9ad59b8160d',1,'jquery.js']]],\n ['css',['css',"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_3.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_3.js",
"chars": 414,
"preview": "var searchData=\n[\n ['indexsectionlabels',['indexSectionLabels',['../searchdata_8js.html#a529972e449c82dc118cbbd3bcf50c4"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_4.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_4.js",
"chars": 106,
"preview": "var searchData=\n[\n ['k',['k',['../jquery_8js.html#ab26645c014aa005ecedef329ecf58c99',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_5.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_5.js",
"chars": 106,
"preview": "var searchData=\n[\n ['l',['L',['../jquery_8js.html#a38ee4c0b5f4fe2a18d0c783af540d253',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_6.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_6.js",
"chars": 619,
"preview": "var searchData=\n[\n ['searchdata',['searchData',['../all__0_8js.html#ad01a7523f103d6242ef9b0451861231e',1,'searchData():"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_7.html",
"chars": 1001,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search/variables_7.js",
"chars": 106,
"preview": "var searchData=\n[\n ['z',['Z',['../jquery_8js.html#adc18d83abfd9f87d396e8fd6b6ac0fe1',1,'jquery.js']]]\n];\n"
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/search_8js.html",
"chars": 12413,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/searchdata_8js.html",
"chars": 7247,
"preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
},
{
"path": "xf/doc/iFlytek MSC Reference Manual/tabs.css",
"chars": 1163,
"preview": ".tabs, .tabs2, .tabs3 {\n background-image: url('tab_b.png');\n width: 100%;\n z-index: 101;\n font-size: 13px;\n"
},
{
"path": "xf/doc/tts_sample/32bit_make.sh",
"chars": 92,
"preview": "#编译32位可执行文件\nmake clean;make\n#设置libmsc.so库搜索路径\nexport LD_LIBRARY_PATH=$(pwd)/../../libs/x86/\n"
},
{
"path": "xf/doc/tts_sample/64bit_make.sh",
"chars": 102,
"preview": "#编译64位可执行文件\nmake clean;make LINUX64=1\n#设置libmsc.so库搜索路径\nexport LD_LIBRARY_PATH=$(pwd)/../../libs/x64/\n"
},
{
"path": "xf/doc/tts_sample/Makefile",
"chars": 597,
"preview": "#common makefile header\r\n\r\nDIR_INC = ../../include\r\nDIR_BIN = ../../bin\r\nDIR_LIB = ../../libs\r\n\r\nTARGET\t= tts_sample\r\nBI"
},
{
"path": "xf/doc/tts_sample/tts_sample.c",
"chars": 5214,
"preview": "/*\r\n* 语音合成(Text To Speech,TTS)技术能够自动将任意文字实时转换为连续的\r\n* 自然语音,是一种能够在任何时间、任何地点,向任何人提供语音信息服务的\r\n* 高效便捷手段,非常符合信息时代海量数据、动态更新和个性化"
},
{
"path": "xf/include/convert.h",
"chars": 3132,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/time.h>\n\n#include \"qtts.h\"\n"
},
{
"path": "xf/include/msp_cmn.h",
"chars": 11543,
"preview": "/**\r\n * @file msp_cmn.h\r\n * @brief Mobile Speech Platform Common Interface Header File\r\n * \r\n * This file contains"
},
{
"path": "xf/include/msp_errors.h",
"chars": 33304,
"preview": "#ifndef __MSP_ERRORS_H__\r\n#define __MSP_ERRORS_H__\r\n\r\n#define MSP_HTTP_ERROR(x) ((x) + MSP_ERROR_HTTP_BASE )\r\n\r\nenum\r\n{\r"
},
{
"path": "xf/include/msp_types.h",
"chars": 4309,
"preview": "#ifndef __MSP_TYPES_H__\r\n#define __MSP_TYPES_H__\r\n\r\n#if !defined(MSPAPI)\r\n#if defined(WIN32) || defined(WINPHONE8) || de"
},
{
"path": "xf/include/qtts.h",
"chars": 8090,
"preview": "/** \n * @file\tqtts.h\n * @brief iFLY Speech Synthesizer Header File\r\n * \r\n * This file contains the quick application "
},
{
"path": "xf/xf.go",
"chars": 1235,
"preview": "package xf\n\n/*\n\n#cgo CFLAGS:-g -Wall -I./include\n#cgo LDFLAGS:-L./lib -lmsc -lrt -ldl -lpthread\n\n#include \"convert.h\"\n\n*"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the imroc/ontts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 168 files (952.7 KB), approximately 328.2k tokens, and a symbol index with 367 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.